Page 1 of 2

Trouble chainloading from USB?

Posted: Tue Jun 16, 2015 9:55 am
by zehawk
Hey! So this should be a simple question with most likely a simple answer. I have successfully created a single stage bootloader on a USB (Minus booting or loading files into RAM. Most of my tests thus far have been checking for functionality and printing Hello World along with keyboard input.)

So I decided to try and split my bootloader into two parts, a single stage loader that will load the second stage. Problem is, for some reason or another, the first stage is having trouble reading from the USB. Here's the code for the first stage bootloader:

Code: Select all

BITS 16

org 0x7C00

start:
        mov ax, 0
        mov ds, ax
        mov es, ax
        mov fs, ax
        mov gs, ax

        mov ss, ax 		;Puts 0 into the segment pointer, we are using real memory.
        mov sp, 0x7C00 		;Moves 7C00 into the stack pointer, so that all data <7C00 is stack.
        
        ;moves the drive number into memory
        mov dl, [DRIVE_NUMBER] 
          
            
        xor bx, bx
        mov ah, 0x0E
        mov al, 'S'
        int 10h
        
          
              
        ;Will attempt to read from the disk
    .Reset:
        xor bx, bx
        mov ah, 0x0E
        mov al, 'L'
        int 10h
            
        mov		ah, 0					; reset floppy disk function
    	mov		dl, [DRIVE_NUMBER]					; drive 0 is floppy drive
    	int		0x13					; call BIOS
    	jc		.Reset					; If Carry Flag (CF) is set, there was an error. Try resetting again
     
    	mov		ax, 0x7E00				; we are going to read sector to into address 0x7E00:0
    	mov		es, ax
    	xor		bx, bx
     
    .Read:
        xor bx, bx
        mov ah, 0x0E
        mov al, 'R'
        int 10h
        
	xor 		bx, bx
    	mov		ah, 0x02				; function 2
    	mov		al, 4					; read 4 sectors from drive.  
    	mov		ch, 1					; we are reading the second sector past us, so its still on track 1
    	mov		cl, 2					; sector to read (The second sector)
    	mov		dh, 0					; head number.  USBs only have one head, right?
    	mov		dl, [DRIVE_NUMBER]			; drive number that the BIOS passed to us from earlier.
    	int		0x13					; call BIOS - Read the sector
    	jc		.Read					; Error, so try again
         
        
        mov ax, 0
        ;Resets es to 0.
        mov es, ax
    	jmp 0x7E00				        ; jump to execute the sector!
           
        jmp Exit                                          ;shouldn't be called!


    DRIVE_NUMBER db 0

Exit:
    hlt    ;halts the program for now

times 510-($-$$) db 0   ; Pad remainder of boot sector with 0s
dw 0xAA55      ; The standard PC boot signature
When ran, it just prints 'R' infinitely. I don't know why the drive is failing to be read. It is highly possible that I put some incorrect numbers in before calling int 0x13. Problem is, that I couldn't find any tutorial or guides on how to do this with a USB. Most are with floppy disks or CDs, which I am not using, and had to sort of change and set for USB. I am trying to load the second bootloader into memory 7E00, and the USB is unformatted. (Using and reading raw sectors). I didn't put in the second bootloader code because I don't think that has to do with the drive failing to be read.


EDIT:
Worth noting, both bootloaders are in the same unformatted USB. Bootloader 1 is in sector 0, and bootloader 2 is in sectors 1-4.

Re: Trouble chainloading from USB?

Posted: Tue Jun 16, 2015 10:13 am
by glauxosdev
Hi,

Don't assume cylinder-head-sector numbers. Write a function to convert logical sector number to CHS numbers.

Or, if your computer is new enough, you could use extended read (INT 0x13, AH 0x42), where you don't even bother with CHS numbers. http://home.teleport.com/~brainy/interrupts.htm If you scroll down, you will the ah=42h section.

Also you need to learn how real mode segmentation works. Setting ES to 0x7E00 and BX to 0 before invoking int 0x13 will load the sectors to 0x7E000 and not to 0x7E00.

Regards,
glauxosdev

Re: Trouble chainloading from USB?

Posted: Tue Jun 16, 2015 10:24 am
by zehawk
glauxosdev wrote: Hi,

Don't assume cylinder-head-sector numbers. Write a function to convert logical sector number to CHS numbers.
Hmm, the cylinder-head-sectors thing was from a tutorial. I understand how it works on a floppy disk, but the main problem is that I'm not entirely sure how that translates to USB, where there really isn't any disk or 'head' or even cylinders. Will check out the link though!

glauxosdev wrote: Or, if your computer is new enough, you could use extended read (INT 0x13, AH 0x42), where you don't even bother with CHS numbers. http://home.teleport.com/~brainy/interrupts.htm If you scroll down, you will the ah=42h section.

