RIOT-OS / RIOT

RIOT - The friendly OS for IoT
https://riot-os.org
GNU Lesser General Public License v2.1
4.92k stars 1.99k forks source link

bootloader and application section #7081

Closed photonthunder closed 6 years ago

photonthunder commented 7 years ago

I want to setup a bootloader for my samd21 board. I have a simple test program for the boot section that just tries to jump to application section (I have set the application section at 0x4000).

In the Makefile.include I added the following for the application memory:

export LINKFLAGS += -Wl,--section-start=.text=0x4000

After programming both to the board I get the following error:

Stack pointer corrupted, rest to top of stack Misc EXC_RET: 0xfffffffd

I have read the jump memory location and it does have the correct hex data from the application so I "think" that is working. Is there something I am missing from my link flag?

photonthunder commented 7 years ago

So I switched to 0x2000 instead of 0x4000 as the start of my application section (made the change in both my boot and application code) and it works. So must be some issue with 0x4000 as an application memory start location?

kYc0o commented 7 years ago

Can you share your code to see what's really happening? I implemented a bootloader (very basic and simple) in #6922, but unfortunately it only works for cortex-m3 boards. If you have some other approach maybe we can merge both to add m0 boards to the supported devices? It would be awesome since it would bring OTA updates also for such cpu.

photonthunder commented 7 years ago

At this point all I was trying to do was to get it to jump in memory (no actual bootloader code yet). I will implement an extremely simple bootloader and when i do I will share it. For now here is the code I am using for my jump (simplified from ASF).

#include "board.h"

#define APP_START_ADDRESS          0x00004000

static void jumpToApplication(void);

int main(void)
{
    jumpToApplication();
    while (1) {
    }
}

static void jumpToApplication(void)
{
    uint32_t app_check_address;
    uint32_t *app_check_address_ptr;

    app_check_address = APP_START_ADDRESS;
    app_check_address_ptr = (uint32_t *) app_check_address;

    /*
     * Read the first location of application section
     * which contains the address of stack pointer.
     * If it is 0xFFFFFFFF then the application section is empty.
     */
    if (*app_check_address_ptr == 0xFFFFFFFF) {
        //printf("Application section empty\n");
        while (1) {
        }
    }
    //printf("Application=0x%08lX\n", *app_check_address_ptr);

    /* Pointer to the Application Section */
    void (*application_code_entry)(void);

    /* Rebase the Stack Pointer */
    __set_MSP(*(uint32_t *) APP_START_ADDRESS);

    /* Rebase the vector table base address */
    SCB->VTOR = ((uint32_t) APP_START_ADDRESS & SCB_VTOR_TBLOFF_Msk);

    /* Load the Reset Handler address of the application */
    application_code_entry = (void (*)(void))(unsigned *)(*(unsigned *)
                                                          (APP_START_ADDRESS + 4));

    /* Jump to user Reset Handler in the application */
    application_code_entry();
}
biboc commented 7 years ago

This might help you: https://community.arm.com/processors/f/discussions/4633/cortex-m0-how-to-switch-from-one-program-to-another/11627?pi1735=141&pi1738=2#

photonthunder commented 7 years ago

Thanks for the link, I will have to compare the assembly code and see if there is any difference but it is just odd that it will jump without issue at 0x2000 but not at 0x4000.

biboc commented 7 years ago

@photonthunder Have you done any progress on the bootloader? Could you do a PR please so we can review your code.

photonthunder commented 7 years ago

Still working on it. One of the challenges I have is that I would like to keep my code under the 0x2000 mark. Thus, I have a standalone version where I use a mini printf, hard code the baud rate, etc. I tried to setup a bootloader define but then you end up hacking the reset_handler and kernel_init to help keep code size small. Been following #6922 and waiting to see how that flushes out. Is there a bootloader placeholder that allows for implementation within riot but without all the extras?

kYc0o commented 7 years ago

Great to see some advancements here! I also tried to keep code small by squeezing out as much as possible the RIOT code and remove all "extra" things, without much success. The problem here is, as you mentioned it, you always need to hack the init functions to "isolate" the RIOT kernel from device drivers, which are the only needed part from the base code.

@haukepetersen has some ideas to do so, but I think he has other priorities right now so we won't have much man power on that matter. Besides, I don't think scheduler and core functions are using too much space, if any can be saved. Moreover, some RIOT features are useful such as the packaging for the use of external libraries (security libraries like tweetnacl, etc) which can be replaced by the user and/or extended by the developers.

So for now, I'd suggest to try the current bootloader in a samr21-xpro and extend it with the functions you propose here to jump to the application code from the bootloader, if possible.

Let's use this space to share ideas on how we can improve the approaches and bring as fast and best as possible firmware update features to these boards!

BTW, I'm working on a OTA module inside RIOT which allows binary fetching from remote servers, I'll come with a PR very soon.

photonthunder commented 7 years ago

What I would like to do first is to get flash read/write/erase added; however, I don't see much except for flashpage.c which doesn't seem to be implemented by any boards. Is there a riot method for flash access that is used? If not where would I put my flash functions?

kYc0o commented 7 years ago

There is actually #5366 which already proposes a driver for that. However something is wrong with erasing... Maybe you can take a look and see if you can find the bug?

