Re: [i386 BOOT CODE] kernel bootable again

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Pascal Bellard wrote:
> Hello,
> 
> Please find attached a patch to build i386/x86_64 kernel directly
> bootable. It may be usefull for rescue floppies and installation
> floppies.

Pascal,

In commit f8eeaaf4180334a8e5c3582fe62a5f8176a8c124, build.c has already
changed, and I don't believe it's very compatible with this change.

See
http://www.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=f8eeaaf4180334a8e5c3582fe62a5f8176a8c124

Also, we'll need to see comments from H. Peter Anvin on this patch.
CC'ing him.


> System sector count is updated by tools/bluid.c at offset 495. The
> system could be up to 32 Mb long.
> 
> -- support any drive geometry --
> 
> Disk geometry detection assumes that no error raises on first track.
> First track is read sector by sector and the first error determinates
> the sectors per track count. Next, reads are performed track by track.
> Tested on floppy H2/S18 and harddisk H16/S63. Should work on
> every exotic drive supporting Int13h CHS.
> 
> -- cmdline support --
> 
> If ram_size (offset 504) bit 13 is set, the kernel cmdline
> is load from the sector following the kernel image.
> 
> If Ctrl, Alt, Shift or CapsLock is pressed the kernel cmdline is
> prompted until Enter key. BackSpace is supported.
> 
> Regards,
> 
> -pascal

Frank
- --
Frank Sorenson - KD7TZK
Systems Manager, Computer Science Department
Brigham Young University
[email protected]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org

iD8DBQFDJyKhaI0dwg4A47wRAjMoAJ9laL5kvIzkbJAxigSJahqkdnmx8gCg6dOy
V6ML9fJQjmfjcdxNGnbSuZ0=
=7P9o
-----END PGP SIGNATURE-----
diff -purN linux-2.6.13/arch/i386/boot/bootsect.S linux-2.6.13-bootblock/arch/i386/boot/bootsect.S
--- linux-2.6.13/arch/i386/boot/bootsect.S	2005-09-12 10:37:52.000000000 +0200
+++ linux-2.6.13-bootblock/arch/i386/boot/bootsect.S	2005-09-12 10:40:19.000000000 +0200
@@ -4,20 +4,31 @@
  *	modified by Drew Eckhardt
  *	modified by Bruce Evans (bde)
  *	modified by Chris Noe (May 1999) (as86 -> gas)
- *	gutted by H. Peter Anvin (Jan 2003)
+ *
+ *      rewritten to support up to 32Mb kernel, cmdline
+ *      and any disk geometry by <[email protected]>
  *
  * BIG FAT NOTE: We're in real mode using 64k segments.  Therefore segment
  * addresses must be multiplied by 16 to obtain their respective linear
  * addresses. To avoid confusion, linear addresses are written using leading
  * hex while segment addresses are written as segment:offset.
  *
+ * bde - should not jump blindly, there may be systems with only 512K low
+ * memory.  Use int 0x12 to get the top of memory, etc.
+ *
+ * It then loads 'setup' directly after itself (0x90200), and the system
+ * at 0x10000, using BIOS interrupts. 
+ *
+ * The loader has been made as simple as possible, and continuous
+ * read errors will result in a unbreakable loop. Reboot by hand. It
+ * loads pretty fast by getting whole tracks at a time whenever possible.
  */
 
 #include <asm/boot.h>
 
 SETUPSECTS	= 4			/* default nr of setup-sectors */
 BOOTSEG		= 0x07C0		/* original address of boot-sector */
-INITSEG		= DEF_INITSEG		/* we move boot here - out of the way */
+INITSEG		= DEF_INITSEG		/* we load boot here - out of the way */
 SETUPSEG	= DEF_SETUPSEG		/* setup starts here */
 SYSSEG		= DEF_SYSSEG		/* system loaded at 0x10000 (65536) */
 SYSSIZE		= DEF_SYSSIZE		/* system size: # of 16-byte clicks */
@@ -25,6 +36,12 @@ SYSSIZE		= DEF_SYSSIZE		/* system size: 
 ROOT_DEV	= 0 			/* ROOT_DEV is now written by "build" */
 SWAP_DEV	= 0			/* SWAP_DEV is now written by "build" */
 
+/* some extra features */
+#define DISPLAY_KERNEL_VERSION		as soon as possible
+#define	EDIT_CMDLINE			on hotkey
+#define	LOAD_CMDLINE			according to bit ramsize.13
+#define EXTRA_DEVICES			to fill root_device
+
 #ifndef SVGA_MODE
 #define SVGA_MODE ASK_VGA
 #endif
