From cb7e6d5e4edae4c35204a278ffc7e0c733b2b313 Mon Sep 17 00:00:00 2001 From: okuji Date: Mon, 12 Jul 1999 00:47:11 +0000 Subject: [PATCH] Bug fixes in the geometry detection. Linux video mode selection support. New Stage 1 stage1_lba is added. Stage 1.5 becomes much smaller. --- BUGS | 3 - ChangeLog | 72 ++++++++ NEWS | 2 + stage1/Makefile.am | 11 +- stage1/Makefile.in | 33 ++-- stage1/stage1.S | 97 +++++------ stage1/stage1_lba.S | 394 ++++++++++++++++++++++++++++++++++++++++++++ stage2/bios.c | 9 +- stage2/boot.c | 38 +++++ stage2/char_io.c | 117 +++++++------ stage2/shared.h | 18 +- 11 files changed, 655 insertions(+), 139 deletions(-) create mode 100644 stage1/stage1_lba.S diff --git a/BUGS b/BUGS index a82d03f92..6fef95ab4 100644 --- a/BUGS +++ b/BUGS @@ -29,7 +29,4 @@ Known problems/bugs: - Hitting Ctrl-Alt-Delete when in GRUB locks some machines. Use Reset for now. (This apparently hasn't been observed for a while) - - Stage 1 cannot read data from any >4128705 block. Ask okuji for more - information. - - Stage 1.5 does not work in some environments. Why? diff --git a/ChangeLog b/ChangeLog index 2dc5fdb14..da4094e81 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,75 @@ +1999-07-12 OKUJI Yoshinori + + * stage2/bios.c (get_diskinfo): In LBA mode, compute + TOTAL_SECTORS from DRP instead of GEOMETRY. + Clear GEOMETRY->FLAGS first. + + * stage2/boot.c (load_image): Fix inverted lines. + +1999-07-12 OKUJI Yoshinori + + Support Linux video mode selection. + + * stage2/shared.h (LINUX_VID_MODE_OFFSET): New macro. + (LINUX_VID_MODE_NORMAL): Likewise. + (LINUX_VID_MODE_EXTENDED): Likewise. + (LINUX_VID_MODE_ASK): Likewise. + [!WITHOUT_LIBC_STUBS] (strlen): Likewise. + (grub_strlen): Declared. + * stage2/boot.c (load_image): Added Linux video mode selection. + * stage2/char_io.c [!STAGE1_5] (grub_strlen): New function. + +1999-07-12 OKUJI Yoshinori + + * stage2/char_io.c (print_error): Print ERRNUM in the format of + %u instead of %d. + (convert_to_ascii) [STAGE1_5]: Eliminate the `x' and `d' + handling code. + (grub_printf): Declare FORMAT as `const char *'. + (grub_printf) [STAGE1_5]: Eliminate the `x' and `d' handling + code. + (get_based_digit): Removed. + (safe_parse_maxint): Remove unnecessary `register' prefixes, + because GCC does better optimization. + Declare DIGIT as `unsigned int' and calculate the value by more + compact instructions. + [!STAGE1_5] (grub_strncat): Declare S2 as `const char *'. + [!STAGE1_5] (grub_strcmp): Declare S1 and S2 as `const char *'. + [!STAGE1_5] (grub_strstr): Likewise. + (grub_memmove): Declare FROM as `const char *'. + The copy code is replaced with inline assembly code stolen from + Linux-2.2.2. + + * stage2/shared.h (grub_printf) : Corrected. + (grub_strncat): Likewise. + (grub_memmove): Likewise. + (grub_strstr): Likewise. + (grub_strcmp): Likewise. + +1999-07-11 OKUJI Yoshinori + + * stage1/stage1.S (sectors): Change the size to long. + (heads): Likewise. + (sector_start): New variable. + (head_start): Likewise. + (cylinder_start): Likewise. + (final_init): Set %si to SECTORS first, and use %si for memory + references. + Zero %eax so that the high 16 bits are always zero. + Set %di to FIRSTLIST - LISTSIZ instead of FIRSTLIST. + (bootloop): Omit the complex CHS recomputation, and always + compute them from LBA address instead. + Call 32bits div instructions instead of 16bits div instructions. + Update the position where to load data from at the end of this + loop, instead of the beginning. + + * stage1/stage1_lba.S: New file. + * stage1/Makefile.am (nodist_pkgdata_DATA): Added stage1_lba. + (LDFLAGS): New variable. + (noinst_PROGRAMS): Added stage1_lba.exec. + (stage1_lba_exec_SOURCES): New variable. + (%: %.exec): New rule. + 1999-06-28 OKUJI Yoshinori * grub/main.c (main): The third argument for strtoul is changed diff --git a/NEWS b/NEWS index ab6d419f9..2b4a40e56 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,8 @@ New: * New command "quit". * The man page for /sbin/grub. * All documents become Texinfo. +* Linux video mode selection is supported. +* The new Stage 1 `stage1_lba' supports LBA addressing mode. New in 0.5.91 - 1999-03-14, Gordon Matzigkeit: * LBA and preliminary AWARD BIOS disk extension support. diff --git a/stage1/Makefile.am b/stage1/Makefile.am index 19c29e1f4..3b7a59a99 100644 --- a/stage1/Makefile.am +++ b/stage1/Makefile.am @@ -1,15 +1,14 @@ pkgdatadir = $(datadir)/$(PACKAGE)/$(host_cpu)-$(host_vendor) -nodist_pkgdata_DATA = stage1 +nodist_pkgdata_DATA = stage1 stage1_lba CLEANFILES = $(pkgdata_DATA) # We can't use builtins or standard includes. AM_CFLAGS = @STAGE1_CFLAGS@ -fno-builtin -nostdinc +LDFLAGS = -nostdlib -Wl,-N -Wl,-Ttext -Wl,7C00 -noinst_PROGRAMS = stage1.exec +noinst_PROGRAMS = stage1.exec stage1_lba.exec stage1_exec_SOURCES = stage1.S +stage1_lba_exec_SOURCES = stage1_lba.S -stage1.exec: stage1.o - $(LD) -N -Ttext 7C00 -o $@ $< - -stage1: stage1.exec +%: %.exec $(OBJCOPY) -O binary $< $@ diff --git a/stage1/Makefile.in b/stage1/Makefile.in index 42d85d93f..090169f58 100644 --- a/stage1/Makefile.in +++ b/stage1/Makefile.in @@ -76,14 +76,16 @@ install_sh = @install_sh@ pkgdatadir = $(datadir)/$(PACKAGE)/$(host_cpu)-$(host_vendor) -nodist_pkgdata_DATA = stage1 +nodist_pkgdata_DATA = stage1 stage1_lba CLEANFILES = $(pkgdata_DATA) # We can't use builtins or standard includes. AM_CFLAGS = @STAGE1_CFLAGS@ -fno-builtin -nostdinc +LDFLAGS = -nostdlib -Wl,-N -Wl,-Ttext -Wl,7C00 -noinst_PROGRAMS = stage1.exec +noinst_PROGRAMS = stage1.exec stage1_lba.exec stage1_exec_SOURCES = stage1.S +stage1_lba_exec_SOURCES = stage1_lba.S subdir = stage1 mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_CLEAN_FILES = @@ -92,18 +94,22 @@ PROGRAMS = $(noinst_PROGRAMS) DEFS = @DEFS@ -I. -I$(srcdir) CPPFLAGS = @CPPFLAGS@ -LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ am_stage1_exec_OBJECTS = stage1.o stage1_exec_OBJECTS = $(am_stage1_exec_OBJECTS) stage1_exec_LDADD = $(LDADD) stage1_exec_DEPENDENCIES = stage1_exec_LDFLAGS = +am_stage1_lba_exec_OBJECTS = stage1_lba.o +stage1_lba_exec_OBJECTS = $(am_stage1_lba_exec_OBJECTS) +stage1_lba_exec_LDADD = $(LDADD) +stage1_lba_exec_DEPENDENCIES = +stage1_lba_exec_LDFLAGS = COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) CFLAGS = @CFLAGS@ CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -DIST_SOURCES = $(stage1_exec_SOURCES) +DIST_SOURCES = $(stage1_exec_SOURCES) $(stage1_lba_exec_SOURCES) DATA = $(nodist_pkgdata_DATA) DIST_COMMON = Makefile.am Makefile.in @@ -112,9 +118,9 @@ DIST_COMMON = Makefile.am Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) GZIP_ENV = --best -DEP_FILES = .deps/stage1.P -SOURCES = $(stage1_exec_SOURCES) -OBJECTS = $(am_stage1_exec_OBJECTS) +DEP_FILES = .deps/stage1.P .deps/stage1_lba.P +SOURCES = $(stage1_exec_SOURCES) $(stage1_lba_exec_SOURCES) +OBJECTS = $(am_stage1_exec_OBJECTS) $(am_stage1_lba_exec_OBJECTS) all: all-redirect .SUFFIXES: @@ -145,6 +151,14 @@ distclean-compile: -rm -f *.tab.c maintainer-clean-compile: + +stage1.exec: $(stage1_exec_OBJECTS) $(stage1_exec_DEPENDENCIES) + @rm -f stage1.exec + $(LINK) $(stage1_exec_LDFLAGS) $(stage1_exec_OBJECTS) $(stage1_exec_LDADD) $(LIBS) + +stage1_lba.exec: $(stage1_lba_exec_OBJECTS) $(stage1_lba_exec_DEPENDENCIES) + @rm -f stage1_lba.exec + $(LINK) $(stage1_lba_exec_LDFLAGS) $(stage1_lba_exec_OBJECTS) $(stage1_lba_exec_LDADD) $(LIBS) .S.o: $(COMPILE) -c $< @@ -305,10 +319,7 @@ installdirs mostlyclean-generic distclean-generic clean-generic \ maintainer-clean-generic clean mostlyclean distclean maintainer-clean -stage1.exec: stage1.o - $(LD) -N -Ttext 7C00 -o $@ $< - -stage1: stage1.exec +%: %.exec $(OBJCOPY) -O binary $< $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/stage1/stage1.S b/stage1/stage1.S index a99b09435..18314711b 100644 --- a/stage1/stage1.S +++ b/stage1/stage1.S @@ -1,6 +1,7 @@ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 1996 Erich Boleyn + * Copyright (C) 1999 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -73,11 +74,17 @@ /* scratch space */ sectors: - .word 0 + .long 0 heads: - .word 0 + .long 0 cylinders: .word 0 +sector_start: + .byte 0 +head_start: + .byte 0 +cylinder_start: + .word 0 /* more space... */ . = _start + BPBEND @@ -130,11 +137,14 @@ after_BPB: jmp hd_probe_error final_init: + /* set %si to sectors */ + movw $ABS(sectors), %si + /* save number of heads */ - xorb %ah, %ah + xorl %eax, %eax movb %dh, %al incw %ax - movw %ax, heads + movl %eax, 4(%si) xorw %dx, %dx movb %cl, %dl @@ -144,64 +154,59 @@ final_init: /* save number of cylinders */ incw %ax - movw %ax, cylinders + movw %ax, 8(%si) xorw %ax, %ax movb %dl, %al shrb $2, %al /* save number of sectors */ - movw $ABS(sectors), %si - movw %ax, (%si) + movl %eax, (%si) /* this sets up for the first run through "bootloop" */ - movw $ABS(firstlist), %di + movw $ABS(firstlist - LISTSIZ), %di /* this is the loop for reading the secondary boot-loader in */ bootloop: - /* update position to load from */ - subw $LISTSIZ, %di - /* check the number of sectors to read */ cmpw $0, 4(%di) /* if zero, go to the start function */ je bootit +setup_sectors: /* load logical sector start (bottom half) */ - movw (%di), %ax + movl (%di), %eax - /* load logical sector start (top half) */ - movw 2(%di), %dx + /* zero %edx */ + xorl %edx, %edx /* divide by number of sectors */ - divw (%si) + divl (%si) /* save sector start */ - movb %dl, (%di) + movb %dl, 10(%si) - xorw %dx, %dx /* zero %dx */ - divw 2(%si) /* divide by number of heads */ + xorl %edx, %edx /* zero %edx */ + divl 4(%si) /* divide by number of heads */ /* save head start */ - movb %dl, 1(%di) + movb %dl, 11(%si) /* save cylinder start */ - movw %ax, 2(%di) + movw %ax, 12(%si) /* do we need too many cylinders? */ - cmpw 4(%si), %ax + cmpw 8(%si), %ax jge geometry_error -setup_sectors: - /* determine the maximum sector length of this read */ movw (%si), %ax /* get number of sectors per track/head */ /* subtract sector start */ - subb (%di), %al + subb 10(%si), %al /* how many do we really want to read? */ cmpw %ax, 4(%di) /* compare against total number of sectors */ @@ -217,27 +222,30 @@ more_sectors: /* subtract from total */ subw %ax, 4(%di) + /* add into logical sector start */ + addl %eax, (%di) + /* * This is the loop for taking care of BIOS geometry translation (ugh!) */ /* get high bits of cylinder */ - movb 3(%di), %dl + movb 13(%si), %dl shlb $6, %dl /* shift left by 6 bits */ - movb (%di), %cl /* get sector */ + movb 10(%si), %cl /* get sector */ incb %cl /* normalize sector (sectors go from 1-N, not 0-(N-1) ) */ orb %dl, %cl /* composite together */ - movb 2(%di), %ch /* sector+hcyl in cl, cylinder in ch */ + movb 12(%si), %ch /* sector+hcyl in cl, cylinder in ch */ /* restore %dx */ popw %dx pushw %dx /* head number */ - movb 1(%di), %dh + movb 11(%si), %dh pushw %ax /* save %ax from destruction! */ @@ -304,34 +312,13 @@ more_sectors: /* check if finished with this dataset */ cmpw $0, 4(%di) - je bootloop + jne setup_sectors - /* find out the next BIOS set to load in */ - movb $0, (%di) /* set the sector start */ - - xorb %ah, %ah /* zero %ah */ - movb 1(%di), %al /* load head number into %al */ - - incw %ax /* increment current head number */ - cmpw 2(%si), %ax /* compare to total number of heads */ - jne update_heads - - movw 2(%di), %ax /* load cylinder number into %ax */ - - incw %ax /* increment current cylinder number */ - cmpw 4(%si), %ax /* compare to total number of cylinders */ - - je geometry_error /* display error and die if greater */ - - movw %ax, 2(%di) /* store new cylinder number */ - - movb $0, %al /* for storing new head number */ - -update_heads: - movb %al, 1(%di) /* store new head number */ - - /* jump to "setup_sectors" to determine length of the new read */ - jmp setup_sectors + /* update position to load from */ + subw $LISTSIZ, %di + + /* jump to bootloop */ + jmp bootloop /* END OF MAIN LOOP */ diff --git a/stage1/stage1_lba.S b/stage1/stage1_lba.S new file mode 100644 index 000000000..f956437eb --- /dev/null +++ b/stage1/stage1_lba.S @@ -0,0 +1,394 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1996 Erich Boleyn + * Copyright (C) 1999 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * defines for the code go here + */ + +#define SIGNATURE 0xaa55 +#define BPBEND 0x3e +#define PARTSTART 0x1be /* starting address of partition table */ +#define PARTEND 0x1fe /* ending addres of the partition table */ +#define MINPARAMSIZ 13 /* size of extra data parameters */ +#define LISTSIZ 8 /* size of sector list */ +#define REALSTACK 0x2000 /* stack for this code and BIOS calls */ +#define BUFFERSEG 0x7000 /* segment address of disk buffer, the + disk buffer MUST be 32K long and cannot + straddle a 64K boundary */ + + /* Absolute addresses + This makes the assembler generate the address without support + from the linker. (ELF can't relocate 16-bit addresses!) */ +#define ABS(x) (x-_start+0x7c00) + + /* Print message string */ +#define MSG(x) movw $x, %si; call message + + .file "stage1_lba.S" + + .text + + /* Tell GAS to generate 16-bit instructions so that this code works + in real mode. */ + .code16 + +.globl _start; _start: + /* + * _start is loaded at 0x7c00 and is jumped to with CS:IP 0:0x7c00 + */ + + /* + * Beginning of the sector is compatible with the FAT/HPFS BIOS + * parameter block. + */ + + jmp after_BPB + nop /* do I care about this ??? */ + + /* + * This space is for the BIOS parameter block!!!! Don't change + * the first jump, nor start the code anywhere but right after + * this area. + */ + + . = _start + 4 + + /* scratch space */ +drive_parameter: +disk_address_packet: + /* Actually, must set these values at the runtime. */ + .byte 0x10 + .byte 0 + .word 0 + .long 0 + .quad 0 + /* more space... */ + + . = _start + BPBEND + + /* + * End of BIOS parameter block. + */ + +after_BPB: + +/* general setup */ + cli /* we're not safe here! */ + + /* set up %ds and %ss as offset from 0 */ + xorw %ax, %ax + movw %ax, %ds + movw %ax, %ss + + /* set up the REAL stack */ + movw $REALSTACK, %sp + + sti /* we're safe again */ + + /* + * Check if we have a forced disk reference here + */ + movb firstlist, %al + cmpb $0xff, %al + je 1f + movb %al, %dl +1: + /* save drive reference first thing! */ + pushw %dx + + /* check if LBA is supported */ + movb $0x41, %ah + movw $0x55aa, %bx + int $0x13 + + jc lba_probe_error + cmpw $0xaa55, %bx + jne lba_probe_error + + /* get the geometry (limited to 2TB!) */ + movb $0x48, %ah + movw $ABS(drive_parameter), %si + movw $0x1a, (%si) + int $0x13 + + jc lba_probe_error + + /* save the total number of sectors */ + movl 0x10(%si), %ecx + + /* this sets up for the first run through "bootloop" */ + movw $ABS(firstlist - LISTSIZ), %di + + /* this is the loop for reading the secondary boot-loader in */ +bootloop: + + /* check the number of sectors to read */ + cmpw $0, 4(%di) + + /* if zero, go to the start function */ + je bootit + +setup_sectors: + /* load logical sector start */ + movl (%di), %ebx + + /* check for the geometry */ + cmpl %ecx, %ebx + jge geometry_error + + /* the maximum is limited to 0x7f because of Phoenix EDD */ + xorl %eax, %eax + movb $0x7f, %al + + /* how many do we really want to read? */ + cmpw %ax, 4(%di) /* compare against total number of sectors */ + + /* which is greater? */ + jg more_sectors + + /* if less than, set to total */ + movw 4(%di), %ax + +more_sectors: + /* subtract from total */ + subw %ax, 4(%di) + + /* add into logical sector start */ + addl %eax, (%di) + + /* set up disk address packet */ + + /* the size and the reserved byte */ + movw $0x0010, (%si) + + /* the number of sectors */ + movw %ax, 2(%si) + + /* the absolute address (low 32 bits) */ + movl %ebx, 8(%si) + + /* the segment of buffer address */ + movw 6(%di), %bx + movw %bx, 6(%si) + + pushw %ax /* save %ax from destruction! */ + + /* zero %eax */ + xorl %eax, %eax + + /* the offset of buffer address */ + movw %ax, 4(%si) + + /* the absolute address (high 32 bits) */ + movl %eax, 12(%si) + + +/* + * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory + * Call with %ah = 0x42 + * %dl = drive number + * %ds:%si = segment:offset of disk address packet + * Return: + * %al = 0x0 on success; err code on failure + */ + + movb $0x42, %ah + int $0x13 + + jc read_error + + /* save source segment */ + movw %es, %bx + + /* load addresses for copy from disk buffer to destination */ + movw 6(%di), %es /* load destination segment */ + + /* restore %ax */ + popw %ax + + /* determine the next possible destination address (presuming + 512 byte sectors!) */ + shlw $5, %ax /* shift %ax five bits to the left */ + addw %ax, 6(%di) /* add the corrected value to the destination + address for next time */ + + /* save addressing regs */ + pushw %ds + pushw %si + pushw %di + pushw %cx + + /* get the copy length */ + shlw $4, %ax + movw %ax, %cx + + xorw %di, %di /* zero offset of destination addresses */ + xorw %si, %si /* zero offset of source addresses */ + movw %bx, %ds /* restore the source segment */ + + cld /* sets the copy direction to forward */ + + /* perform copy */ + rep /* sets a repeat */ + movsb /* this runs the actual copy */ + + /* restore addressing regs */ + popw %cx + popw %di + popw %si + popw %ds + + /* check if finished with this dataset */ + cmpw $0, 4(%di) + jne setup_sectors + + /* update position to load from */ + subw $LISTSIZ, %di + + /* jump to bootloop */ + jmp bootloop + +/* END OF MAIN LOOP */ + +/* + * BIOS Geometry translation error (past the end of the disk geometry!). + */ +geometry_error: + MSG(geometry_error_string) + jmp general_error + +/* + * Disk probe failure. + */ +lba_probe_error: + MSG(lba_probe_error_string) + jmp general_error + +/* + * Read error on the disk. + */ +read_error: + MSG(read_error_string) + +general_error: + MSG(general_error_string) + +/* go here when you need to stop the machine hard after an error condition */ +stop: jmp stop + +geometry_error_string: .string "Geom" +lba_probe_error_string: .string "LBA" +read_error_string: .string "Read" +general_error_string: .string " Error" + +/* + * message: write the string pointed to by %si + * + * WARNING: trashes %si, %ax, and %bx + */ + + /* + * Use BIOS "int 10H Function 0Eh" to write character in teletype mode + * %ah = 0xe %al = character + * %bh = page %bl = foreground color (graphics modes) + */ +1: + movw $0x0001, %bx + movb $0xe, %ah + int $0x10 /* display a byte */ + + incw %si +message: + movb (%si), %al + cmpb $0, %al + jne 1b /* if not end of string, jmp to display */ + ret +lastlist: + +/* + * This area is an empty space between the main body of code below which + * grows up (fixed after compilation, but between releases it may change + * in size easily), and the lists of sectors to read, which grows down + * from a fixed top location. + */ + + /* + * This data area is for keeping general parameters. + */ + . = _start + PARTSTART - MINPARAMSIZ - LISTSIZ + + /* this next data area before the partition area is specifically + sized, you should update "MINPARAMSIZ" to reflect any additions + or deletions to this area */ + + .word 0 + .word 0 + + /* fill the first data listing with the default */ +#ifdef FFS_STAGE1_5 + .long 2 /* this is the sector start parameter, in logical + sectors from the start of the disk, sector 0 */ + .word 14 /* this is the number of sectors to read */ + .word 0x0200 /* this is the segment of the starting address + to load the data into */ +#else + .long 1 /* this is the sector start parameter, in logical + sectors from the start of the disk, sector 0 */ + .word 90 /* this is the number of sectors to read */ + .word 0x0800 /* this is the segment of the starting address + to load the data into */ +#endif +firstlist: /* this label has to be after the list data!!! */ + + .byte 0xff /* the disk to load stage2 from */ + /* 0xff means use the boot drive */ + + /* + * Jump here when all data loading is done. This + * goes to the second stage bootloader. + */ + +bootit: + popw %dx /* this makes sure %dl is our "boot" drive */ + ljmp $0, $0x8000 /* FIXME: make 0x2000 for stage1_5 */ + + /* + * This is the compatibility version number. + * See ../stage2/shared.h for COMPAT_VERSION_* definitions used + * in stage2 and stage1_5 modules. + * + * DO NOT MOVE THIS!!! + */ + .byte 2, 1 + + /* + * This is where an MBR would go if on a hard disk. The code + * here isn't even referenced unless we're on a floppy. Kinda + * sneaky, huh? + */ + + . = _start + PARTSTART + + /* This space cannot be used by any bootloader, uggh... */ + + . = _start + PARTEND + +/* the last 2 bytes in the sector 0 contain the signature */ + .word SIGNATURE diff --git a/stage2/bios.c b/stage2/bios.c index cdcd980f3..e2c89f801 100644 --- a/stage2/bios.c +++ b/stage2/bios.c @@ -118,6 +118,9 @@ int get_diskinfo (int drive, struct geometry *geometry) { int err; + + /* Clear the flags. */ + geometry->flags = 0; if (drive & 0x80) { @@ -159,11 +162,7 @@ get_diskinfo (int drive, struct geometry *geometry) /* Check if CHS information is valid. */ if (drp.flags & 0x02) - { - total_sectors = (geometry->cylinders - * geometry->heads - * geometry->sectors); - } + total_sectors = drp.cylinders * drp.heads * drp.sectors; } } diff --git a/stage2/boot.c b/stage2/boot.c index 6bf6992dc..7d0643424 100644 --- a/stage2/boot.c +++ b/stage2/boot.c @@ -198,6 +198,44 @@ load_image (void) if (mbi.mem_lower >= 608) { + /* Video mode selection support. What a shit! */ + { + char *vga; + + /* Find the substring "vga=". */ + vga = grub_strstr (cur_cmdline, "vga="); + if (vga) + { + char *value = vga + 4; + char *vga_end; + int vid_mode; + + /* Handle special strings. */ + if (substring ("normal", value) < 1) + vid_mode = LINUX_VID_MODE_NORMAL; + else if (substring ("ext", value) < 1) + vid_mode = LINUX_VID_MODE_EXTENDED; + else if (substring ("ask", value) < 1) + vid_mode = LINUX_VID_MODE_ASK; + else if (safe_parse_maxint (&value, &vid_mode)) + ; + else + /* ERRNUM is already set inside the function + safe_parse_maxint. */ + return 0; + + /* Set the vid mode to VID_MODE. Note that this can work + because i386 architecture is little-endian. */ + grub_memmove (buffer + LINUX_VID_MODE_OFFSET, + (char *) &vid_mode, + sizeof (unsigned short)); + + /* Remove the "vga=...". */ + vga_end = skip_to (0, vga); + grub_memmove (vga, vga_end, grub_strlen (vga_end)); + } + } + memmove ((char *) LINUX_SETUP, buffer, data_len + SECTOR_SIZE); /* copy command-line plus memory hack to staging area */ diff --git a/stage2/char_io.c b/stage2/char_io.c index 2a44fd225..7e8b19f9d 100644 --- a/stage2/char_io.c +++ b/stage2/char_io.c @@ -31,7 +31,7 @@ print_error (void) /* printf("\7\n %s\n", err_list[errnum]); */ printf ("\n %s\n", err_list[errnum]); #else /* STAGE1_5 */ - printf ("Error: %d\n", errnum); + printf ("Error: %u\n", errnum); #endif /* STAGE1_5 */ errnum = ERR_NONE; @@ -44,6 +44,7 @@ convert_to_ascii (char *buf, int c,...) unsigned long num = *((&c) + 1), mult = 10; char *ptr = buf; +#ifndef STAGE1_5 if (c == 'x') mult = 16; @@ -53,6 +54,7 @@ convert_to_ascii (char *buf, int c,...) *(ptr++) = '-'; buf++; } +#endif do { @@ -80,7 +82,7 @@ convert_to_ascii (char *buf, int c,...) void -grub_printf (char *format,...) +grub_printf (const char *format,...) { int *dataptr = (int *) &format; char c, *ptr, str[16]; @@ -94,9 +96,11 @@ grub_printf (char *format,...) else switch (c = *(format++)) { +#ifndef STAGE1_5 case 'd': - case 'u': case 'x': +#endif + case 'u': *convert_to_ascii (str, c, *((unsigned long *) dataptr++)) = 0; ptr = str; @@ -422,39 +426,11 @@ get_cmdline (char *prompt, char *commands, char *cmdline, int maxlen, #endif /* STAGE1_5 */ -int -get_based_digit (int c, int base) -{ - int digit = -1; - - /* make sure letter in the the range we can check! */ - c = tolower (c); - - /* - * Is it in the range between zero and nine? - */ - if (base > 0 && c >= '0' && c <= '9' && c < (base + '0')) - { - digit = c - '0'; - } - - /* - * Is it in the range used by a letter? - */ - if (base > 10 && c >= 'a' && c <= 'z' && c < ((base - 10) + 'a')) - { - digit = c - 'a' + 10; - } - - return digit; -} - - int safe_parse_maxint (char **str_ptr, int *myint_ptr) { - register char *ptr = *str_ptr; - register int myint = 0, digit; + char *ptr = *str_ptr; + int myint = 0; int mult = 10, found = 0; /* @@ -466,8 +442,22 @@ safe_parse_maxint (char **str_ptr, int *myint_ptr) mult = 16; } - while ((digit = get_based_digit (*ptr, mult)) != -1) + while (1) { + /* A bit tricky. This below makes use of the equivalence: + (A >= B && A <= C) <=> ((A - B) <= (C - B)) + when C > B and A is unsigned. */ + unsigned int digit; + + digit = tolower (*ptr) - '0'; + if (digit > 9) + { + digit -= 'a' - '0'; + if (mult == 10 || digit > 5) + break; + digit += 10; + } + found = 1; if (myint > ((MAXINT - digit) / mult)) { @@ -513,7 +503,7 @@ grub_isspace (int c) #ifndef STAGE1_5 int -grub_strncat (char *s1, char *s2, int n) +grub_strncat (char *s1, const char *s2, int n) { int i = -1; @@ -533,7 +523,7 @@ grub_strncat (char *s1, char *s2, int n) int -grub_strcmp (char *s1, char *s2) +grub_strcmp (const char *s1, const char *s2) { while (*s1 || *s2) { @@ -571,9 +561,9 @@ substring (char *s1, char *s2) #ifndef STAGE1_5 char * -grub_strstr (char *s1, char *s2) +grub_strstr (const char *s1, const char *s2) { - char *ptr, *tmp; + const char *ptr, *tmp; while (*s1) { @@ -583,11 +573,22 @@ grub_strstr (char *s1, char *s2) while (*s1 && *s1++ == *tmp++); if (tmp > s2 && !*(tmp - 1)) - return ptr; + return (char *) ptr; } return 0; } + +int +grub_strlen (const char *str) +{ + int len = 0; + + while (*str++) + len++; + + return len; +} #endif /* ! STAGE1_5 */ @@ -616,31 +617,39 @@ memcheck (int start, int len) char * -grub_memmove (char *to, char *from, int len) +grub_memmove (char *to, const char *from, int len) { - char *ret = to; if (memcheck ((int) to, len)) { - if ((to >= from + len) || (to <= from)) + /* This assembly code is stolen from + linux-2.2.2/include/asm-i386/string.h. This is not very fast + but compact. */ + int d0, d1, d2; + + if (to < from) { - while (len >= sizeof (unsigned long)) - { - len -= sizeof (unsigned long); - *(((unsigned long *) to)++) = *(((unsigned long *) from)++); - } - while (len-- > 0) - *(to++) = *(from++); + asm volatile ("cld\n\t" + "rep\n\t" + "movsb" + : "=&c" (d0), "=&S" (d1), "=&D" (d2) + : "0" (len),"1" (from),"2" (to) + : "memory"); } else { - /* We have a region that overlaps, but would be overwritten - if we copied it forward. */ - while (len-- > 0) - to[len] = from[len]; + asm volatile ("std\n\t" + "rep\n\t" + "movsb\n\t" + "cld" + : "=&c" (d0), "=&S" (d1), "=&D" (d2) + : "0" (len), + "1" (len - 1 + from), + "2" (len - 1 + to) + : "memory"); } } - return errnum ? NULL : ret; + return errnum ? NULL : to; } diff --git a/stage2/shared.h b/stage2/shared.h index 5ae068947..d7efb12cc 100644 --- a/stage2/shared.h +++ b/stage2/shared.h @@ -89,6 +89,12 @@ extern char *grub_scratch_mem; #define LINUX_SETUP_CODE_START 0x214 #define LINUX_SETUP_INITRD 0x218 +/* Linux's video mode selection support. Actually I hate it! */ +#define LINUX_VID_MODE_OFFSET 0x1FA +#define LINUX_VID_MODE_NORMAL 0xFFFF +#define LINUX_VID_MODE_EXTENDED 0xFFFE +#define LINUX_VID_MODE_ASK 0xFFFD + #define CL_MY_LOCATION RAW_ADDR (0x92000) #define CL_MY_END_ADDR RAW_ADDR (0x920FF) #define CL_MAGIC_ADDR RAW_ADDR (0x90020) @@ -235,6 +241,7 @@ extern char *grub_scratch_mem; #define strstr grub_strstr #define strcmp grub_strcmp #define tolower grub_tolower +#define strlen grub_strlen #endif /* WITHOUT_LIBC_STUBS */ @@ -503,14 +510,15 @@ cmdline_t enter_cmdline (char *script, char *heap); #endif /* C library replacement functions with identical semantics. */ -void grub_printf (char *format,...); +void grub_printf (const char *format,...); int grub_tolower (int c); int grub_isspace (int c); -int grub_strncat (char *s1, char *s2, int n); -char *grub_memmove (char *to, char *from, int len); +int grub_strncat (char *s1, const char *s2, int n); +char *grub_memmove (char *to, const char *from, int len); void *grub_memset (void *start, int c, int len); -char *grub_strstr (char *s1, char *s2); -int grub_strcmp (char *s1, char *s2); +char *grub_strstr (const char *s1, const char *s2); +int grub_strcmp (const char *s1, const char *s2); +int grub_strlen (const char *str); /* misc */ void init_page (void);