Also you need to learn how real mode segmentation works. Setting ES to 0x7E00 and BX to 0 before invoking int 0x13 will load the sectors to 0x7E000 and not to 0x7E00.
Oh! Must have been a typo. I know that I should load it to 7E0, because 7E0(es) * 16 + 0(bx) = real address. Added an extra 0 there by accident. Will edit and fix that now.

Re: Trouble chainloading from USB?

Posted: Tue Jun 16, 2015 10:26 am
by Brendan
Hi,
zehawk wrote:When ran, it just prints 'R' infinitely. I don't know why the drive is failing to be read.
It's because (near the start of the code) you've got this:

Code: Select all

        ;moves the drive number into memory
        mov dl, [DRIVE_NUMBER]
And it should be this:

Code: Select all

        ;moves the drive number into memory
        mov [DRIVE_NUMBER],dl
Note that this will only (hopefully) fix the "read error" problem. You will find that (after fixing this bug and the bugs glauxosdev mentioned) the code only works when you're lucky, because there's no partition table and no BPB (causing some BIOSs to get confused and "randomly" treat the device as either an emulated floppy disk or an emulated hard disk).


Cheers,

Brendan

Re: Trouble chainloading from USB?

Posted: Tue Jun 16, 2015 10:29 am
by zehawk
Brendan wrote:Hi,
zehawk wrote:When ran, it just prints 'R' infinitely. I don't know why the drive is failing to be read.
It's because (near the start of the code) you've got this:

Code: Select all

        ;moves the drive number into memory
        mov dl, [DRIVE_NUMBER]
And it should be this:

Code: Select all

        ;moves the drive number into memory
        mov [DRIVE_NUMBER],dl
How did I miss this?! Can't believe I wrote that. Will change as soon as I get back.
Brendan wrote: Note that this will only (hopefully) fix the "read error" problem. You will find that (after fixing this bug and the bugs glauxosdev mentioned) the code only works when you're lucky, because there's no partition table and no BPB (causing some BIOSs to get confused and "randomly" treat the device as either an emulated floppy disk or an emulated hard disk).


Cheers,

Brendan
I've looked around for a good tutorial on partition tables. Will have to check again, but if you could provide a link on a good partition table guide or specification, that would be helpful!

EDIT: Nevermind, found some. Don't worry about the partition tables thing, I can get that figured out on my own. Thanks for your help guys! If I run into any more issues, will post here!

Re: Trouble chainloading from USB?

Posted: Tue Jun 16, 2015 11:33 am
by Octocontrabass
glauxosdev wrote:Or, if your computer is new enough, you could use extended read (INT 0x13, AH 0x42), where you don't even bother with CHS numbers.
This only works when the USB drive is emulated in hard disk mode.
zehawk wrote:Hmm, the cylinder-head-sectors thing was from a tutorial. I understand how it works on a floppy disk, but the main problem is that I'm not entirely sure how that translates to USB, where there really isn't any disk or 'head' or even cylinders.
Use INT 0x13 AH=0x08 to get the CHS geometry the BIOS is using. It works in both floppy disk and hard disk mode.
Brendan wrote:You will find that (after fixing this bug and the bugs glauxosdev mentioned) the code only works when you're lucky, because there's no partition table and no BPB (causing some BIOSs to get confused and "randomly" treat the device as either an emulated floppy disk or an emulated hard disk).
CHS reads will work regardless of the emulation mode, as long as you use the geometry reported by the BIOS. (I haven't tried any flash drives bigger than 8GB yet, so I'm not sure what happens when the disk is too big for CHS addresses.)

Re: Trouble chainloading from USB?

Posted: Tue Jun 16, 2015 12:44 pm
by zehawk
Octocontrabass wrote: This only works when the USB drive is emulated in hard disk mode.
Yeah, I'm going to be using the normal, int 13h ah = 0x02 way. Having to understand sectors and heads and things is, in my opinion, important for learning about booting and I/O.
Octocontrabass wrote: Use INT 0x13 AH=0x08 to get the CHS geometry the BIOS is using. It works in both floppy disk and hard disk mode.
I'll have to look into this function! What registers are these values placed in/are there registers to be set to anything before this is called?
EDIT: Here's the page I was looking for:
http://www.delorie.com/djgpp/doc/rbinter/id/27/6.html
Octocontrabass wrote: CHS reads will work regardless of the emulation mode, as long as you use the geometry reported by the BIOS. (I haven't tried any flash drives bigger than 8GB yet, so I'm not sure what happens when the disk is too big for CHS addresses.)
Now I'm curious about how real operating systems do it. Just curious in a 'learning exerpience' sort of way. Doesn't Windows boot from the main hard drive attached? That can be massive amounts of storage space, much larger than 8 gigs. (Mine is 2 TB. How does Windows manage that?)

Re: Trouble chainloading from USB?

