
Booting to 'Hello Rust' on x86_64 - micouay
https://micouy.github.io/posts/low-level-pt-1/
======
ajross
For those checking comments first: this is mostly a tutorial on how to build
and link a 16/32 bit MBR loader and mode switch implementation in assembly.
It's very similar to what you'll find on osdev.org. The Rust content is
limited to a single function at the end which writes some characters to an
assumed-pre-initialized EGA text console.

While modern hardware does continue to support these modes via various
detection tricks, "real" handoff from the firmware happens in a 64 bit EFI
environment these days, and the framebuffer tends to be in a graphics mode and
expects to be accesesed initially via the exposed UEFI API (or to be reset
from scratch by an OS graphics driver, of course).

This is fun and instructive, but not really reflective of the way modern
hardware boots.

~~~
javajosh
I'm curious to learn more about "real" handoffs and the EFI environment. If
you had any suggestions about where to start I'd be grateful.

~~~
ajross
There's sort of a paucity of tutorials in that space. You're pretty much stuck
reading specs and studying source code. Here's the UEFI specification and a
link to the "gnu-efi" project, which provide a reasonably clean (if...
idiosyncratic) environment for building and running EFI binaries with a free
toolchain:

[https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8...](https://uefi.org/sites/default/files/resources/UEFI_Spec_2_8_final.pdf)

[https://sourceforge.net/projects/gnu-
efi/](https://sourceforge.net/projects/gnu-efi/)

Broadly: EFI is a simple C environment (albeit one based on windows-style PE
binaries and not ELF). The firmware will load and run your application binary
and jump to its entry point, passing a giant table of API hooks and callbacks
for you to use to interact with its services. You then read and assemble your
own OS image using these tools and jump into it.

~~~
moonchild
Personally, I haven't found the gnu efi toolchain very helpful. I just use
clang with the appropriate flags, which are:

    
    
      -target x86_64-unknown-windows -ffreestanding -fno-builtin -fshort-wchar -mno-red-zone
    

And link, again with clang, using these flags:

    
    
      -target x86_64-unknown-windows -nostdlib -Wl,-entry:efi_main -Wl,-subsystem:efi_application -fuse-ld=lld-link
    

I believe you can do the same with gcc and gold|gnu ld without much more
effort. The toolchain doesn't really justify its complexity at all.

The headers, on the other hand, are invaluable. I use c-efi[1]'s headers, but
would probably use gnu efi another time; c-efi has some bugs and missing
structures. Worth noting: despite the name, gnu efi isn't gpl licensed. In
fact, I don't think either (c-efi or gnu efi) can really be considered
copyrightable; they're essentially direct translations of the spec.

1\. [https://github.com/c-util/c-efi](https://github.com/c-util/c-efi)

~~~
moonchild
Additionally, efi isn't useful for much beyond a bootloader, so you can use
clang for the bootloader and gcc (or whatever else) for the OS itself. So you
can depend on non-clang features.

------
Tainnor
I really appreciate posts like this and wish there were more of them.
Tutorials by experts are valuable, but I also find it comforting to read that
other people are sometimes as clueless as I am, and I think posts like this
can end up explaining some of the pitfalls that some more expert users aren't
really aware of. We need more humility in software.

(I remember that I had tried to write a bootloader myself in assembler, maybe
10 years ago or so, and in the end was just incapable of getting it to work.)

~~~
micouay
Thank you! I also wish there was more content like this.

------
ekidd
If anybody enjoys this and wants to take it much further, you might want to
check out [https://os.phil-opp.com/](https://os.phil-opp.com/). This is
basically a book-in-progress that shows how to build an OS in Rust OS from
scratch. It shows how boot up an OS, and it goes on to show how to set up
basic drivers and memory management. He does a great job of explaining each
step as he goes.

(This is linked near the end of the article, but I think it's worth pointing
out.)

Full disclosure: I think he uses a library I wrote to initialize the keyboard
controller.

------
micouay
It's my first post. I'd appreciate your feedback :)

~~~
messe
Hey, just a couple of points

1\. Don't link the bootsector and the rest of your binary together. It's more
trouble than it's worth, and if you ever progress to loading off a filesystem,
then you won't be linking them together anyway.

2\. You can use LBA addressing in real mode with int 13h (just specify a hard
disk rather than a floppy when booting):

[https://wiki.osdev.org/ATA_in_x86_RealMode_(BIOS)#LBA_in_Ext...](https://wiki.osdev.org/ATA_in_x86_RealMode_\(BIOS\)#LBA_in_Extended_Mode)

3\. You need to switch to protected mode, set up paging, and then enter long
mode.

4\. I'd recommend compiling your rust code to an ELF binary, and then using
objcopy to create a binary:

    
    
        objcopy -O binary $input.elf $output.bin
    

Alternatively, ELF isn't that hard to parse, so you could load it yourself and
then copy sections to the right locations with your bootsector.

5\. OSDev.org is your friend. Maybe get a C-based kernel running first, as the
toolchain when dealing with freestanding code is a lot friendlier for
beginners

\-
[https://wiki.osdev.org/Protected_Mode](https://wiki.osdev.org/Protected_Mode)

\-
[https://wiki.osdev.org/Long_Mode#Long_Mode](https://wiki.osdev.org/Long_Mode#Long_Mode)

You might be better off starting with 32-bit rust code instead, in order to
avoid paging and long mode at first (you'll still need to switch to protected
mode)

~~~
steveklabnik
I am extremely biased, but I’ve always found the rust tool chain far easier
than the C ones, especially as a Windows user. No need to set up a cross
compiler, given that rustc already is one, and is easy to add more targets to
via the JSON target spec.

~~~
messe
A cross compiler is easy enough to set up with clang too (just specify the
-target command line option).

I just think that when starting out in kernel development, you're likely going
to be dealing with linker scripts for likely the first time. It's been a while
since I tried rust for this, but C generates pretty clean object files, so the
linker scripts from the OSDev wiki can often be copied verbatim.

~~~
steveklabnik
That’s fair re:clang, seems like gcc is used way more often in tutorials.

I don’t think that the linker script stuff ends up different but it’s been a
minute since I started from scratch.

------
eloisius
Neat! I read The little book about OS development[1] and wrote this little OS
in Rust a few years ago:
[https://github.com/zacstewart/tinyos](https://github.com/zacstewart/tinyos)

1: [https://littleosbook.github.io/](https://littleosbook.github.io/)

------
kgraves
This post got me extremely excited, will have a read on the way home!

great job OP!

~~~
micouay
Thank you so much!

------
azhenley
A relevant tutorial on making an OS in Rust but for RISC-V:
[http://osblog.stephenmarz.com/index.html](http://osblog.stephenmarz.com/index.html)

------
secondcoming
I did something similar many years ago using nothing but debug.exe that came
with windows 95, a spare floppy and an older version of The Indispensible PC
Hardware Book [0].

It was very satisfying seeing my PC boot to printing my name.

[0] [https://www.amazon.co.uk/Indispensable-PC-Hardware-
Book/dp/0...](https://www.amazon.co.uk/Indispensable-PC-Hardware-
Book/dp/0201596164)

------
ggcr3ss
Thank you for this!

~~~
micouay
I'm glad you like it.

------
blargmaster33
God Rusts syntax is off-putting.

------
historyremade
Boot to "FUCK RUST PROPAGANDA"

