2014-05-08 02:57 AM
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: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!2014-05-08 06:25 AM
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) );
2014-05-08 06:45 AM
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.2014-05-08 06:49 AM
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 ?
2014-05-08 07:17 AM
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.2014-05-08 07:25 AM
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 !2014-05-08 07:49 AM
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.2014-05-08 08:05 AM
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?2014-05-08 08:21 AM
''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.2014-05-08 08:41 AM
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.2014-05-08 08:51 AM
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