@@ -42,57 +59,470 @@ SWAP_DEV	= 0			/* SWAP_DEV is now writte
 
 .global _start
 _start:
+	cld				# assume nothing
+	movw	$INITSEG, %ax
+	movw	%ax, %es		# %ax = %es = INITSEG
+#if MOVE_BOOTSECTOR
+# First things first. Move ourself from 0x7C00 -> 0x90000 and jump there.
+	movw	$BOOTSEG, %cx
+	movw	%cx, %ds		# %ds = BOOTSEG
+	movb	$1, %ch			# %cx = 256
+	subw	%si, %si
+	subw	%di, %di
+	rep
+	movsw
+	ljmp	$INITSEG, $go
+go:	
+#else
+	xorw	%cx, %cx		# %cx = 0
+#endif
 
-	# Normalize the start address
-	jmpl	$BOOTSEG, $start2
+# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde).  We
+# wouldn't have to worry about this if we checked the top of memory.  Also
+# my BIOS can be configured to put the wini drive tables in high memory
+# instead of in the vector table.  The old stack might have clobbered the
+# drive table.
+
+	movw	$0x4000-12, %di		# 0x4000 is an arbitrary value >=
+					# length of bootsect + length of
+					# setup + room for stack;
+					# 12 is disk parm size.
+					# gdt will heat 48 more bytes.
+	movw	%ax, %ss		# %ax and %es already contain INITSEG
+	movw	%di, %sp		# put stack at INITSEG:0x4000-12.
+
+# Many BIOS's default disk parameter tables will not recognize
+# multi-sector reads beyond the maximum sector number specified
+# in the default diskette parameter tables - this may mean 7
+# sectors in some cases.
+#
+# Since single sector reads are slow and out of the question,
+# we must take care of this by creating new parameter tables
+# (for the first disk) in RAM.  We can set the maximum sector
+# count to 36 - the most we will encounter on an ED 2.88.  
+#
+# High doesn't hurt.  Low does.  Let's use the max: 63
+#
+# Segments are as follows: %es = %ss = INITSEG,
+# %fs and %gs are unused.
+
+	movw	$0x78, %bx		# %ds:%bx is parameter table address
+	movw	%cx, %ds		# %ds = 0
+	ldsw	(%bx), %si		# %ds:%si is source
+	movb	$6, %cl			# copy 12 bytes
+	rep				# don't worry about cld
+	movsw				# already done above
+	movw	%cx, %ds		# %ds = 0
+	movw	%sp, (%bx)		# %sp = 0x4000-12
+	movw	%es, 2(%bx)
+	pushw	%es
+	popw	%ds			# now %ds = %es = %ss = INITSEG
+	movb	$63, 0x4-12(%di)	# patch sector count, %di = 0x4000
+
+	cli
+	cbw				# %ax = 0 : geometry unknown yet
+	xorb	%dh, %dh		# head 0, current drive
+	incw	%cx			# cylinder 0, sector 1
+	movw	%cx, %di		# read 1 sector
+	call	read_first_sector	# read bootsector again to have
+					# access to data segment
+
+#ifndef	DISPLAY_KERNEL_VERSION
+	movw	$msg1, %si
+	call	puts
+#endif
 
-start2:
-	movw	%cs, %ax
-	movw	%ax, %ds
-	movw	%ax, %es
-	movw	%ax, %ss
-	movw	$0x7c00, %sp
-	sti
-	cld
+# Load the setup-sectors directly after the moved bootblock (at 0x90200).
 
-	movw	$bugger_off_msg, %si
+	movb	setup_sects, %al
+#ifdef CHECK_SETUP_SIZE
+	orb	%al, %al
+	jne	setup_size_ok
+	movb	$SETUPSECTS, %al
+setup_size_ok:	
+#endif
+	cbw
+	xchg	%ax, %di
+	call	read_sectors		# read setup
+
+#ifdef	DISPLAY_KERNEL_VERSION
+#define kernel_version	0xE
+	movw	$0x200,%si
+	addw	kernel_version(%si),%si	# starting protocol 2.00, Kernel 1.3.73
+	call	puts			# show which kernel we are loading
+#endif
 
-msg_loop:
-	lodsb
-	andb	%al, %al
-	jz	die
-	movb	$0xe, %ah
-	movw	$7, %bx
-	int	$0x10
-	jmp	msg_loop
+# This routine loads the system at address SYSSEG, making sure
+# no 64kB boundaries are crossed. We try to load it as fast as
+# possible, loading whole tracks whenever we can.
+
+#ifdef __BIG_KERNEL__
+type_of_loader	=	528
+	movw	$24, %cx		# allocate 48 bytes in stack
+init_gdt:
+	push	$0			#   initialized with 0
+	loop	init_gdt
+	movw	%sp, %si		# for bootsect_gdt
+	movw	$0x9300+(SYSSEG/4096), %ax	# source = SYSSEG
+	cwd
+	movw	%ax, 20(%si)		# bootsect_src_base+2
+	movb	$0x10-1, %al		# destination = 0x100000
+	movw	%ax, 28(%si)		# bootsect_dst_base+2
+	movw	%dx, 16(%si)		# bootsect_src = 64Kb
+	movw	%dx, 24(%si)		# bootsect_dst = 64Kb
+	movb	%dl, type_of_loader	# loader type = 0xFF
+syslp:
+	movw	$SYSSEG, %ax
+	movw	%ax, %es
+	movw	$128,%di		# 64Kb in sectors
+	subw	%di, sys_sects
+	pushf
+	jnc	not_last
+	addw	sys_sects, %di
+not_last:
+	call	read_sectors
+	incw	28(%si)			# bootsect_dst_base+2
+	movw	$0x8000, %cx		# full 64K
+	movb	$0x87, %ah
+	push	%ss
+	pop	%es			# gdt in es:si
+	int	$0x15
+	popf
+	ja	syslp
+#else
+	movw	$SYSSEG, %ax
+	movw	%ax, %es
+syslp:
+	movw	$128*32,%ax		# 64Kb in paragraphs
+	subw	%ax, syssize
+	pushf
+	jnc	not_last
+	addw	syssize, %ax
+not_last:
+	movb	$5, %cl			# paragraphs -> sectors
+	shrw	%cl, %ax
+	xchg	%ax, %di
+	call	read_sectors
+	popf
+	ja	syslp
+#endif
 
-die:
-	# Allow the user to press a key, then reboot
-	xorw	%ax, %ax
-	int	$0x16
-	int	$0x19
+# After that we check which root-device to use. If the device is
+# defined (!= 0), nothing is done and the given device is used.
+# Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8)
+# depending on the number of sectors we pretend to know we have.
+
+
+	movw	root_dev, %ax
+	orw	%ax, %ax
+	jnz	root_defined
+
+	movw	$floppy_table,%si
+scan_floppy_table:
+	lodsw
+	cmpb	limits, %ah
+	je	set_root
+	jno	scan_floppy_table
+set_root:
+	movb	$2, %ah
+	addb	%dl, %al		# add current (floppy) drive
+	movw	%ax, root_dev
+root_defined:
+
+#if defined(LOAD_CMDLINE) || defined(EDIT_CMDLINE)
+#define BUFFER	0x7F00
+	movw	$BUFFER, %si
+	movw	%si, (%si)		# default : no cmdline
+#endif
 
-	# int 0x19 should never return.  In case it does anyway,
-	# invoke the BIOS reset code...
-	ljmp	$0xf000,$0xfff0
+#ifdef	LOAD_CMDLINE
+	testb	$0x20,ram_size+1	# bit 11 to 13 where unused
+					# now bit 13 means: load cmdline
+	jz	nocmd
+	movw	$BUFFER/16+INITSEG, %ax
+	movw	%ax, %es
+	movw	$1, %di			# cmdline = 512 bytes max
+	call	read_sectors
+nocmd:
+#endif
 
+#define RSHFT   0x01
+#define LSHFT   0x02
+#define CTRL    0x04
+#define ALT     0x08
+#define SCRLCK  0x10
+#define NUMLCK  0x20
+#define CAPSLCK 0x40
+#define INSERT  0x80
+
+#ifdef	EDIT_CMDLINE
+# The cmdline can be entered and modifed on hot key.
+# Only characters before the cursor are passed to the kernel.
+	movb	$2, %ah			# get keyboard status
+	int	$0x16
+	testb	$RSHFT|LSHFT|CTRL|ALT|CAPSLCK, %al
+	jz	nocmdline
+	pushw	%si
+	call	puts			# set %ah and %bx
+cmdlp:
+# if 1
+	movb	$32, %al		# clear end of line
+	int	$0x10			#  with Space
+	movb	$8, %al			#   and BackSpace
+	int	$0x10
+# endif
+	decw	%si
+cmdget:
+	cbw				# %ah = 0, get keyboard character
+	int	$0x16
+	cmpb	$8, %al			# BackSpace ?
+	je	cmdbs
+	cbw
+	movw	%ax, (%si)		# store end of string too
+	incw	%si
+	incw	%si
+cmdbs:
+	cmpw	$BUFFER, %si		# lower limit is checked
+	je	cmdget			#   but upper limit not
+	call	putc
+	cmpb	$13, %al		# Enter ?
+	jne	cmdlp
+	popw	%si
+nocmdline:
+#endif
 
-bugger_off_msg:
-	.ascii	"Direct booting from floppy is no longer supported.\r\n"
-	.ascii	"Please use a boot loader program instead.\r\n"
-	.ascii	"\n"
-	.ascii	"Remove disk and press any key to reboot . . .\r\n"
-	.byte	0
+#if defined(LOAD_CMDLINE) || defined(EDIT_CMDLINE)
+#if 1
+cmd_line_magic	=	0x20
+cmd_line_offset	=	0x22
+	movw	$0xA33F, cmd_line_magic
+	movw	%si, cmd_line_offset
+#else
+# protocol >= 2.02, starting Kernel 2.4.0-test3-pre3
+cmd_line_ptr	=	0x228
+	movzx	%si, %eax
+	addl	$INITSEG*16, %eax
+	movl	%eax, cmd_line_ptr
+#endif
+emptycmdline:
+#endif
+	
+# This procedure turns off the floppy drive motor, so
+# that we enter the kernel in a known state, and
+# don't have to worry about it later.
+
+#if 1
+kill_motor:
+	xorw	%ax, %ax		# reset FDC
+	int	$0x13
+#else
+kill_motor:
+	movw	$0x3f2, %dx
+	xorb	%al, %al
+	outb	%al, %dx
+#endif
 
+	call	print_nl
 
-	# Kernel attributes; used by setup
+# After that (everything loaded), we jump to the setup-routine
+# loaded directly after the bootblock:
+# Segments are as follows: %ds = %ss = INITSEG
+
+	ljmp	$SETUPSEG, $0
+
+# read_sectors reads %di sectors into %es:0 buffer.
+# %es:0 is updated to the next memory location.
+# First, sectors are read sector by sector until
+# sector per track count is known. Then they are
+# read track by track.
+# Assume no error on first track.
+
+#define TRACK_BY_TRACK	load as fast as possible !
+#define SHOW_REGS	show int13 status & parameters
+
+check_limits:
+        cmpb    %al, %cl		# max sector known ?
+        ja	next_head		#   no -> store it
+        cmpb    %ah, %dh		# max head known ?
+        ja	next_track		#   no -> store it
+	pusha				# save context
+#ifdef SHOW_REGS
+	pushw	%es			# print %es (named EX)
+	pushw	%dx			# print %dx
+	pushw	%cx			# print %cx
+	xorw	%cx, %cx
+	pushw	%cx			# print %bx
+# ifdef TRACK_BY_TRACK
+	movb	$2, %bh
+# endif
+	pushw	%bx			# print %ax
+	movb	$6,%cl
+	jmp	print_all		# print %bp (status)
+print_loop:
+	movb	$0x6 + 'A' - 1, %al
+	subb	%cl, %al
+	movw	$regs, %si		# caller %si is saved
+	call	putcs			# putc(%al) + puts(%si)
+# it will print out all of the registers.
+	popw	%bp			# load word into %bp
+print_all:
+	pushw	%cx			# save count remaining
+	movb	$4, %cl			# 4 hex digits
+print_digit:
+	rolw	$4, %bp			# rotate to use low 4 bits
+	movb	$0x0f, %al
+	andw	%bp, %ax		# %al = mask for nybble
+	addb	$0x90, %al		# convert %al to ascii hex
+	daa				# in only four instructions!
+	adcb	$0x40, %al
+	daa
+	call	putc			# set %ah and %bx
+	loop	print_digit
+	movb	$0x20, %al		# SPACE
+	int	$0x10
+	popw	%cx
+	loop	print_loop
+	xchg	%ax, %cx		# %ah = 0
+#else
+	cbw				# %ah = 0
+#endif
+        int     $0x13			# reset controler
+	popa				# restore context
+read_sectorslp:
+read_first_sector:
+        pushw   %di			# sector count
+        pushw   %ax			# limits
+        pushw   %dx
+        pushw   %cx
+	xorw	%bx, %bx
+#ifdef TRACK_BY_TRACK
+	subb	%cl, %al		# sectors remaining in track
+	ja	tolastsect
+	movb	$1, %al			# 1 sector mini
+tolastsect:
+	cbw
+	cmpw	%di, %ax
+	jb	more1trk
+	movw	%di, %ax		# sectors to read
+more1trk:
+	push	%ax
+	movb	$2, %ah			# cmd: read chs
+#else
+        movw    $0x201, %ax		# sector by sector
+	push	%ax
+#endif
+        int     $0x13
+	xchg	%ax, %bp		# status
+        popw    %bx			# %ax 
+        popw    %cx
+        popw    %dx
+        popw    %ax			# limits
+        popw    %di			# sector count
+	jc	check_limits
+next_sector:
+#ifdef TRACK_BY_TRACK
+	subw	%bx,%di			# update sector counter
+	addw	%bx,%cx			# next sector
+	shlw	$5,%bx			# sectors -> paragraghs
+	movw	%es, %bp
+	addw	%bx, %bp
+#else
+	decw	%di			# update sector counter
+	incw	%cx			# next sector
+	movw	%es, %bp
+	addw	$32, %bp		# sector size in paragraghs
+#endif
+	movw	%bp, %es		# next location
+        cmpb    %al,%cl			# reach sector limit ?
+        jne     bdendlp
+next_head:
+        movb    %cl,%al
+        incb    %dh			# next head
+        movb    $1,%cl			# first sector
+        cmpb    %ah,%dh			# reach head limit ?
+        jne     bdendlp
+next_track:
+        movb    %dh,%ah
+        movb    $0,%dh			# first head
+# NOTE : support 256 cylinders max
+        incb    %ch			# next cylinder
+bdendlp:
+curdrive =	_start
+curdx	=	_start
+curcx	=	_start+2
+limits	=	_start+4
+	movw	%dx, curdx		# save disk state
+	movw	%cx, curcx
+	movw	%ax, limits
+read_sectors:
+	movw	curdx, %dx		# restore disk state
+	movw	curcx, %cx
+#   al is last sector+1
+#   ah is last head+1
+	movw	limits, %ax
+	orw	%di,%di
+        jne	read_sectorslp
+	movb	$0x2e, %al 		# loading... message 2e = .
+putc:
+	movb	$0xe, %ah
+	movw	$7, %bx			#   one dot each 64k
+ 	int	$0x10
+return:
+	ret
+
+print_nl:
+	
+	movb	$0xd, %al		# CR
+	call	putc
+	movb	$0xa, %al		# LF
+	jmp	putc
+
+puts:
+	call	print_nl		# set %ah and %bx
+putslp:
+	lodsb
+	orb	%al,%al			# end of string is \0
+	jz	return
+putcs:
+	int	$0x10
+	jmp	putslp
 
-	.org 497
+floppy_table:
+#ifdef EXTRA_DEVICES		
+		.byte	16,9			# /dev/fd0u720
+		.byte	12,10			# /dev/fd0u800
+		.byte	80,11			# /dev/fd0u880
+		.byte	125,20			# /dev/fd0u1600
+		.byte	44,21			# /dev/fd0u1680
+		.byte	96,22			# /dev/fd0u1760
+		.byte	116,23			# /dev/fd0u1840
+		.byte	100,24			# /dev/fd0u1920
+		.byte	105,40			# /dev/fd0u3200
+		.byte	109,44			# /dev/fd0u3520
+		.byte	113,48			# /dev/fd0u3840
+#endif		
+		.byte	8,15			# /dev/fd0h1200 - /dev/ps0
+		.byte	28,18			# /dev/fd0u1440 - /dev/PS0
+		.byte	32,36			# /dev/fd0u2880 - /dev/fd0H2880
+		.byte	0,128			# /dev/fd0 - default: autodetect
+
+regs:		.asciz	"X:"
+
+#ifndef	DISPLAY_KERNEL_VERSION
+msg1:		.asciz	"Loading"
+#endif		
+
+tail:
+		.space	495+start-tail		
+
+#.org 495
+sys_sects:	.word SYSSIZE / 32		# size max is now 32Mb
+#.org 497
 setup_sects:	.byte SETUPSECTS
 root_flags:	.word ROOT_RDONLY
-syssize:	.word SYSSIZE
+syssize:	.word SYSSIZE % 0x10000		# size max was 1Mb
 swap_dev:	.word SWAP_DEV
-ram_size:	.word RAMDISK
+ram_size:	.word RAMDISK			# ramdisk size, ramdisk flags and cmdline flag
 vid_mode:	.word SVGA_MODE
 root_dev:	.word ROOT_DEV
 boot_flag:	.word 0xAA55
diff -purN linux-2.6.13/arch/i386/boot/tools/build.c linux-2.6.13-bootblock/arch/i386/boot/tools/build.c
--- linux-2.6.13/arch/i386/boot/tools/build.c	2005-09-12 10:38:52.000000000 +0200
+++ linux-2.6.13-bootblock/arch/i386/boot/tools/build.c	2005-09-12 10:46:25.000000000 +0200
@@ -168,10 +168,13 @@ int main(int argc, char ** argv)
 	}
 	close(fd);
 
-	if (lseek(1, 497, SEEK_SET) != 497)		    /* Write sizes to the bootsector */
+	if (lseek(1, 495, SEEK_SET) != 495)		    /* Write sizes to the bootsector */
 		die("Output: seek failed");
