Page 1 of 1

Not sure how correct my bootloader code is.

Posted: Mon Feb 27, 2012 3:00 pm
by Synon
I've (partially) written the first stage of a bootloader which, hopefully, will load 32 kiB from either a floppy disk (using CHS) or the bootable partition of a hard disk (using CHS or LBA), then parse that 32 kiB as an ELF executable, and then jump to its entry function. The ELF executable will be my second stage.

The thing is, I'm not entirely sure if my code is correct. The main things I'm concerned about are:
1. The code for parsing the partition table ("dap" is a Data Address Packet, 0x0c is the offset into a partition table entry for the LBA of the partition's first sector).
2. The LBA code
3. The ELF parser. I haven't written this yet, but I only have 192 bytes left in my boot sector to write it with. I might be able to do a super rudimentary ELF parser in 192 bytes; plus, I have some duplicated code that I could probably un-duplicate if I really tried, so I could probably get more space. If I can't write an ELF parser with however much space I can get, then I'll just have to have three stages rather than two.

And if anyone is feeling super generous with their time: Full code (X11 licensed).

I'm aware that the code is not perfect or anything; this is a first draft (I just spent the last 3 or so hours writing it) which needs a lot of cleaning up. Hopefully the comments are helpful and not excessive (and not indicative of my relative lack of experience with assembly). I want to get it working before I try making it pretty.

Thanks.

[edit 1] I've noticed a few comments that say "parse" where they should say "loaded"; it's because I had a function called "loaded" which I renamed "parse" using find-and-replace-all... I forgot it includes comments :P
[edit 2] Also, just in case anyone is in any way interested in the name ("Schwann") it's a reference to Schwann cells which, among other things, are responsible for the myelin sheath that surrounds neurons in the peripheral nervous system of animals. Since my kernel is called Myelin*, I thought of calling the bootloader Myelinator (the process of myelin production being "myelination"), but that was too long, so I decided to name it Schwann after the cells that perform myelination.

* the rationale behind that name is simpler - I heard the word in biology and I thought it was an auditorily (like aesthetically, but for sounds) pleasing word (maybe because it rhymes with violin).

Re: Not sure how correct my bootloader code is.

Posted: Mon Feb 27, 2012 3:19 pm
by bluemoon
For ELF, if you don't need to do relocations you may just do a one lookup in the main headers to get the entry point. If you really lack of space or don't bother with ELF parser now, try jumping to offset 1024.

EDIT:
Here is an example:

Code: Select all

typedef struct {
    unsigned char   e_ident[16];        // EI_NIDENT = 16
    Elf32_Half      e_type;
    Elf32_Half      e_machine;
    Elf32_Word      e_version;
    Elf32_Addr      e_entry;
    Elf32_Off       e_phoff;
    Elf32_Off       e_shoff;
    Elf32_Word      e_flags;
    Elf32_Half      e_ehsize;
    Elf32_Half      e_phentsize;
    Elf32_Half      e_phnum;
    Elf32_Half      e_shentsize;
    Elf32_Half      e_shnum;
    Elf32_Half      e_shstrndx;
} Elf32_Ehdr;

Elf32_Ehdr* ehdr = (Elf32_Ehdr*) image;  // the file content
// TODO: do some validations
entry = image + ehdr->e_entry;

Re: Not sure how correct my bootloader code is.

Posted: Mon Feb 27, 2012 3:22 pm
by Synon
bluemoon wrote:For ELF, if you don't need to do relocations you may just do a few lookup in the main headers to get the entry point. If you really lack of space or don't bother with ELF parser now, try jumping to offset 1024.
Thanks for the advice. I guess what I'll do is look in the header to get the magic number and the entry point, then I'll just try to validate the magic number and if it succeeds, jump straight to the entry point.

Re: Not sure how correct my bootloader code is.

Posted: Mon Feb 27, 2012 3:52 pm
by Synon
berkus wrote:Why not use multiboot? It fits nicely.
This is just stage 1 of a bootloader that will have a second stage (maybe supporting multiboot). Currently my kernel boots from GRUB but I want to write my own :)

Re: Not sure how correct my bootloader code is.

Posted: Mon Feb 27, 2012 4:26 pm
by stevenson
original

Code: Select all

        int     0x13
        and     cx,     0       ; CX bitflags bit 0 -> LBA supported?
        cmp     cx,     0       ; CX = value of bit 0.
        je      use_chs         ; CX = 0 therefore LBA not supported.
new

Code: Select all

        int     0x13
        jc      use_chs
        cmp     bx, 0xaa55
        jnz     use_chs
        test    cl, 1
        jz      use_chs      
I would suggest to drop ah=0x48 function because disks will be emulated as 512byte sector disks. For USB you need to boot as USB-ZIP or USB-CD for non standart sector size. For CDs I would make separate boot sector.

USB can be booted as floppy emulated but if use dynamic CHS/LBA detection then your boot sector doesn't need any change.

If you use CHS then use ah=8, int 0x13 function. Very important for USB and hard drives.

