rtic-rs / rtic

Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers
https://rtic.rs
Apache License 2.0
1.8k stars 209 forks source link

Consider adding a hook to run code at the beginning of the entry point #923

Closed ellenhp closed 6 months ago

ellenhp commented 6 months ago

This is a pretty goofy feature request and could be an example of the XY problem, but I want to open an issue about it anyway. I'm doing some adversarial firmware development for a TYT MD-UV380 DMR transceiver, which has a bootloader that will refuse to start your firmware unless the initial stack size is exactly 512 bytes. Specifically, it needs to be at address 0x20000200 otherwise the radio will boot into firmware update mode instead of the actual entry point. This is no good for a system like RTIC or Embassy where the main stack is shared. 512 bytes is not a lot to work with.

To deal with this, I execute this inline ASM at startup:

unsafe {
    asm!(
        "ldr SP, =0x20020000",
    );
}

Right now I put that at the top the main function macro in rtic-macros/src/codegen/main.rs before anything else can run. I (selfishly) don't like that I have to maintain a fork of RTIC to do this so I was wondering if anyone could think of something better or a way for me to hook into the entry point to perform this operation. I'm not an expert on embedded stuff, but I think this is an edge case so I understand if it's outside the scope of what RTIC should be providing.

ellenhp commented 6 months ago

For now I've "solved" this by providing a _pre_init function and doing the remainder of the core setup from cortex_m there.

core::arch::global_asm!(
    "
    .text
    .globl __pre_init
    __pre_init:
    ldr sp, =0x20020000
    ",
    // Initialise .bss memory. `__sbss` and `__ebss` come from the linker script.
    "ldr r0, =__sbss
     ldr r1, =__ebss
     movs r2, #0
     2:
     cmp r1, r0
     beq 3f
     stm r0!, {{r2}}
     b 2b
     3:",
    // Initialise .data memory. `__sdata`, `__sidata`, and `__edata` come from the linker script.
    "ldr r0, =__sdata
     ldr r1, =__edata
     ldr r2, =__sidata
     4:
     cmp r1, r0
     beq 5f
     ldm r2!, {{r3}}
     stm r0!, {{r3}}
     b 4b
     5:",
    // Enable the FPU.
    // SCB.CPACR is 0xE000_ED88.
    // We enable access to CP10 and CP11 from priviliged and unprivileged mode.
    "ldr r0, =0xE000ED88
     ldr r1, =(0b1111 << 20)
     ldr r2, [r0]
     orr r2, r2, r1
     str r2, [r0]
     dsb
     isb",
    // Jump to user main function.
    // `bl` is used for the extended range, but the user main function should not return,
    // so trap on any unexpected return.
    "bl main
     udf #0",
);

Which is kind of evil, but it does mean I can use upstream RTIC. I'm going to leave this open but you can close it if you'd like.

perlindgren commented 6 months ago

I think the pre-init rout is ok for such special cases

perlindgren commented 6 months ago

We close it as an acceptable solution, feel free to-reopen if anyone wants to add to this.