User:Combuster/Recovered MBR

From OSDev Wiki
Jump to: navigation, search

Copy of the MBR stuff for archival purposes and backup of the original after the author ragequit on it. Once the world has calmed down, I ought to reconstitute the better parts and improve the theoretical wiki parts on bootloaders.

Stuff looks safe for MBR use. All other purposes are a lot of extended truths from the original author. Discussion included for reference; use sourcecode with caution.

Contents

Goal?

I'm not sure I understand the purpose of this page, as it isn't really an article---it just seems to be the source code for a simplistic boot loader; while it may be useful to the community, I'm not sure source code listings are the best use of a wiki. Perhaps it would make more sense to provide a link to this boot loader somewhere? --Love4boobies 04:19, 30 March 2012 (CDT)

That would probably be for the best, overall. Look at it this way: If you provide source, you should also provide maintenance and support. That's easy to do on a private project page, but somewhat difficult with a Wiki article. Plus, most of the use cases of this bootloader could be just as well covered by writing a GRUB extension, unless I'm seriously mistaken. -- Solar 04:29, 30 March 2012 (CDT)

Corrections Explained

I made some corrections, Turdus "un-corrected" them (possibly because he couldn't have known why they were made), and then I rolled back his "un-corrections" and wrote this to explain the reasons for the original corrections.

From the old preface: "If you're happy with fat32, extfs and pe, elf or even aout, they are supported by multi-boot, so leave now and use GRUB.". This is wrong because the multiboot specification says nothing about any file system or any executable file format. It could say "If you're happy with fat32, extfs and pe, elf or even aout, they are supported by GRUB" but then it'd still be a little wrong because GRUB legacy (one implementation of multiboot) has no support for PE or aout (unlike ELF). To avoid confusion it's just simpler to not say so much, especially as it's unnecessary (as this page isn't about multiboot or GRUB anyway).

From the old chainloading section: "For drive optimization, the first partition usually started on the second cylinder, leaving the remainder of the first track (a 63-sector gap) between the MBR and the first VBR.". For modern/large drives there are 63 sectors per track (due to old BIOS limits). However, for old/small drives there can be less than 63 sectors per track. For example, a 512 MiB USB flash drive could be presented as 32 sectors per track (with 128 heads and 256 cylinders). Assuming there's always 63 sectors per track is a bad idea. Note: I didn't realise this before, but if there are 63 sectors per track (the maximum) then the gap would be 62 sectors and not 63 sectors (as one of the 63 sectors is consumed by the MBR and therefore isn't part of the gap).

From the GPT section: "Intel engineers and others decided to create a new partitioning scheme in the gap after the MBR, called GPT. This however made non IBM PC compatible boot managers (occupying the same sectors) incompatible.". For GPT there's 2 partition tables for redundancy (one near the start of the disk an one near the end), and the entire partitioning scheme (including the second partition table) is not in the gap. I had also removed the "(occupying the same sectors)" part. Mostly (at the time) I was tempted to replace the majority of the history section with links to the relevant wiki pages, and compromised (by reducing unnecessary detail) instead.

--Brendan 15:37, 20 March 2012 (CDT)


One correction:

This is wrong because the multiboot specification says nothing about 
any file system or any executable file format.

Multiboot requires reading ELF headers for load addresses, and provides the not-so-aptly named "a.out" kludge (while nobody uses a.out) for any other format to provide similar information. I plan on doing a thorough read of the article later and fix things where necessary -- Combuster 01:05, 21 March 2012 (CDT)


I made some corrections, Turdus "un-corrected" them #2. I therefore added the disputed tag as a warning to all.

Point is, the bootloader is in essence designed with non-portability in mind. The claim is that it can be used as a VBR, but it fails that claim for any filesystem that embeds information on sector 0 - which include FAT*, HPFS, NTFS, SFS and XFS and only excludes Ext2/3 for as far as I can check. It also promotes using hardcoded continuous blocks for booting which fails when the second stage is a file that might be touched by the filesystem as well as being a good way of causing the typical starter bugs by not loading enough sectors.

I also explicitly added the floppy exclusion because it is considered a VBR by the world (apaprently minus Turdus as per the forums), and because many BIOSes don't support LBA for those devices making any attempt to use this code a shortcut for screwing up that way.

And that the device must support LBA should be obvious, especially to the writer. -- Combuster 18:14, 28 March 2012 (CDT)

Corrections:

It is not designed for FAT, HPFS, NTFS,... it would be obvious if you kept "If you're happy with fat32, extfs and pe, elf or even aout, they are supported by GRUB". Maybe pe and aout should have been left out, but the rest applies.

"Point is, the bootloader is in essence designed with non-portability in mind."

How do you know that? It was not designed for existsing filesystems, yes, but it's portable. Tested scenarios:

  • MBR (UBRL), VBR (LILO) -> Works,
  • MBR (UBRL), VBR (MSDOS FAT32) -> Works,
  • MBR (LILO), VBR (UBRL) -> Works.
"It also promotes using hardcoded continuous blocks for booting which fails when the second stage is a file"

You are wrong (as usual), and you really should learn to READ. I wrote in section "2nd stage":

"Even better, store it in a defragmented, continuous file on your filesystem"

Do you know what "defragmented, continous" means, don't you?

"I therefore added the disputed tag as a warning to all."

Show me the details of your testing before you do that.

"I had also removed the "(occupying the same sectors)" part." and "with links to the relevant wiki pages"

You should really read those wiki pages and specifications: http://en.wikipedia.org/wiki/GUID_Partition_Table

It's very clear that first GPT starts right after the MBR: "Partition table header (LBA 1)" Don't see any option on LBA 1 here. Also, it reads: "Partition entries starting LBA (always 2 in primary copy)"

Doesn't matter if first partition starts at 63 or 32 or whatever, the point is, you cannot use the gap after the MBR because it's for the GPT now. Rephrase the sentences if you like. And on modern disks it's not 63 (that's the past), in the last 5 years all disks we bought had partitions megabyte aligned.

