So, this post is a day or two late, but I haven’t had time to work on it since then so this is still current information: I got something to boot!
I’m keeping my code in a Git repository, so I can track changes and such. The changelog from my commit the other night reads:
seth@darwin ~/code/os $ git log -1 8544dcc commit 8544dccc0269b02de59ed608fe7d6083ac568afb Author: Seth Wright <email@example.com> Date: Mon Mar 14 02:51:05 2011 -0400 working "prototype" bootloader. The code in mbr.s copies itself to another location in memory, jumps to that location, then starts executing there. Right now all it does is print a little banner and the current IP, albeit backwards. That was mainly to test whether the code-jump actually worked (it does).
You can view the file, if you care to see ugly x86 assembly. (Note that “IP” above means “instruction pointer”, not “internet protocol”. Just in case you were wondering.)
So what exactly does that get me? This:
When a computer starts (at least, a “PC-compatible” computer), the BIOS
begins executing. The last thing it does is look for the first drive
where the boot sector (first 512 bytes of a disk) ends with 0xAA55.
This is a magic number that tells the BIOS that this disk has a valid
“boot sector”, or Master Boot Record. What it does next is copy the
disk’s boot sector to a specific place in memory (
segment:offset notation), and then “jump” to it and start executing
whatever is there.
That’s where I am now. The BIOS hands control to the compiled code in my mbr.s file, and the result of it running is what you see up above. Not very shiny or exciting to look at, but let me tell you: after spending multiple hours
- writing code;
- seeing nothing on the screen;
- rewriting code;
- seeing ten thousand “Þ” characters echoed onto the screen;
- re-rewriting code;
when you actually see what you told it to print, it’s a pretty nice feeling.
What It Does
When the BIOS jumps to the boot loader, the first thing it does is copy itself to another location in memory. For simplicity’s sake (and because I’m not totally aware of the consequences of picking another location yet), I chose to have it copy itself to the same location that one of Microsoft’s early MBRs used. (By the way, using that page is fairly difficult, since I want to see how they did things but also don’t want to rip off their code, obviously. I have to take quick glances to get the gist of what’s going on, then try to do it myself.)
After that, it “jumps” down to where it copied itself and resumes executing. The rest of the code is to make it print the banner, then get and print the instruction pointer to verify that it actually did jump correctly; had it not, instead of displaying “426” above–which is the string-reversed memory location 0x0624–it would have shown “42C7”, denoting memory location 0x07C24. Once it’s done there, it jumps into a never-ending loop.
Next up on the agenda is to have the MBR code read the disk’s partition table, searching for an active partition. If it doesn’t find one, it should bail. If it does, it will load that partition’s first sector (the Volume Boot Record; or more likely, the first sector of the VBR) into the memory location 0x7C00 (the one we just vacated), and jump there. If you’ve ever heard of chainloading, that’s what this is. Load a boot sector, relocate, find another boot sector, relocate, etc.
After that, work on the second stage bootloader begins, which will be what actually loads the (currently non-existent) kernel.
This is actually more fun than I thought it would be, and I think part of the reason for that is because I’m writing about it as it happens. I get to explain what is going on, why it’s going on, and where it’s going, which I think helps me understand all three of those points better. Plus, I find a lot of the arcane knowledge about the early days of computing to be quite interesting. (Case in point: the blog referenced at the bottom of Wikipedia’s article on a CPU triple-fault. Very clever idea!)
That’s all for now; hopefully I’ll have more to report in the next day or two.