Setting up MPU and recovering from memfaults  - Page 1
Forum section: electronics / general

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 04, 2019, 12:04:04 pm
content: Hi,Here is the thing, in the cortex M manual said that memfaults are faults and they can not be recovered, the only way is system resetHere is the thing, I have setup some example, I just want to be able to continue normal program execution and just disable some access by some system task to a memory location, for example 0x20080000,Here is my sample code on LPC1768Code:#include "LPC17xx.h" // Device headerint main(void){int i=0;static int test;i = MPU->TYPE;MPU->RNR = 0;//indicate MPU region 0MPU->RBAR = 0x00000000; // update the base address for the region 0MPU->RASR = 0x03070025;MPU->RNR = 1;MPU->RBAR = 0x10000000; // update the base address for the region 1MPU->RASR = 0x0307001D;MPU->RNR = 2;MPU->RBAR = 0x40000000; // update the base address for the region 2MPU->RASR = 0x03050027;MPU->RNR = 3;MPU->RBAR = 0x50000000; // update the base address for the region 3MPU->RASR = 0x03050029;MPU->RNR = 4;MPU->RBAR = 0xE0000000; // update the base address for the region 4MPU->RASR = 0x03070027;MPU->RNR = 5;MPU->RBAR = 0x2009C000; // update the base address for the region 5MPU->RASR = 0x0307001B;MPU->RNR = 6;MPU->RBAR = 0x20080000; // update the base address for the region 6MPU->RASR = 0x0007001B;SCB->SHCSR |=(1<<16); //Enable Memory management faultMPU->CTRL |= 1;//enable MPUtest = (*(unsigned int *)0x20080000);while(1){i++;}}__asm void pupStacktorecover ();int value;void HardFault_Handler (void){int r1;value=r1;pupStacktorecover ();}__asm void pupStacktorecover(){pop {r0,r1,r2,r3,lr}}void MemManage_Handler(void){int j;j++;__ pupStacktorecover ();}The thing is, that after memfault, the system traps there, so I have added stack pop function to recover the last stack before the memfault, but now my system resets, do you have any idea?

author: mikerj
author_rank: Super Contributor
timestamp: January 04, 2019, 01:17:51 pm
content: Why are you getting memory faults to start with? You need to fix that. How can you continue execution reliably after an access violation, you don't know what has been corrupted at that point so further exceptions are very likely.

author: TomS_
author_rank: Frequent Contributor
timestamp: January 04, 2019, 01:31:32 pm
content: Quote from: ali_asadzadeh on January 04, 2019, 12:04:04 pmDoes the stack only contain return addresses, or does it also contain application data? Popping data off the stack assuming it is a return address sounds fraught with disaster...

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 04, 2019, 02:03:53 pm
content: Quoteit's a simple test for now, it's in the sample code.Code:test = (*(unsigned int *)0x20080000);nothing has been corrupted

author: andersm
author_rank: Super Contributor
timestamp: January 04, 2019, 02:49:01 pm
content: Make sure you haven't disabled access to memory regions you actually need. I would simplify the setup by enabling the background region, or defining one region covering the whole address space allowing all access, and then just defining one region blocking access to a specific memory range.I don't understand why you're popping part of the exception frame off the stack. It isn't going to "recover" anything, quite the opposite, as the CPU will try restoring the frame when exiting the handler, and will end up with garbage in the registers.Edit: As a sidenote, the protection region size and alignment restrictions means the ARMv7-M MPU is all but unusable in practice. This was much improved in the ARMv8-M architecture, so it might be more useful to look at Cortex-M33 devices instead.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 05, 2019, 11:16:28 am
content: QuoteThanks, I must do it in M4 for now, I have managed to trap the memfault,and successfully run to the next instruction, the problem for now is that if I try to use it more than once, the next time I access the violating memory, it would generate hardfault!!!Code:#include "LPC43xx.h" // Device headerint i=0;int main(void){static int test;MPU->RNR = 0;//indicate MPU region 0MPU->RBAR = 0x00000000; // update the base address for the region 0MPU->RASR = 0x0307003F;test = MPU->RASR;MPU->RNR = 6;MPU->RBAR = 0x20080000; // update the base address for the region 6MPU->RASR = 0x0007001B;i = MPU->RASR;SCB->SHCSR |=(1<<16); //Enable Memory management faultMPU->CTRL |= 1;//enable MPUfor(i=0;i<10;i++){test = (*(unsigned int *)0x20080000);}while(1){i++;}}__asm void __setPsp();int value;void HardFault_Handler (void){int r1;value=r1;__setPsp();}__asm void __setPsp(){add r13,32ldr r14,add r14,#2mov r0,#0MSR IPSR,r0mov r15,r14}void MemManage_Handler(void){__setPsp();}Isn't it suppose to generate memfault again?

