Author Topic: Setting up MPU and recovering from memfaults  (Read 3460 times)

0 Members and 1 Guest are viewing this topic.

Offline ali_asadzadehTopic starter

  • Super Contributor
  • ***
  • Posts: 1902
  • Country: ca
Setting up MPU and recovering from memfaults
« on: January 04, 2019, 12:04:04 pm »
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 reset >:(

Here 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 LPC1768


Code: [Select]
#include "LPC17xx.h"                    // Device header

int main(void)
{
    int i=0;
    static int test;

         i = MPU->TYPE;
MPU->RNR = 0;//indicate MPU region 0
MPU->RBAR = 0x00000000; // update the base address for the region 0
MPU->RASR = 0x03070025;
MPU->RNR = 1;
MPU->RBAR = 0x10000000; // update the base address for the region 1
MPU->RASR = 0x0307001D;
MPU->RNR = 2;
MPU->RBAR = 0x40000000; // update the base address for the region 2
MPU->RASR = 0x03050027;
MPU->RNR = 3;
MPU->RBAR = 0x50000000; // update the base address for the region 3
MPU->RASR = 0x03050029;
MPU->RNR = 4;
MPU->RBAR = 0xE0000000; // update the base address for the region 4
MPU->RASR = 0x03070027;
MPU->RNR = 5;
MPU->RBAR = 0x2009C000; // update the base address for the region 5
MPU->RASR = 0x0307001B;
MPU->RNR = 6;
MPU->RBAR = 0x20080000; // update the base address for the region 6
MPU->RASR = 0x0007001B;

        SCB->SHCSR |=(1<<16); //Enable Memory management fault 
MPU->CTRL |= 1;//enable MPU


test = (*(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?
« Last Edit: January 04, 2019, 12:05:58 pm by ali_asadzadeh »
ASiDesigner, Stands for Application specific intelligent devices
I'm a Digital Expert from 8-bits to 64-bits
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3238
  • Country: gb
Re: Setting up MPU and recovering from memfaults
« Reply #1 on: January 04, 2019, 01:17:51 pm »
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.
 

Offline TomS_

  • Frequent Contributor
  • **
  • Posts: 834
  • Country: gb
Re: Setting up MPU and recovering from memfaults
« Reply #2 on: January 04, 2019, 01:31:32 pm »
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?

Does 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...
 

Offline ali_asadzadehTopic starter

  • Super Contributor
  • ***
  • Posts: 1902
  • Country: ca
Re: Setting up MPU and recovering from memfaults
« Reply #3 on: January 04, 2019, 02:03:53 pm »
Quote
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.
it's a simple test for now, it's in the sample code.

   
Code: [Select]
test = (*(unsigned int *)0x20080000);nothing has been corrupted ;)
ASiDesigner, Stands for Application specific intelligent devices
I'm a Digital Expert from 8-bits to 64-bits
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: Setting up MPU and recovering from memfaults
« Reply #4 on: January 04, 2019, 02:49:01 pm »
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.
« Last Edit: January 04, 2019, 03:32:22 pm by andersm »
 

Offline ali_asadzadehTopic starter

  • Super Contributor
  • ***
  • Posts: 1902
  • Country: ca
Re: Setting up MPU and recovering from memfaults
« Reply #5 on: January 05, 2019, 11:16:28 am »
Quote
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.
Thanks, 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: [Select]
#include "LPC43xx.h"                    // Device header

int i=0;
int main(void)
{
    static int test;

        MPU->RNR = 0;//indicate MPU region 0
        MPU->RBAR = 0x00000000; // update the base address for the region 0
        MPU->RASR = 0x0307003F;
        test = MPU->RASR;

        MPU->RNR = 6;
        MPU->RBAR = 0x20080000; // update the base address for the region 6
        MPU->RASR = 0x0007001B;

        i = MPU->RASR;

        SCB->SHCSR |=(1<<16); //Enable Memory management fault
        MPU->CTRL |= 1;//enable MPU

    for(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,32
        ldr r14,[r13]
        add r14,#2
        mov r0,#0
        MSR IPSR,r0
        mov r15,r14
}

void MemManage_Handler(void)
{
        __setPsp();
}

Isn't it suppose to generate memfault again?
ASiDesigner, Stands for Application specific intelligent devices
I'm a Digital Expert from 8-bits to 64-bits
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 824
  • Country: es
Re: Setting up MPU and recovering from memfaults
« Reply #6 on: January 06, 2019, 11:10:03 am »
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-78gc
It’s about HardFault there, but the idea is similar. So do something like this:
Code: [Select]
__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[6]+=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.
« Last Edit: January 06, 2019, 12:43:31 pm by abyrvalg »
 

Offline ali_asadzadehTopic starter

  • Super Contributor
  • ***
  • Posts: 1902
  • Country: ca
Re: Setting up MPU and recovering from memfaults
« Reply #7 on: January 06, 2019, 01:53:25 pm »
Quote
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).

Really 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 naked |O |O they 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: [Select]
__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"
    );
}
ASiDesigner, Stands for Application specific intelligent devices
I'm a Digital Expert from 8-bits to 64-bits
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: Setting up MPU and recovering from memfaults
« Reply #8 on: January 06, 2019, 02:48:07 pm »
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).
If 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.

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Setting up MPU and recovering from memfaults
« Reply #9 on: January 06, 2019, 03:04:46 pm »
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 offensive thread. 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.
« Last Edit: January 06, 2019, 03:06:22 pm by Jeroen3 »
 

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1198
  • Country: fi
