Page 1 of 1

Multi-stage booting problems... :(

Posted: Sun Jan 24, 2010 2:58 pm
by atc
Hi guys,

I just registered here, and I was hooked by the wonderful tutorials which actually show you how to work on Windows in VS! :) This was about the only thing I could find with any comprehensive information (And took me 3 days just to find this). Everything else was for Linux/Unix users and a bunch of command line junk we Windows guys don't like, heheh.

I've been programming for a couple years, but strictly applications. I'm totally new to bootloaders, asm, system programming, etc. It's a daunting task, but I've learned a great deal from the first bootloader tutorials. However, I've hit the snag I've been fearing all along. I've made a single boot.bin before and booted it on Virtual PC; you know, the whole "Hello world" thing. I've done that many times, even before I came here. What I could never get was how I can get at a second stage boot/kernel loader and then to load a kernel.

The problem is, I can't (and don't want to) use Floppy disks. I don't even have a floppy drive or anything to facilitate it. Then, I simply don't want to go the floppy route and want to use ISO and CD/DVD instead. So the whole 2-stage bootloader example with the FAT12 file system doesn't work. It sends the processor straight into Triple Fault. :cry: I've stayed up all night reading and trying to get a solution; I've searched the forums and the internet endlessly, and can't find an answer. I hate to bother you folks, but I need help.

When I create an ISO image, I can easily put the 512b boot.bin in the bootsector and have it load when I capture the ISO on Virtual PC. What in the world I do with stage two or will have to do with the kernel, I have no clue. I can drop it into the ISO file system, like I'm making a regular ISO to burn a CD or something, but I don't know how to load it or even find it. So, I essentially have this:

512b BootSector: Boot.bin
ISO File List: <I can put other things here/root dir>

I have no way to see how memory is mapped in an ISO file. I also can't find any decent information online about it. I'm sure someone here must know how this works. I'm assuming that using an ISO on Virtual PC is akin to using a real computer and a CD/DVD. So it would have to work similar I guess. I just don't know HOW it works, and all attempts to research it turn out fruitless. I hope this makes sense, and I really hope someone can point me in the right direction. :oops: I've been awake over 26hrs now and can't sleep! Lol, I'm serious. Computers never get out of my head!