author: abyrvalg
author_rank: Frequent Contributor
timestamp: January 06, 2019, 11:10:03 am
content: You don’t need that complicated return sequence. Just do normal return instead (jumping to that strange value passed in LR initiates an "exception unstacking" - the CPU will restore saved state from stack and jump to saved PC value on its own, no stack manipulations needed). To skip the problematic instruction you need to modify the saved PC right in the stack before return (read it from there, add 2, write back).An example of accessing the saved context:https://www.silabs.com/community/mcu/32-bit/knowledge-base.entry.html/2014/05/26/debug_a_hardfault-78gcIt’s about HardFault there, but the idea is similar. So do something like this:Code:__attribute__( (naked) )void MemManage_Handler(void){__asm volatile("tst lr, #4 \n""ite eq \n""mrseq r0, msp \n""mrsne r0, psp \n""ldr r1, myMemfault_address \n""bx r1 \n""myMemfault_address: .word myMemfault \n");}void myMemfault(uint32_t *sp){sp+=2; //skip current instruction}Edit: the reason why you get a HardFault instead of MemManage next time: by not doing a correct exception unstacking your main code continues in exception context, so a second MemFault looks like a fault in exception handler, causing HardFault.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 06, 2019, 01:53:25 pm
content: QuoteReally thanks for the feedback, I have tried it in keil, but there are a few warning and errors,First of all keil doesn't recognize nakedthey claimed it in keil website! what should I do about it?Secondly, it does not recognize this asm syntax in keil, how should I do it in here? using a separate function?Code:__attribute__( (naked) )void MemManage_Handler(void){__asm volatile("tst lr, #4 \n""ite eq \n""mrseq r0, msp \n""mrsne r0, psp \n""ldr r1, myMemfault_address \n""bx r1 \n""myMemfault_address: .word myMemfault \n");}

author: andersm
author_rank: Super Contributor
timestamp: January 06, 2019, 02:48:07 pm
content: Quote from: abyrvalg on January 06, 2019, 11:10:03 amIf you really wanted to do that, you'd have to determine whether the PC was pointing to a 2- or 4-byte instruction. However, just continuing execution would leave the program in an invalid state, so it's not a useful thing to do. The only thing you can safely do is to report the error and restart.

author: Jeroen3
author_rank: Super Contributor
timestamp: January 06, 2019, 03:04:46 pm
content: The only way to restore from a hardfault is to do a reset. Unless you can determine with 100% certainty the fault was software generated, which it doesn't have to be.You don't have to do a cold boot after a reset from hardfault, so if you trust the sram content, you should be able to do a hot reboot. Peripheral states are lost though.What you can do, is enable the other exceptions, such as MPU, and handle them by quitting the offensivethread. Obviously there is something wrong with the code.You can't continue normal execution of code that throws faults, since the code will not be changed by returning. It will just throw the fault again.You could skip the instruction causing faults, but what will the code do then?An alternative is dynamically changing the MPU allowing the thread to access what it faulted on. But that defeats the purpose of the MPU imho.

author: andersm
author_rank: Super Contributor
timestamp: January 06, 2019, 03:28:03 pm
content: Quote from: Jeroen3 on January 06, 2019, 03:04:46 pmIf you take that approach, you have to design the application carefully so that a fault can never cause invalid state. Eg. code like this is dangerous if the executing thread can just die inside the critical section.Code:lock();sharedvar = *validpointer;othersharedvar = *invalidpointer;unlock();It is not impossible to work around this, but it can be tricky to find and analyze all hazards. For example, something like this is a bit safer than the above:Code:temp1 = *validpointer;temp2 = *invalidpointer;lock();sharedvar = temp1;othersharedvar = temp2;unlock();The code will still crash, but at least there's no risk of corrupting the shared state.