-	buf[0] = setup_sectors;
-	if (write(1, buf, 1) != 1)
+	sz = (sb.st_size + 511) / 512;
+	buf[0] = (sz & 0xff);
+	buf[1] = ((sz >> 8) & 0xff);
+	buf[2] = setup_sectors;
+	if (write(1, buf, 3) != 3)
 		die("Write of setup sector count failed");
 	if (lseek(1, 500, SEEK_SET) != 500)
 		die("Output: seek failed");
diff -purN linux-2.6.13/arch/i386/kernel/setup.c linux-2.6.13-bootblock/arch/i386/kernel/setup.c
--- linux-2.6.13/arch/i386/kernel/setup.c	2005-09-12 10:38:53.000000000 +0200
+++ linux-2.6.13-bootblock/arch/i386/kernel/setup.c	2005-09-12 10:57:01.000000000 +0200
@@ -156,6 +156,7 @@ unsigned long saved_videomode;
 #define RAMDISK_IMAGE_START_MASK  	0x07FF
 #define RAMDISK_PROMPT_FLAG		0x8000
 #define RAMDISK_LOAD_FLAG		0x4000	
+#define CMDLINE_LOAD_FLAG		0x2000	
 
 static char command_line[COMMAND_LINE_SIZE];
 
diff -purN linux-2.6.13/arch/x86_64/boot/bootsect.S linux-2.6.13-bootblock/arch/x86_64/boot/bootsect.S
--- linux-2.6.13/arch/x86_64/boot/bootsect.S	2005-09-12 10:37:58.000000000 +0200
+++ linux-2.6.13-bootblock/arch/x86_64/boot/bootsect.S	2005-09-12 10:58:07.000000000 +0200
@@ -4,20 +4,31 @@
  *	modified by Drew Eckhardt
  *	modified by Bruce Evans (bde)
  *	modified by Chris Noe (May 1999) (as86 -> gas)