If you are planning to support real(non-emulated) floppy then make another boot sector and use values from fat12 bpb and don't use ah=8 at all. Floppies don't use LBA.

Stay away from USB fash drives less than 1GB. They work time to time, there are some rules but I won't talk about that now.

Re: Not sure how correct my bootloader code is.

Posted: Mon Feb 27, 2012 5:05 pm
by Synon
stevenson wrote:I would suggest to drop ah=0x48 function because disks will be emulated as 512byte sector disks.
Even for LBA?
stevenson wrote:For USB you need to boot as USB-ZIP or USB-CD for non standart sector size.
Oh, ok; thanks.
stevenson wrote:For CDs I would make separate boot sector.
That's what I was planning to do.
stevenson wrote:USB can be booted as floppy emulated but if use dynamic CHS/LBA detection then your boot sector doesn't need any change.
It does detect CHS/LBA at runtime so that should be fine.
stevenson wrote:If you use CHS then use ah=8, int 0x13 function. Very important for USB and hard drives.
Why do I need to do that?
stevenson wrote:If you are planning to support real(non-emulated) floppy then make another boot sector and use values from fat12 bpb and don't use ah=8 at all. Floppies don't use LBA.
It detects whether it's booting from a hard drive or floppy disk at boot time in the 'branch' procedure. If it's a floppy (DL <= 0x01) it jumps to the 'is_fdd' function which is located at sector 1 byte 446 (on hard disks, the floppy disk loader will not exist since that's where the partition table goes). If DL is a hard disk then it jumps to the 'is_hdd' procedure instead, which then checks whether the HDD supports LBA or CHS. If LBA is supported it jumps to 'use_lba'; otherwise, it jumps to 'use_chs'. The 'is_fdd' function loads the rest of the track (the 63 sectors that follow the boot sector) while the 'use_lba' and 'use_chs' functions find the active partition and load the first 32 kiB.

I'm planning to have my OS boot from ext2+ file systems only (at least for now) and AFAIK ext2 doesn't support a BPB.

Re: Not sure how correct my bootloader code is.

Posted: Mon Feb 27, 2012 5:27 pm
by stevenson
stevenson wrote:
If you are planning to support real(non-emulated) floppy then make another boot sector and use values from fat12 bpb and don't use ah=8 at all. Floppies don't use LBA.

It detects whether it's booting from a hard drive or floppy disk ...
I actually don't know how BIOS will behave(int13 geometry) if it doesn't find FAT BPB on a device that it considers floppy.

A lot depends on USB. You can't distinguish between non-emulated floopy and usb emulated floppy(same # in DL). And for usb its better to use ah=8 regadless if its floppy or disk emulated. Some bioses (probably older) may not boot if MBR and partition table on USB is present.
stevenson wrote:
If you use CHS then use ah=8, int 0x13 function. Very important for USB and hard drives.

Why do I need to do that?
What do you mean why? Look description.
And for testing purposes turn of LBA, use CHS only for hard disk and usb(with usb it might not work once in while). Create bootable partitions in different areas of hard disk.

Re: Not sure how correct my bootloader code is.

Posted: Tue Feb 28, 2012 10:09 am
by Synon
I actually don't know how BIOS will behave(int13 geometry) if it doesn't find FAT BPB on a device that it considers floppy.
SeaBIOS (the Qemu BIOS) seems to accept it (sometimes I have Qemu load just my stage 1 binary file directly and it doesn't complain). I don't know how other BIOSes (especially old ones) would treat it though.
A lot depends on USB. You can't distinguish between non-emulated floopy and usb emulated floppy(same # in DL). And for usb its better to use ah=8 regadless if its floppy or disk emulated. Some bioses (probably older) may not boot if MBR and partition table on USB is present.
I was planning to have my installer detect whether the target device is a hard disk, floppy disk, USB or CD. If it's a hard disk it will write just the first 446 bytes of the stage 1 binary (so it doesn't destroy the partition table) and then the boot signature (0xaa55) at the end. On floppies and USBs it will write the entire file, because the bytes 446-510 contain the floppy booting code (since floppies don't have a partition table). That way, the hard drive partition table won't be destroyed and floppies and USBs won't have a partition table.
What do you mean why? Look description.
So, for floppies, I need to use INT 13/AH=8 to get the Drive Parameter Table and then use INT 13/AH=2 to load the appropriate amount of sectors? Should I also use it for non-LBA hard disks?
And for testing purposes turn of LBA, use CHS only for hard disk and usb(with usb it might not work once in while).
Are you saying not to use LBA for hard disks?
Create bootable partitions in different areas of hard disk.
I thought you could only have one active partition. Currently my first stage assumes stage 2 is at the start of the first active partition, so it loads the first 32 kiB (meaning the ELF binary can be up to 32 kiB in size; I may increase that if necessary).

Re: Not sure how correct my bootloader code is.

Posted: Fri Mar 02, 2012 3:49 pm
by Synon
I've pretty much rewritten my bootloader; I think it's better.

The first stage successfully finds and parses the ELF file (at least on floppy disks it does, I haven't tested it on other devices yet), then finds the entry point and jumps to it. It's definitely getting the right entry point and jumping there but when I look at the registers, the wrong value is in EAX.

This is the code that jumps to the ELF entry point in stage 1:

Code: Select all

	; Get the entry point and jump to it.
	.get_elf_entry:
		mov	ebx,	[bx + 24]
		mov	eax,	0x1BADB002
		jmp	[es:ebx]
And this is the code that should be called in stage 2:

Code: Select all

[bits	32]
[global	_start]

_start:
	mov	eax,	0x2BADB002
; Prevents execution of data.
hang:
	cli
	.hang:
		hlt
		jmp	.hang
The value in EAX is 0x1BADB002, not 0x2BADB002 as it would be if _start was called.

Here is the state of all the registers:

Code: Select all

eax            0x1badb020	464367648
ecx            0xc2bd	49853
edx            0x0	0
ebx            0x8048060	134512736
esp            0x9be2	0x9be2
ebp            0x0	0x0
esi            0x0	0
edi            0x0	0
eip            0xc2c1	0xc2c1
eflags         0x46	[ PF ZF ]
cs             0xf000	61440
ss             0x0	0
ds             0x0	0
es             0x0	0
fs             0x0	0
gs             0x0	0
ES:EBX is set correctly to 0000:8048060

Code: Select all

ELF Header:
  ...
  Entry point address:               0x8048060
  ...
Even if I jump to [EBX] rather than [ES:EBX], the same thing happens.

Full code: http://pastebin.com/HHcqnAuT

Re: Not sure how correct my bootloader code is.

Posted: Sat Mar 03, 2012 8:31 am
by egos
Synon wrote:If it's a floppy (DL <= 0x01) it jumps to the 'is_fdd' function...
Your code for floppy detection is wrong. Think about what "jle" does.
If it's a hard disk it will write just the first 446 bytes of the stage 1 binary (so it doesn't destroy the partition table)...
But it destroy disk signature.
So, for floppies, I need to use INT 13/AH=8 to get the Drive Parameter Table and then use INT 13/AH=2 to load the appropriate amount of sectors? Should I also use it for non-LBA hard disks?
Usually for floppies BPB fields (SPT, Heads) are used. But function 8 is used just for hard disks (when EDD service is not supported) to detect BIOS geometry for LBA-to-CHS translation (CHS parameters stored on the disk are not used at all).

Re: Not sure how correct my bootloader code is.

Posted: Sat Mar 03, 2012 9:12 am
by Synon
egos wrote:
Synon wrote:If it's a floppy (DL <= 0x01) it jumps to the 'is_fdd' function...
Your code for floppy detection is wrong. Think about what "jle" does.
It compares DL to 0x01 (the second floppy disk) and then jumps if DL is less than or equal to 0x01. Why isn't that right? Can the index go all the way to up to 0x7F for floppy disks (in which case, I should just test for DL < 0x80)?
If it's a hard disk it will write just the first 446 bytes of the stage 1 binary (so it doesn't destroy the partition table)...
But it destroy disk signature.
Good point, I'll only write the first 444 bytes then.
So, for floppies, I need to use INT 13/AH=8 to get the Drive Parameter Table and then use INT 13/AH=2 to load the appropriate amount of sectors? Should I also use it for non-LBA hard disks?
Usually for floppies BPB fields (SPT, Heads) are used. But function 8 is used just for hard disks (when EDD service is not supported) to detect BIOS geometry for LBA-to-CHS translation (CHS parameters stored on the disk are not used at all).
Oh ok, I'll do that instead.

p.s. Do you know why the second stage isn't running even though the first stage is jumping to the correct address?

Re: Not sure how correct my bootloader code is.

Posted: Sat Mar 03, 2012 10:25 am
by egos
Synon wrote:It compares DL to 0x01 (the second floppy disk) and then jumps if DL is less than or equal to 0x01. Why isn't that right? Can the index go all the way to up to 0x7F for floppy disks (in which case, I should just test for DL < 0x80)?
It's not essential. Do you know that e.g. 0x80 "is less than or equal to 0x01"? Use "jbe" instead.
Good point, I'll only write the first 444 bytes then.
440 bytes.
p.s. Do you know why the second stage isn't running even though the first stage is jumping to the correct address?
No, I'm not telepath. Try to debug the first stage. Check offsets used in the second stage after it has been run.

Re: Not sure how correct my bootloader code is.

Posted: Sat Mar 03, 2012 11:07 am
by Synon
egos wrote:
Synon wrote:It compares DL to 0x01 (the second floppy disk) and then jumps if DL is less than or equal to 0x01. Why isn't that right? Can the index go all the way to up to 0x7F for floppy disks (in which case, I should just test for DL < 0x80)?
It's not essential. Do you know that e.g. 0x80 "is less than or equal to 0x01"? Use "jbe" instead.
Ohhh, because JBE is unsigned.
Good point, I'll only write the first 444 bytes then.
440 bytes.
That one.
p.s. Do you know why the second stage isn't running even though the first stage is jumping to the correct address?
No, I'm not telepath. Try to debug the first stage.
That's what I have been doing, it's just not going very well.