author: Jeroen3
author_rank: Super Contributor
timestamp: January 06, 2019, 04:27:28 pm
content: Off course, when a certain thread is killed and leaves some device in an invalid state, you get an error.It's common to have a kernel panic or BSOD. Basically, nobody tries to recover from these kind of faults without reboot.Simply becauSegmentation fault

author: abyrvalg
author_rank: Frequent Contributor
timestamp: January 06, 2019, 08:25:06 pm
content: A couple of things to prevent further derailing:- this thread is NOT about HardFault, so all those scarecrows like "unpredictable core state" are irrelevant.- the OP didn’t stated this clear here, but AFAIK his final goal is to create a virtual peripheral, so he wants those MemManage faults to happenFound an example for RVCT/Keil syntax:Code:__asm void MemManage_Handler(void){TST lr, #4 // Test for MSP or PSPITE EQMRSEQ r0, MSPMRSNE r0, PSPB __cpp(myMemfault)}

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 07, 2019, 12:38:23 pm
content: QuoteReally thanks for the helpQuoteExactlyQuoteQuotePlease do not panic, because my code is generating memfault deliberatelyas I have shown in the sample code

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 09, 2019, 01:52:58 pm
content: I have don it successfully in the keil simulator and I was able to distinguish between the LDR and STR instructions too, Now when I try it on the LPC1768 or LPC4337 the MPU simply would not catch the mem access faults, and after stepping into the instruction it would pass it!!! and if it hit another instruction with illegal address it just make a hardfaultHere is the setup of MPU for the LPC4337,Code:MPU->RNR = 0;//indicate MPU region 0MPU->RBAR = 0x00000000; // update the base address for the region 0MPU->RASR = 0x0307003F;//4GB free accessMPU->RNR = 6;//trap for 0x40023000 range addressMPU->RBAR = 0x40023000; // update the base address for the region 6MPU->RASR = 0x0007001B;Any Idea why in the hardware the MPU would not act as it should do?

author: abyrvalg
author_rank: Frequent Contributor
timestamp: January 09, 2019, 09:52:19 pm
content: Try inspecting the fault status registers (SHCSR, CFSR) after the first stepping into the "bad" instruction - there can be some clue.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 12, 2019, 11:47:48 am
content: Thanks, Now I managed to solve it for now, The problem is now I had to determine LDR and STR and the destination register for that instructions,I have used some simple methods to detect LDR and STR like this,Code:volatile uint32_t inst = (*(unsigned int *)sp) & 0xFFFF;if(inst == 0x6088){//it's an STR instrcution}if(inst == 0x680A){//it's an LDR}The problem with this approach is that every Reg has a different opcode, so I should write all the cases that would slow down the thingsAlso I have set the return answer using this methodCode:sp = calculated value;If the used address in some other parts of the code changes and uses another register, it would fail,So the question is how to detect LDR and STR instruction with the used Register efficiently, do we have a better way?

author: nctnico
author_rank: Super Contributor
timestamp: January 12, 2019, 02:31:39 pm
content: If you look in the instruction set then you'll see that the registers are specific bits of the opcode. If you mask these out then you should be able to determine the type of instruction using one AND operation and an IF statement.Still I'm wondering why you are trying to create a virtual peripheral. Wouldn't having an API to a set of function be quicker? An interrupt has a lot of overhead. Even if this is to offer a form of platform independence an API will likely be more efficient for device with or without the peripheral.

author: ali_asadzadeh
author_rank: Super Contributor
timestamp: January 12, 2019, 06:44:49 pm
content: QuoteThe problem is That There is no opcode in ARM processors datasheets, do you know where I can find them?

author: nctnico
author_rank: Super Contributor
timestamp: January 12, 2019, 06:51:41 pm
content: Ask Google. It is probably somewhere on ARM's website for sure.

author: Jeroen3
author_rank: Super Contributor
timestamp: January 14, 2019, 09:15:04 am
content: Topic: STM32F0 /F1 /F4 Opcode List?