"I made some corrections, Turdus "un-corrected" them (possibly because he couldn't have known why they were made)"

Because they do not mean the same, or you've removed important information. For example:

"If you are happy with multiboot"

How would a reader know if it's multiboot he/she is looking for? I wrote some examples on purpose.

Now it reads: "If you're happy with ELF, FAT or ext (fileformat and filesystems supported by version of multi-boot)"

Hope it's good for you. -- Turdus 12:15, 29 March 2012 (CDT)


The Wiki format does not cater well for such point-by-point replies. If you have an argument, take it to the forum. And please keep it civil. Calling each other names serves no purpose. What might be marginal on the forum is definitely out of place on a Wiki discussion page.

Advertising own code has always been met with criticism here (as I can attest); take it with style and address it instead of trying to shoot it down. Perhaps it would be better to keep the code and prose in a separate place (your project website), and merely provide a link to it in the relevant context; it's not really tutorial style, it does contain statements that are disputed. -- Solar 09:04, 29 March 2012 (CDT)


Wiki format rules understood.

About the rewrite: it's much more readble indeed, and it reflects what I originally meant, so thank you very much. I wrote only a few words explaining why BIOS Boot partition is useful.

I did not want to advertise my code on the wiki. I wanted to show how to write a 2nd stage that capable of booting via different methods, and for that I needed a working 1st stage. Never wanted to say it's the only way to do it, or anything, it's nothing more than a working example. This is now made clear in Preface, thanks.