- *	gutted by H. Peter Anvin (Jan 2003)
+ *
+ *      rewritten to support up to 32Mb kernel, cmdline
+ *      and any disk geometry by <[email protected]>
  *
  * BIG FAT NOTE: We're in real mode using 64k segments.  Therefore segment
  * addresses must be multiplied by 16 to obtain their respective linear
  * addresses. To avoid confusion, linear addresses are written using leading
  * hex while segment addresses are written as segment:offset.
  *
+ * bde - should not jump blindly, there may be systems with only 512K low
+ * memory.  Use int 0x12 to get the top of memory, etc.
+ *
+ * It then loads 'setup' directly after itself (0x90200), and the system
+ * at 0x10000, using BIOS interrupts. 
+ *
+ * The loader has been made as simple as possible, and continuous
+ * read errors will result in a unbreakable loop. Reboot by hand. It
+ * loads pretty fast by getting whole tracks at a time whenever possible.
  */
 
 #include <asm/boot.h>
 
 SETUPSECTS	= 4			/* default nr of setup-sectors */
 BOOTSEG		= 0x07C0		/* original address of boot-sector */
-INITSEG		= DEF_INITSEG		/* we move boot here - out of the way */
+INITSEG		= DEF_INITSEG		/* we load boot here - out of the way */
 SETUPSEG	= DEF_SETUPSEG		/* setup starts here */
 SYSSEG		= DEF_SYSSEG		/* system loaded at 0x10000 (65536) */
 SYSSIZE		= DEF_SYSSIZE		/* system size: # of 16-byte clicks */