Re: Multi-stage booting problems... :(

Posted: Sun Jan 24, 2010 5:21 pm
by Andyhhp
First of all, taking the code verbatim and trying to boot from CD/DVD wont work.

The code is limited to the int 0x13 drive number. The tutorial here hard codes this to be 0 meaning that it will always try to get the second stage from the floppy drive.

The BIOS helps you out a bit here. when your bootloader starts executing, the value of dl is the boot drive which you booted off. This means that you should save its value and plug it back into int 0x13 call, so the bootloader will try to load the next stage from the drive it was loaded from.

As for filesystem, this really makes no difference at all. It is perfectly easy to put FAT12 onto a CD. All this means is that you will have compatabiliy problems with other operating systems.

What I suggest is that you make a trivial hello world second stage bootloader, and attempt to get that loaded using a FAT12 filesystem from a CD iso. Once that is working, you can worry about changing from FAT12 to ISO 9660 (the file format for CDs)

A couple of useful links:

Source code for the GRUB stage1 loader: ... 3A/stage1/
Description of ISO 9660:


Re: Multi-stage booting problems... :(

Posted: Mon Jan 25, 2010 2:36 am
by atc
Indeed, that's all I'm trying to do. If I can overcome this hurdle and get to working in C, then it will be relatively smooth sailing. I guess my biggest problem is I'm inexperienced with ASM, especially in the Ring 0 realm. I've been in user-land my whole programming career, and only learned some basic assembly for debugging and security on existing operating systems. Writing it by hand and having 1:1 control over the machinery is totally new to me. I would have never believed you if you told me I would one day be a n00b AGAIN! :oops: Heheh... I guess if I learned how to write flight and ballistics physics algorithms on my own I can get this down too. :| I hope... lol...

Well, I looked at the code and I don't see where I can get the drive like you described. The first place dl pops up is:

mov dl, BYTE [bsDriveNumber] ; drive

I understand that's telling the BIOS to look on the floppy drive, but where I get the value of the booting drive I'm not sure. I DO see where you're talking about it being hardcoded to 0. Ehhh... I'm pretty clueless I guess, lol. If you can recommend any up-to-date books on Amazon that I should read, please link me. I've been wanting to get some, but they are expensive (for books/$100+ sometimes) and I fear buying the wrong thing or hideously outdated material. I guess I just need to study up more on ASM so I don't have to bother you kind folks with "trivial" things. But I still appreciated any and all help in the meantime.

Also, I have no option to put a FAT12 system on an ISO. I have ImgBurn and MagicISO. Neither seem to have the capability. And I simply don't understand the way memory is laid out in ISO. As far as I know, it's contiguous. I think it also provides an information section one needs to parse to find the files, but I don't know how to translate that into ASM at this point.

Also, I believe each sector is 2KiB, as opposed to 512b.

Re: Multi-stage booting problems... :(

Posted: Mon Jan 25, 2010 6:30 am
by Andyhhp
The value of dl that I am talking about is given to you when you enter the boot loader.

The BIOS sets the value of DL to be the drive used, then executes the first instruction of the bootloader. This is typically a jump to get past the biosparameterblock, followed by some instructions to clear certain registers. you have to ensure that you save the value in dl before you zero it, so it can be used later.

As for ISO files, they are no more than the physical layout of the disk in a single file. An ISO file (as far as I am aware) is byte for byte identical to the same data burnt onto a real CD. Therefore, you should be able to make your own custom filesystem (in this case FAT12) by taking an ISO of a FAT12 filesystem, (virtual floppy drive is fantastic for this - it will pretend to be a floppy drive for reading and writing to, but you can extract an ISO from the data at any time that you wish).

With any luck, that iso should be bootable from VirtualPC, even though it isnt strictly a CD image.


Re: Multi-stage booting problems... :(

Posted: Mon Jan 25, 2010 7:55 am
by atc
Yeah, that's sort of a big portion of the problem. It must be a real CD/DVD image, no floppy emulation. It's a basic design requirement, and the final target of this physically requires it. I wish I could elaborate, but I'm keeping my end goal under wraps for the time being. But I've got to learn how all this works before I can do any serious planning for the real thing.

I have the Singularity RDK from Microsoft, which you might know, is a managed operating system/kernel implemented mainly in C#. Looking through the source, I found one ASM file which looks like it's geared in the same direction I'm trying to go. It can't be compiled with Nasm, and I don't know enough about all of this to hope to translate it. So I'm going to post it here and see if you guys can make heads or tails out of it.

(EDIT: And NO, I'm NOT trying to make a managed OS, lol. Not even a desktop OS for that matter.)

Sorry I have to post this since it won't allow me to attach any extension:

;; Copyright (c) Microsoft Corporation. All rights reserved.

; Concerns
; 1 - there is no error checking on the int13 calls
; 2 - we assume that the block size is 2048 bytes
; 3 - this cannot handle large root directories (>64KB)

; Constants

BootSecOrigin EQU 07c00h ; the BIOS puts the boot sector at 07c0h:0000 == 0000:7c00h
StackOffset EQU -12 ; we will put the stack a small bit below it (we hardly use the stack, so it is safe...)

; Macros

db 0eah
dw OFF
dw SEG

; Directives

.model tiny

; Begin Code segment

_TEXT SEGMENT use16 ; 16-bit code segment
ORG 0h ; ETFS puts us at 07c0h:0000

JMPF16 07c0h,OFFSET Step1

Step1: ; set stack and data segments
mov cx, cs
mov ss, cx
mov sp, BootSecOrigin + StackOffset
mov es, cx
mov ds, cx
mov bp, sp

Step2: ; Save the boot drive (dl holds it on boot)
mov [CDDrive], dl

Step3: ; Clear the Screen
mov ax, 02h
int 010h

;; Configure GS to point to the text-mode video console.
mov ax, 0b800h
mov gs, ax

;; Write 'A' to position 0.
mov ax, 04f41h
mov gs:[0], ax

;; Write 'B' to position 1.
mov ax, 04f42h
mov gs:[2], ax

Step4: ; Load the PVD to get the Logical Block Size
mov eax, 10h ; the PVD is in the 16th block
mov bx, 2000h
mov es, bx ; transfer address = 2000:0000
mov cx, 1
call ReadDisk
mov ax, es:128 ; block size is at offset 128
mov [BlockSize], ax

;; Write 'C' to position 2.
mov ax, 04f43h
mov gs:[4], ax

Step5: ; Find the Joliet SVD, and then find the Root Directory Information
mov eax, 10h ; start with the PVD, even though it will fail
push eax
mov cx, 1
call ReadDisk
mov si, OFFSET SVDesc ; [ds:si] points to the desired first 6 bytes of this VD
xor di, di ; [es:di] points to the start of what we just read
mov cx, 6
repe cmpsb
je FoundSVD
mov al, es:0000h
cmp al, 0FFh ; is this the last Volume Descriptor?
je SVDError
pop eax
inc eax
jmp GetNextVD ; try another VD

FoundSVD: ; need to make sure this is a Joliet SVD - we need 025h, 02Fh, 045h in [88,89,90]
mov si, OFFSET JolietSig ; [ds:si] points to the Joliet Signature
mov di, 88 ; [es:di] points to the escape sequence field of the current SVD
mov cx, 3
repe cmpsb
je FoundJoliet
pop eax
inc eax
jmp GetNextVD

;; Write 'D' to position 3.
mov ax, 04f44h
mov gs:[6], ax

mov eax, es:158 ; now get the rootstart and rootsize fields
mov [RootStart], eax
mov eax, es:166
mov [RootSize], eax

Step6: ; Load the Root Directory (SVD), and search it for SINGLDR
movzx ebx, [BlockSize]
div ebx ; eax has # blocks in root directory. Round up if necessary:
cmp edx, 0
je ReadyToLoad
add eax, 1
ReadyToLoad: ; we're going to assume that the root directory will not be bigger than 64K
mov ecx, eax
mov eax, [RootStart]
call ReadDisk

xor ebx, ebx ; bx will hold the start of the current entry
mov di, bx
add di, 25 ; let's check the file flags - should be 00
mov al, es:[di]
cmp al, 0
jne PrepNextEntry
; file flags are good. now check the file identifier:
mov si, OFFSET Stage2FileSize
xor cx, cx
mov cl, ds:[si] ; first byte is file name length
add cx, 2 ; add two because we check the length byte of the directory entry and the padding byte, too
add di, 7 ; now es:di points to the file length/name field, and ds:si has our desired content
repe cmpsb
je FoundEntry

xor cx, cx ; increment bx by adding the byte value in es:[bx]
mov cl, es:[bx] ; if es:[bx]==0 and ebx!= [RootSize], then we are in a padding zone
cmp cx, 0 ; designed to prevent a directory entry from spilling over a block.
jne LoadNext ; Should this be the case, we will increment bx until es:[bx] is not null
inc bx
jmp PrepNextEntry

add bx, cx
cmp ebx, [RootSize]
jl CheckEntry
jmp FileNotFoundError

;; Write 'E' to position 5.
mov ax, 04f45h
mov gs:[8], ax

mov eax, es:[bx+2]
mov [FileStart], eax
mov eax, es:[bx+10]
mov [FileSize], eax

Step7: ; Load the file to 57c0:0000
mov cx, 057c0h
mov es, cx
movzx ebx, [BlockSize]
div ebx ; eax has # blocks in root directory
cmp edx, 0 ; on carry, there will be one more block
je ReadyToLoadFile
add eax, 1

mov ecx, eax
mov eax, [FileStart]
call ReadDisk

;; Write 'F' to position 6.
mov ax, 04f46h
mov gs:[10], ax

Step8: ; Now we need to set up the stack for SINGLDR and do a jump
xor cx, cx ; Always point the stack to 0000:7c00h - 12
mov ss, cx
mov sp, BootSecOrigin + StackOffset

movzx edx, [CDDrive]
push edx ; SINGLDR will need to know the boot drive #
pushd 04344h ; CD boot signature
pushw offset infloop ; return address = "infloop", which is the infinite loop
push cs

;; Write 'G' to position 7.
mov ax, 04f47h
mov gs:[12], ax

db 0EAh ; emit a long jump to 5000:7c00
dd 50007c00h

; ReadDisk
; Inputs: eax = Block Number
; cx = number of blocks to read (warning: cx > 32 will cause overflow)
; es = destination segment
; Assumptions: 1 - assumes request will not cause overflow of es:00 (limit on # sectors)
; 2 - assumes int13 extensions available

mov dl, [CDDrive] ; set the drive

pushd 00
push eax ; push 64-bit block number (top half always null)

push es
pushw 00h ; push transfer address

push cx ; # sectors
pushw 0010h ; this request packet is 16 bytes
mov ah,42h ; extended read
mov si,sp ; ds:si = address of params
int 13h ; perform the read

add sp, 10h ; clean the stack and return
ReadDisk ENDP

; Error Routines (these are jump points that never return)

mov si, offset SvdFailMsg
call PrintString
jmp @b

mov si, offset FileNotFoundMsg
call PrintString
jmp @b

or al, al
jz done

;;; Write directly to memory.
mov ah, 047h
mov bx, [Cursor]
mov gs:[bx], ax
add bx, 2
mov [Cursor], bx

mov bx, 07h ; normal attribute
mov ah, 0eh ; default print 1 char
int 10h
jmp psnext

jmp infloop

; Global Vars

RootStart DD 0
RootSize DD 0
CDDrive DB 0
BlockSize DW 0
FileStart DD 0
FileSize DD 0
Cursor DW 640

; String Constants

SVDesc DB 02h, "CD001"
JolietSig DB 25h, 2fh, 45h ; this is the value of the escape sequence for a Joliet CD
; we'll use it as the signature...
Stage2FileSize DB OFFSET Stage2FilePad - OFFSET Stage2File
Stage2File DB 0,"S",0,"i",0,"n",0,"g",0,"l",0,"d",0,"r" ; in unicode, this is how our filename will appear
Stage2FilePad DB 0
SvdFailMsg DB 10,13,"SVD Failed",0
FileNotFoundMsg DB 10,13,"File not found",0

; Boot Sector Signature
ORG 510
DW 0AA55h

end start


Thanks a million! If anyone can make sense of how this works and point me in the right direction I will be forever indebted! :D

Re: Multi-stage booting problems... :(

Posted: Mon Jan 25, 2010 5:41 pm
by Andyhhp
Ok - so we are going to attempt to make an ISO 9660 booting bootloader.

First and formost, you have to understand the ISO 9660 file system (the link from before). The filesystem is rather easy to understand.

That code extract you gave is written for MASM by the looks of it but its not too hard to translate to NASM.

What I suggest you do is make a toy bootloader which reads all the Volume Descriptors and prints out the Volume Descriptor Names in a list. This way, you can get to grips with both writing a bootloader, and with the ISO 9660 filesystem together, without attempting to do everything at once


Re: Multi-stage booting problems... :(

Posted: Mon Jan 25, 2010 8:32 pm
by atc
Thanks again. I'm also trying to make sense of ISOLINUX bit by bit. Slowly but surely, all the ops are coming together in my head. I did pick up what you mentioned:

mov [CDDrive], dl ; Save value of booting drive

So I've got that saved. Going to follow your suggesting and see what I come up with. I'll let you know how it goes. Thanks for your generosity!

Re: Multi-stage booting problems... :(

Posted: Wed Jan 27, 2010 12:25 am
by atc
Ok, I've got a MUCH better idea of what I need to do and how this works. I discovered that a HUGE part of my problem was that I was making the ISO images wrong. I kept wondering why ISOLINUX and other renowned bootloaders wouldn't even work when I built them, lol! :oops: That's sorted out now though. So, first thing I'm trying to do is relocated the code on the stack. Well, great examples here to learn from; I thought. I followed along exactly like ISOLINUX does it and waited to see if I could relocate the code and then just print some simple text to the screen as I'd been doing. Absolute failure!

I'm thinking something is wrong with the way my code is structured and executes. ISOLINUX and its offshoots are quite different from what I learned from the floppy examples here; they're also pretty complex. I realized I must fill the whole sector to 2048b before putting the boot signature per El Torito's specs, and when I create the image I have to set it to load 4 sectors instead of 2. When I figured that out, all the ISO/CD/DVD bootstrap examples I have worked! I guess with all this new-found room, there's no need for an actual stage 2 loader; except for learning purposes. So back to the problem. When I try to relocate my code, it fails and doesn't work anymore. The cursor also blinks like crazy. When I tried some restructuring, it throws a triple fault.

ISOLINUX, somehow, defines their string (db) debug messages at the END of the file. If I do that, the code doesn't even compile. They also define the start: routine first, which for me, results in calls to functions the code doesn't know exists (doesn't even compile, really). As I understood it, we were defining helper functions and variables at the top and jumping over them so they were declared before we used them. Somehow, this is all not working and confusing the hell out of me. How ISOLINUX and other bootstappers like this even compile (much less work) is beyond my current understanding.

Here is my current code, where I'm just trying to accomplish relocation and continuing along:

Code: Select all

; AKBoot.asm
; No Macros defined...

[bits 16]		; Still in RealMode 16-bit
org 7000h		; Align to correct offset

start:			; Start routine...
	jmp main	; Now jump to Main routine to begin

; BootLoader Data & BIOS Parameter Block

;***** STRINGS *****;
titleMsg db "AK-47X OS v0.1.0", 0													; Initial title message
ldMsg	 db "Loading, Please wait...", 0x0A, "Stage-1 Bootstrap initiated", 0		; The loading message
stg1	 db "Parsing Stage-2...", 0													; Success/Moving stage 2 msg

CDDrive DB 0																		; Holds number of CDrive


; PrintString - Prints text to screen
		lodsb				; load next byte from string from SI to AL
		or		al, al		; Does AL=0?
		jz		Done		; Then return...
		mov		ah, 0eh		; Else, print next char
		int 	10h			; Call BIOS to write char
		jmp		PrintString	; Recurse through process again


; CLRSCRN : Clears the screen
	mov ax, 02h			; Clear the screen
	int 010h			; Interupt...
	ret					; Return

; CARRET : Returns carriage
	mov		ah, 0eh		; Set BIOS subfunction
	mov		al, 0x0D	; Move carriage return to al
	int		10h			; Interupt...
	ret					; Return

; SKPLN : Skips to next line
	mov		ah, 0eh		; Set BIOS subfunction
	mov		al, 0x0A	; Move newline to al
	int		10h			; Interupt...
	ret					; Return
; MAIN ROUTINE - EntryPoint
	cli						; Clear interrupts
	xor	ax, ax				; Setup segments to insure they are 0. Remember that
	mov	ss, ax				; Initialize stack segment
	mov	sp, main			; Set up stack
	mov	ds, ax				; we have ORG 0x7c00. This means all addresses are based
	mov	es, ax				; from 0x7c00:0. Data segments are in CS so set to null...
	mov	fs, ax
	mov	gs, ax
	sti						; Enable interrupts
	cld						; Increment pointers

	mov	cx, 2048 >> 2		; Copy the bootsector
	mov	si, 0x7C00			; from 0000:7C00
	mov	di, 0x7000			; to 0000:7000
	rep	movsd				; copy the program
	jmp	0:relocate			; jump into relocated code

	mov [CDDrive], dl		; Save value of booting drive
	call CLRSCRN			; Clear the screen...
	; Print title/loading message...
	mov si, titleMsg		; Put titleMsg in si
	call PrintString		; Now print titleMsg

	call SKPLN				; Skip line again...
	call CARRET				; Return carriage

	mov si, ldMsg			; Put ldMsg in si
	call PrintString		; Now print ldMsg

    mov dh, 3				; Set carriage to row 3
    mov dl, 23				; ...and to column 23
    mov bh, 0				; Stay on page 0...
    mov ah, 2				; Set sub-function
    int 10h					; Interupt...

	mov si, stg1			; Set next param to print
	call PrintString		; Print message...
	xor	ax, ax				; Now clear ax...
	cli						; Clear all ints
	hlt						; Pause system

; Fill file to proper size...
times 2046-($-$$) db 0		; Pad to file offset 2046 per El Torito
dw 0aa55h					; BootSector signature
I don't understand why this doesn't work, and how the code structure of ISOLINUX.asm even compiles and works. When I try to restructure to mimic it more closely, it won't even compile. :lol:

NOTE: Sorry, the code formatting here went wacky and scattered comments, probably due to tabs vs spaces. :-/

Re: Multi-stage booting problems... :(

Posted: Wed Jan 27, 2010 12:31 am
by Andyhhp
I will look at the code more closely after I get some sleep.

However, AFAIR, bootsectors are ALWAYS 512 bytes long for comptability reasons. The first code you posted demonstrates that.


Re: Multi-stage booting problems... :(

Posted: Wed Jan 27, 2010 1:06 am
by atc
According to El Torito Spec, that's only true for *Floppy Emulation* (and of course, a real floppy works that way). This, and ISOLINUX, are meant to be run under *NO Emulation*. ISOLINUX has the same thing at the end - fill to 2046B and then add the boot signature. If that's not done, it won't even load in the first place. I've got a couple modified versions of ISOL* and they do the same thing. And like I discovered, when you create the CD/ISO you have to specify it as "No Emulation" and "Load 4 Sectors" (which is 2048B), or it won't work. Trust me, this I did learn! :wink:

Well, I'll keep pushing and trying to figure it out. Honestly, I hope I figure it out before you have to spend any time analyzing my code! :)

EDIT: YIPPEE!!! :D I got it! I just needed to define some new things and put them at absolute addresses for this to work. I was also structuring the code wrong. But it does now successfully relocate itself and continue working (prints the strings out and does whatever I ask it to). I'll continue following along with ISOLINUX as my example and modify it work the way I need it to. There's no need for a stage 2 loader since I have 2048B to work with. But, I'm still going to do a simple "Hello OS" program and load it for practice; then I'll focus on booting my kernel in PMode. My kernel is going to be pretty nice, me thinks! :shock: But let me not get ahead of my self here, lol.

Re: Multi-stage booting problems... :(

Posted: Wed Jan 27, 2010 6:23 am
by atc
Thank God, finally! :shock: Stage two loads successfully now, and I get a little bit of error feedback when something goes wrong (but it doesn't go wrong now, since it works, lol). Now I've got to get this kernel booted in Pmode and I can get back to what I'm comfortable with: C! At least I learned enough ASM to do this again and throw some inline in my C code if needed. 8) I'm going to read your kernel boot tutorials and adapt where necessary. Thanks everyone, and it's an honor to be here and learn!

Re: Multi-stage booting problems... :(

Posted: Thu Jan 28, 2010 3:08 pm
by Andyhhp

All the Stage 2 tutorials should be suitable to help you (with the exception of the FAT driver to load the kernel but you already have an ISO 9660 driver so that is no problem).

(On a point of informtation, these arnt my tutorials - im just a comunity member :P)


Re: Multi-stage booting problems... :(

Posted: Fri Jan 29, 2010 12:18 pm
by atc
I know you weren't THE author, heheh. I say "your tutorials" talking about the community in general. :)

Thanks for all of the help and wonderful tutorials, once again! I'm going to be hanging out here for a while if no one minds! :D

Re: Multi-stage booting problems... :(

Posted: Sat Jan 30, 2010 4:31 am
by atc

Finally figured out the trick to getting Grub to work. So problem solved. I also made my PE kernel multi-boot compliant so Grub loads it with ease! Now I like Grub so much, I'm going to stick with it. I've automated Visual Studio and my build process all the way up to automatically putting the entire OS and all files on ISO and plopping it on my desktop. So I just boot VPC with it and run! :) It's lovely! Now I want automatically deployment to an emulator with kernel-level debugging! :lol: Must figure this out now. That will be absolute heaven!

Re: Multi-stage booting problems... :(

Posted: Sun Jun 06, 2010 5:08 pm
by astrocrep
Got any hints for getting Tutorial Kernels to boot with grub?