Computers are amazing machines. They can perform massive amounts of stuff every second, and they’re being put into everything around us to make the things we interact with on a day-to-day basis smarter and better.
This is made all the more remarkable by the fact that computers are astonishingly dumb. My favorite explanation of just how dumb computers are can be found in one of my favorite recorded talks, Richard Feynman’s lecture about computer heuristics. (Start around the 10-minute mark for the particularly relevant bit.)
One of the secrets to making these dumb machines do smart things is the way applications get loaded and executed within your OS. But have you ever thought about who loads the code that loads your code?
In every computer, there’s a little chunk of under-appreciated code that someone put a lot of sweat and tears into. Known as a bootloader, this little piece of hardware-specific code knows how to put together a sane world for your operating system to live in.
Its responsibilities vary by CPU architecture, but they generally include:
- Low-level hardware initialization
- Initial kernel loading
- Handling of tricky things like reset logic
On some platforms, such as ARM, the bootloader can have even more responsibilities, such as:
- Memory controller setup
- Peripheral clock tree configuration
- Complex memory mapping
- Processor cache management
- Voltage regulation
Just think, before the bootloader runs and cleans up the world, your processor may not even able to remember things or talk to anything outside the small world of the CPU core.
When you take a closer look at the way your computer sets up its world state, it starts to seem like a miracle that it can boot at all. For example, the way your PC’s BIOS decides that a storage device is a valid source to boot from is simple, but brain-dead. The BIOS first loads the first sector (512 bytes) off a device and checks the contents of bytes #511 and #512. If they’re 0x55 and 0xAA respectively, the BIOS decides the device is good and will execute bytes 1-510 as code–which is then responsible for loading any further code needed to grab a kernel and create a sane execution environment from your boot volumes.
Should you want to read more about the wonders of early system boot, I’ve assembled a few links:
- The Wikipedia overview with a bunch of good reference links
- A good general overview of x86 booting for modern PCs
- A fairly accessible overview of the x86 boot process, including annotated sample code from several common Linux boot loaders
- An overview of ARM boot responsibility sharing and how crazy processor init can become, with multiple stages each with their own responsibilities
The boot process of modern PCs is truly fascinating, and I think it’s often unappreciated. What code do you use day-to-day that doesn’t get enough love?