About dispute: the usability of the code as VBR was questioned by someone who didn't even bothered to test it, or even clearly reasoning why. His argument was invalid (does not support floppies), since floppies does not have VBRs. But I accept your opinion it's disputed, so my question is, how to make this clear? All I can say is test it, and see it working (I spent endless nights to test it in different scenarios, so I know it's okay. I wouldn't have posted it here if I wasn't 100% sure). --Turdus 0926, 30 March 2012 (CST)

Not really my bowl of fish here (and I don't feel like testing it ATM), but what about your code requiring LBA addressing? Does that work on floppies, too? Combuster says it doesn't. -- Solar 03:15, 30 March 2012 (CDT)



History

Boot Sector

When the system powers up, it determines the boot device, loads the first sector from that device (usually the first sector on the device), and executes it. On early systems, like floppy-based ones, the boot sector then proceeded to load the OS kernel directly.

Chainloading

With the advent of hard disks, drive partitioning became the rule. The partition table also resided in the boot sector, and the available space for the boot code was reduced to 440 bytes.

The code in the boot sector (now called Master Boot Record -- MBR) would parse the partition table, select one of the partitions to boot from (usually through an "active" flag), load the first sector of that partition (the Volume Boot Record -- VBR), and execute it. The VBR code would then proceed to load the OS kernel.

Since the VBR code expected to be executed from the exact same memory address as the MBR -- as if it were just loaded by the BIOS -- the MBR code had to relocate itself to a different memory location before loading the VBR code to its previous location. This was called chainloading.

LILO was an example of this approach.

Boot Managers

If you wanted a sophisticated selection menu (a "boot manager"), the 440 bytes size limit of the MBR was crippling.

Hard drive addressing at that time followed the Cylinder-Head-Sector (CHS) scheme. The MBR resided in the first sector, first head, first cylinder. For access optimization, the rest of the first cylinder was usually left unassigned, and the first partition started on the second cylinder, leaving a gap of unassigned sectors. Many boot managers took advantage of this and stored additional code in this gap. That was convenient, but risky - sometimes other software used those sectors too, potentially destroying the boot manager.

Two-Stage Bootloader

One solution to that problem was to split up the boot manager into two "stages": A first stage (residing in the MBR) loads the second stage from a partition's file system. The second stage then provides traditional "boot manager" services (a pretty selection menu, decent error handling etc.).

GRUB is an example of this approach.

Multiboot

GRUB is capable of traditional chainloading of VBR's as well as booting Linux kernels directly. But the GRUB developers additionally specified the Multiboot standard: A Multiboot-compliant kernel could be booted with protected mode already set up, kernel modules loaded to memory, a memory map provided, and several other niceties. The idea was to provide a standard where any Multiboot-loader could load any Multiboot-kernel.

ROM expansion

Another way of booting was invented for diskless systems. In this case bootloader code resides in ROM, most commonly on a network card. Instead of booting from disk, it loads the OS over the network.

It is possible to hack such a ROM, and make the BIOS load your bootloader code, which in turn loads your OS. Details can be found in the BIOS boot specification.

GPT

As disks became larger, the limits of the MBR partitioning scheme became an issue. Together with a new boot mechanism (EFI), a new partitioning scheme called GPT was devised, using the sectors in the gap after the MBR for its data.

Old MBR-based tools - like the aforementioned boot managers - could accidentially overwrite the GPT data structures. As a signal that a given device is using GPT for its partitioning, its MBR partition table marks the whole device as being one single partition of the new type 0xEE. Within the scope of GPT, the so-marked MBR is called Protective Master Boot Record.

BIOS Boot Partition

Some people find EFI overcomplicated, and wanted the ability to use a GPT-partitioned device but still boot via the old BIOS / two-stage bootloader mechanism. They introduced a special GPT partition type, the BIOS Boot Partition, which does not hold any filesystem, but is a place to store the second stage of the bootmanager.

The goal

The goal is to load a code and provide consistent environment for it in different scenarios, such as:

  • MBR(UBRL) -> 2nd stage
  • MBR -> VBR(UBRL) -> 2nd stage
  • PMBR -> BBP -> 2nd stage
  • GRUB(chainload) -> VBR -> 2nd stage
  • GRUB(fs, as kernel) -> 2nd stage
  • VBR(partitionless disk) -> 2nd stage
  • BIOS -> expansion ROM -> 2nd stage

The boot code is responsible for finding a dedicated filesystem (marked by an active flag for example), locate standard file(s) on it (the kernel and optionally an initrd or modules), load them into memory and pass control over. To achieve this, first you have to get your boot code loaded.

I'll show you how to write a code that can be loaded by standard BIOS boot method, via Multiboot and from ROM as well.

If you use one of the standard filesystems (ext, fat etc.) I advise to read no further, simply use GRUB or other MultiBoot compliant boot loader. You can take advantage of a loader of your own only if you want to go down through the rabbit hole, and use your own file system or executable format.

Finally, the code

1st stage

This is a minimal code (440 bytes at most) that can be placed in a Master Boot Record or GPT's Protective MBR. It's loaded by the BIOS or a boot record via chainload.

The 1st stage code shown here is:

  • 100% compatible with the original IBM PC master boot record code
  • Same binary can be installed as MBR, PMBR or VBR as well
  • Forward GPT compatible on non-EFI machines
  • Loads a 2nd stage loader from any partition
  • Works with harddisks and USB keys (pendrives). Floppies not supported.
  • Requires LBA support, no CHS (could be a problem for BIOSes older than 10 years).
  • Tested on several virtual and real machines.

stage1.asm:

;*********************************************************************
;*                                                                   *
;*     OS/3D - written by Zoltan Baldaszti (aka Turdus) in 2008      *
;*        Compilation: fasm -d EFISIZE=1 stage1.asm ubrl.bin         *
;*                                                                   *
;*  Universal Boot Record Loader.                                    *
;*                                                                   *
;*  memory occupied: 0500-800                                        *
;*                                                                   *
;*********************************************************************
 
;get default EFISIZE, PARTSIZE
;EFISIZE=EFI System partition size in kilobytes
;PARTSIZE=size of your filesystem's partition in kilobytes
include "config.inc"
;--------------------------Macros----------------------------
;the writestr mnemonic. Writes a message on screen.
macro writestr msg
{
if ~ msg eq si
	push	si
	mov	si, msg
end if
	call	writestrfunc
if ~ msg eq si
	pop	si
end if
}
 
;the die mnemonic. Writes a reason and reboots.
macro die msg
{
if ~ msg eq si
	mov	si, msg
end if
	jmp	diefunc
}
 
;2nd stage header format (64 bytes):
;offset length  desc
; 0      2      expansion ROM magic (AA55h)
; 2      1      size in blocks (40h)
; 3      1      magic E9h
; 4      2      real mode entry point (relative)
; 6      2      checksum
; 7      1      revision, must be zero
; 8     18      architecture (default "x86_bios")
;26      2      pnp ptr, must be zero
;28      4      flags, must be zero
;32     32      Multiboot header (for GRUB compatibility, must contain a
;               protected mode entry point)
;any format can follow.
 
ldr.id		equ		800h		;position of 2nd stage loader
ldr.firstbyte	equ		840h		;first non-header byte
ldr.executor	equ		803h		;ptr to init code
ldr.loader	equ		500h		;loader and partition type
ldr.drive	equ		501h		;bios drive of loader
ldr.begin	equ		506h		;lba of loader
ldr.bootdiskid	equ		50Eh		;boot disk unique winnt id
ldr.bootbegin	equ		522h		;lba of boot partition
ldr.bootsize	equ		52Ah		;size of boot partition
lba_packet	equ		532h
virtual at lba_packet
lbapacket.size:	dw		?
lbapacket.count:dw		?
lbapacket.addr0:dw		?
lbapacket.addr1:dw		?
lbapacket.sect0:dw		?
lbapacket.sect1:dw		?
lbapacket.sect2:dw		?
lbapacket.sect3:dw		?
end virtual
 
;*********************************************************************
;*                             code                                  *
;*********************************************************************
 
;-----------------BIOS called ENTRY POINT--------------------
		ORG		0600h
		USE16
universal_boot_record:
		cli
		jmp		short .skipid
.system:	db		"UBRL"		;Universal Boot Record Loader
.version:	db		0		;version 0.0.1beta
 
.skipid:	;NOTE: not sure about 32 bit processor yet, so we have to
		;use only 16 bit operands
		;relocate our code to offset 600h
		xor		ax, ax
		mov		ss, ax
		mov		sp, 600h
		push		ax
		pop		es
		push		cs
		pop		ds
		;find our position in memory.
		call		.getaddress
.getaddress:	pop		bx
		sub		bx, .getaddress-universal_boot_record
		mov		si, bx
		cld
		mov		di, sp
		;clear data area 500h-600h
		sub		di, 100h
		mov		cx, 80h
		repnz		stosw
		;and copy ourselves to 600h
		mov		cx, 100h
		repnz		movsw
		jmp		0:.start
 
.start:		;get boot drive code - original MBR and BIOS passes it in dl,
		;so all bootmanagers should do.
 
		;save drive code
		mov		byte [ldr.drive], dl
		;initialize lba packet
		mov		byte [lbapacket.size], 16
		mov		byte [lbapacket.count], 58
		mov		byte [lbapacket.addr0+1], 08h
		push		dx
		writestr		.system
		pop		dx
		;check for lba presistance - floppy not supported any more
		;we use pendrive as removable media for a long time
		cmp		dl, byte 80h
		jl		.nolba
.notfloppy:	mov		ah, byte 41h
		mov		bx, word 55AAh
		int		13h
		jc		.nolba
		cmp		bx, word 0AA55h
		jne		.nolba
		test		cl, byte 1
		jnz		.lbaok
.nolba:		die		lbanotf
.lbaok:		;try to load stage2 - it's a continous area on disk
		;started at given sector with maximum size of 7400h bytes
		;doesn't matter if we can load it or not,
		;we still want to search for partitions
		mov		si, stage2_addr
		mov		di, lbapacket.sect0
		push		di
		movsw
		movsw
		movsw
		movsw
		call		loadsectorfunc
 
		;we have to load the MBR beacuse we may be loaded
		;as a volume boot record
		mov		byte [lbapacket.addr0+1], 07Ch
		pop		di
		xor		ax, ax
		stosw
		stosw
		stosw
		stosw
		;actually we load more than 2 sectors, but it's fast and
		;does not bother anybody. The code is smaller this way,
		;and nevertheless pre-caching fasten up overall boot time.
		call		loadsectorfunc
		jc		.nombr
		mov		bp, 07DFEh
		cmp		word [bp], bx
		je		.mbrok
.nombr:		die		mbrnotf
.mbrok:		inc		byte [bp]	;mess up id
		;copy disk id from MBR
		;NOTE: si now points to 7DB8h
		mov		di, diskid
		movsw
		movsw
 
		;searching for active partition (skip over 2 more zero bytes)
		inc		si
		inc		si
		;check for EFI partitioning scheme (so we will not depend on
		;special partition record EEh and such)
		;NOTE: bp+2 is 7E00h
		cmp		word [bp+2], 'EF'
		jne		.nextpartition
		cmp		word [bp+4], 'I '
		jne		.nextpartition
		;generate fake MBR partition entry for it
		xor		dx, dx
		xor		cx, cx
		inc		cx
		jmp		near .partitionok
 
.nextpartition:	cmp		si, bp
		jb		.ok
		;indicate no boot partition found
		mov		byte [ldr.loader], 16
		jmp		near .chk
.ok:		mov		ax, word [si+12]
		mov		word [ldr.bootsize], ax
		mov		al, byte [si]
		mov		cx, word [si+8]
		mov		dx, word [si+10]
		add		si, word 16
		cmp		al, byte 80h		;check for active partition
		jb		.nextpartition
		;this is a dirty hack. It allows to boot from a different disk.
		;this should not be used, but we must keep compatibility with
		;the M$ boot record shit. Anyway, if you use 80h, we won't
		;change the device code that the BIOS gave us.
		je		.partitionok
		mov		byte [ldr.drive], al
		;save the partition info
.partitionok:	mov		word [ldr.bootbegin], cx
		mov		word [ldr.bootbegin+2], dx
		;load partition's (or GPT table's) first 58 sector
		mov		word [lbapacket.sect0], cx
		mov		word [lbapacket.sect1], dx
		call		loadsectorfunc
		;do we have a 2nd stage loader?
.chk:		cmp		word [ldr.id], bx
		jne		.nostage2
		cmp		bzte [ldr.id+3], 0E9h
		jne		.nostage2
		;invoke stage2 real mode code
		writestr		okay
		jmp		ldr.executor
 
.nostage2:	;if no 2nd stage loader, continue with standard boot mechanism
		;check if it's a valid boot record
		cmp		word [bp], bx
		jne		.noos
		;do not load ourself again, that would be a loop forever
		cmp		word [bp+2+.system-universal_boot_record], 'UB'
		je		.noos
		writestr		okay
		;dl must contain bootdrive
		mov		dl, byte [ldr.drive]
		jmp		07C00h
.noos:		die		osnotfound
 
;*********************************************************************
;*                          functions                                *
;*********************************************************************
 
;loads an LBA sector
loadmbr:
loadsectorfunc:
		push		bx
		push		si
		mov		ah, byte 42h
		mov		dl, byte [ldr.drive]
		mov		si, lba_packet
		int		13h
		pop		si
		pop		bx
		ret
 
;ds:si zero terminated string to write
writestrfunc:
		lodsb
		or		al, al
		jz		.end
		mov		ah, byte 0Eh
		mov		bx, word 11
		int		10h
		jmp		writestrfunc
.end:		ret
 
;writes the reason, waits for a key and reboots.
diefunc:
		writestr		panic
		call 		writestrfunc
		mov		si, found
		call		writestrfunc
		xor		ax, ax
		int		16h
		mov		al, 0FEh
		out		64h, al
;		hlt
		jmp		far 0FFFFh:0		;invoke BIOS POST
 
;*********************************************************************
;*                          data area                                *
;*********************************************************************
 
panic:		db		"-PANIC: no ",0
lbanotf:	db		"LBA support",0
mbrnotf:	db		"PMBR",0
osnotfound:	db		"operating system",0
found:		db		" found",0
okay:		db		".",10,13,0
		db		01B0h-($-$$) dup 0
 
;right before the partition table some data
stage2_addr:	dd		0FFFFFFFFh,0	;1B0h 2nd stage loader address
diskid:		dd		012345678h	;1B8h WinNT expects it here
		dw		0
 
		;fake partition tables
		;EFI Partition
		db		0h		;bootable
		db		0FEh,0FFh,0FFh	;CHS not used
		db		0EEh		;fs type
		db		0FEh,0FFh,0FFh	;CHS not used
		dd		1		;start LBA
		dd		0ffffffffh	;end LBA
		;OS/3D root, modify to your needs
		db		80h		;bootable
		db		0FEh,0FFh,0FFh	;CHS not used
		db		03dh		;fs type
		db		0FEh,0FFh,0FFh	;CHS not used
		dd		(EFISIZE*2)+8	;start LBA
		dd		0ffffffffh	;end LBA
 
		db              01FEh-($-$$) dup 0
		db		55h,0AAh
 
;*********************************************************************
;*                   end of Universal Boot Record                    *
;*********************************************************************
 
;*********************************************************************
;*                      GUID Partition Table                         *
;*********************************************************************
;NOTE: the image creator must calculate correct checksums.
gpt_header:
		;gpt header
.signature:	db		"EFI PART"
.revision:	dw		0,1
.size:		dd		92
.crc:		dd		0DEADCC32h	;not used, but should be valid
.reserved:	dd		0
.primarylba:	dd		1,0
.backuplba:	dd		1,0		;not used, but should be valid
.firstusable:	dd		64,0
.lastusable:	dd		0FFFFFFFFh,0	;not used, but should be valid
.diskuuid:	dd		1,2,3,4
.partitionlba:	dd		2,0
.numentries:	dd		2
.sizeentry:	dd		128
.partitioncrc:	dd		0DEADCC32h	;not used, but should be valid
		db		0200h-($-gpt_header) dup 0
 
gpt_partitions:
.entry0_type:	db		028h,073h,02Ah,0C1h,01Fh,0F8h,0D2h,011h
		db		0BAh,04Bh,0,0A0h,0C9h,03Eh,0C9h,03Bh
.entry0_uuid:	db		1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4
.entry0_start:	dd		8,0
.entry0_ends:	dd		(EFISIZE*2)+8,0
.entry0_attrib:	dd		0,0
.entry0_name:	db		'E',0,'F',0,'I',0,' ',0,'s',0,'y',0,'s',0
		db		't',0,'e',0,'m',0,' ',0,'p',0,'a',0,'r',0
		db		't',0,'i',0,'t',0,'i',0,'o',0,'n'
		db		128-($-.entry0_type) dup 0
 
;our test OS/3D system partition
;my stage2 code will look for BOOTDISK first, but if none found, it will use the
;first partition labeled ROOTDISK.
;modify this entry to your needs.
.entry1_type:	db		'FS3D',0,1,0,0,'ROOTDISK'
.entry1_uuid:	db		1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
.entry1_start:	dd		(EFISIZE*2)+8,0
.entry1_ends:	dd		(EFISIZE*2)+(PARTSIZE*2)+8,0
.entry1_attrib:	dd		0,0
.entry1_name:	db		'B',0,'e',0,'n',0,'d',0,'e'
		db		128-($-.entry1_type) dup 0
 
;-----------make it logical sector (4k) long----------
		db		4096-($-universal_boot_record) dup 0

2nd stage

The rest of the code can be right after the 1st stage, although it's discouraged due to compatibility issues (see section GPT). It's better to put it on a separate mbr or gpt partition (to protect it from overwriting by accident). Even better, store it in a defragmented, continuous file on your filesystem (note that being continous is essential since 1st stage cannot parse your fs). Do not forget to record the starting LBA address in 1st stage before using. This gives you the ability to use different 2nd stage code each time you reboot.

This is just a skeleton to aid you. It will not parse your filesystem, or set up the environment for your kernel. You have to do these on your own. But everything else is done so you can focus on these important matters.

stage2.asm:

;*********************************************************************
;*                                                                   *
;*     OS/3D - written by Zoltan Baldaszti (aka Turdus) in 2008      *
;*              Compilation: fasm stage2.asm stage2.bin              *
;*                                                                   *
;*  Example 2nd stage loader for UBRL, compatible with GRUB and      *
;*  BIOS boot specification 1.0.1 (expansion ROM) too.               *
;*                                                                   *
;*  memory occupied: 800-7C00                                        *
;*                                                                   *
;*********************************************************************
 
;-----------header section-------------
;UBR header
                USE16
                ORG                     800h
loader:         db                      55h,0AAh                ;ROM magic
                db                      (loader_end-loader)/512 ;size in 512 blocks
.executor:      jmp                     near realmode_start     ;entry point
.checksum:      dw                      0                       ;checksum
@@:             db                      "Your OS name here"
                db                      18-($-@b) dup 0
.pnpptr:        dw                      0
.flags:         dd                      0
;GRUB header
MB_MAGIC	equ			01BADB002h
MB_FLAGS	equ			01000h
		align			8
.mb_header:	dd			MB_MAGIC		;magic
		dd			MB_FLAGS		;flags
		dd			-(MB_MAGIC+MB_FLAGS)	;checksum (0-magic-flags)
		dd			.mb_header		;our location (GRUB should load us here)
		dd			0800h			;the same... load start
		dd			07C00h			;load end
		dd			0h			;no bss
		dd			multiboot_start		;entry point
 
;-----------realmode-protmode stub-------------
;MEMORY LAYOUT ON EXECUTION:
;500h		loader and partition type
;501h		bios drive code of loader
;506h		starting lba of this stage on disk
;50Eh		boot disk unique winnt id
;522h		starting lba of MBR boot partition/GPT table
;52Ah		size of MBR boot partition/GPT table
;800h		LDRF header
;7C00h		MBR boot partition/GPT table preloaded (first 16 sectors)
;		appropriate for chainloading
 
realmode_start: cld
                cli
                mov                     sp, 800h
                ;relocate ourself from ROM to RAM if necessary
                call                    .getaddress
.getaddress:    pop                     si
                mov                     ax, cs
                or                      ax, ax
                jnz                     .reloc
                cmp                     si, .getaddress
                je                      .noreloc
.reloc:         mov                     ds, ax
                xor                     ax, ax
                mov                     es, ax
                mov                     di, loader
                sub                     si, .getaddress-loader
                mov                     cx, ((loader_last+8)-loader)/2
                repnz                   movsw
                xor                     ax, ax
                mov                     ds, ax
                jmp                     0:.relocated
.relocated:	;you'll need to locate boot partition
		;and load it to 7C00h, because neither
		;BIOS nor UBRL have done this for you
		;in this case (loaded from ROM)
.noreloc:
		;do any real mode initialization here
		;(e820, A20 gate etc.)
 
		lgdt			[GDT_value]
		mov			eax, cr0		;enable protected mode
		or			al, 1
		mov			cr0, eax
		jmp			16:protmode_start
 
		;global descriptor table
		align			8
GDT_table:	dd			0, 0			;null descriptor
		dd			0000FFFFh,008F9200h	;flat ds
		dd			0000FFFFh,00CF9A00h	;32-bit ring0 cs
GDT_value:	dw			$-GDT_table
		dd			GDT_table
 
;----------------Multiboot stub-----------------
;MEMORY LAYOUT ON EXECUTION: see Multiboot spec
		USE32
multiboot_start:
		cld
		cli
		;you'll need to locate boot partition
		;and load it to 7C00h, because GRUB
		;hasn't done this for you
		lgdt			[GDT_value]
		cmp			eax, 2BADB002h
		je			protmode_start
		;no GRUB environment available?
		;something really nasty happened, restart computer
		mov			al, 0FEh
		out			64h, al
		hlt
 
protmode_start:	mov			ax, 8
		mov			ds, ax
		mov			es, ax
		mov			fs, ax
		mov			gs, ax
		mov			ss, ax
		mov			esp, 800h
 
		;do any prot mode initialization here
		;(irq remap, paging etc.)
 
;-----------longmode stub if you want-------------
		;enable long mode
 
;*********************************************************************
;*                        your loader code                           *
;*********************************************************************
		;put your own kernel loader code here.
		;parse your executable format here.
		;call your kernel's main here.
 
;-----------padding to be multiple of 512----------
                db                      (511-($-loader+511) mod 512) dup 0
loader_end:
;-----------BIOS checksum------------
chksum = 0
repeat $-loader
                load b byte from (loader+%-1)
                chksum = (chksum + b) mod 100h
end repeat
                store byte (100h-chksum) at (loader.checksum)
 
;-----------bound check-------------
		;fasm will generate an error if your code
		;is bigger than it should be
		db			07400h-($-loader) dup ?

Testing

UBRL in MBR

Quite straightforward, create a disk image with UBRL in MBR, and record stage2.bin's position at 1B0h.

UBRL in VBR

Create a disk image with your favourite MBR, and put UBRL in the 1st sector of a partition. You still have to record stage2.bin's position at 1B0h in MBR.

GRUB Multiboot

Specify stage2.bin as "kernel" in menu.lst.

BIOS ROM

Use bochs. Configure optional ROM, specify stage2.bin as ROM's name, and 0xC0000+(vga_bios_size rounded to 2k) as address (as of writing, bochs vga bios is 40960 bytes long, so the address will be 0xCA000). Also modify boot options to boot from "network". Ready to go!

Personal tools
Namespaces
Variants
Actions
Navigation
About
Toolbox