@@ -25,6 +36,12 @@ SYSSIZE		= DEF_SYSSIZE		/* system size: 
 ROOT_DEV	= 0 			/* ROOT_DEV is now written by "build" */
 SWAP_DEV	= 0			/* SWAP_DEV is now written by "build" */
 
+/* some extra features */
+#define DISPLAY_KERNEL_VERSION		as soon as possible
+#define	EDIT_CMDLINE			on hotkey
+#define	LOAD_CMDLINE			according to bit ramsize.13
+#define EXTRA_DEVICES			to fill root_device
+
 #ifndef SVGA_MODE
 #define SVGA_MODE ASK_VGA
 #endif
@@ -42,57 +59,470 @@ SWAP_DEV	= 0			/* SWAP_DEV is now writte
 
 .global _start
 _start:
+	cld				# assume nothing
+	movw	$INITSEG, %ax
+	movw	%ax, %es		# %ax = %es = INITSEG
+#if MOVE_BOOTSECTOR
+# First things first. Move ourself from 0x7C00 -> 0x90000 and jump there.
+	movw	$BOOTSEG, %cx
+	movw	%cx, %ds		# %ds = BOOTSEG
+	movb	$1, %ch			# %cx = 256
+	subw	%si, %si
+	subw	%di, %di
+	rep
+	movsw
+	ljmp	$INITSEG, $go
+go:	
+#else
+	xorw	%cx, %cx		# %cx = 0
+#endif
 
-	# Normalize the start address
-	jmpl	$BOOTSEG, $start2
+# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde).  We
+# wouldn't have to worry about this if we checked the top of memory.  Also
+# my BIOS can be configured to put the wini drive tables in high memory
+# instead of in the vector table.  The old stack might have clobbered the
+# drive table.
+
+	movw	$0x4000-12, %di		# 0x4000 is an arbitrary value >=
+					# length of bootsect + length of
+					# setup + room for stack;
+					# 12 is disk parm size.
+					# gdt will heat 48 more bytes.
+	movw	%ax, %ss		# %ax and %es already contain INITSEG
+	movw	%di, %sp		# put stack at INITSEG:0x4000-12.
+
+# Many BIOS's default disk parameter tables will not recognize
+# multi-sector reads beyond the maximum sector number specified
+# in the default diskette parameter tables - this may mean 7
+# sectors in some cases.
+#
+# Since single sector reads are slow and out of the question,
+# we must take care of this by creating new parameter tables
+# (for the first disk) in RAM.  We can set the maximum sector
+# count to 36 - the most we will encounter on an ED 2.88.  
+#
+# High doesn't hurt.  Low does.  Let's use the max: 63
+#
+# Segments are as follows: %es = %ss = INITSEG,
+# %fs and %gs are unused.
+
+	movw	$0x78, %bx		# %ds:%bx is parameter table address
+	movw	%cx, %ds		# %ds = 0
+	ldsw	(%bx), %si		# %ds:%si is source
+	movb	$6, %cl			# copy 12 bytes
+	rep				# don't worry about cld
+	movsw				# already done above
+	movw	%cx, %ds		# %ds = 0
+	movw	%sp, (%bx)		# %sp = 0x4000-12
+	movw	%es, 2(%bx)
+	pushw	%es
+	popw	%ds			# now %ds = %es = %ss = INITSEG
+	movb	$63, 0x4-12(%di)	# patch sector count, %di = 0x4000
+
+	cli
+	cbw				# %ax = 0 : geometry unknown yet
+	xorb	%dh, %dh		# head 0, current drive
+	incw	%cx			# cylinder 0, sector 1
+	movw	%cx, %di		# read 1 sector
+	call	read_first_sector	# read bootsector again to have
+					# access to data segment
+
+#ifndef	DISPLAY_KERNEL_VERSION
+	movw	$msg1, %si
+	call	puts
+#endif
 
-start2:
-	movw	%cs, %ax
-	movw	%ax, %ds
-	movw	%ax, %es
-	movw	%ax, %ss
-	movw	$0x7c00, %sp
-	sti
-	cld
+# Load the setup-sectors directly after the moved bootblock (at 0x90200).
 
-	movw	$bugger_off_msg, %si
+	movb	setup_sects, %al
+#ifdef CHECK_SETUP_SIZE
+	orb	%al, %al
+	jne	setup_size_ok
+	movb	$SETUPSECTS, %al
+setup_size_ok:	
+#endif
+	cbw
+	xchg	%ax, %di
+	call	read_sectors		# read setup
+
+#ifdef	DISPLAY_KERNEL_VERSION
+#define kernel_version	0xE
+	movw	$0x200,%si
+	addw	kernel_version(%si),%si	# starting protocol 2.00, Kernel 1.3.73
+	call	puts			# show which kernel we are loading
+#endif
 
-msg_loop:
-	lodsb
-	andb	%al, %al
-	jz	die
-	movb	$0xe, %ah
-	movw	$7, %bx
-	int	$0x10
-	jmp	msg_loop
+# This routine loads the system at address SYSSEG, making sure
+# no 64kB boundaries are crossed. We try to load it as fast as
+# possible, loading whole tracks whenever we can.
+
+#ifdef __BIG_KERNEL__
+type_of_loader	=	528
+	movw	$24, %cx		# allocate 48 bytes in stack
+init_gdt:
+	push	$0			#   initialized with 0
+	loop	init_gdt
+	movw	%sp, %si		# for bootsect_gdt
+	movw	$0x9300+(SYSSEG/4096), %ax	# source = SYSSEG
+	cwd
+	movw	%ax, 20(%si)		# bootsect_src_base+2
+	movb	$0x10-1, %al		# destination = 0x100000
+	movw	%ax, 28(%si)		# bootsect_dst_base+2
+	movw	%dx, 16(%si)		# bootsect_src = 64Kb
+	movw	%dx, 24(%si)		# bootsect_dst = 64Kb
+	movb	%dl, type_of_loader	# loader type = 0xFF
+syslp:
+	movw	$SYSSEG, %ax
+	movw	%ax, %es
+	movw	$128,%di		# 64Kb in sectors
+	subw	%di, sys_sects
+	pushf
+	jnc	not_last
+	addw	sys_sects, %di
+not_last:
+	call	read_sectors
+	incw	28(%si)			# bootsect_dst_base+2
+	movw	$0x8000, %cx		# full 64K
+	movb	$0x87, %ah
+	push	%ss
+	pop	%es			# gdt in es:si
+	int	$0x15
+	popf
+	ja	syslp
+#else
+	movw	$SYSSEG, %ax
+	movw	%ax, %es
+syslp:
+	movw	$128*32,%ax		# 64Kb in paragraphs
+	subw	%ax, syssize
+	pushf
+	jnc	not_last
+	addw	syssize, %ax
+not_last:
+	movb	$5, %cl			# paragraphs -> sectors
+	shrw	%cl, %ax
+	xchg	%ax, %di
+	call	read_sectors
+	popf
+	ja	syslp
+#endif
 
-die:
-	# Allow the user to press a key, then reboot
-	xorw	%ax, %ax
-	int	$0x16
-	int	$0x19
+# After that we check which root-device to use. If the device is
+# defined (!= 0), nothing is done and the given device is used.
+# Otherwise, one of /dev/fd0H2880 (2,32) or /dev/PS0 (2,28) or /dev/at0 (2,8)
+# depending on the number of sectors we pretend to know we have.
+
+
+	movw	root_dev, %ax
+	orw	%ax, %ax
+	jnz	root_defined
+
+	movw	$floppy_table,%si
+scan_floppy_table:
+	lodsw
+	cmpb	limits, %ah
+	je	set_root
+	jno	scan_floppy_table
+set_root:
+	movb	$2, %ah
+	addb	%dl, %al		# add current (floppy) drive
+	movw	%ax, root_dev
+root_defined:
+
+#if defined(LOAD_CMDLINE) || defined(EDIT_CMDLINE)
+#define BUFFER	0x7F00
+	movw	$BUFFER, %si
+	movw	%si, (%si)		# default : no cmdline
+#endif
 
-	# int 0x19 should never return.  In case it does anyway,
-	# invoke the BIOS reset code...
-	ljmp	$0xf000,$0xfff0
+#ifdef	LOAD_CMDLINE
+	testb	$0x20,ram_size+1	# bit 11 to 13 where unused
+					# now bit 13 means: load cmdline
+	jz	nocmd
+	movw	$BUFFER/16+INITSEG, %ax
+	movw	%ax, %es
+	movw	$1, %di			# cmdline = 512 bytes max
+	call	read_sectors
+nocmd:
+#endif
 
+#define RSHFT   0x01
+#define LSHFT   0x02
+#define CTRL    0x04
+#define ALT     0x08
+#define SCRLCK  0x10
+#define NUMLCK  0x20
+#define CAPSLCK 0x40
+#define INSERT  0x80
+
+#ifdef	EDIT_CMDLINE
+# The cmdline can be entered and modifed on hot key.
+# Only characters before the cursor are passed to the kernel.
+	movb	$2, %ah			# get keyboard status
+	int	$0x16
+	testb	$RSHFT|LSHFT|CTRL|ALT|CAPSLCK, %al
+	jz	nocmdline
+	pushw	%si
+	call	puts			# set %ah and %bx
+cmdlp:
+# if 1
+	movb	$32, %al		# clear end of line
+	int	$0x10			#  with Space
+	movb	$8, %al			#   and BackSpace
+	int	$0x10
+# endif
+	decw	%si
+cmdget:
+	cbw				# %ah = 0, get keyboard character
+	int	$0x16
+	cmpb	$8, %al			# BackSpace ?
+	je	cmdbs
+	cbw
+	movw	%ax, (%si)		# store end of string too
+	incw	%si
+	incw	%si
+cmdbs:
+	cmpw	$BUFFER, %si		# lower limit is checked
+	je	cmdget			#   but upper limit not
+	call	putc
+	cmpb	$13, %al		# Enter ?
+	jne	cmdlp
+	popw	%si
+nocmdline:
+#endif
 
-bugger_off_msg:
-	.ascii	"Direct booting from floppy is no longer supported.\r\n"
-	.ascii	"Please use a boot loader program instead.\r\n"
-	.ascii	"\n"
-	.ascii	"Remove disk and press any key to reboot . . .\r\n"
-	.byte	0
+#if defined(LOAD_CMDLINE) || defined(EDIT_CMDLINE)
+#if 1
+cmd_line_magic	=	0x20
+cmd_line_offset	=	0x22
+	movw	$0xA33F, cmd_line_magic
+	movw	%si, cmd_line_offset
+#else
+# protocol >= 2.02, starting Kernel 2.4.0-test3-pre3
+cmd_line_ptr	=	0x228
+	movzx	%si, %eax
+	addl	$INITSEG*16, %eax
+	movl	%eax, cmd_line_ptr
+#endif
+emptycmdline:
+#endif
+	
+# This procedure turns off the floppy drive motor, so
+# that we enter the kernel in a known state, and
+# don't have to worry about it later.
+
+#if 1
+kill_motor:
+	xorw	%ax, %ax		# reset FDC
+	int	$0x13
+#else
+kill_motor:
+	movw	$0x3f2, %dx
+	xorb	%al, %al
+	outb	%al, %dx
+#endif
 
+	call	print_nl
 
-	# Kernel attributes; used by setup
+# After that (everything loaded), we jump to the setup-routine
+# loaded directly after the bootblock:
+# Segments are as follows: %ds = %ss = INITSEG
+
+	ljmp	$SETUPSEG, $0
+
+# read_sectors reads %di sectors into %es:0 buffer.
+# %es:0 is updated to the next memory location.
+# First, sectors are read sector by sector until
+# sector per track count is known. Then they are
+# read track by track.
+# Assume no error on first track.
+
+#define TRACK_BY_TRACK	load as fast as possible !
+#define SHOW_REGS	show int13 status & parameters
+
+check_limits:
+        cmpb    %al, %cl		# max sector known ?
+        ja	next_head		#   no -> store it
+        cmpb    %ah, %dh		# max head known ?
+        ja	next_track		#   no -> store it
+	pusha				# save context
+#ifdef SHOW_REGS
+	pushw	%es			# print %es (named EX)
+	pushw	%dx			# print %dx
+	pushw	%cx			# print %cx
+	xorw	%cx, %cx
+	pushw	%cx			# print %bx
+# ifdef TRACK_BY_TRACK
+	movb	$2, %bh
+# endif
+	pushw	%bx			# print %ax
+	movb	$6,%cl
+	jmp	print_all		# print %bp (status)
+print_loop:
+	movb	$0x6 + 'A' - 1, %al
+	subb	%cl, %al
+	movw	$regs, %si		# caller %si is saved
+	call	putcs			# putc(%al) + puts(%si)
+# it will print out all of the registers.
+	popw	%bp			# load word into %bp
+print_all:
+	pushw	%cx			# save count remaining
+	movb	$4, %cl			# 4 hex digits
+print_digit:
+	rolw	$4, %bp			# rotate to use low 4 bits
+	movb	$0x0f, %al
+	andw	%bp, %ax		# %al = mask for nybble
+	addb	$0x90, %al		# convert %al to ascii hex
+	daa				# in only four instructions!
+	adcb	$0x40, %al
+	daa
+	call	putc			# set %ah and %bx
+	loop	print_digit
+	movb	$0x20, %al		# SPACE
+	int	$0x10
+	popw	%cx
+	loop	print_loop
+	xchg	%ax, %cx		# %ah = 0
+#else
+	cbw				# %ah = 0
+#endif
+        int     $0x13			# reset controler
+	popa				# restore context
+read_sectorslp:
+read_first_sector:
+        pushw   %di			# sector count
+        pushw   %ax			# limits
+        pushw   %dx
+        pushw   %cx
+	xorw	%bx, %bx
+#ifdef TRACK_BY_TRACK
+	subb	%cl, %al		# sectors remaining in track
+	ja	tolastsect
+	movb	$1, %al			# 1 sector mini
+tolastsect:
+	cbw
+	cmpw	%di, %ax
+	jb	more1trk
+	movw	%di, %ax		# sectors to read
+more1trk:
+	push	%ax
+	movb	$2, %ah			# cmd: read chs
+#else
+        movw    $0x201, %ax		# sector by sector
+	push	%ax
+#endif
+        int     $0x13
+	xchg	%ax, %bp		# status
+        popw    %bx			# %ax 
+        popw    %cx
+        popw    %dx
+        popw    %ax			# limits
+        popw    %di			# sector count
+	jc	check_limits
+next_sector:
+#ifdef TRACK_BY_TRACK
+	subw	%bx,%di			# update sector counter
+	addw	%bx,%cx			# next sector
+	shlw	$5,%bx			# sectors -> paragraghs
+	movw	%es, %bp
+	addw	%bx, %bp
+#else
+	decw	%di			# update sector counter
+	incw	%cx			# next sector
+	movw	%es, %bp
+	addw	$32, %bp		# sector size in paragraghs
+#endif
+	movw	%bp, %es		# next location
+        cmpb    %al,%cl			# reach sector limit ?
+        jne     bdendlp
+next_head:
+        movb    %cl,%al
+        incb    %dh			# next head
+        movb    $1,%cl			# first sector
+        cmpb    %ah,%dh			# reach head limit ?
+        jne     bdendlp
+next_track:
+        movb    %dh,%ah
+        movb    $0,%dh			# first head
+# NOTE : support 256 cylinders max
+        incb    %ch			# next cylinder
+bdendlp:
+curdrive =	_start
+curdx	=	_start
+curcx	=	_start+2
+limits	=	_start+4
+	movw	%dx, curdx		# save disk state
+	movw	%cx, curcx
+	movw	%ax, limits
+read_sectors:
+	movw	curdx, %dx		# restore disk state
+	movw	curcx, %cx
+#   al is last sector+1
+#   ah is last head+1
+	movw	limits, %ax
+	orw	%di,%di
+        jne	read_sectorslp
+	movb	$0x2e, %al 		# loading... message 2e = .
+putc:
+	movb	$0xe, %ah
+	movw	$7, %bx			#   one dot each 64k
+ 	int	$0x10
+return:
+	ret
+
+print_nl:
+	
+	movb	$0xd, %al		# CR
+	call	putc
+	movb	$0xa, %al		# LF
+	jmp	putc
+
+puts:
+	call	print_nl		# set %ah and %bx
+putslp:
+	lodsb
+	orb	%al,%al			# end of string is \0
+	jz	return
+putcs:
+	int	$0x10
+	jmp	putslp
 
-	.org 497
+floppy_table:
+#ifdef EXTRA_DEVICES		
+		.byte	16,9			# /dev/fd0u720
+		.byte	12,10			# /dev/fd0u800
+		.byte	80,11			# /dev/fd0u880
+		.byte	125,20			# /dev/fd0u1600
+		.byte	44,21			# /dev/fd0u1680
+		.byte	96,22			# /dev/fd0u1760
+		.byte	116,23			# /dev/fd0u1840
+		.byte	100,24			# /dev/fd0u1920
+		.byte	105,40			# /dev/fd0u3200
+		.byte	109,44			# /dev/fd0u3520
+		.byte	113,48			# /dev/fd0u3840
+#endif		
+		.byte	8,15			# /dev/fd0h1200 - /dev/ps0
+		.byte	28,18			# /dev/fd0u1440 - /dev/PS0
+		.byte	32,36			# /dev/fd0u2880 - /dev/fd0H2880
+		.byte	0,128			# /dev/fd0 - default: autodetect
+
+regs:		.asciz	"X:"
+
+#ifndef	DISPLAY_KERNEL_VERSION
+msg1:		.asciz	"Loading"
+#endif		
+
+tail:
+		.space	495+start-tail		
+
+#.org 495
+sys_sects:	.word SYSSIZE / 32		# size max is now 32Mb
+#.org 497
 setup_sects:	.byte SETUPSECTS
 root_flags:	.word ROOT_RDONLY
-syssize:	.word SYSSIZE
+syssize:	.word SYSSIZE % 0x10000		# size max was 1Mb
 swap_dev:	.word SWAP_DEV
-ram_size:	.word RAMDISK
+ram_size:	.word RAMDISK			# ramdisk size, ramdisk flags and cmdline flag
 vid_mode:	.word SVGA_MODE
 root_dev:	.word ROOT_DEV
 boot_flag:	.word 0xAA55
diff -purN linux-2.6.13/arch/x86_64/boot/tools/build.c linux-2.6.13-bootblock/arch/x86_64/boot/tools/build.c
--- linux-2.6.13/arch/x86_64/boot/tools/build.c	2005-09-12 10:38:57.000000000 +0200
+++ linux-2.6.13-bootblock/arch/x86_64/boot/tools/build.c	2005-09-12 11:00:44.000000000 +0200
@@ -169,10 +169,13 @@ int main(int argc, char ** argv)
 	}
 	close(fd);
 
-	if (lseek(1, 497, SEEK_SET) != 497)		    /* Write sizes to the bootsector */
+	if (lseek(1, 495, SEEK_SET) != 495)		    /* Write sizes to the bootsector */
 		die("Output: seek failed");
