top | item 40959742

Writing a BIOS bootloader for 64-bit mode from scratch

260 points| thasso | 1 year ago |thasso.xyz

80 comments

order

5-|1 year ago

note that you can switch to long mode directly, without going into protected mode first, with way less code:

https://wiki.osdev.org/Entering_Long_Mode_Directly

i've had a bootloader for a small 64-bit kernel based on this that fit comfortably into the bootsector, including loading the kernel from disk and setting up vesa modes, no stage2 required.

dataflow|1 year ago

> i've had a bootloader for a small 64-bit kernel based on this that fit comfortably into the bootsector, including loading the kernel from disk and setting up vesa modes, no stage2 required.

How in the world do you fit all that in 512 bytes? I'm guessing you don't have a real-world filesystem (that allows the kernel to be anywhere on the disk just as a normal file)? Because just dealing with file fragmentation should bump you way over 512 bytes I would imagine.

ChickeNES|1 year ago

Or just use https://limine-bootloader.org/, which greatly simplifies everything. No messing around in real mode (even when doing SMP), automatically loads your kernel using a higher-half mapping, and also works on aarch64 and riscv64.

sim7c00|1 year ago

you are right. Though with the partition table in there so u can support a 'modern' AHCI controller and SATA it will shrink your bootloader further and require some optimizations.... - you don't have 510 bytes for the loader in this case but a bunch less. if you want to populate the table with valid entries then it becomes even more tricky (can't use any bytes inside of the table...)

If you want to use an actual modern harddisk, you might want to look at GPT rather than MBR, as it won't overflow partition table stuff and allow for very large disks (2TB+?) (uefi gets rid of all of that and allows u to use a proper disk layout without any additional difficulty!)

there is no need for protected mode if you want to launch into 64-bit mode. I would say though, DO NOT USE BIOS. It's a piece of rubbish which will just make things more tedious.

Using UEFI via EDK2 or GnuEFI is the way to go, and both methods are really easy and a blessin to implement. It's a bit hard to get around the initial idea of UEFI, but if you view some other people's example projects on github you can find easily how it works. EDK is a bit shitty with .dec and .inf files etc, and GnuEFI is just reading headerfiles to discover what is there, but it's inifnitely better than the unspecified bios interface. You can litterally not even assume the int 0x10, int 0x15 etc. are there properly if you start running on real hardware. On UEFI systems, you can assume a stable minimal basis, and easily enumerate other things (hardware/platform capabilities etc.) in a sane way.

Also UEFI sets up the platform a long way already, so you don't need to do any initialization for your os-loader component (stage2 or 3). You can simply start loading your os right away. or drivers or whatever kind of design your kernel is. get memory map, get some access to efi file system, start pulling in and loading stuff.

xelxebar|1 year ago

Oh, cool. I never knew that was possible. Showing my ignorance here, but assuming we're just trying to get to long mode, why would we tour through protected mode at all?

D4ckard|1 year ago

Yes, you can do that too

hyperman1|1 year ago

The 80286 has the Machine Status Word (MSW), a 16 bit register. The 80386 expands this to CR0, a 32 bits register. Then 64 bit long mode adds the EFER MSR and expands CR0 to 64 bits. But even today only 11 bits of CR0 are in use and EFER has 8 active bits. I wonder why intel/AMD did not simply use the free bits of the existing register, and made that decision twice?

https://wiki.osdev.org/CPU_Registers_x86-64#CR0.

rcxdude|1 year ago

Probably for more robust backwards compatibility with software that might assume a given value for or write to the reserved bits. The assignment of bits to registers like this in the hardware is pretty arbitrary, there's not really any cost to using the higher bits

userbinator|1 year ago

The one-word answer is probably "bureaucracy". Large groups of people just don't tend to make particularly good decisions overall, and a lot of nonsensical choices arise from that.

Ditto for why CR1 and 5-7 are still "reserved" and CR8 came into existence.

rep_lodsb|1 year ago

The most unnecessarily complicated thing in this article to me is the Makefile and linker script. NASM supports generating flat binary output, but apparently using it would be too "hacky"?

darby_nine|1 year ago

I find linker scripts much easier to read and reason about than flat nasm but that's just me. Especially with multiple source files.

sim7c00|1 year ago

you are totally right.

Later on, make files and linker scripts are an important headache. but when generating flat binary, just generate flat binary! No need to bloat it.