Posted: Tue Jun 16, 2015 1:07 pm
by Octocontrabass
zehawk wrote:Having to understand sectors and heads and things is, in my opinion, important for learning about booting and I/O.
I don't think it's nearly as relevant in 2015 as it was in 1985, but it's still interesting to learn about.
zehawk wrote:What registers are these values placed in/are the registers to be set to anything before this is called?
RBIL is one of the best resources for answering that kind of question. (Google can find some HTML versions that are very easy to navigate.)
zehawk wrote:Now I'm curious about how real operating systems do it.
An internal hard disk will never be emulated as a floppy disk, so the bootloader can use the extended read functions (INT 0x13 AH=0x42). You can see some interesting analysis of the boot code used by different Windows versions here.

BIOS booting is still (usually) limited to 2TB; for larger disks, it's easier to just use UEFI instead. (UEFI is the modern replacement for BIOS. Unfortunately, it's a lot less beginner-friendly.)

Re: Trouble chainloading from USB?

Posted: Tue Jun 16, 2015 1:26 pm
by zehawk
Octocontrabass wrote: I don't think it's nearly as relevant in 2015 as it was in 1985, but it's still interesting to learn about.
Yeah, I suppose when I said important, I guess I meant 'something that I find interesting and should learn for the sake of learning'. :)
Octocontrabass wrote: RBIL is one of the best resources for answering that kind of question. (Google can find some HTML versions that are very easy to navigate.)
VERY useful link! Will keep bookmarked!
Octocontrabass wrote: An internal hard disk will never be emulated as a floppy disk, so the bootloader can use the extended read functions (INT 0x13 AH=0x42). You can see some interesting analysis of the boot code used by different Windows versions here.
Ohhh, that makes sense. When he was talking about using int 0x13 AH = 0x42, and calling it something newer hardware supports, I didn't know exactly what he meant by 'newer'. (Last year, since Windows 95, etc) But will look into this more for sure!
Octocontrabass wrote: BIOS booting is still (usually) limited to 2TB; for larger disks, it's easier to just use UEFI instead. (UEFI is the modern replacement for BIOS. Unfortunately, it's a lot less beginner-friendly.)
I think for now, BIOS works. If I ever need a hard drive that is larger than 2 TB to store my OS(Which I certainly hope will never be the case xD), will look into UEFI!

Thanks again for your help!

Re: Trouble chainloading from USB?

Posted: Tue Jun 16, 2015 3:06 pm
by zehawk
So after having done more research, I'm curious, how do you set up a partition table with LBA values instead of CHS? Wouldn't you have to do that for hard drives larger than 8 gigs?

Re: Trouble chainloading from USB?

Posted: Wed Jun 17, 2015 12:10 am
by Brendan
Hi,
zehawk wrote:So after having done more research, I'm curious, how do you set up a partition table with LBA values instead of CHS? Wouldn't you have to do that for hard drives larger than 8 gigs?
You should use the LBA fields in partition table entries for all partitions. For partitions that start or end within the "CHS addressable" area of the device the CHS fields should also be correct (mostly for backward compatibility); and for partitions that start or end outside the "CHS addressable" area you set the CHS fields to "max. values".

Note that the size of "CHS addressable area" depends on how many sectors per track and how many heads the device pretends it has. For example, if the device says it has 8 heads and 32 sectors per track then CHS would be limited to 1024*32*8 sectors (128 MiB). "Almost 8 GiB" is only the best possible case (1024*256*63 sectors).

This also means that you have to know how many sectors per track and how many heads the BIOS pretends the device has so that you can create the CHS fields in partition table entries correctly. This is a pain in the neck for a lot of cases (e.g. for an OS that runs in long mode or protected mode) because what the device itself reports can have very little to do with the values that the BIOS uses. For example, for a device that reports "4096 cylinders, 16 heads and 63 sectors per track" the BIOS can tell software "1024 cylinders, 64 heads and 63 sectors per track" (to work around limitations of its API), and it's these fake/BIOS numbers that matter (and not the real numbers that an OS would get from the device). Note: To work around this properly, a boot loader for a good (protected mode or long mode) OS would gather the parameters for devices from the BIOS (while in real mode) and pass them to the OS, so that the OS can use the correct parameters later (for creating partitions, etc).

LBA is also limited. Assuming 512-byte sectors; all partitions must start within the first "almost 2 TiB" of the disk and the maximum partition size is "almost 2 TiB". This means that disks larger than 2 TiB start to get awkward, and the largest disk it can support is "almost 4 TiB" (e.g. with a partition that starts on sector 0xFFFFFFFF and has a total of 0xFFFFFFFF sectors). There is no work-around for this.