-	buf[0] = setup_sectors;
-	if (write(1, buf, 1) != 1)
+	sz = (sb.st_size + 511) / 512;
+	buf[0] = (sz & 0xff);
+	buf[1] = ((sz >> 8) & 0xff);
+	buf[2] = setup_sectors;
+	if (write(1, buf, 3) != 3)
 		die("Write of setup sector count failed");
 	if (lseek(1, 500, SEEK_SET) != 500)
 		die("Output: seek failed");
diff -purN linux-2.6.13/Documentation/i386/boot.txt linux-2.6.13-bootblock/Documentation/i386/boot.txt
--- linux-2.6.13/Documentation/i386/boot.txt	2005-09-12 10:36:50.000000000 +0200
+++ linux-2.6.13-bootblock/Documentation/i386/boot.txt	2005-09-12 12:22:43.000000000 +0200
@@ -103,6 +103,7 @@ The header looks like:
 Offset	Proto	Name		Meaning
 /Size
 
+01EF/2	N/A	sys_sects	DO NOT USE - for bootsect.S use only
 01F1/1	ALL	setup_sects	The size of the setup in sectors
 01F2/2	ALL	root_flags	If set, the root is mounted readonly
 01F4/2	ALL	syssize		DO NOT USE - for bootsect.S use only
