Advertisment!
Banner ad for Made to Order Software Corporation
English, français
y español
Fox Trot -- go back to the linux home page!

Booting

Problems with booting... numerous emails & posts about this one, that's for sure!!! Real solutions? Not really... In many cases people end up reinstalling their system from Scratch and once in a while even that doesn't work! But smart people have a way to fix their boot sectors without too much hurdle (instead of the hours it takes to install an OS.)

Yes! There ought to be a reason for things not to work so well once in while. In most cases, if you only have proper Unix systems (Linux, FreeBSD, HURD, etc.) you will be fine since these systems can be anywhere any time and they usually don't walk on each others feet.

Many people, however, want to be able to use MS-Windows with Linux (dual boot) and if possible multiple versions of Windows on the same drive (triple or quadruple boot!). Not wise! some people say, but I do see a great advantage to that: I don't need to have multiple computers or swap hard drives each time I want to boot on a different system.

My new system will have (once complete 8-P) the following systems: Win98, Win2k, WinXP, FC5, RH9.

RH9 is mainly to test new code on older systems and Win98 is for the kids to play older video games which don't work on Win2k or WinXP (or Linux if that matter... 8-P).

System Partition GRUB info
Windows 98 /dev/hda1
unhide (hd0,0)
unhide (hd0,1)
rootnovefiry (hd0,0)
makeactive
chainloader +1
Windows 2000 /dev/hda2
hide (hd0,0)
unhide (hd0,1)
rootnovefiry (hd0,1)
makeactive
chainloader +1
Windows XP /dev/hda3
hide (hd0,0)
hide (hd0,1)
rootnovefiry (hd0,2)
makeactive
chainloader +1
Fedora Core 5 /dev/hda5
unhide (hd0,0)
unhide (hd0,1)
root (hd0,5)
kernel linuz...
rdinit rdinit...
Red Hat 9 /dev/hda6
unhide (hd0,0)
unhide (hd0,1)
root (hd0,5)
kernel linuz...
rdinit rdinit...
My GRUB Setup

