Replacing MBR with custom bootloader

Hi,

I'm developing a custom MBR/bootloader for NRF52811 which is used as a second/radio MCU on the board. The MCU will need to run applications with and without the softdevice, based on current usecase (ble and non-BLE protocols). The official uart bootloader takes way too much flash (25k + 8k of config space), even after removing crypto (still 15k+config), leaving us with only few kB of Flash for application logic.

So the current approach is to write a custom MBR which would contain a simple UART protocol to rewrite the flash, nothing fancy, just writing to flash and getting CRC of the data written, a data blob with app or app+softdevice would be written from 0x1000.

So, my question is how the official MBR actually works. Is it enough to change VTOR to 0x1000, jump to reset handler in vector table at that address and stop worrying (assuming the softdevice, if present, handles forwarding all interrupts to app)? Is there anything else, except the interrupt vectors redirection needed to support the softdevice?

Thank you

Parents
  • Hi,

    I see that if you use e.g. the S140 v7.3.0, not much Flash is left on the nRF52811 for other purposes. (S140 takes up 156 kiB, leaving 24 kiB for MBR, bootloader and application.)

    Is it enough to change VTOR to 0x1000, jump to reset handler in vector table at that address and stop worrying (assuming the softdevice, if present, handles forwarding all interrupts to app)?

    That should basically be it, yes. Then make sure not to do any MBR calls from the application.

    Please note that our bootloader in nRF5 SDK do rely somewhat on MBR functionality, so if you use that bootloader for inspiration, then you should be aware of the usage of MBR functionality there.

    Please also note that the SoftDevice must start at 0x1000, which may have some implications for your custom MBR/bootloader implementation. (The SoftDevice is built for being executed from that fixed location.)

    Regards,
    Terje

  • I've created a minimalistic MBR that only changes VTOR to softdevice, sets stack pointer and jumps to the reset vector, but it doesn't seem to do the trick (code below). The reset vector from the S112 seems to be at 0x18a59 (just at the end of the softdevice flash area), stack pointer set to 0x20000cd0, so this is probably valid.

    Flashing softdevice + app works (the device is advertising on BLE), rewriting flash below 0x1000 with my MBR implementation results hanging on address 0x189b8.

    As the softdevice is a binary blob, it's quite hard to debug this... Any ideas what I'm missing?

    #define APP_VECTOR_TABLE_ADDR 0x1000UL

    static void jump_to_app()
    {
        uint32_t *vector = (uint32_t *)APP_VECTOR_TABLE_ADDR;
        uint32_t new_sp = vector[0];
        uint32_t new_pc = vector[1];

        SCB->VTOR = APP_VECTOR_TABLE_ADDR;
        __DSB();
        __ISB();

        __set_MSP(new_sp);
        ((void (*)(void))new_pc)();
    }
Reply
  • I've created a minimalistic MBR that only changes VTOR to softdevice, sets stack pointer and jumps to the reset vector, but it doesn't seem to do the trick (code below). The reset vector from the S112 seems to be at 0x18a59 (just at the end of the softdevice flash area), stack pointer set to 0x20000cd0, so this is probably valid.

    Flashing softdevice + app works (the device is advertising on BLE), rewriting flash below 0x1000 with my MBR implementation results hanging on address 0x189b8.

    As the softdevice is a binary blob, it's quite hard to debug this... Any ideas what I'm missing?

    #define APP_VECTOR_TABLE_ADDR 0x1000UL

    static void jump_to_app()
    {
        uint32_t *vector = (uint32_t *)APP_VECTOR_TABLE_ADDR;
        uint32_t new_sp = vector[0];
        uint32_t new_pc = vector[1];

        SCB->VTOR = APP_VECTOR_TABLE_ADDR;
        __DSB();
        __ISB();

        __set_MSP(new_sp);
        ((void (*)(void))new_pc)();
    }
