Worlds Simplest Bootloader :: Bare Metal Programming Series 4

  Рет қаралды 22,312

Low Byte Productions

Low Byte Productions

Күн бұрын

In this episode of the bare metal programming series, we're taking our first steps into building a fully fledged bootloader! To do this, we need to split the application in two, build some supporting scripts, learn how to use linker scripts, and understand some of the internal CPU registers for relocating the interrupt vector table at runtime.
=[ 🔗 Links 🔗 ]=
🎥 Series Playlist: • Blinky To Bootloader: ...
🗣 Discord: / discord
⭐️ Patreon: / lowleveljavascript
💻 Github Repo: github.com/low...

Пікірлер: 44
@ivsuk
@ivsuk 9 ай бұрын
Thank you very much. Very clear and articulate explanation. My windows toolchain with stlink debug on WSL was a bit tricky but got there in the end. Anyone struggling: multiarch-gdb symlinked to arm + usbipd.
@stevezhou184
@stevezhou184 7 ай бұрын
like your video so much. bootloader is flying everywhere, but nobody explains exactly what "bootloader" is. This is problem of software, everyone coins their own concept, then use the popular NAME to name it. like your clearness
@alexyoung6418
@alexyoung6418 Жыл бұрын
I like how you linked two otherwise separated projects together to paint a complete picture of how they are allocated in the memory and how they work together, or at least not against each other. It's worth pointing out that some applications also jump back to the bootloader to trigger the firmware update feature. Assuming the worst scenario where main application hadn't done a good cleanup by resetting the interrupt vector offset register, it's recommended to force reset that exact register in the very beginning of the bootloader execution.
@LowByteProductions
@LowByteProductions Жыл бұрын
Great point, the bootloader should also explicitly set the vector table offset.
@CuriousCyclist
@CuriousCyclist 9 ай бұрын
Thank you for taking the time to make this video. Really good educational content. 👍
@フィリップフーベル
@フィリップフーベル 11 ай бұрын
Again, I realy like your project! Some improvement-suggestions: to really understand the content of the jump_to_main function (better name would be jump_to_app_main), it is essential that the listener understand that the linker will do all the main work for us. The address of the jump_fn which we are digging out of the addresses with the pointer joggling is correct BECAUSE the linker puts it there (will/has put it there) with the linker script of the app.
@stevekoehn1675
@stevekoehn1675 11 ай бұрын
Your thinking is so clear, your explanations so clear and logical I Get It! (I'm older and learning on my own so I need all the help I can get, ha)
@johnhansson8646
@johnhansson8646 8 ай бұрын
Regarding the padding of the bootloader to 32kb, couldn’t you configure that in your linker script, so that it is done for you when linking?
@anshulmaurya6913
@anshulmaurya6913 9 ай бұрын
It's been done really really nicely - Thank You
@lorito6995
@lorito6995 8 күн бұрын
many thanks!
@AbidAli-mj8cu
@AbidAli-mj8cu 7 ай бұрын
At @46:51, the first 4 bytes represents Main stack pointer, which is end of the RAM (from where stack starts), 0x20000000U + 0x18000U (96KB) = 0x20018000U
@gionag
@gionag 9 ай бұрын
in linker, why don't use FILL to pad the bootloader ?
@gcm4312
@gcm4312 Жыл бұрын
Amazing content as usual. Thank you for sharing.
@MarvinPranajaya
@MarvinPranajaya 10 ай бұрын
Great content! It has helped me learn so much about bootloaders. Just wondering why do you not set the Stack Pointer to the APP before jumping to APP. I believe we need to do so to utilize all the RAM space? I also realized the reset handler by Libopencm3 doesnt include a Stack Pointer intialisation.
@kilwo
@kilwo Жыл бұрын
Just wondering why you do a C++ function call in the bootloader? If you did an assembly jump (branch I think in ARM) then you could reset the vector table offset address before the jump and the main app wouldn't need to know anything about the size of the bootloader. The main app would just work as if it was the only thing in memory and you could change the bootloader size without needing to update the main app.
@LowByteProductions
@LowByteProductions Жыл бұрын
First of all, thanks for taking the time to comment! I'm not sure what you mean by a C++ function call. Where possible I'm trying to avoid inline assembly - the compiler is usually (like 99% of the time) smart enough to do the right thing when expressed in C. For example, the process for getting the address of the reset vector, casting it to a void (*)(void) function pointer and calling it, results in basically the assembly you describe; figuring out an offset, getting the reset vector address indirectly, and using a bx (branch) instruction to jump to it. Your point about setting the vector table offset in the bootloader is a really good suggestion - I guess that does make a lot more sense!
@kilwo
@kilwo Жыл бұрын
@@LowByteProductions Thanks for taking the time to make the videos! I really enjoy low level stuff. Sorry, my thoughts were not 100% clear. The C++ function call I was referring to was just the fact that you cast the pointer into a function, and then call that. The reason I was thinking this should be an unconditional jump, or equivalent, was because a function call effects the stack, where a jump doesn’t. At least in the processors I have used. Once you have changed the vector offset register, you don’t want to do a call that will push onto the stack, as it will never be returned and will waste memory. Keep up the great videos.
@LowByteProductions
@LowByteProductions Жыл бұрын
Ah OK I see what you mean. The C++ threw me off there - I don't think there is a relation to C++ here. You're right that you'll end up with an unecessary stack frame, but one of the first things that happens in the reset vector for both the bootloader and the main application is setting the stack pointer to an explicit value. That has the effect of essentially removing tany stack frames that would have been there previously, and so doing a cast/call isn't a problem. And even so, no arguments or other information is pushed to the stack (at least the way this is currently compiled), as the cast is a 0 argument function. Even if the blx instruction were used (branch and link), the return address of the function is still only placed into a cpu register, not the stack. I really enjoy digging into the details of this kind of thing, so the discussion is very much appreciated.
@ZeroPlayerGame
@ZeroPlayerGame 11 ай бұрын
@@LowByteProductions I actually went and checked, and I'm sorry to say, but libopencm3's reset handler does none such. All it does is copy the .data section into RAM, and run global constructors.
@nhanNguyen-wo8fy
@nhanNguyen-wo8fy 6 ай бұрын
10:30 flash memory 15:50 fix make file 17:10 what bootloader do 50:40 main 59:30 vector table offset register
@john999
@john999 Жыл бұрын
Thank you very much for your tutorial. It is so kind of you, to give us this free and easy to understand introduction into bootloaders. When I started to experiment with bootloaders, I was overwhelmed by the whole documentation of the chip. And of course it did not work, so I was thrown off, not knowing why it failed. With this start-to-finish mini-demo it is a lot easier to get going and building from that on forward. Would be nice if you could mention some ways of including your handmade changes of the linker script to IDEs (e.g. Eclipse). Like, where you can use the settings of the project to change the memory layout or add sections. Maybe this helps other to include your bootloader to their current project w/o too much editing of linker files that are handled by the autogen of the IDE.
@LowByteProductions
@LowByteProductions Жыл бұрын
Thank you John. Indeed - I'll point out whenever I'm making changes to linkerscripts etc, and what those changes imply. As for playing well with other IDEs, I'm not sure how much help I can be 😄 I try to avoid eclipse wherever I can! I am familiar with STM32CubeIDE however, and how it can be configured to use a makefile build, which gives a lot more control. There are still autogenerated files to deal with, but typically the linkerscript is not one of the files which is not regenerated across builds. I'll add it to the list of future topics.
@LaSDetta
@LaSDetta Жыл бұрын
Really nice video! I was wondering if you could use the FILL command in the linker script to ensure that the bootloader binary is always 0x8000 bytes?
@LowByteProductions
@LowByteProductions Жыл бұрын
Thanks Håkan! Yes this is definitely possible, but I think you'd need to change the semantics a little. To use fill, I think you'd need to define a memory region for the bootloader. Not a bad way of doing it at all, bit there is one advantage to the current approach that I'm not sure how to replicate using fill: its easy to wrap the bootloader.S code, and the vector_setup() function in #ifdefs, and pass a flad to the compiler like -DINCLUDE_BOOTLOADER. This way, you can conditionally include the bootloader blob in the application, or choose to omit it. Without the image, the main application would end up at 0x08000000, and no VTOR offset would be required. Not a deal breaker by any means, but something to consider.
@bobweiram6321
@bobweiram6321 Ай бұрын
Is it possible to pad the bootloader using the assembler. You can create a data section and then include your bin .
@aymaneeljahrani2280
@aymaneeljahrani2280 3 ай бұрын
I’m queen on your videos !
@MohammadShikha
@MohammadShikha Жыл бұрын
Hey, thanks for the video. I'm currently following along with the STM32F767ZI and the vector offset macro SCB_VTOR seems to require the final vector table address (0x08008000U) rather than an offsetting value (0x8000U) in order for interrupts (like SYS_TICK) to work. I'm not sure as to why this is the case, am I missing something obvious?
@LowByteProductions
@LowByteProductions Жыл бұрын
That shouldn't be the case - the VTOR is literally the "vector table offset register", i.e. it always specifies an offset. When you are moving between the bootloader and main application, you need to disable any interrupts that could occur before you're able to change the offset value. Just to check: you are using libopencm3 right? Because there is only one macro definition there, and it just points to a memory mapped io register. Edit: just checked the reference manual for cortex-m7, and indeed, the vtor specifies and offset from 0x00000000 - so yes it is effectively an absolute address. You'll have to adjust the M4 based code to account for this. I'm sure they're be other subtle detail changes too!
@twitchy9948
@twitchy9948 2 ай бұрын
How did you come with this? I have exact stm32, and after setting it 0x08008000U it worked, thanks!
@VectorNodes
@VectorNodes 9 ай бұрын
37:36 it stands for “bull shit stuff”
@SumitAdep
@SumitAdep 5 ай бұрын
well explained
@dty999
@dty999 Жыл бұрын
I'm not a C programmer, but at 25:10, why don't you let the compiler do the pointer math for you, and use reset_vector[1] instead of adding 4 to the address?
@dty999
@dty999 Жыл бұрын
Or, strictly, I guess you should redefine reset_vector as vector_table, then vector_table[1] makes more sense!
@LowByteProductions
@LowByteProductions Жыл бұрын
Definitely possible, and perhaps the better way to do it! Libopencm3 actually defines a structure for the vector table, so casting the address to a pointer of that structure, and referring to the reset vector by name would another option.
@dty999
@dty999 Жыл бұрын
Or better yet, just have something like void_fn* interrupt_table = (void_fn*)MAIN_APP_START_ADDRESS; and then you can just go interrupt_table[1](); to call it.
@angryman9333
@angryman9333 Жыл бұрын
you are a genius.
@stati5tik
@stati5tik 9 ай бұрын
30:30 who set the address to the reset_vector in reset_vector_entry? is this hardware specific?
@LowByteProductions
@LowByteProductions 9 ай бұрын
It's per-platform, indeed. On the STM32 Cortex-M4 chips it tends to be at 0x80000000, but another vendor might map the beginning of flash elsewhere
@saturdaysequalsyouth
@saturdaysequalsyouth Жыл бұрын
.bss is something like "block started by segment". It's probably an outdated term that was repurposed which is why it doesn't make sense.
@viniciusgabriellinden4724
@viniciusgabriellinden4724 Жыл бұрын
why not something like `void_fn jump = (void_fn) (MAIN_APP_START_ADDRESS + sizeof(int)); return jump();`? more concise and does not depend on the bit width...
@LowByteProductions
@LowByteProductions Жыл бұрын
You're absolutely right, and I have a feeling you'll enjoy the next video in the series 😁
@ulysses_grant
@ulysses_grant 7 ай бұрын
I am a simple man. I see a random channel with a video of a guy dumping game boy cartridges and I subscribe.
@LowByteProductions
@LowByteProductions 7 ай бұрын
Me too buddy, me too.
@ulysses_grant
@ulysses_grant 7 ай бұрын
​@@LowByteProductionsJust learned about your channel. Sent the link to my brother and he just "LowByte?! This guy is a monster!" lol. Definitely subscribed!
Understanding Assembly Generated From C :: Bare Metal Programming Series 4.1
26:50
Bare-Metal MCU #4 - Bootloaders and Programmers
18:24
Mitch Davis
Рет қаралды 59 М.
Сюрприз для Златы на день рождения
00:10
Victoria Portfolio
Рет қаралды 2 МЛН
1 сквиш тебе или 2 другому? 😌 #шортс #виола
00:36
Smart Sigma Kid #funny #sigma
00:14
CRAZY GREAPA
Рет қаралды 9 МЛН
Hello World Blinky! Bare Metal Programming Series 1
38:34
Low Byte Productions
Рет қаралды 32 М.
Introduction to CPU Pipelining
10:29
Merlin Wellington
Рет қаралды 43 М.
How does an OS boot? //Source Dive// 001
50:22
Low Byte Productions
Рет қаралды 419 М.
Interrupts and Memory Mapped I/O :: Bare Metal Programming Series 2
1:17:25
Low Byte Productions
Рет қаралды 18 М.
Bootloading 101
16:17
Texas Instruments
Рет қаралды 75 М.
Embedded Linux Booting Process (Multi-Stage Bootloaders, Kernel, Filesystem)
33:13
How Does Linux Boot Process Work?
4:44
ByteByteGo
Рет қаралды 662 М.
Сюрприз для Златы на день рождения
00:10
Victoria Portfolio
Рет қаралды 2 МЛН