I don’t self-promote usually, but I asked some people on Reddit[1] if they would like an educational stream on embedded systems, and a lot of people said yes. I’m doing one on Sunday most probably. Feel free to join in because that’s one of the things I will be talking about.
Hey kahlonel! Just wanted to tell you that I watched your stream and it was really good, I learned a lot. I don't have a twitch account so I couldn't use the chat when you were waiting for someone to show up. So I just wanted to tell you here that everything was great from sound quality to the content you provided! :)
OS dev[1] has a full page on how to do this and more, as well as links to sample code, etc. Unfortunately it's down right now, so here's a webarchive link from 1st Sept[1.5]
A resource you'll find immensely useful is ctyme's interrupt jump table[2][3], which is easier than reading Ralf Brown's ASCII formatted notes[4]. Of course there are easier ways to do it, but printf() debugging (or the assembly equivalent) is still by far the easiest to set up.
If you want to go write code for modern systems -- UEFI code, you can find a tutorial here[5] (I didn't write this, however, so I don't know how good it is, I can't find the resources I used :c), and the EFI spec here[6]
Well it's pretty platform dependent, but basically all you need is to do is make sure that you're in a consistent state with regards to IRQs and other low level configuration, then clear the BSS, then setup the stack pointer, then you can jump to C.
Here's a random bootloader init code I wrote a while ago for an ARM SoC (probably heavily inspired by uboot):
The vectors are a table that must be placed in a specific location in memory and are used by the CPU to know what code to execute when certain events occur. Here I only fill the "reset" entry to call my init code when the CPU boots up.
Everything in my "reset" function up to line 93 is low level ARM things: making sure the CPU is in a well defined mode, disable IRQs, initialize the cache (optional, but good for perfs) etc... All that stuff is usually described in the docs for the CPU/SoC/Controller. Note that this SoC is fairly complex, if you used a smaller microcontroller it's usually much more straightforward.
Then after that you have a loop to clear the BSS (the _bss_start and _bss_end are generated automatically by the linker script, normally when you load an executable the operating system clears those regions for you, but obviously here we're on our own).
Then I set the stack pointer register SP to _stack_base which is also defined in the linker script to point at some memory location (typically the top of the RAM). And then we're good to go, we can do the rest in C with only small bits of ASM here and there for special instructions.
I think the best way to learn that stuff is by doing, get some ARM chip with an u-boot loader and dig into it, break it, see what code does etc... Obviously you need a good knowledge of C to begin with, and at least a basic understanding of assembly, so start with that if you're not there yet.
Just read the source to crt0 which contains the entry point called by the kernel (a symbol called _start or __start) and which finishes up by calling main(). It should be in the source of your c runtime library (just look at any of the open source ones) with different versions for each architecture and with slight variations for the os.
Might you or someone else have a resource on learning how to do this?