photonthunder commented 7 years ago

Thanks for the link, bug found...

emmanuelsearch commented 7 years ago

@photonthunder if you identified/fixed a bug, can you PR the fix?

kYc0o commented 7 years ago

@emmanuelsearch The solution was pointed out in the PR, Hauke already fix it so we are in testing phase.

emmanuelsearch commented 7 years ago

awesome ;)

photonthunder commented 7 years ago

So I have a simple bootloader programmed that just splits the flash memory in half and if the fletcher checksum matches it updates the application section. Then any updating is done outside of the bootloader. I can get it to work great outside of riot but once I put it into riot then my size triples and the jumpToApplication function above no longer works (brings me back to my original question). I think the stack pointer or the vector table base address must be tweaked in riot. I also have to add some edits to openocd script to allow for writing to bootloader and to application section. Makes me think there needs to be a riot-os implemented "boot" section to facilitate basic bootloaders?

kYc0o commented 7 years ago

You must adjust the stack pointer and be sure that the clocking system is correctly reseted, either before jumping or in the sam0 code before configure it again. You can check #6922 for more details, specifically here.

kYc0o commented 7 years ago

For the flashing issue, it's a bit more complex than that. You can see in 76501fcde101c02844978f32109aa4c057418e8e what it's done for this purpose, if you want to absolutely generate an ELF file. Another approach (which was the one I used before) can be to simply join the two firmwares using e.g. srec_cat into an hex file which openocd won't have any issue to flash (it won't look to any section in the ELF file).

photonthunder commented 7 years ago

@kYc0o thanks for the feedback. I must be missing the obvious, in the jumpToApplication function above it does adjust the stack pointer and then jumps to application. This works fine when I compile it outside of riot-os, but when I build it within riot it doesn't jump correctly. You linked code appears to be doing something similar, but I don't see anything that deals with the clock.

On the flashing side I was trying to avoid touching a lot of riot base code. What I have currently implemented is just a few export commands in the board Makefile.include:

export LINKFLAGS += -Wl,--section-start=.text=0x4000
export APPLICATION_START = "0x4000"
export OFLAGS = -O binary
export HEXFILE = $(ELFFILE:.elf=.bin)

And then did a small tweak to openocd.sh:

    if [ -n "${APPLICATION_START}" ]; then
        sh -c "${OPENOCD} -f '${OPENOCD_CONFIG}' \
                ${OPENOCD_EXTRA_INIT} \
                -c 'tcl_port 0' \
                -c 'telnet_port 0' \
                -c 'gdb_port 0' \
                -c 'init' \
                -c 'targets' \
                -c 'reset halt' \
                ${OPENOCD_PRE_FLASH_CMDS} \
                -c 'flash write_image erase \"${HEXFILE}\" ${APPLICATION_START}' \
                -c 'reset halt' \
                ${OPENOCD_PRE_VERIFY_CMDS} \
                -c 'reset run' \
                -c 'shutdown'" &&
        echo "App Done"
    else
        sh -c "${OPENOCD} -f '${OPENOCD_CONFIG}' \
                ${OPENOCD_EXTRA_INIT} \
                -c 'tcl_port 0' \
                -c 'telnet_port 0' \
                -c 'gdb_port 0' \
                -c 'init' \
                -c 'targets' \
                -c 'reset halt' \
                ${OPENOCD_PRE_FLASH_CMDS} \
                -c 'flash write_image erase \"${HEXFILE}\"' \
                -c 'reset halt' \
                ${OPENOCD_PRE_VERIFY_CMDS} \
                -c 'verify_image \"${HEXFILE}\"' \
                -c 'reset run' \
                -c 'shutdown'" &&
        echo "Boot Done"
    fi

Makes it nice for writing the application code without having to mess with the boot section each time.

kYc0o commented 7 years ago

Sorry, I didn't look again at the code before answering, my bad. So, looking again at the code, it seems that you are moving the vector table before jumping, but that operation is already done by RIOT in the initailisation code, have you taken a look at that?

About the clock, it was reworked for stm32f1 in #6970 to ensure a correct initial status before configuring. So, you must be sure that the clock in the sam0 code ensures a kind of reset status before attempting a new configuration.

For the flashing issue, I don't really get how do you first flash your bootloader and then the application code, since the script you show it's only about the application code. AFAIK, the HEX file contains addresses information so you don't need to include it in the command. However, you need to be sure that you're correctly linking you application to fit in the desired addresses for the allocated space. I'm not completely sure that the "only" thing you need is to change the .text address, but have it explicitly in the linker script. I'm not a linker script expert, but only my guess.

photonthunder commented 7 years ago

Thanks for the information. I was assuming that RIOT played with the vector table, but was hoping that maybe there would be a larger picture bootloader implementation so that I didn't have to tweak the base files. Any chance of a RIOT bootloader placeholder being implemented?

On the flash side, it is set up so I can flash them seperately. if you take out the export statements then it will just fall through to the else option and load the boot section. If you look at the export statements I switch my application code to binary, makes it easier for bluetooth transmission.

waehlisch commented 6 years ago

Is this issue still needed or can we close because of #9342?

danpetry commented 6 years ago

Due to the time on the comments this seems to be resolved. Closed