Finally; you may want to look into GPT (GUID Partition Table). It solves most of the problems with the MBR partitioning scheme (including several problems I didn't mention) and is required by UEFI. To maximise compatibility (both backward compatibility and forward compatibility) I'd recommend using a "hybrid GPT" scheme with both MBR partitions and GPT partitions. This allows you to have boot code for BIOS (where the MBR might use MBR partitions to find the boot sector but GPT partitions are used after that, and where the boot code uses BIOS functions) and also have boot code for UEFI (that uses GPT partitions including the UEFI system partition, and uses UEFI functions) both on the same USB flash device; where both versions of the boot code hide the differences between BIOS and UEFI and start the same kernel, etc. That way you can plug the same USB flash device into any 80x86 computer and it'll work regardless of what the firmware is.


Cheers,

Brendan

Re: Trouble chainloading from USB?

Posted: Wed Jun 17, 2015 9:46 am
by zehawk
Brendan wrote: This also means that you have to know how many sectors per track and how many heads the BIOS pretends the device has so that you can create the CHS fields in partition table entries correctly. This is a pain in the neck for a lot of cases (e.g. for an OS that runs in long mode or protected mode) because what the device itself reports can have very little to do with the values that the BIOS uses. For example, for a device that reports "4096 cylinders, 16 heads and 63 sectors per track" the BIOS can tell software "1024 cylinders, 64 heads and 63 sectors per track" (to work around limitations of its API), and it's these fake/BIOS numbers that matter (and not the real numbers that an OS would get from the device). Note: To work around this properly, a boot loader for a good (protected mode or long mode) OS would gather the parameters for devices from the BIOS (while in real mode) and pass them to the OS, so that the OS can use the correct parameters later (for creating partitions, etc).
I'm just curious then, if the partition table is set up at runtime, then how can I force the BIOS to read the USB as hard drive among all devices? I know people before said Partition table, but your answer seems to hint that the Partition table is set after the bootloader has already been loaded in. Sorry if I'm misunderstanding what you're saying, but that's sort of what I'm getting from what you're saying.

I have looked into the other partitioning scheme, and will probably be attempting to add it in shortly! Thanks for your help Brendan!

EDIT:
WOW is the GPT SO much easier than the MBR.

Re: Trouble chainloading from USB?

Posted: Wed Jun 17, 2015 12:53 pm
by Antti
zehawk wrote:I'm just curious then, if the partition table is set up at runtime, then how can I force the BIOS to read the USB as hard drive among all devices?
The partition table is not modified at runtime (this may not be true in some specific chain loading scenarios) but it is important to get geometry values from the BIOS if you want to create partition tables later for that specific computer. This may be a rather advanced topic at this point but still worth keeping in mind.

Re: Trouble chainloading from USB?

Posted: Wed Jun 17, 2015 1:52 pm
by zehawk
Antti wrote:
zehawk wrote:I'm just curious then, if the partition table is set up at runtime, then how can I force the BIOS to read the USB as hard drive among all devices?
The partition table is not modified at runtime (this may not be true in some specific chain loading scenarios) but it is important to get geometry values from the BIOS if you want to create partition tables later for that specific computer. This may be a rather advanced topic at this point but still worth keeping in mind.
Actually, I understand now. So in practice, the partition table I setup for the USB would only work for one specific BIOS at a time. (Or is expected to work only on one, it could potentially work on many others but would not be gauranteed.) And we use GPT now because at least it's a bit more standardized, with the use of LBA(Which I quite honestly much prefer over CHS).

Another question I have about the theory of partition tables and what not. How does a BIOS know that a bootloader has a valid MBR? From what I checked, an MBR doesn't have a 'signature' like a GTP header does. In theory, the MBR table could be filled with random values and the BIOS wouldn't know, so how would the BIOS be able to tell that those bytes are a table and boot it as a hard drive? Or does it rely on the GTP nowadays to make that distinction? (GTPs DO have a distinct signature)

Re: Trouble chainloading from USB?

Posted: Wed Jun 17, 2015 2:40 pm
by Octocontrabass
zehawk wrote:So in practice, the partition table I setup for the USB would only work for one specific BIOS at a time.
In practice, you can always use 255 heads and 63 sectors to calculate the CHS values in your USB flash drive's partition table. (You still need to use INT 0x13 AH=0x08 if you plan on reading the disk using CHS addressing.)
zehawk wrote:And we use GPT now because at least it's a bit more standardized, with the use of LBA(Which I quite honestly much prefer over CHS).
MBR is also standardized and also uses LBA. My own bootloaders completely ignore the CHS values; they're only in the partition table for the BIOS's benefit.
zehawk wrote:How does a BIOS know that a bootloader has a valid MBR?
It checks for a signature, exactly one partition marked active, and start and end values that don't overlap. It only checks for USB flash drives and the like; it does not check hard disks or floppy disks.

You can't start a partition at CHS (0,0,1). The previous partition table you posted (and have since deleted) booted in floppy disk mode on your test machine because of that mistake.