I am working on a project that contains a firmware update failure feature that :
notifies user/host that firmware update failed whenever the device gets unplugged in the middle of a firmware update does not attempt to run jump to main() in application code.
To achieve this, I created some code that is designed to trap program execution when a DFU update is not completed. This code is located at the very beginning of the .text section so that it makes it pretty likely that this code gets successfully flashed before the user prematurely unplugs the device (this section of flash is done being programmed after only about 500 ms of the DFU, which takes about 13 seconds overall).
checks magic number at start of .text and end of .text section matches, and if not blinks LED at the user emits 0xAA byte over and over again on the UART so that the host can detect the failure signature I have this working using the following technique:
app_magic_start
app_magic_end
.init3
section which evaluates whether or not the magic number at start of text section matches the one at the end of text section, and if it doesn't then it starts calling the stall_and_scream()
function in the .app_early
section.text 0xC0 :
{
KEEP(*(.app_magic_start))
KEEP(*(.app_fw_version))
*(.app_early)
/* edited location of INIT sections!!! */
/* From this point on, we don't bother about wether the insns are
below or above the 16 bits boundary. */
*(.init0) /* Start here after reset. */
KEEP (*(.init0))
*(.init1)
KEEP (*(.init1))
*(.init2) /* Clear __zero_reg__, set up stack pointer. */
KEEP (*(.init2))
*(.init3)
KEEP (*(.init3))
*(.init4) /* Initialize data and BSS. */
KEEP (*(.init4))
*(.init5)
KEEP (*(.init5))
*(.init6) /* C++ constructors. */
KEEP (*(.init6))
*(.init7)
KEEP (*(.init7))
*(.init8)
KEEP (*(.init8))
*(.init9) /* Call main(). */
KEEP (*(.init9))
/* For data that needs to reside in the lower 64k of progmem. */
*(.progmem.gcc*)
/* PR 13812: Placing the trampolines here gives a better chance
that they will be in range of the code that uses them. */
. = ALIGN(2);
__trampolines_start = . ;
/* The jump trampolines for the 16-bit limited relocs will reside here. */
*(.trampolines)
*(.trampolines*)
__trampolines_end = . ;
/* avr-libc expects these data to reside in lower 64K. */
*libprintf_flt.a:*(.progmem.data)
*libc.a:*(.progmem.data)
*(.progmem*)
. = ALIGN(2);
/* For future tablejump instruction arrays for 3 byte pc devices.
We don't relax jump/call instructions within these sections. */
*(.jumptables)
*(.jumptables*)
/* For code that needs to reside in the lower 128k progmem. */
*(.lowtext)
*(.lowtext*)
__ctors_start = . ;
*(.ctors)
__ctors_end = . ;
__dtors_start = . ;
*(.dtors)
__dtors_end = . ;
KEEP(SORT(*)(.ctors))
KEEP(SORT(*)(.dtors))
/* original location of INIT sections */
*(.text)
. = ALIGN(2);
*(.text.*)
. = ALIGN(2);
*(.fini9) /* _exit() starts here. */
KEEP (*(.fini9))
*(.fini8)
KEEP (*(.fini8))
*(.fini7)
KEEP (*(.fini7))
*(.fini6) /* C++ destructors. */
KEEP (*(.fini6))
*(.fini5)
KEEP (*(.fini5))
*(.fini4)
KEEP (*(.fini4))
*(.fini3)
KEEP (*(.fini3))
*(.fini2)
KEEP (*(.fini2))
*(.fini1)
KEEP (*(.fini1))
*(.fini0) /* Infinite loop after program termination. */
KEEP (*(.fini0))
*(.app_magic_end)
_etext = . ;
} > text
//compile app with "magic number" as the first and last
//32-bit values in the app,
/*
memory map would be like:
vtors
uint32_t magic_start;
....
uint32_t magic_end;
(end of .txt section)
*/
__attribute__((section(".app_magic_start"))) const uint32_t app_magic_start_flash = BUILD_GIT_SHA;
__attribute__((section(".app_magic_end"))) const uint32_t app_magic_end_flash = BUILD_GIT_SHA;
uint32_t __attribute__((section(".app_early"))) read_flash32(uint16_t addr)
{
uint32_t ret = 0;
for (int i = 0; i < 4; i++)
{
ret = ret | (((uint32_t)(pgm_read_byte(i + addr))) << (i * 8));
}
return ret;
}
void __attribute__((section(".app_early"))) stall_and_scream(uint16_t count)
{
for(volatile uint16_t c= 1; c< count;){
c++;
while((UCSR0A & (1<<UDRE0)) == 0){
};
//Delay for a byte or two so that host uart can sycnronize the the 0xAA streams.
for(volatile uint16_t i = 0; i < 0xFF;){
i++;
}
UDR0 = 0xAA;
UCSR0A = ((UCSR0A) & ((1 << U2X0) | (1 << MPCM0))) | (1 << TXC0);
wdt_reset();
}
}
#define MS_TO_CHARCOUNT(x) (2*x)
//Put this piece of code in the init3 section, which gets called early during gcc
//initialization steps, just after clear zero reg and setup stack pointer (just before initialize data an bss)
//NAKED
//declare the function naked so that it simply runs without trying to return to previous stack pointer location
//--> basically becomes inline assembly code in init sequence right before data and bss initialization
//--> TODO: convert the function to only use inline assembly, as per requirements for the naked attribute
//USED
//declare the function used so that link-time optimization will not discard it before it can be
//included into the init code
void __attribute__((section(".init3"), used, naked)) app_valid_check()
{
wdt_enable(WDTO_8S);
SWITCH_DDR |= SWITCH_PORT_MSK; //Configure switch enable pin to output;
SWTICH_PORT |= SWITCH_PORT_MSK;//Turn the switch on!
volatile uint32_t magic_start = read_flash32(uint16_t(&app_magic_start_flash));
volatile uint32_t magic_end = read_flash32(uint16_t(&app_magic_end_flash));
if(magic_start != magic_end){
GREEN_LED_1_DDR |= GREEN_LED_1_PORT_MSK;
GREEN_LED_2_DDR |= GREEN_LED_2_PORT_MSK;
//configure the uart
UCSR0A = 0x20 | (1 << U2X0);
UBRR0 = 0x0019; //Set baud rate
UCSR0C = 0x06; //Set 8 bit char size
UCSR0B = (1<<TXEN0); //enable transmitter (no interrupts)
UCSR0D = 0xA0;
//magic numbers don't match, app is invalid! scream over uart and Blink red LED continuously
while(1){
//Turn on LEDS
GREEN_LED_1_PORT |= GREEN_LED_1_PORT_MSK;
GREEN_LED_2_PORT |= GREEN_LED_2_PORT_MSK;
stall_and_scream(MS_TO_CHARCOUNT(50));
//Turn off LEDS
GREEN_LED_1_PORT &= ~(GREEN_LED_1_PORT_MSK);
GREEN_LED_2_PORT &= ~(GREEN_LED_2_PORT_MSK);
stall_and_scream(MS_TO_CHARCOUNT(200));
}
}
}
This all seems to work as expected in that:
stall and scream()
[N] 2465 : non-ASM statement in naked function is not supported JP2App C:\PARTICLE_LOCAL\JP2\JP2App\JP2App\JP2App.ino 1066
I know that this warning is being generated because I am using the naked
AVR-GCC function attribute in app_valid_check()
According to the documentation (6.31.5 AVR Function Attributes) :
This attribute allows the compiler to construct the requisite function declaration, while allowing the body of the function to be assembly code. The specified function will not have prologue/epilogue sequences generated by the compiler. Only basic asm statements can safely be included in naked functions (see Basic Asm). While using extended asm or a mixture of basic asm and C code may appear to work, they cannot be depended upon to work reliably and are not supported.
So, "though it appears to work", it "cannot be depended upon to work reliably".
Tried removing the naked attribute from app_valid_check()
, and instead making it inline
, and used attribute always_inline
I was expecting that this would allow the function to simply be plopped directly into the .init3
section (with no jump instruction to get to it) and run properly, but this did not seem to work.
It compiles albeit with a bunch of warnings about app_valid_check
declared but never defined.
If I run this program, it seems to execute the code in app_valid_check()
, however for some reason it appears that there is assembly code at the end of app_valid_check()
with a return statement (see screenshot below). This doesn't make sense though, if the function really is "inline" in the .init3
section... inline functions should have no RET
instruction at the end, right?
You may be wondering what my question is by now. Here it is:
How can I call a C function from the init3 section in AVR-GCC?
I think it may involve:
app_valid_function()
into the .app_early
section, and remove the naked
attributecall_app_valid_check()
that is included in the .init3
section, which contains only basic ASM code (not extended ASM) which:
But I'm not really experienced in assembly code...
I assume it would involve:
Your guidance would be greatly appreciated.
What you can do is to call your function via a stub like in:
// Define or declare my_func()...
__attribute__((__naked__,__used__,__unused__,__section__(".init3")))
static void call_my_func (void)
{
__asm ("%~call %x0" :: "i" (my_func));
}
Notice that .init3
runs prior to .init4
which initializes static storage (.data, .bss etc), i.e. using static storage in my_func
comes with some limitations.
Also notice that a part of startup code may run in .init3
, so you must make sure that the (unspecified) ordering of the code in .init3
does not matter. See AVR-LibC: Memory Sections: The .initN Sections.
When the time to initialize static storage is no issue, then you can run my_func
at .init6
as a static constructor:
__attribute__((__constructor__))
static void my_func (void)
{
// C/C++ Code
}
In that case, there's no need for code in .init3
.