cancel
Showing results for 
Search instead for 
Did you mean: 

How to access *any* memory address without a bus fault?

gw
Associate II
Posted on May 08, 2014 at 11:57

How could I write some code that would access a memory address and wouldn't generate a bus fault if it was incorrect? Or failing that, is there a way to safely check if an address is valid, or maybe even a way to have the BusFault handler return and have normal execution resume?

I'm writing a language interpreter for an STM32F103, and there are some cases when this would be very handy:

  • Providing a 'peek' instruction that wouldn't crash the micro if an invalid address was given (presumably some addresses would still cause pain - but it'd be a lot less likely!)
  • Checking at run-time how much usable RAM there was, and expanding the stack+heap to use it - because some chips appear to just be re-badged higher end parts (eg. some STM32F103VC chips have 64kb instead of the marketed 48kb) - this is for use by enthusiasts - obviously it wouldn't be relied on for production.

I've tried quite hard to get something working, but haven't had much luck so far - if I return from the Bus Fault handler (even after clearing the flags) the processor immediately HardFaults.

thanks!
20 REPLIES 20
Posted on May 08, 2014 at 15:25

Didn't the STM32F1xx parts have a location in System OTP describing the FLASH/RAM validated on the tester?

printf(''STM32 %d KB FLASH, %d KB RAM, %08X-%08X-%08X UNIQUE

'',
*((unsigned short *)0x1FFFF7E0),
*((unsigned short *)0x1FFFF7E2),
*((unsigned long *)0x1FFFF7E8),
*((unsigned long *)0x1FFFF7EC),
*((unsigned long *)0x1FFFF7F0) );

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
gw
Associate II
Posted on May 08, 2014 at 15:45

Thanks! That would be ideal... I'd found the flash size at 0x1FFFF7E0, but never managed to find the RAM size.

I've just checked and on:

F103RG : *0x1FFFF7E2 = 96 (great!)

F103RC : *0x1FFFF7E2 = 65535 (hmm...)

There happens to be 64kb of available RAM on the F103RC, but I'd imagine that 65535 just implies that the value wasn't set.

Also, on that same chip, 0x1FFFF7E0 says 256 even though there appears to be 512kB of available (perfectly working) flash. My guess is that even though the chips are more capable, ST write lower values into the ROM when packaging as the lower-sized part.
stm322399
Senior
Posted on May 08, 2014 at 15:49

Are you sure ? There is a corner case you must take care about:

imagine the compiler generates this pseudo code:

; R1 contains the address to test
mov R0, 0xAAAAAAAA
str R0, [R1]
ldr R0, [R1]
mov R4, 0xAAAAAAAA
cmp R4, R0

As you noopified ldr/str, the comparison will always tell you that you read the same value that you write, and you procedure will falsely conclude that there is memory beyond. Perhaps, str can be no-op, but IMHO ldr must return something deterministic (bushandler must decode the destination register and set it to a known value). That way you idea of teting with two different values looks good. Are you not afraid that the address to be tested will possibly be inside your procedure stack, or inside global variables area ? In that case the result will be catastrophic, isn't it ?
gw
Associate II
Posted on May 08, 2014 at 16:17

Thanks - that's a very good point - and has probably just saved me quite a bit of debugging time ;)

I may end up doing it in the assembler of Reset_Handler anyway, so I could work around that pretty easily.