In this table, I show you what I use in my GRUB configuration file (menu.conf, menu.lst or grub.conf). I use hide/unhide to hide partitions since MS-Windows boots on C:, no other drive (well, with WinXP they may have ameliorated that a bit, but don't count on it.) So when you boot on /dev/hda2, it is important that /dev/hda1 is not recognized as a Windows partition of any kind. For this reason, I use hide(hd0,0) when I want to start Win2k (note that GRUB [and HURD] see partitions starting at 0 instead of 1; Linux uses 0 as the number representing the whole device rather than the first partition.)

Once the drives are properly hidden/shown, you are ready to boot from them. You need to specify the root drive with root or rootnoverify. The former will check the file-system which needs to be a valid Unix file-system (per GRUB at least) or you get a warning. Use rootnoverify for MS-Windows partitions so it skips the warning.

Another draw back from MS-Windows, they need to have the boot flag active on the partition you're booting from. In other words, the boot flag needs to change between /dev/hda1, /dev/hda2 and /dev/hda3 as I boot Win98, Win2k and WinXP. For the Linux partitions it is not required and plus these are not primary partitions so it's not useful (the boot flag won't work properly on extended partitions anyway.) The makeactive command from GRUB is what you use to make a partition bootable (it automatically clears the boot flag from all the other partitions.)

IMPORTANT: There can be only one partition marked as bootable on any given drive, otherwise your BIOS will not know which partition to boot from (this is not really the case, but that's what BIOSes do: check for the boot flag and its unicity.) The reality is that the BIOS will boot using the boot sector, not the partition boot strap or MBR. Actually for Linux there isn't a need for an MBR. But the BIOSes are for M$-Windowz...

Finally, to load the actual MBR to boot Windows, you use the chainloader command. The +<number> represents the number of sectors you need to load. In most cases it will be 1, but a few systems use more sectors.

Okay! We're ready to run... but what really happens?! Let's see the list of steps to boot your PC:

  • Connect your PC to the power (he! he!)
  • Push the ON button...
  • The processor wakes up with a cold reset; this means that your processor is in real mode and most of the registers are set to a specific value (this depends on your processor); the two important ones are CS and IP which are loaded with 0xF000 and 0x0000FFF0 respectively, and for more recent processors the base descriptor is set to 0xFFFF0000; this is where your BIOS [E]EPROM is visible and thus this is where everything starts!
  • The BIOS scans and tests your PC hardware and builds a list in RAM
  • The BIOS then tries to boot according to the table you defined in it (for example: start with the floppy, then CDROM, then Hard Disk)
  • Whichever you boot with, the BIOS loads the very first sector at 0x7C00 (for CDROMs, it's sector 17... but you need a complete & correct setup for the booting process to work properly of a CDROM anyway and this page is not about CDROM, at least not yet!). That single sector includes code and the drive partitions information. See below for code samples from Linux and MS-Windows. (If you were wondering why your 1st partition had to start on sector 1 and not 0, now you know why... hmmm... except that the 1st partition really starts on block 0x3F on a hard drive; that's sector 63! More on that later.)
  • Okay! So now you have code at 0x7C00 on 0x200 (or 512) bytes (up to 0x7DFF). In general, it will start with a jump instruction to skip some table to the actual code. I'm not too sure why the Linux version has such a table since it's not used. But anyway, the fact is that this code will issue INT 13 interrupts in order to load other sectors. These will represent other boot programs and/or a kernel.
  • The kernel is started... ha! no, if you have LILO or GRUB actually that tool is started! Cool! Now we can do more, such as say we need to load an MBR to really boot this or that OS. That's what happens when you want to load a MS-Windows kernel. It reads sector 0 of the specified partition (the MBR) at the same address as the boot sector (i.e. 0x7C00) and runs it.

But... but... but... I need a driver to boot windows!!!
Ho, yeah... that's because your BIOS has trouble accessing your hard disk. This is a problem whatever the system except that Linux is likely to understand your hard disk without any extra driver. However, if this is the case, you will need to boot from the beginning of your hard drive (from the first 7.6Gb to 8Gb depending on the support you have in your BIOS.)

What the driver does is take over the 0x13 interrupt. This means it will read the structure you pass to it and convert that in the proper read commands for your hard drive. These can in general be used with any systems, but Linux is not likely to recognize such a driver and it will certainly bypass it. (On the other hand you have options to ask Linux to continue to use the BIOS even after it started its kernel in which case it will use that special driver.)

Okay, now let's move on to the hard code stuff. The actual code in your boot strap and MBRs.

MBR Structure

The basic MBR structure is as follow:

struct fdisk_partition {
	uint8_t		f_boot_flag;		/* 0: not bootable, 128: bootable */
	uint8_t		f_begin_head;		/* beginning head number */
	uint8_t		f_begin_cylhi : 2;	/* 2 high bits for beginning cylinders */
	uint8_t		f_begin_sector : 6;	/* beginning sector number */
	uint8_t		f_begin_cyllo;		/* beginning cylinder low bits */
	uint8_t		f_system_id;		/* Operating System type indicator code */
	uint8_t		f_end_head;		/* ending head number */
	uint8_t		f_end_sector : 2;	/* 2 high bits for ending cylinder number */
	uint8_t		f_end_sector : 6;	/* ending sector number */
	uint8_t		f_end_cyllo;		/* ending cylinder low bits */
	uint32_t	f_relative_sector;	/* first sector relative to start of disk */
	uint32_t	f_sectors_count;	/* number of sectors in partition */
};

// f_begin_cylinder = f_begin_cyllo + (f_begin_cylhi << 8);
// f_end_cylinder = f_end_cyllo + (f_end_cylhi << 8);
//
// Note that the maximum of each entry is:
//	1024 cylinder (210)
//	256 heads (28)
//	64 sectors (26)
// and thus the maximum addressable sector with these metrics are:
//	1024 x 256 x 64 = 16,777,216 (~8Gb)
//
// Until we got the LBA which solved the problem by using the same
// addressing as the SCSI drives which had it right from the beginning...


struct master_boot_record {
	int8_t    	f_bootinst[446];	/* boot code and filesystem information */
	fdisk_partition	f_partitions[4];	/* primary partitions definitions */
	uint16_t	f_signature;		/* magic word always set to 0xAA55 */
};


// Floppy parameters at 0000:001E
// This is called Floppy but is used by hard drives as well.

struct floppy_parameters {
	uint8_t		f_step_rate : 4;	/* speed x 2 ms */
	uint8_t		f_head_unload : 4;	/* 0x0F = 240 ms */
	uint8_t		f_head_load : 7;	/* time x 4 ms */
	uint8_t		f_no_dma : 1;		/* always 0 */
	uint8_t		f_turn_off_motor;	/* in clock ticks */
	uint8_t		f_bytes_per_sector;	/* 0: 128, 1: 256, 2: 512, 3: 1024 */
	uint8_t		f_sectors_per_track;	/* 0xFF means variable */
	uint8_t		f_gap_length;		/* 0x2A for 5.25", and 0x1B for 3.5" */
	uint8_t		f_data_length;		/* used if bytes per sector is zero */
	uint8_t		f_format_gap_length;	/* 0x50 for 5.25", and 0x6C for 3.5" */
	uint8_t		f_filler_byte;		/* default 0xF6 */
	uint8_t		f_head_settle_time;	/* in ms */
	uint8_t		f_turn_on_motor;	/* in 1/8th of a second */
};
// size is 11 bytes

struct surepath {
	struct floppy_parameters	f_common;
	uint8_t		f_max_track;
	uint8_t		f_transfer_rate;
	uint8_t		f_drive_type;
};


// INT 13, AH = 42 uses the following structure which needs to be pointed by
// DS:SI (and DL is the drive number)
struct disk_packet_address {
	uint8_t		f_size;			/* size of this packet (0x10 or 0x18) */
	uint8_t		f_reserved;		/* always 0 */
	uint16_t	f_blocks_to_transfer;	/* max 0x007F on Phoenix EDD */
	uint32_t	f_buffer_ptr;		/* pointer where to load */
	uint64_t	f_start_sector;		/* LBA or see below */
	// the following is optional and only available with EDD 3.0
	uint64_t	f_buffer64_ptr;		/* used this when f_buffer_ptr is 0xFFFFFFFF */
};
// Start Sector when LBA isn't used:
//	(Cylinder x Heads + Selected Head) x Sectors per Track + Selected Sector - 1

};

Win98 Boot Sector

The following is a copy of the Win98 boot strap (or MBR). INT 19 will read that information and jump to 0x0000:0x7C00. It is expected to read other sectors and start running some operating system.

	# Note: it is assumed that CS is 0x0000 by this time
	7C00: EB 3C		jmps		start		go to 7C3E
	7C02: 90		nop

	BIOS Parameter Block and Extended Block
	(not 100% sure, but it could be that the BIOS [or LILO/GRUB] will
	poke some values in here before jumping to 7C00)

	7C03: 4D 53 57 49 4E 34 2E 31		MSWIN4.1
	7C0B: 00 02				0x0200 (512) bytes per sector
	7C0D: 40				0x40 (64) sectors per cylinder
	7C0E: 01 00				0x0001 (1) reserved sector (this MBR)
	7C10: 02				0x02 (2) number of FATs (File Allocation Tables)
	7C11: 00 02				0x0200 (512) number of files in the root directory
	7C13: 00 00				0x0000 (0) number of sectors (if 0, check large count below)
	7C15: F8				media type
	7C16: FA 00				0x00FA (250) sectors per FAT
	7C18: 3F 00				0x003F (63) sectors per track
	7C1A: FF 00				0x00FF (255) heads
	7C1C: 3F 00 00 00			0x003F (63) hidden sectors
	7C20: B5 CF 3B 00			0x003BCFB5 (3919797) sectors (too large for 7C13)
	7C24: 80				physical disk number (always 0x80 for hard drives)
	7C25: 00				unused (current head)
	7C26: 29				signature (must be 0x28 or 0x29)
	7C27: F8 11 08 42			Some Unique ID
	7C2B: 20 20 20 20 20 20 20 20 20 20 20	Unused disk label
	7C36: 46 41 54 31 36 20 20 20		FAT16, the system ID (FAT12 or FAT16)

	start:
	7C3E: 33 C9				xor	cx,cx		clear CX
	7C40: 8E D1				mov	ss,cx		clear SS
	7C42: BC FC 7B				mov	sp,0x7BFC	set SP to7BFC (just before this block)
	7C45: 16				push	ss		copy SS to ES (i.e. clear ES)
	7C46: 07				pop	es
	7C47: BD 78 00				mov	bp,0x0078	address of INT 1E
	7C4A: C5 76 00				lds	si,[bp+0x00]	loading DS:SI (source)
	7C4D: 1E				push	ds		save DS:SI (original INT 1E content)
	7C4E: 56				push	si		(need to restore that later!)
	7C4F: 16				push	ss		save SS:BP (address to INT 1E)
	7C50: 55				push	bp
	7C51: BF 22 05				mov	di,0x0522	address = 0x0522 (dest.)
	7C54: 89 7E 00				mov	[bp+0x00],di	save ES:DI (dest.) in INT 1E
	7C57: 89 4E 02				mov	[bp+0x02],cx	clear (segment = 0)
	7C5A: B1 0B				mov	cl,0x0B
	7C5C: FC				cld
	7C5D: F3 A4				rep movsb		copy 11 bytes (floppy parameters)
	7C5F: 06				push	es		clear ds (segment = 0)
	7C60: 1F				pop	ds
	7C61: BD 00 7C				mov	bp,0x7C00
	7C64: C6 45 FE 0F			mov	[di-0x02],0x0F	set settle time to 15 ms
	7C68: 38 4E 24				cmp	[bp+0x24],cl	is [bp+24] negative? (i.e. hard drive)
	7C6B: 7D 20				jnl	boot_floppy	go to 7C8D

	harddrive:
	7C6D: 8B C1				mov	ax,cx		clear ax (read sector 0, partition?)
	7C6F: 99				cwd			clear dx
	7C70: E8 7E 01				calln	read		go to 7DF1
	7C73: 83 EB 3A				sub	bx,0x3A		go to the 1st partition table
	7C76: 66 A1 1C 7C			mov	eax,[0x7C1C]	# of hidden sectors
	next_partition:
	7C7A: 66 3B 07				cmp	eax,[bx]
	7C7D: 8A 57 FC				mov	dl,[bx-0x04]	
	7C80: 75 06				jne	keep_old_read	go to 7C88
	7C82: 80 CA 02				or	dl,0x02
	7C85: 88 56 02				mov	[bp+0x02],dl	if 0x0E, use extended read
	keep_old_read:
	7C88: 80 C3 10				add	bl,0x10		next partition
	7C8B: 73 ED				jnc	next_partition	go to 7C7A

	The follow reads the root directory in which it expects to find the file
	JO.SYS (unlikely) or IO.SYS (more likely!)

	7C8D: 33 C9				xor	cx,cx
	7C8F: FE 06 D8 7D			inc	[0x7DD8]	character I -> J
	retry:
	7C93: 8A 46 10				mov	al,[bp+0x10]	number of FATs
	7C96: 98				cbw			clear ah
	7C97: F7 66 16				mul	[bp+0x16]	sectors per FAT
	7C9A: 03 46 1C				add	ax,[bp+0x1C]	hidden sectors (low)
	7C9D: 13 56 1E				adc	dx,[bp+0x1E]	hidden sectors (high)
	7CA0: 03 46 0E				add	ax,[bp+0x0E]	reserved sectors (low)
	7CA3: 13 D1 				adc	dx,cx		add carry if any (high)
	7CA5: 8B 76 11				mov	si,[bp+0x11]	number of files in root dir.
	7CA8: 60				pushaw
	7CA9: 89 46 FC				mov	[bp-0x04],ax	save sector #
	7CAC: 89 56 FE				mov	[bp-0x02],dx
	7CAF: B8 20 00				mov	ax,0020
	7CB2: F7 E6				mul	si		ax,dx = si x 32 (i.e. 0x00004000)
	7CB4: 8B 5E 0B				mov	bx,[bp+0x0B]	size of 1 sector
	7CB7: 03 C3				add	ax,bx
	7CB9: 48				dec	ax
	7CBA: F7 F3				div	bx		(ax,dx + bx - 1) / bx (i.e. 0x41FF / 0x200 = 0x20)
	7CBC: 01 46 FC				add	[bp-0x04],ax
	7CBF: 11 4E FE				adc	[bp-0x02],cx
	7CC2: 61				popaw
	7CC3: BF 00 07				mov	di,0x0700
	7CC6: E8 28 01				calln	read		go to 7DF1
	7CC9: 72 3E				jc	io_error	go to 7D09
	compare_next:
	7CCB: 38 2D				cmp	[di],ch		is [di] null?
	7CCD: 74 17				je	try_again	go to 7CE6
	7CCF: 60				pushaw
	7CD0: B1 0B				mov	cl,0x0B		11 chars
	7CD2: BE D8 7D				mov	si,0x7DD8	"JO      SYS" or "IO      SYS"
	7CD5: F3 A6				rep cmpsb		assumes that the D flag is still 0
	7CD7: 61				popaw
	7CD8: 74 3D				je	load_system	go to 7D17
	7CDA: 4E				dec	si
	7CDB: 74 09				je	try_again	go to 7CE6
	7CDD: 83 C7 20				addw	di,0x0020
	7CE0: 3B FB				cmp	di,bx
	7CE2: 72 E7				jc	compare_next	go to 7CCB
	7CE4: EB DD				jmps			go to 7CC3


	try_again:
	7CE6: FE 0E D8 7D			dec	[0x7DD8]	restore the 'I' and try again (when we get an 'H' we don't branch)
	7CEA: 7B A7				jnp	retry		go to 7C93

	not_a_system_disk:
	7CEC: BE 7F 7D				mov	si,0x7D7F

	print:		// print 2 strings (input si and then "Press Any Key")
	7CEF: AC				lodsb			load 1 byte from si (implies "inc si")
	7CF0: 98				cbw			clear ah
	7CF1: 03 F0				add	si,ax		add to si
	next_char:
	7CF3: AC				lodsb			load 1 character from string
	7CF4: 98				cbw
	7CF5: 40				inc	ax		is it 0xFF? (end of string followed by press a key)
	7CF6: 74 0C				je	msg_press_key	go to 7D04
	7CF8: 48				dec	ax		is it 0x00? (end of string)
	7CF9: 74 13				je	msg_beep	go to 7D0E
	7CFB: B4 0E				mov	ah,0x0E		put char in al on screen
	7CFD: BB 07 00				mov	bx,0x0007
	7D00: CD 10				int	0x10
	7D02: EB EF				jmps	next_char

	msg_press_key:
	7D04: BE 82 7D				mov	si,0x7D82
	7D07: EB E6				jmps	print		go to 7CEF

	io_error:
	7D09: BE 80 7D				mov	si,0x7D80
	7D0C: EB E1				jmps	print		go to 7CEF

	msg_beep:
	7D0E: CD 16				int	16		getchar() (keyboard read)
	7D10: 5E				pop	si
	7D11: 1F				pop	ds
	7D12: 66 8F 04				popd	[si]
	7D15: CD 19				int	19		bootstrap loader (does not return)

	The following found the file named JO.SYS or IO.SYS in the root directory
	and it starts loading its first 4 sectors and execute them

	On my system this file is 218Kb, so that's quite big. It certainly is
	the handling of all of MSDOS.

	load_system:
	7D17: BE 81 7D				mov	si,0x7D81	<- this is weird...
	7D1A: 8B 7D 1A				mov	di,[di+0x1A]
	7D1D: 8D 45 FE				lea	ax,[di-0x02]
	7D20: 8a 3E 0D				mov	cl,[bp+0x0D]
	7D23: F7 E1				mulw	cx
	7D25: 03 46 FC				add	ax,[bp-0x04]
	7D28: 13 56 FE				adc	dx,[bp-0x02]
	7D2B: B1 04				mov	cl,0x04		read 4 sectors
	7D2D: E8 C2 00				calln	read_nsectors	go to 7DF2
	7D30: 72 D7				jc	io_error	go to 7D09
	7D32: EA 00 02 70 00			jmpf	0x0070:0x0200	start the system!


	read_sector:
			//
			// all these push generate a Disk Address Packet structure
			// note that the input is dx,ax with the LBA sector number
			// and bx with the address where the sector is to be read (0x0700)
			//
			// the dx,ax notation is "advanced" and here they compute
			// the old fashion read in al,ch,cl,dh,dl and es:bx even
			// if we are to read with the new extended read... This
			// is the opposite of that computation:
			//	(Cylinder x Heads + Selected Head) x Sectors per Track + Selected Sector - 1
			//
	7D37: 52				push	dx		block # (high)
	7D38: 50				push	ax		block # (low)
	7D39: 06				push	es		buffer segment
	7D3A: 53				push	bx		buffer address
	7D3B: 6A 01				push	0x0001		# of blocks to transfer
	7D3D: 6A 10				push	0x0010		packet size (16 bytes) + reserved
	7D3F: 91				xchg	ax,cx		mov ax in cx in 1 byte
	7D40: 8B 46 18				mov	ax,[bp+18]	get our sectors per track
	7D43: A2 26 05				mov	[0x0526],al	copy our sectors per track info
	7D46: 96				xchg	ax,si		mov si,ax (sectors per track)
	7D47: 92				xchg	ax,dx		mov ax,dx
	7D48: 33 D2				xor	dx,dx		clear dx
	7D4A: F7 F6				div	si		dx,ax / si => ax (remainder in dx)
	7D4C: 91				xchg	ax,cx		restore ax
	7D4D: F7 F6				div	si		dx,ax / si => ax (remainder in dx)
	7D4F: 42				inc	dx
	7D50: 87 CA				xchg	cx,dx
	7D52: F7 76 1A				div	[bp+0x1A]	dx,ax / [bp+1A] (# of heads)
	7D55: 8A F2				mov	dh,dl		duplicate remainder
	7D57: 8A E8				mov	ch,al		ch = low eight bits of cylinder #
	7D59: C0 CC 02				ror	ah,2		put bits 0..1 in bits 6..7
	7D5C: 0A CC				or	cl,ah		cl = sector number + high cylinder bits
	7D5E: B8 01 02				mov	ax,0x0201	read disk command (non-extended!), 1 sector
	7D61: 80 7E 02 0E			cmp	[bp+0x02],0x0E	extended detected?
	7D65: 75 04				jne	not_extended	go to 7D16B
	7D67: B4 42				mov	ah,42		extended read disk command
	7D69: 8B F4				mov	si,sp		pointer to the disk address packet
	not_extended:
	7D6B: 8A 56 24				mov	dl,[bp+0x24]	drive number
	7D6E: CD 13				int	13		floppy/hard disk service
	7D70: 61				popaw			get rid of the disk address packet
	7D71: 61				popaw			restore all the registers
	7D72: 72 0A				jc	read_err	go to 7D7E
	7D74: 40				inc	ax		next sector (dx:ax + 1)
	7D75: 75 01				jne	no_carry	go to 7D78
	7D77: 42				inc	dx		manage overflow (32 bits number!)
	no_carry:
	7D78: 03 5E 0b				add	bx,[bp+0x0B]	add size of 1 sector to address
	7D7B: 49				dec	cx		read more sectors?
	7D7C: 75 77				jne	read_more	go to 7DF5
	read_err:
	7D7E: C3				retn

	7D7F: 03				0x7D83 -- message "\r\nDisque non systeme \377"
	7D80: 18				0x7D99 -- message "\r\nErreur d'E/S  \377"
	7D81: 01				0x7D83 -- message "\r\nDisque non systeme \377"
	7D82: 27				0x7DAA -- message "\r\nRemplacez-le et appuyez sur une touche  \r\n\0"
	7D83: 0D 0A 44 69 73 71 75 65 20 6E 6F 6E 20 73 79 73 74 65 6D 65 20 FF
	7D99: 0D 0A 45 72 65 75 72 20 64 27 45 2F 53 20 20 FF
	7DAA: 0D 0A 52 65 6D 70 6C 61 63 65 7A 2D 6C 65 20 65 74 20 61 70 70 75
	7DC0: 79 65 7A 20 73 75 72 20 75 6E 65 20 74 6F 75 63 68 65 20 20 0D 0A 00

	7DD7: 00				.byte	0x00		unused

	7DD8: 49 4F 20 20 20 20 20 20 53 59 53	"IO      SYS"
	7DE3: 4D 53 54 4F 53 20 20 20 53 59 53	"MSDOS   SYS"

	7DEE: 7F 01 00				.byte	0x7F, 0x01, 0x00	seems unused

	read:
	7DF1: 41				inc	cx		it is assumed that cx = 0 on entry, so read 1 sector
	read_nsectors:
	7DF2: BB 00 07				mov	bx,0x0700	address where sector is to be read
	load_next:
	7DF5: 60				pushaw			save all registers
	7DF6: 66 6A 00				push	0x00000000	start a read structure on the stack
	7DF9: E9 3B FF				jmpn	load_sector	go to 7D37

	7DFC: 00 00				.byte	0x00, 0x00	unused
	7DFE: 55 AA				.byte	0x55, 0xAA	valid MBR magic word

Win2k Boot Sector

Now there is my Win2k boot sector. It is very similar with some better handling such as interrupt protections. Also this booter will use the LBA only when necessary (i.e. whenever it has to load a sector over the drive limits, thus if over 8Gb or so)

Since I moved my Win2k partition from partition 1 to partition 2, I had to change the value at offset 0x001C (0x0000003F is for partition 1). This represents the sector # of the partition where Win2k is to be booted from. (Note: You will find that number looking at the very first sector of your hard drive since that's where you have the partition.) This makes the boot go a little way further but it is not enough.

What the MBR does is load some sort of NTFS loader. That loader looks for the file named boot.ini on your C: drive. You may have heard of boot.ini since it has existed for a while now.

That file includes the drive and partition on which Win2k (applies to WinXP too) was installed and thus should "boot from" (i.e. read the kernel from.) Of course, mine was saying Partition(1) since it was on partition 1 before. I went in and changed the number from 1 to 2 and this time it worked all the way. The boot process succeeded and I can now enjoy Win2k.

Note that you will need to make the file not Read Only and since there is no editor in the Rescue Mode of the Win2k or the WinXP CDs, I actually made the change from another Win2k install, copied the file on some Win98 partition and finally copied that file to the Win2k I needed to fix. You could also use a floppy disk. Note that if you used a FAT partition instead of NTFS, you can boot from Fedora Core or some other Linux Rescue and in there you have VI and usually one other editor like pico.

	# Note: it is assumed that CS is 0x0000 by this time
	7C00: EB 52				jmps	start		go to 7C54
	7C02: 90				nop

	7C03: 4E 54 46 53 20 20 20 20		"NTFS    "
	7C0B: 00 02				0x0200 (512) bytes per sector
	7C0D: 08				0x08 (64) sectors per cylinder
	7C0E: 00 00				number of sectors to read (run time)
	7C10: 00 00 00 00			sector to read (relative to start of partition defined in 001C) (run time)
	7C14: 00				has LBA capability when not zero (run time)
	7C15: F8				media type
	7C16: 00 00				0x0000 (0) sectors per FAT (unused)
	7C18: 3F 00				0x003F (63) sectors per track
	7C1A: FF 00				0x00FF (255) heads
	7C1C: 3F 00 00 00			0x003F (63) start of partition (if partition 1, 0x3F)
	7C20: 00 00 00 00			sector upper limit (sector max. #)
	7C24: 80				physical disk number (always 0x80 for hard drives)
	7C25: 00				unused (current head)
	7C26: 80				signature (must be 0x80?)
	7C27: 00				unused (?)
	7C28: DF AD 31 01 00 00 00 00		0x0131ADDF (20032991) total number of sectors in this partition (here ~10Gb)
	7C30: 04 00 00 00 00 00 00 00		0x04 (4) logical sector for file $MFT
	7C38: DD 1A 13 00 00 00 00 00		0x00131ADD (1252061) logical sector for file $MFTMirr
	7C40: F6 00 00 00			0x000000F6 (246) number of sectors per file record segment
	7C44: 01 00 00 00			0x01 (1) sectors per index block
	7C48: 80 FA 37 7C 33 38 7C 68		volume unique identification number (GUID/Serial Number)
	7C50: 00 00 00 00			checksum (always 0...)

	start:
	7C54: FA				cli
	7C55: 33 C0				xor	ax,ax
	7C57: 8E D0				mov	ss,ax
	7C59: BC 00 7C				mov	sp,0x7C00	set stack at 0x0000:0x7C00
	7C5C: FB				sti
	7C5D: B8 C0 07				mov	ax,0x07C0
	7C60: 8E D8				mov	ds,ax
	7C62: E8 16 00				calln	check_disk	go to 7C7B
	7C65: B8 00 0D				mov	ax,0D00		read sectors at 0x0D00:0000
	7C68: 8E C0				mov	es,ax
	7C6A: 33 DB				xor	bx,bx
	7C6C: C6 05 0E 00 10			mov	[0x7C0E],10	read 10 sectors
	7C71: E8 53 00				call	read		go to 7CC7

	check_disk:
	7C7B: 8A 16 24 00			mov	dl,[0x7C24]	get disk #
	7C7F: B4 08				mov	ah,0x08		check disk info from BIOS
	7C81: CD 13				int	13
	7C83: 73 05				jnc	valid_disk_request	go to 7C8A
	7C85: B9 FF FF				mov	cx,0xFFFF	no valid info, use max.
	7C88: 8A F1				mov	dh,cl
	valid_disk_request:
	7C8A: 66 0F B6 C6			movzxb	eax,dh		compute cylinder x heads x sectors
	7C8E: 40				inc	ax
	7C8F: 66 0F B6 D1			movzxb	edx,cl
	7C93: 80 E2 3F				and	dl,0x3F
	7C96: F7 E2				mulw	dx
	7C98: 86 CD				xchg	cl,ch
	7C9A: C0 ED 06				shrb	ch,6
	7C9D: 41				inc	cx
	7C9E: 66 0F B7 C9			movzxd	ecx,cx
	7CA2: 66 F7 E1				muld	ecx
	7CA5: 66 A3 20 00			mov	[0x7C20],eax	save as the max. to otherwise switch to LBA feature
	7CA9: C3				retn

	The input parameters are saved in the BPB table:
		Offset		Size	Comment
		======		====	=======
		0x7C0E		 2	number of sectors to read (must be 1 or more)
		0x7C10		 4	logical sector to read (LBA # on 32 bits)
		0x7C14		 1	if 0, not checked for LBA availability; if 1 LBA capability available
		0x7C18		 2	number of sectors per track
		0x7C1A		 2	number of heads
		0x7C1C		 4	partition start sector number (partition 1 is at 0x3F usually)
		0x7C20		 4	old read limit (cylinders x heads x sectors)
		0x7C24		 1	drive number (usually 0x80 for hard drives)

	read:
	7CC7: 66 60				pushad
	7CC9: 1E				push	ds
	7CCA: 06				push	es
	next_sector:
	7CCB: 66 A1 10 00			mov	eax,[0x7C10]	sector to read (relative to partition start)
	7CCF: 66 03 06 1C 00			add	eax,[0x7C1C]	add partition start (partition 1 is at 0x003F)
	7CD4: 66 3B 06 20 00			cmp	eax,[0x7C20]	check against upper limit (LBA needed?)
	7CD9: 0F 82 3A 00			jc	old_read	go to 7D17
	7CDD: 1E				push	ds
	7CDE: 66 6A 00				pushd	0
	7CE1: 66 50				push	eax
	7CE3: 06				push	es
	7CE4: 53				push	bx
	7CE5: 66 68 10 00 01 00			push	0x00010010
	7CEB: 80 3E 14 00 00			cmpb	[0x7C14],0x00	disk mode (enhanced LBA or not)
	7CF0: 0F 85 0C 00			jne	valid_lba	go to 7D00
	7CF4: E8 B3 FF				calln	check_lba	go to 7CAA
	7CF7: 80 3E 14 00 00			cmpb	[0x7C14],0x00	
	7CFC: 0F 84 61 00			je	io_error	go to 7D61
	valid_lba:
	7D00: B4 42				mov	ah,42		read floppy/hard disk
	7D02: 8A 16 24 00			mov	dl,[0x7C24]	disk #
	7D06: 16				push	ss
	7D07: 1F				pop	ds
	7D08: 8B F4				mov	si,sp
	7D0A: CD 13				int	13		read 1 sector
	7D0C: 66 58				pop	eax
	7D0E: 5B				pop	bx
	7D0F: 07				pop	es
	7D10: 66 58				pop	eax
	7D12: 66 58				pop	eax
	7D14: 1F				pop	ds
	7D15: EB 2D				jmps	check_error	go to 7D44

	old_read:
	7D17: 66 33 D2				xor	edx,edx
	7D1A: 66 0F B7 0E 18 00			movzxd	ecx,[0x7C18]	sectors per track (word extended)
	7D20: 66 F7 F1				divd	ecx		eax / ecx
	7D23: FE C2				inc	dl
	7D25: 8A CA				mov	cl,dl
	7D27: 66 8B D0				mov	edx,eax
	7D2A: 66 C1 EA 10			shrd	edx,10
	7D2E: F7 36 1A 00			divw	[0x7C1A]	number of heads
	7D32: 86 D6				xchg	dl,dh
	7D34: 8A 16 24 00			mov	dl,[0x7C24]	disk #
	7D38: 8A E8				mov	ch,al
	7D3A: C0 E4 06				shlb	ah,06
	7D3D: 0A CC				or	cl,ah
	7D3F: B8 01 02				mov	ax,0x0201	read 1 sector
	7D42: CD 13				int	13
	check_error:
	7D44: 0F 82 19 00			jc	io_error	go to 7D61
	7D48: 8C C0				mov	ax,es		increment the segment instead of the address
	7D4A: 05 20 00				add	ax,0x0020	this is equivalent to bx += 0x0200
	7D4D: 8E C0				mov	es,ax
	7D4F: 66 FF 06 10 00			incd	[0x7C10]	next sector to read
	7D54: FF 0E 0E 00			decw	[0x7C0E]	number of sectors to read
	7D58: 0F 85 6F FF			jne	next_sector	go to 7CCB
	7D5C: 07				pop	es		done with this read request, return
	7D5D: 1F				pop	ds
	7D5E: 66 61				popad
	7D60: C3				retn

	io_error:
	7D61: A0 F8 01				mov	al,[0x7DF8]	the message to print (can change)
	7D64: E8 09 00				calln	print		go to 7D70
	7D67: A0 FB 01				mov	al,[0x7DFB]	"\r\nPress Ctrl+Alt+Del to restart\r\n\0"
	7D6A: E8 03 00				calln	print		go to 7D70
	7D6D: FB				sti			ensure interrupts are working
	hang:
	7D6E: EB FE				jmps	hang		go to 7D6E
	print:
	7D70: B4 01				mov	ah,0x01
	7D72: 8B F0				mov	si,ax		si equal 0x7D<al>
	next_char:
	7D74: AC				lodsb			load next character
	7D75: 3C 00				cmp	al,0x00
	7D77: 74 09				je	exit_print	go to 7D82
	7D79: B4 0E				mov	ah,0x0E		print character command
	7D7B: BB 07 00				mov	bx,0x0007	screen or page where to write (current)
	7D7E: CD 10				int	10
	7D80: EB F2				jmps	next_char	go to 7D74
	exit_print:
	7D82: C3				retn

Booting RedHat 9

At this time I still have a problem with my Red Hat install. I moved it from one drive partition 2 (/dev/hda2) to another driver partition 6 (/dev/hda6). Usually, all you have to do is add "root=/dev/hda6" in the Grub configuration file and up it comes. In this one, it has a problem reading files on the root partition (I assume it is the root partition...) as follow:

attempt to read beyond end of device
03:06: rw=0, want=1219858868, limit=2002491
kernel panic: No init found. Try passing init= to kernel.

Before that, we can see nash in action loading everything it is supposed to load with success. So it looks like the loader tries to load my init file from /dev/hda1 (which is limited to 2Gb) instead of /dev/hda6 (which is 10 times bigger with 20Gb and much further away from the beginning of the drive: the partition roughly falls between 40Gb and 60Gb.)

If you ask folks, most of the time they will answer you saying something like this: your disk got fried. I can see my disk just fine since I can mount it from my Fedora Core 5 and I don't even need an fsck. It is just plainly fine. The main problem is that /dev/hda2 assumption (I assume...). I regenerated the initrd file in case it was written in there and it didn't seem to make any difference either.

Argh! So I looked at the script mkinitrd under Red Hat 9 and tried to rebuild my initrd.img file. This was easy, actually no errors at all (see Booting RAID 1... on that one I checked with Debian and I got plenty of errors). This was nice, so now I can add & remove files from the RAM disk. I put ls and checked out what was going on. ls of /dev shown that everything was fine. Correct /dev/root, /dev/hda and /dev/hdc are present as expected. It still panics though. Then after the mount of /dev/root I looked in /sysroot (what it is being mounted under) and all my files are right there; right name, etc. The init file is there too and ls in the RAM disk can see it just fine. Then the RAM disk exists and the kernel barks these out of range errors and panics. I'm kind of out of ideas right now (probably because it's really late too 8-). The few references to this error on the net say that happens when you specify the wrong file system in your /etc/fstab. I tried to change from ext3 to ext2 and I do get errors, much different and of course it won't boot since in this case it cannot mount the root drive.

Some progress? Now I tried to do the ls right after the pivot_root and that fails. It looks very much like the pivot_root fails really bad. I'll have to look a little closer, but it could be that I need a few more instructions to make it work properly... more later!

Booting RAID 1

Another booting problem... I have another system which I use to develop under Linux. It had a single hard drive and a CD-ROM. Good enough in most cases, but I really wanted a RAID Level 1. So I decided to use /dev/md[5-10] for my different partitions. It seems that you should be able to boot from any partition, but at this time I get an error:

	md: md driver 0.90.0 MAX_MD_DEVS=256, MD_SB_DISK=27
	Journal Block Device driver loaded
	EXT3-fs:unable to read superblock
	EXT2-fs:unable to read superblock
	cramfs: wrong magic
	pivot_root: no such file or directory
	/sbin/init: 432: cannot open dev/console: no such file
	Kernel panic: Attempted to kill init!

I checked out this one page: Installing Debian with SATA based RAID from Transtronics (after checking many others!) And this one gave me the answer to my problem. What I'm still very much wondering about is: how/when is that initrd.img file regenerated and/or modified?!

I'm wondering because I got the opposite problem: I first have 160Gb x 2 in that system and I switch to a single drive because these 2 hard disks went in another system. Okay! So I had just one drive and I could make it boot (after a few tweaks as described in my pivot_root problem page.) And that changed the initrd.img file with the proper ROOT entry. But when trying to get this new 300Gb x 2 work, it would not! It just was saying that it could not mount the drives (with the errors I just presented; that what that meant and not that the hard disk was bad as some people would most certainly tell you.) Okay... so... here is the result of my research:

  1. Checking out what is inside the initrd.img file
	# cd
	# cp /boot/initrd.img initrd.gz
	# gunzip initrd
	# mount -o ro,loop=/dev/loop1 initrd /mnt/initrd

Notice that since the initrd file is compressed with gzip, you first need to gunzip it (may not be necessary on newer systems!) Also, I very strongly suggest that you make a copy so you have your original in a safe place!

You will of course need to change each parameter to fit your system. loop1 may already be in use, just try loop2, loop3, etc. The filename for you initrd image usually includes the version of the image (i.e. initrd-2.4.20-8.img). The name /mnt/initrd is the name of any folder. Note that I put ro in the options because you cannot really write in the files you see in there... So it's just safer to mount the whole thing read-only.

So? What's that you're looking for in there now?! If you need some special file systems or driver support, you can verify that these are present under /mnt/initrd/lib/modules/<version>/driver/.... Another file of interest is the file named script (/mnt/initrd/script). Do a cat on it, it is only a few lines (3 or 4 or so). The first line defines the ROOT directory. If that is wrong, you're not likely to boot properly. The second line defines all the modules to not load (weird...) The 3rd line is a mdadm command to load the multi-disk support in case you are trying to boot on /dev/md0 or some other md device. If you do not see the proper ROOT and mdadm command, then your RAID will not be loaded properly.

  1. Setting up your new initrd.img file

At least under Debian, you have a directory named /etc/mkinitrd inside which you have a configuration file. It is not extremely important to change it since you can specify most of the options on the command line. Yet, if you are not going to change your system all the time, it's easier to keep the info in one place and thus in that one file. Change the ROOT to /dev/md0 or some other multi-disk. Change the MODULES to dep. Change other variables if it seems that it is required.

  1. Creating a new initrd.img file

There is a command (at least under Debian) to re-generate your initrd file. This is called mkinitrd. Use the -o option to specify the output is an excellent idea to not overwrite another initrd.img file.

	# mkinitrd -o /boot/initrd-md.img

For more info about this command, try man mkinitrd or info mkinitrd. In my current version, I get errors saying that some commands cannot be run because some libraries cannot be loaded (libdl.so, libc.so) for different commands. I suppose they do some chroot in there and these commands just don't run. Somehow this did not prevent the boot from working so I'm fine, but it still feels as if something wasn't 100% complete in there!

WARNING: You need to do a chroot, clean up your /etc/mtab file, mount your /usr if needed (and other partitions, avoid /home for now if possible!) and then run the mkinitrd. This will ensure the proper ROOT and if you are using a RAID it will use the RAID device and thus include the mdadm command as required.

Copying a partition

There are several ways to copy partitions which enable you or not to grow/reduce an existing partition.

Some people will use the cp command. This works under Linux when you use the right flags (i.e. making sure that devices and FIFOs are copied as special files and not fopen()'ed)

Similar to the cp command, you can use the tar command. This one too knows when it changes device and it can recognize special devices.

A little better to my pointer of view: the dump + restore commands. These can be used to duplicate an entire partition file by file. Thus you can (like with cp and tar) copy a partition to another when both have different sizes and different file formats. Use a level of 0, and if you have /tmp on a separate partition, just don't copy it and tell dump + restore to use /tmp to save their temporary files. You will get only one error in this case: a warning that lost+found already exists.

Now, if both your source and destination partitions have exactly the same size and exactly the same file format, you should use dd since it is a lot faster. It will read each source sector and write it on the destination as is. (actually, it can be a lot slower, not faster; but it sure copies your drive as is; i.e. it generates a perfect clone). dd will create a destination which is 100% the same and thus you need the exact same number of sectors (or more wasted sectors...) on the destination.

Note that all of these can be used over your network if you have ssh/sshd working (which is pretty much the default on Linux these days.) So, what you can do with dd is copy an entire partition from one computer to another:

	dd if=/dev/hda1 | ssh copy.com 'dd of=/dev/hde1'

This command copies partition hda1 from the source computer to hde1 on the destination computer (which name is copy.com.) Of course, for large partitions, this will take about forever, but it's fun since it goes from one computer to another. blah blah blah... maybe I'm too much of a geek 8-).

Now there is no limit to the copying of partition, is there? 8-) This means next time you want to build a new computer, you can just copy entire partitions from computer A to computer B (say, your /boot and / partitions, then just change the /etc/hosts and other network identifiers, and voila!)

I'm using this technique right now to copy a Win 2k partition from one to another computer. This way I will be able to run Windows from either of my systems. This is neat... we'll see if it works okay (since the hardware will be different, windows may just choke!) Okay, it choked... It says "unrecognized boot device". Looks like it's trying to read from a SATA using a totally standard IDE driver. Or something like that. I may try to install the darn thing instead... it's going to destroy my stage 1 boot loader, unless I copy that first. Hmmm... More later!