diff -purN linux-2.6.13/Documentation/ramdisk.txt linux-2.6.13-bootblock/Documentation/ramdisk.txt
--- linux-2.6.13/Documentation/ramdisk.txt	2005-09-12 10:35:39.000000000 +0200
+++ linux-2.6.13-bootblock/Documentation/ramdisk.txt	2005-09-12 10:55:35.000000000 +0200
@@ -63,12 +63,14 @@ to 2 MB (2^11) of where to find the RAM 
 14 indicates that a RAM disk is to be loaded, and bit 15 indicates whether a
 prompt/wait sequence is to be given before trying to read the RAM disk. Since
 the RAM disk dynamically grows as data is being written into it, a size field
-is not required. Bits 11 to 13 are not currently used and may as well be zero.
+is not required. Bit 13 indicates that a cmdline is to be loaded by the kernel
+bootblock, Bits 11 to 12 are not currently used and may as well be zero.
 These numbers are no magical secrets, as seen below:
 
 ./arch/i386/kernel/setup.c:#define RAMDISK_IMAGE_START_MASK     0x07FF
 ./arch/i386/kernel/setup.c:#define RAMDISK_PROMPT_FLAG          0x8000
 ./arch/i386/kernel/setup.c:#define RAMDISK_LOAD_FLAG            0x4000
+./arch/i386/kernel/setup.c:#define CMDLINE_LOAD_FLAG            0x2000
 
 Consider a typical two floppy disk setup, where you will have the 
 kernel on disk one, and have already put a RAM disk image onto disk #2.
@@ -159,6 +161,8 @@ users may wish to combine steps (d) and 
 Changelog:
 ----------
 
+09-05 :		Reserve CMDLINE_LOAD bit.
+
 10-22-04 :	Updated to reflect changes in command line options, remove
 		obsolete references, general cleanup.
 		James Nelson ([email protected])

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]
  Powered by Linux