My OS used to have a file called make.sh to tease at this :D Now i am using a 'fileformat' and other fancy things and alas -fbin and --oformat=binary are but fleeting memories. I tried a long time to write separate data c files and code c files, dump it out to binary, and then building some monstrosity from there but it gets really difficult to link&load etc. xD --- better to just use the ELFs or PEs! I suppose that is litteraly what they do for u ;P

ForOldHack|1 year ago

This seems both cool, and a good exercise, but is it useful? Does it have a UX like a fisher/price toy that you can verify/change your settings on the fly?

Booting is the process of going from mini-me mode/single user/recovery mode to flying.

I have been running Unix along side a Microsoft product since Xenix/dos. ( Looks like 40 years...) How much have we advanced?

I also have been using Linux since the swedish version came out ( first release ) and GNU 0.1.

My apologies about calling Xenix, Unix, It is a has-been wanna-be me-too square-excrament from shortly after release until it's languishing demise.

Microsoft does not release products, they empty their cat boxes onto customers. ( The most recent example is both co-pilot And 22H2. )

If you look at how F1 cars have evolved, and pencils as well as pocket calculators - how close are we to the usable ideal?

Why isn't the bootloader a static kernel mode? It used to be. Someone recently suggested it should be, and I agreed.

ThinkBeat|1 year ago

All to me entirely unnecessary steps required to get the CPU into the correct mode is astounding.

They all seem to be steps needed for backwards compatibility.

Could Intel just provide a flag, command, to start in the right mode from the beginning.

Or just remove all the backwards compatibility.

I think I remember doing some research and ARM64 has some of the same issues.

Are there any CPUs that are designed from scratch as 64 bit it will not have any need for backwards compatibility and would enter the required state by default?

I guess sthat was the goal / design of Itanium?

are made to start in the desired 64 bit state from th

nullindividual|1 year ago

This is what Intel's proposed X86S [0] is designed for.

> X86S is a legacy-reduced-OS ISA that removes outdated execution modes and operating system ISA.

> The presence of the X86S ISA is enumerated by a single, main CPUID feature LEGACY_REDUCED_ISA in CPUID 7.1.ECX[2] which implies all the ISA removals described in this document. A new, 64-bit “start-up” interprocessor interrupt (SIPI) has a separate CPUID feature flag.

[0] https://cdrdv2.intel.com/v1/dl/getContent/776648 [pdf warning]

userbinator|1 year ago

Intel tried that with the 80376 and it did not go well: https://en.wikipedia.org/wiki/Intel_80376

Neither did the Itanium (Itanic).

Backwards compatibility is the whole reason for choosing x86 over ARM, MIPS, RISC-V, etc. Sadly it seems some people at Intel and AMD don't realise this.

LiamPowell|1 year ago

UEFI exists. You just put a Windows-like binary in a folder on a partition and it runs in a hosted environment in 64-bit mode. And of course there's countless bootloaders that can take care of all this for you too.

saagarjha|1 year ago

What’s wrong with arm64?

cf100clunk|1 year ago

A laudable project. UEFI proponents here wondering why the person bothered to create a new bootloader approach might be missing the point of why people undertake such tasks as this. As the writer ends:

> Cool if you actually came along this far.

Cool indeed.

AstralStorm|1 year ago

How old is UEFI now? Pity nobody deprecated BIOS alongside long mode.

deaddodo|1 year ago

BIOS is deprecated. All of its functionality on new motherboards is basically emulated via the UEFI; and it's certainly not being extended upon.

Deprecated doesn't mean deleted, it just means "no longer updated/developed with a goal towards removal".

ruslan|1 year ago

Does this boot procedure work with EFI/UEFI ? If so, does UEFI supervisor emulate swithing real/protected/long modes or does it go in real hardware ?

khaledh|1 year ago

No. UEFI firmware creates a completely different environment for a UEFI bootloader than the legacy BIOS environment (real-address mode). The UEFI firmware enters 64-bit long mode directly on modern systems, and sets up a flat memory model GDT, as well as identity-mapped paging.

I've written about creating a UEFI bootloader (for my hobby OS) here: https://0xc0ffee.netlify.app/osdev/05-bootloader-p1.html

amelius|1 year ago

Is this any simpler on ARM?

rwmj|1 year ago

Only in the sense that every board vendor does their own random thing, which makes it simpler for the board vendors and horribly complicated for everyone else.

surajrmal|1 year ago

Yes. Bootloaders are still complex, but there is less legacy setup that is required. That said, if you're targeting UEFI instead of BIOS, it's a great deal simpler on x86 as well.

gtirloni|1 year ago

Not sure, I wouldn't count on it. Currently deep in RISC-V and it seems there's hope.