Previouslywe wrote a startup file to bootstrap our C environment, and a linkerscript to get the right data at the right addresses. These two will allow us towrite a monolithic firmware which we can load and run on our microcontrollers.
Bootloaders also allow you to decouple parts of the program that are missioncritical, or that have security implications, from application code whichchanges regularly. For example, your bootloader may contain firmware updatelogic so your device can recover no matter how bad a bug ships in yourapplication firmware.
Last but certainly not least, bootloaders are an essential component of atrusted boot architecture. Your bootloader can, for example, verify acryptographic signature to make sure the application has not been replaced ortampered with.
We must first decide on how much space we want to dedicate to our bootloader.Code space is precious - your application may come to need more of it - and youwill not be able to change this without updating your bootloader, so makethis as small as you possibly can.
Another important factor is your flash sector size: you want to make sure youcan erase app sectors without erasing bootloader data, or vice versa.Consequently, your bootloader region must end on a flash sector boundary(typically 4kB).
We know how to do the first part from our previous post: we need a valid stackpointer at address 0x0 , and a valid Reset_Handler function setting up ourenvironment at address 0x4. We can reuse our previous startup file and linkerscript, with one change: we use memory_map.ld rather than define our ownMEMORY section.
To jump into our application, we need to know where the Reset_Handler of theapp is, and what stack pointer to load. Again, we know from our previous postthat those should be the first two 32-bit words in our binary, so we just needto dereference those addresses using the __approm_start__ variable from ourmemory map.
Note: hardware resources initialized in the bootloader must be de-initializedbefore control is transferred to the app. Otherwise, you risk breakingassumptions the app code is making about the state of the system
We must update our app to take advantage of our new memory map. This is againdone by updating our linker script to include memory_map.ld and changing oursections to go to the approm region rather than rom.
We also need to update the vectortableused by the microcontroller. The vector table contains the address of everyexception and interrupt handler in our system. When an interrupt signal comesin, the ARM core will call the address at the corresponding offset in the vectortable.
By default, the vector table is at address 0x0, which means that when our chippowers up, only the bootloader can handle exceptions or interrupts! Fortunately, ARMprovides the Vector Table OffsetRegisterto dynamically change the address of the vector table. The register is ataddress 0xE000ED08 and has a simple layout:
The Vector table must be naturally aligned to a power of two whose alignment value is greater than or equalto (Number of Exceptions supported x 4), with a minimum alignment of 128 bytes.The entry at offset 0 isused to initialize the value for SP_main, see The SP registers on page B1-8. All other entries must have bit[0] set, as the bit is used to define the EPSR T-bit on exception entry (see Reset behavior on page B1-20 andException entry behavior on page B1-21 for details).
Our SAMD21 MCU has 28 interrupts on top of the 16 system reserved exceptions,for a total of 44 entries in the table. Multiply that by 4 and you get 176. Thenext power of 2 is 256, so our vector table must be 256-byte aligned.
You can compile both these programs and load the resulting elf files with gdbwhich will put them at the correct address. However, the more convenient thingto do is to build a single binary which contains both programs.
This requires shared, persistent data between the application and the bootloaderwhich is retained across reboots. On some architectures, non volatile registersare available which make this easy. This is the case on all STM32microcontrollers which have RTC backup registers.
More commonly, bootloaders are used to relocate applications before they areexecuted. Relocations involves copying the application code from one place toanother in order to execute it. This is useful when your application is storedin non-executable memory like a SPI flash.
We know from our previous blog post that executable code typically ends up inthe .text section so we must tell the linker that this section is stored inapprom but executed from eram so our program can execute correctly.
This is similar to our .data section, which is stored in rom but lives inram while the program is running. We use the AT linker command to specifythe storage region and the > operator to specify the load region. This is theresulting linker script section:
I have searche multiple websites for the .ino source code of the Arduino's bootloader. I found .hex and .c code but didn't find .ino source code. Can someone please refer a link or post the code? (Arduino UNO's bootloader)
It is, ah, not the easiest program to understand - lots of optimizations are done to keep the size of the source code down, and being straight c, it's tough to understand if you are used to reading "easy mode" arduino sketches.
There would be no need to "decompile" the .hex. The source code for the Uno's optiboot bootloader is included with the Arduino IDE and the Arduino AVR Boards platform, but you should be aware that there has been quite a bit of development on optiboot since the version used by Arduino. So if you want to look at the source code, you'd do well to use the latest version, which is available here:
This is written in C, which is supported by the Arduino IDE, so you would be far better off just adding the readily available C source code to an Arduino sketch. I think that would be pretty cool, and did some work toward that goal a few years ago, but never finished it. I remember I ran into some difficulties, but I'd need to look through my notes to refresh my memory on exactly what it was.
You could probably get it into an ino style, but would need to extensively alter the additional code that the IDE adds to the ino file when compiling, and the end product would need to result in the C code that is already available.
Your previous question about decompiling the hex file makes little sense, since the code that generated the hex file is freely available, and anyone with the skills to successfully reverse engineer the code from a hex file should be capable of learning enough C language to understand the source code.
Bootloaders also can be fairly cryptic at times, using techniques that may be considered bad programming in another context because of the need to have a very small and highly efficient code. I particularly liked the bootstrap loader for the PDP-11 minicomputer, which had to be keyed in by hand every time, and used a self-modifying program where the code being read in overwrote part of the bootloader itself.
Posts like this go by a couple of times a year... where the person seems to be asking super basic questions relating to some advanced area.... then he gets an answer, thanks people, and scurries off. You always wonder!
Or maybe what they wanted to change was dead simple once they knew where the source was and how to build it? (ex "the LED is on a different pin", or (slightly) less complicated "if this pin is driven low, don't start the app" kind of things)
DrAzzy:
Posts like this go by a couple of times a year... where the person seems to be asking super basic questions relating to some advanced area.... then he gets an answer, thanks people, and scurries off. You always wonder!
@ArnavPawarAA, I think you are reading too much into the answer. We see lots of questions where one thing is asked, but something else is really the intent, but the question asker didn't know enough to ask the proper question.
Usually noted as an X:Y question.
This morning I tried using that same code with a custom STM32f301C8TX board and it hard faulted over and over when trying to jump to the application code. I double and triple checked the memory bounds to make sure everything was aligned (0x5000 NVIC offset). Nothing worked. Main application worked fine when I debug downloaded it through CubeIDE.
Then I retested the F303R3 bootloader code I previously developed and had the same issue: hard faults when trying to jump to the main application. And then after a bunch of debugging and trying different code I found online I went back to my same code and low-and-behold it worked! So then I tried again with the custom STM32F301 board and it worked, too! Hallelujah!
This is good. If the problem reproduces in 100% cases it should be easy to spot. Now it's the time to get know the debugger, especially its hard fault analyzer tool. What does it show? Can you make sense of it?
A hint: local variables may sit on the stack. When you arbitrarily move the main stack pointer, guess what happens to the stack. What if a systick interrupt occurs before the main app sets its vector table?
Also, I don't understand the claim in that article that the bootloader starts at address 0x1FF09800 when the documentation says it starts at 0x1FFF0000 (see AN2606). In any case, I tried jumping to 0x1FFF0000 and got a hardfault, so 0x1FFF0000 seems like the wrong address.
We're trying to automate the process of firmware updates where we have an external control board talking to the STM32H745 MCU and allowing us to do OTA firmware updates, but the BOOT0 line is not connected to the control board. We built a custom board that we don't want to change, so if we have to toggle the BOOT0 line programmatically, it's going to require some really annoying rework we'd rather not do.
3a8082e126