Children
  • Hi,

    Upon further inquiry, we see that more is indeed needed. Here's what is required:

    1. Set 0x20000000 to the application start address
    2. Run the reset vector of the SoftDevice (at address 0x1004)
    3. Set 0x20000000 to 0x1000 (which is the start address of the SoftDevice)
    4. Do a sd_softdevice_vector_table_base_set() with the application start address
    5. Set VTOR to the start address of the application
    6. From the application, whenever the SoftDevice is enabled, forward any RADIO, RTC, POWER_CLOCK, RNG and SWI5 IRQs to the SoftDevice

    Some of those steps may be unnecessary and might be omitted to save space, but a first step should be to do all of the above in order to have a known working starting point.

    Regards,
    Terje

  • Hi,

    thank you very much for the answer, but I still don't understand few points:

    - when I run reset vector at 2), the MBR won't be executed again until reset, right? I assume the reset  vector shall not return - therefore I'll never by able to run 3) to  6)
    - at 4), how do I get the application start address? It depends on softdevice version...
    - 6) so I have to modify the application to do the heavylifting instead of MBR? I thought this was to be done by the MBR, not the application
    - is there a way to detect the binary at 0x1000 is a softdevice?

  • Hi,

    I've added vector forwarding to vector table at 0x20000000. The application seems to be launched correctly though the softdevice, but it always fails starting the softdevice (the sd_softdevice_enable call returns 0x20005fd4 which doesn't make much sense to me, as it's some address in ram, not the actual error code). I'm using S112 7.2.0

  • Ok, I guess I've managed to make it work with help of some MBR binary disassembly, the error mentioned in the previous post was caused in bug in my code around SVCall processing.

    So, the official MBR seems to be doing:

    - After boot, the MBR does some magic at UICR->NRFFW pointed address (with fallback to some hardcoded address), it seems it has something to do with the MBR configuration storage that to my understanding contains data for DFU (e.g. which section is to be written with DFU data,...), so I can ignore this as I don't intend to use the official bootloader
    - Once reading configuration is done, bootloader jumps to application/softdevice at 0x1000 + 0x4 (if not set to different address in the configuration above), before jumping 0x20000000 is written with the vector table address (0x1000)
    - All interrupts except Reset and SVCall are redirected to vector table the 0x20000000 points to.
    - The SVCall interrupt check the argument in svcall instruction, if it's equal to 0x18, it's a bootloader call, else it's passed to softdevice/app in the same way as the other interrupts.

    That seems to be everything the MBR does when not requested otherwise. Implementing similar behavior seems to work for me with BLE uart example (modified as loopback) and S112.

    However I still don't understand, why the VTOR change I used initially doesn't work, what my MBR implementation does now is redirecting all IRQs to addresses at 0x1000 (softdevice vector table). The 0x20000000 doesn't seem to be changing during app/softdevice runtime, so the behavior shall be identical...

  • Hi,

    kajus said:
    That seems to be everything the MBR does when not requested otherwise. Implementing similar behavior seems to work for me with BLE uart example (modified as loopback) and S112.

    That sounds correct, yes.

    To elaborate on the redirection of interrupts and SVCalls, similar redirects are done also from SoftDevice to application, for the interrupts not used by the SoftDevice. In theory, if you redirect anything to the SoftDevice which the SoftDevice doesn't use, then the SoftDevice will redirect that back to the app, and you end up looping infinitely.

    Hence if VTOR is set to the application start, you need to redirect interrupts for certain peripherals to the SoftDevice, but only when the SoftDevice is enabled. Also, you must only redirect interrupts for the peripherals actually used by the SoftDevice. Failing to map this correctly could lead to either the SoftDevice not getting interrupts, or interrupts getting endlessly redirected back and forth between app and SoftDevice.

    kajus said:
    However I still don't understand, why the VTOR change I used initially doesn't work, what my MBR implementation does now is redirecting all IRQs to addresses at 0x1000 (softdevice vector table). The 0x20000000 doesn't seem to be changing during app/softdevice runtime, so the behavior shall be identical...

    The first couple of words in RAM are used for vector table coordination between MBR and SoftDevice. Hence the need to set 0x20000000 to 0x1000.

    Regards,
    Terje

Related