//***************************************************************************** // // mpu_fault.c - MPU example. // // Copyright (c) 2013-2014 Texas Instruments Incorporated. All rights reserved. // Software License Agreement // // Texas Instruments (TI) is supplying this software for use solely and // exclusively on TI's microcontroller products. The software is owned by // TI and/or its suppliers, and is protected under applicable copyright // laws. You may not combine this software with "viral" open-source // software in order to form a larger program. // // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL // DAMAGES, FOR ANY REASON WHATSOEVER. // // This is part of revision 2.1.0.12573 of the DK-TM4C129X Firmware Package. // //***************************************************************************** #include #include #include "inc/hw_ints.h" #include "inc/hw_memmap.h" #include "inc/hw_nvic.h" #include "inc/hw_types.h" #include "driverlib/interrupt.h" #include "driverlib/mpu.h" #include "driverlib/sysctl.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" #include "grlib/grlib.h" #include "drivers/frame.h" #include "drivers/kentec320x240x16_ssd2119.h" #include "drivers/pinout.h" //***************************************************************************** // //! \addtogroup example_list //!

MPU (mpu_fault)

//! //! This example application demonstrates the use of the MPU to protect a //! region of memory from access, and to generate a memory management fault //! when there is an access violation. //! //***************************************************************************** //***************************************************************************** // // Variables to hold the state of the fault status when the fault occurs and // the faulting address. // //***************************************************************************** static volatile uint32_t g_ui32MMAR; static volatile uint32_t g_ui32FaultStatus; //***************************************************************************** // // A counter to track the number of times the fault handler has been entered. // //***************************************************************************** static volatile uint32_t g_ui32MPUFaultCount; //***************************************************************************** // // A location for storing data read from various addresses. Volatile forces // the compiler to use it and not optimize the access away. // //***************************************************************************** static volatile uint32_t g_ui32Value; //***************************************************************************** // // Graphics context used to show text on the CSTN display. // //***************************************************************************** tContext g_sContext; //***************************************************************************** // // The error routine that is called if the driver library encounters an error. // //***************************************************************************** #ifdef DEBUG void __error__(char *pcFilename, uint32_t ui32Line) { } #endif //***************************************************************************** // // A simple function to perform a write using a 16-bit instruction, allowing // for easy fix-up in the mpu fault handler. // //***************************************************************************** #if defined(gcc) || defined(sourcerygxx) void __attribute__((naked)) Write(uint32_t ui32Addr, uint32_t ui32Data) { __asm(" str r1, [r0]\n" " bx lr"); } uint32_t __attribute__((naked)) Read(uint32_t ui32Offset) { __asm(" ldr r0, [r0]\n" " bx lr"); // // This shouldn't be needed but it keeps the compiler from generating // a warning. // return(0); } #endif #if defined(rvmdk) __asm void Write(uint32_t ui32Addr, uint32_t ui32Data) { str r1, [r0]; bx lr; } __asm uint32_t Read(uint32_t ui32Offset) { ldr r0, [r0]; bx lr; } #endif #if defined(ewarm) void Write(uint32_t ui32Addr, uint32_t ui32Data) { __asm("str r1, [r0]\n" "bx lr"); } uint32_t Read(uint32_t ui32Offset) { __asm("ldr r0, [r0]\n" "bx lr\n"); #pragma diag_suppress=Pe940 } #pragma diag_default=Pe940 #endif #if defined(ccs) __asm(" .sect \".text\"\n" " .clink\n" " .thumbfunc Write\n" " .thumb\n" " .global Write\n" "Write:\n" " str r1, [r0]\n" " bx lr\n"); __asm(" .sect \".text\"\n" " .clink\n" " .thumbfunc Read\n" " .thumb\n" " .global Read\n" "Read:\n" " ldr r0, [r0]\n" " bx lr\n"); void Write(uint32_t ui32Addr, uint32_t ui32Data); uint32_t Read(uint32_t ui32Offset); #endif //***************************************************************************** // // The exception handler for memory management faults, which are caused by MPU // access violations. This handler will verify the cause of the fault and // clear the NVIC fault status register. // //***************************************************************************** void MPUFaultHandler(void) { // // Preserve the value of the MMAR (the address causing the fault). // Preserve the fault status register value, then clear it. // g_ui32MMAR = HWREG(NVIC_MM_ADDR); g_ui32FaultStatus = HWREG(NVIC_FAULT_STAT); HWREG(NVIC_FAULT_STAT) = g_ui32FaultStatus; // // Increment a counter to indicate the fault occurred. // g_ui32MPUFaultCount++; // // How the MPU fault is handled is application dependent... // In this sample code, we are skipping the faulted instruction and // continuing through the application. Since we are forcing the Read // and Write function to use 16bits instructions only, it is safe to // add 2 to the faulted instruction address to get the next // instruction address. // #if defined(rvmdk) { uint32_t puiFaultPC; puiFaultPC = __current_sp() + 0x18; *(uint32_t*)puiFaultPC = *(uint32_t*)puiFaultPC + 2; } #else __asm(" mov r0, sp\n" " ldr r1, [r0, #0x18]\n" " adds r1, #2\n" " str r1, [r0, #0x18]"); #endif } //***************************************************************************** // // This example demonstrates how to configure MPU regions for different levels // of memory protection. The following memory map is set up: // // 0000.0000 - 0000.7000 - rgn 0: executable read-only, flash // 0000.7000 - 0000.8000 - rgn 0: no access, flash (disabled sub-region 7) // 2000.0000 - 2000.8000 - rgn 1: read-write, RAM // 2000.8000 - 2000.A000 - rgn 2: read-only, RAM (disabled sub-rgn 4 of rgn 1) // 2000.A000 - 2000.FFFF - rgn 1: read-write, RAM // 4000.0000 - 4001.0000 - rgn 3: read-write, peripherals // 4001.0000 - 4002.0000 - rgn 3: no access (disabled sub-region 1) // 4002.0000 - 4006.0000 - rgn 3: read-write, peripherals // 4006.0000 - 4008.0000 - rgn 3: no access (disabled sub-region 6, 7) // 4400.0000 - 4403.0000 - rgn 4: no access (disabled sub-region 0, 1, 2) // 4403.0000 - 4404.0000 - rgn 4: read-write, peripherals (sub-region 3) // 4404.0000 - 4405.0000 - rgn 4: no access (disabled sub-region 4) // 4405.0000 - 4406.0000 - rgn 4: read-write, peripherals (sub-region 5) // 4406.0000 - 4408.0000 - rgn 4: no access (disabled sub-region 6, 7) // E000.E000 - E000.F000 - rgn 5: read-write, NVIC // 2003.8000 - 2003.FFFF - rgn 6: read-write, upper 32K RAM // 0100.0000 - 0100.FFFF - rgn 7: executable read-only, ROM // // The example code will attempt to perform the following operations and check // the faulting behavior: // // - write to flash (should fault) // - read from the disabled area of flash (should fault) // - read from the read-only area of RAM (should not fault) // - write to the read-only section of RAM (should fault) // //***************************************************************************** int main(void) { bool bFail = 0; uint32_t ui32SysClock; // // Run from the PLL at 120 MHz. // ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000); // // Configure the device pins. // PinoutSet(); // // Initialize the display driver. // Kentec320x240x16_SSD2119Init(ui32SysClock); // // Initialize the graphics context. // GrContextInit(&g_sContext, &g_sKentec320x240x16_SSD2119); // // Draw the application frame. // FrameDraw(&g_sContext, "mpu-fault"); // // Configure an executable, read-only MPU region for flash. It is a 32 KB // region with the last 4 KB disabled to result in a 28 KB executable // region. This region is needed so that the program can execute from // flash. // ROM_MPURegionSet(0, FLASH_BASE, MPU_RGN_SIZE_32K | MPU_RGN_PERM_EXEC | MPU_RGN_PERM_PRV_RO_USR_RO | MPU_SUB_RGN_DISABLE_7 | MPU_RGN_ENABLE); // // Configure a read-write MPU region for RAM. It is a 64 KB region. There // is a 8 KB sub-region in the middle that is disabled in order to open up // a hole in which different permissions can be applied. // ROM_MPURegionSet(1, SRAM_BASE, MPU_RGN_SIZE_64K | MPU_RGN_PERM_NOEXEC | MPU_RGN_PERM_PRV_RW_USR_RW | MPU_SUB_RGN_DISABLE_4 | MPU_RGN_ENABLE); // // Configure a read-only MPU region for the 8 KB of RAM that is disabled in // the previous region. This region is used for demonstrating read-only // permissions. // ROM_MPURegionSet(2, SRAM_BASE + 0x8000, MPU_RGN_SIZE_8K | MPU_RGN_PERM_NOEXEC | MPU_RGN_PERM_PRV_RO_USR_RO | MPU_RGN_ENABLE); // // Configure a read-write MPU region for peripherals. The region is 512 KB // total size, with several sub-regions disabled to prevent access to areas // where there are no peripherals. This region is needed because the // program needs access to some peripherals. // ROM_MPURegionSet(3, 0x40000000, MPU_RGN_SIZE_512K | MPU_RGN_PERM_NOEXEC | MPU_RGN_PERM_PRV_RW_USR_RW | MPU_SUB_RGN_DISABLE_1 | MPU_SUB_RGN_DISABLE_6 | MPU_SUB_RGN_DISABLE_7 | MPU_RGN_ENABLE); // // Configure a read-write MPU region for peripherals. The region is 512 KB // total size, with several sub-regions disabled to prevent access to areas // where there are no peripherals. This region is needed because the // program needs access to some peripherals. // ROM_MPURegionSet(4, 0x44000000, MPU_RGN_SIZE_512K | MPU_RGN_PERM_NOEXEC | MPU_RGN_PERM_PRV_RW_USR_RW | MPU_SUB_RGN_DISABLE_0 | MPU_SUB_RGN_DISABLE_1 | MPU_SUB_RGN_DISABLE_2 | MPU_SUB_RGN_DISABLE_4 | MPU_SUB_RGN_DISABLE_6 | MPU_SUB_RGN_DISABLE_7 | MPU_RGN_ENABLE); // // Configure a read-write MPU region for access to the NVIC. The region is // 4 KB in size. This region is needed because NVIC registers are needed // in order to control the MPU. // ROM_MPURegionSet(5, NVIC_BASE, MPU_RGN_SIZE_4K | MPU_RGN_PERM_NOEXEC | MPU_RGN_PERM_PRV_RW_USR_RW | MPU_RGN_ENABLE); // // Configure a read-write MPU region for the top 32KB of RAM. // This region is used as stack for Sourcery CodeBench. // ROM_MPURegionSet(6, SRAM_BASE + (ROM_SysCtlSRAMSizeGet() - (32 * 1024)), MPU_RGN_SIZE_32K | MPU_RGN_PERM_NOEXEC | MPU_RGN_PERM_PRV_RW_USR_RW | MPU_RGN_ENABLE); // // Configure an executable, read-only MPU region for ROM. It is a 64 KB // region. This region is needed so that ROM library calls work. // ROM_MPURegionSet(7, 0x01000000, MPU_RGN_SIZE_64K | MPU_RGN_PERM_EXEC | MPU_RGN_PERM_PRV_RO_USR_RO | MPU_RGN_ENABLE); // // Need to clear the NVIC fault status register to make sure there is no // status hanging around from a previous program. // g_ui32FaultStatus = HWREG(NVIC_FAULT_STAT); HWREG(NVIC_FAULT_STAT) = g_ui32FaultStatus; // // Enable the MPU fault. // ROM_IntEnable(FAULT_MPU); // // Enable the MPU. This will begin to enforce the memory protection // regions. The MPU is configured so that when in the hard fault or NMI // exceptions, a default map will be used. Neither of these should occur // in this example program. // ROM_MPUEnable(MPU_CONFIG_HARDFLT_NMI); // // Attempt to write to the flash. This should cause a protection fault due // to the fact that this region is read-only. // GrContextFontSet(&g_sContext, g_psFontCm20); GrStringDraw(&g_sContext, "Check flash write", -1, 10, 60, 0); g_ui32MPUFaultCount = 0; Write(0x100, 0x12345678); // // Verify that the fault occurred, at the expected address. // if((g_ui32MPUFaultCount == 1) && (g_ui32FaultStatus == 0x82) && (g_ui32MMAR == 0x100)) { GrStringDraw(&g_sContext, " OK", -1, 210, 60, 0); } else { bFail = 1; GrStringDraw(&g_sContext, "NOK", -1, 210, 60, 0); } // // Attempt to read from the disabled section of flash, the upper 4 KB of // the 32 KB region. // GrStringDraw(&g_sContext, "Check flash read", -1, 10, 85, 0); g_ui32MPUFaultCount = 0; g_ui32Value = Read(0x7820); // // Verify that the fault occurred, at the expected address. // if((g_ui32MPUFaultCount == 1) && (g_ui32FaultStatus == 0x82) && (g_ui32MMAR == 0x7820)) { GrStringDraw(&g_sContext, " OK", -1, 210, 85, 0); } else { bFail = 1; GrStringDraw(&g_sContext, "NOK", -1, 210, 85, 0); } // // Attempt to read from the read-only area of RAM, the middle 8 KB of the // 64 KB region. // GrStringDraw(&g_sContext, "Check RAM read", -1, 10, 110, 0); g_ui32MPUFaultCount = 0; g_ui32Value = Read(0x20008440); // // Verify that the RAM read did not cause a fault. // if(g_ui32MPUFaultCount == 0) { GrStringDraw(&g_sContext, " OK", -1, 210, 110, 0); } else { bFail = 1; GrStringDraw(&g_sContext, "NOK", -1, 210, 110, 0); } // // Attempt to write to the read-only area of RAM, the middle 8 KB of the // 64 KB region. // GrStringDraw(&g_sContext, "Check RAM write", -1, 10, 135, 0); g_ui32MPUFaultCount = 0; Write(0x20008460, 0xabcdef00); // // Verify that the RAM write caused a fault. // if((g_ui32MPUFaultCount == 1) && (g_ui32FaultStatus == 0x82) && (g_ui32MMAR == 0x20008460)) { GrStringDraw(&g_sContext, " OK", -1, 210, 135, 0); } else { bFail = 1; GrStringDraw(&g_sContext, "NOK", -1, 210, 135, 0); } // // Display the results of the example program. // if(bFail) { GrStringDrawCentered(&g_sContext, "Failure!", -1, GrContextDpyWidthGet(&g_sContext) / 2, 220, 0); } else { GrStringDrawCentered(&g_sContext, "Success!", -1, GrContextDpyWidthGet(&g_sContext) / 2, 220, 0); } // // Disable the MPU, so there are no lingering side effects if another // program is run. // ROM_MPUDisable(); // // Loop forever. // while(1) { } }