In terms of overwriting stack - I'm considering compiling for a 'lowest supported spec' device, and then checking the memory addresses above that device so I didn't hit issues (eg if the RC was the lowest spec then I'd start at 48k and work up).

Having said that, for it to be able to relocate the stack easily it probably needs to run at startup, before the stack is in use.
stm322399
Senior
Posted on May 08, 2014 at 16:25

OK, I see now more clearly what you want to do.

Last advice anyway: sometime extra memory looks to be accessible, but in fact this could be wrapped memory. The perfect example is a window of e.g. 1MB that replicates 16x a physical memory of 64KB. An other trap is that this memory can be there for internal use (USB buffers, or DMA FIFO, ...).

Of course the rebadged MCU that embeds more memory than marked onto, is a very realistic scenario as well !

chen
Associate II
Posted on May 08, 2014 at 16:49

Hi

'' does accessing the stack in the BusFault handler cause problems''

You can access the stack at any time.

Cause problems ? - It is up to the user/programmer to do sensible things to the stack!

''Assuming I can effectively 'disable' busfaults''

It is not possible to disable BusFault. It is a NMI (Non maskable Fault)

It is there to stop the CPU

access memory locations that are not there

access miss aligned memory locations

''In terms of overwriting stack''

It is possible to locate the stack anywhere in memory. Look at the linker script. The stack start location is the first entry in the vector table.

It is traditional to locate the stack at top of memory and have it go downwards.

It is up to the user/programmer to not over write/corrupt the stack.

''sometime extra memory looks to be accessible, but in fact this could be wrapped memory.''

Yes. ARM call this bit banding.

gw
Associate II
Posted on May 08, 2014 at 17:05

Thanks! Good to know I can't get rid of the BusFault - what about CCR.

BFHFNMIGN within a software interrupt? I haven't seen any examples of its use though

Any idea what would cause a HardFault immediately after returning from the BusFault handler? I made the handler empty, and stepped through instruction by instruction.

Does extra stuff get put on the stack when the IRQ is called? I guess maybe when it returns it gets the wrong PC or something?
chen
Associate II
Posted on May 08, 2014 at 17:21

''CCR.

BFHFNMIGN

''

Did not know that existed. The ARM documentation says - disables the BusFault IRQ so will not call the busfault ISR.

''

I haven't seen any examples of its use though

''

Not surprised.

''Any idea what would cause a HardFault immediately after returning from the BusFault handler? I made the handler empty, and stepped through instruction by instruction.''

Pay attention to the ProgramCounter, StackPointer (user and supervisor)

Watch registers R0-R3

This is a modified HardFault ISR from Joseph Yiu :

__attribute__(( naked )) void HardFault_Handler( void )

{

    __asm volatile (

                    ''    TST LR, #4                    \n''

                    ''    ITE EQ                        \n''

                    ''    MRSEQ R0, MSP                \n''

                    ''    MRSNE R0, PSP                \n''

                    ''    B hard_fault_handler_c        \n''

                );

}

// From Joseph Yiu, minor edits by FVH

// hard fault handler in C,

// with stack frame location as input parameter

// called from HardFault_Handler above

void hard_fault_handler_c (unsigned int * hardfault_args)

{

  unsigned int stacked_r0;

  unsigned int stacked_r1;

  unsigned int stacked_r2;

  unsigned int stacked_r3;

  unsigned int stacked_r12;

  unsigned int stacked_lr;

  unsigned int stacked_pc;

  unsigned int stacked_psr;

  stacked_r0 = ((unsigned long) hardfault_args[0]);

  stacked_r1 = ((unsigned long) hardfault_args[1]);

  stacked_r2 = ((unsigned long) hardfault_args[2]);

  stacked_r3 = ((unsigned long) hardfault_args[3]);

  stacked_r12 = ((unsigned long) hardfault_args[4]);

  stacked_lr = ((unsigned long) hardfault_args[5]);

  stacked_pc = ((unsigned long) hardfault_args[6]);

  stacked_psr = ((unsigned long) hardfault_args[7]);

  uartPrintf (''\r\n[Hard fault handler - all numbers in hex]\r\n'');

  uartPrintf (''R0 = %x\r\n'', stacked_r0);

  uartPrintf (''R1 = %x\r\n'', stacked_r1);

  uartPrintf (''R2 = %x\r\n'', stacked_r2);

  uartPrintf (''R3 = %x\r\n'', stacked_r3);

  uartPrintf (''R12 = %x\r\n'', stacked_r12);

  uartPrintf (''LR [R14] = %x  subroutine call return address\r\n'', stacked_lr);

  uartPrintf (''PC [R15] = %x  program counter\r\n'', stacked_pc);

  uartPrintf (''PSR = %x\r\n'', stacked_psr);

  uartPrintf (''BFAR = %x\r\n'', (*((volatile unsigned long *)(0xE000ED38))));

  uartPrintf (''CFSR = %x\r\n'', (*((volatile unsigned long *)(0xE000ED28))));

  uartPrintf (''HFSR = %x\r\n'', (*((volatile unsigned long *)(0xE000ED2C))));

  uartPrintf (''DFSR = %x\r\n'', (*((volatile unsigned long *)(0xE000ED30))));

  uartPrintf (''AFSR = %x\r\n'', (*((volatile unsigned long *)(0xE000ED3C))));

  uartPrintf (''SCB_SHCSR = %x\r\n'', SCB->SHCSR);

}

It give you an idea of what ISR entry affects.

If the compiler is not directed to stop automatic register save to stack then an awful lot more stuff is saved to stack!

I think R0-R3 are used for parameter passing. If any of these are not valid memory locations, this usually causes a Hardfault (in my experience).

Not sure what R12 is used for (maybe StackFrame?!?)

If either the PC or SP are not valid memory locations - Hardfault.

Check for these conditions on RTI from busfault.

zzdz2
Associate II
Posted on May 08, 2014 at 17:41

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka14398.html

It looks like the default action is to return to the same instruction that caused the bus fault.

gw
Associate II
Posted on May 08, 2014 at 17:51

Thanks for that link! That'll almost certainly be the issue then.

It seems like in this case the fix may be relatively easy - adding some assembler to increment SP+20 by 2 if we're being evil and assuming that the standard ldr and str will be 2 byte instructions. I'll give it a try...

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337e/Babedgea.html