Re: Setting up MPU and recovering from memfaults
« Reply #10 on: January 06, 2019, 03:28:03 pm »
What you can do, is enable the other exceptions, such as MPU, and handle them by quitting the offensive thread.
If 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: [Select]
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: [Select]
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.

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Setting up MPU and recovering from memfaults
« Reply #11 on: January 06, 2019, 04:27:28 pm »
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 becau
Segmentation fault
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 824
  • Country: es
Re: Setting up MPU and recovering from memfaults
« Reply #12 on: January 06, 2019, 08:25:06 pm »
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 happen :)

Found an example for RVCT/Keil syntax:
Code: [Select]
__asm void MemManage_Handler(void)
{
  TST lr, #4     // Test for MSP or PSP
  ITE EQ
  MRSEQ r0, MSP
  MRSNE r0, PSP
  B __cpp(myMemfault)
}
 

Offline ali_asadzadehTopic starter

  • Super Contributor
  • ***
  • Posts: 1902
  • Country: ca
Re: Setting up MPU and recovering from memfaults
« Reply #13 on: January 07, 2019, 12:38:23 pm »
Quote
A couple of things to prevent further derailing:
Really thanks for the help :-+ :-+ :-+

Quote
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 happen :)
Exactly ;) :)



Quote
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 becau
Segmentation fault

Quote
The code will still crash, but at least there's no risk of corrupting the shared state.

Please do not panic, because my code is generating memfault deliberately :D as I have shown in the sample code
ASiDesigner, Stands for Application specific intelligent devices
I'm a Digital Expert from 8-bits to 64-bits
 

Offline ali_asadzadehTopic starter

  • Super Contributor
  • ***
  • Posts: 1902
  • Country: ca
Re: Setting up MPU and recovering from memfaults
« Reply #14 on: January 09, 2019, 01:52:58 pm »
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 hardfault :palm: |O |O |O

Here is the setup of MPU for the LPC4337,


Code: [Select]

         MPU->RNR = 0;//indicate MPU region 0
MPU->RBAR = 0x00000000; // update the base address for the region 0
MPU->RASR = 0x0307003F;//4GB free access

         MPU->RNR = 6;
//trap for 0x40023000 range address
MPU->RBAR = 0x40023000; // update the base address for the region 6
MPU->RASR = 0x0007001B;

Any Idea why in the hardware the MPU would not act as it should do?
ASiDesigner, Stands for Application specific intelligent devices
I'm a Digital Expert from 8-bits to 64-bits
 

Offline abyrvalg

  • Frequent Contributor
  • **
  • Posts: 824
  • Country: es
Re: Setting up MPU and recovering from memfaults
« Reply #15 on: January 09, 2019, 09:52:19 pm »
Try inspecting the fault status registers (SHCSR, CFSR) after the first stepping into the "bad" instruction - there can be some clue.
 

Offline ali_asadzadehTopic starter

  • Super Contributor
  • ***
  • Posts: 1902
  • Country: ca
Re: Setting up MPU and recovering from memfaults
« Reply #16 on: January 12, 2019, 11:47:48 am »
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: [Select]
volatile uint32_t inst = (*(unsigned int *)sp[6]) & 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 things |O |O

Also I have set the return answer using this method

Code: [Select]
sp[2] = 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?
ASiDesigner, Stands for Application specific intelligent devices
I'm a Digital Expert from 8-bits to 64-bits
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26896
  • Country: nl
    • NCT Developments
Re: Setting up MPU and recovering from memfaults
« Reply #17 on: January 12, 2019, 02:31:39 pm »
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.
« Last Edit: January 12, 2019, 02:34:20 pm by nctnico »
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline ali_asadzadehTopic starter

  • Super Contributor
  • ***
  • Posts: 1902
  • Country: ca
Re: Setting up MPU and recovering from memfaults
« Reply #18 on: January 12, 2019, 06:44:49 pm »
Quote
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.

The problem is That There is no opcode in ARM processors datasheets, do you know where I can find them?
ASiDesigner, Stands for Application specific intelligent devices
I'm a Digital Expert from 8-bits to 64-bits
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26896
  • Country: nl
    • NCT Developments
Re: Setting up MPU and recovering from memfaults
« Reply #19 on: January 12, 2019, 06:51:41 pm »
Ask Google. It is probably somewhere on ARM's website for sure.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf