Hi, This patch enable grub2 to read files from cdrom using int13 service.
To create bootable cdrom: mkdir cdroot cat cdboot.img core.img > cdroot/grub2cd.bin mkisofs -R -no-emul-boot -boot-info-table --boot-load-size 4 -b grub2cd.bin -o aa.iso cdroot diff --git a/boot/i386/pc/cdboot.S b/boot/i386/pc/cdboot.S new file mode 100755 index 0000000..d020651 --- /dev/null +++ b/boot/i386/pc/cdboot.S @@ -0,0 +1,292 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1994-2002 H. Peter Anvin + * Copyright (C) 1999,2000,2001,2004 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. + * + */ + +/* + Most of this file was originally "isolinux.asm" from SYSLINUX package. + It has been very heavily modified. +*/ + + /* 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) + +#define SECTOR_SIZE 0x200 + +#define ISO_SECTOR_SIZE 0x800 +#define ISO_SECTOR_BITS 11 + +#define STAGE_ADDR (0x8000 - SECTOR_SIZE) + +#define STAGE1_STACKSEG 0x2000 + + /* Print message string */ +#define MSG(x) mov $ABS(x), %si; call message; + + .file "cdboot.S" + + .text + + /* Tell GAS to generate 16-bit instructions so that this code works + in real mode. */ + .code16 + + .globl start, _start + +/* + * Primary entry point. Because BIOSes are buggy, we only load the first + * CD-ROM sector (2K) of the file, so the number one priority is actually + * loading the rest. + */ +start: +_start: + cli + ljmp $0, $ABS(real_start) + + . = _start + 8 /* Pad to file offset 8 */ + + /* This table gets filled in by mkisofs using the + -boot-info-table option */ +bi_pvd: .long 0xDEADBEEF /* LBA of primary volume descript */ +bi_file: .long 0xDEADBEEF /* LBA of boot file */ +bi_length: .long 0xDEADBEEF /* Length of boot file */ +bi_csum: .long 0xDEADBEEF /* Checksum of boot file */ +bi_reserved: .space (10*4) /* Reserved */ + +real_start: + xor %ax, %ax + mov %ax, %ss + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov $STAGE1_STACKSEG, %sp /* set up the REAL stack */ + sti + cld + + /* save drive reference first thing! */ + mov %dl, ABS(BootDrive) + + /* print a notification message on the screen */ + MSG(notification_string) + +load_image: + /* Set up boot file sector, size, load address */ + mov ABS(bi_length), %eax + add $(ISO_SECTOR_SIZE-1), %eax + shr $ISO_SECTOR_BITS, %eax /* dwords->sectors */ + mov %ax, %bp /* boot file sectors */ + mov $(STAGE_ADDR >> 4), %bx + mov %bx, %es + xor %bx, %bx + mov ABS(bi_file), %eax + call getlinsec + mov %ds, %ax + mov %ax, %es + + MSG(notification_done) +bootit: + mov ABS(BootDrive), %dl /* this makes sure %dl is our "boot" drive */ + ljmp $0, $(STAGE_ADDR + SECTOR_SIZE * 2) /* jump to main() in asm.S */ + +/* go here when you need to stop the machine hard after an error condition */ +stop: jmp stop + + +/* + * Get linear sectors - EBIOS LBA addressing, 2048-byte sectors. + * + * Note that we can't always do this as a single request, because at least + * Phoenix BIOSes has a 127-sector limit. To be on the safe side, stick + * to 16 sectors (32K) per request. + * + * Input: + * EAX - Linear sector number + * ES:BX - Target buffer + * BP - Sector count + */ +getlinsec: + mov $ABS(dapa), %si /* Load up the DAPA */ + mov %bx, 4(%si) + mov %es, %bx + mov %bx, 6(%si) + mov %eax, 8(%si) +1: + push %bp + push %si + cmp ABS(MaxTransfer), %bp + jbe 2f + mov ABS(MaxTransfer), %bp +2: + mov %bp, 2(%si) + mov ABS(BootDrive), %dl + mov $0x42, %ah /* Extended Read */ + call xint13 + pop %si + pop %bp + movzwl 2(%si), %eax /* Sectors we read */ + add %eax, 8(%si) /* Advance sector pointer */ + sub %ax, %bp /* Sectors left */ + shl $(ISO_SECTOR_BITS-4), %ax /* 2048-byte sectors -> segment */ + add %ax, 6(%si) /* Advance buffer pointer */ + + pushal + MSG(notification_step) + popal + cmp $0, %bp + ja 1b + mov 8(%si), %eax /* Return next sector */ + ret + +/* + * INT 13h with retry + */ +xint13: + movb $6, ABS(RetryCount) + pushal +.try: + int $0x13 + jc 1f + add $(8*4), %sp /* Clean up stack */ + ret +1: + mov %ah, %dl /* Save error code */ + decb ABS(RetryCount) + jz .real_error + mov ABS(RetryCount), %al + mov ABS(dapa+2), %ah /* Sector transfer count */ + cmp $2, %al /* Only 2 attempts left */ + ja 2f + mov $1, %ah /* Drop transfer size to 1 */ + jmp .setmaxtr +2: + cmp $3, %al + ja 3f /* First time, just try again */ + shr $1, %ah /* Otherwise, try to reduce */ + adc $0, %ah /* the max transfer size, but not */ +.setmaxtr: + mov %ah, ABS(MaxTransfer) + mov %ah, ABS(dapa+2) +3: + popal + jmp .try + +.real_error: + MSG(read_error_string) + mov %dl, %al + call printhex2 + popal + jmp stop + + + +/* + * 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: + mov $0x0001, %bx + mov $0x0E, %ah + int $0x10 /* display a byte */ + +message: + lodsb + or %al, %al + jne 1b /* if not end of string, jmp to display */ + ret + +/* + * printhex[248]: Write a hex number in (AL, AX, EAX) to the console + */ +printhex2: + pushal + rol $24, %eax + mov $2, %cx + jmp 1f +printhex4: + pushal + rol $16, %eax + mov $4, %cx + jmp 1f +printhex8: + pushal + mov $8, %cx +1: + rol $4, %eax + push %eax + and $0x0F, %al + cmp $10, %al + jae .high +.low: add $('0'), %al + jmp 2f +.high: add $('A'-10), %al +2: + mov $0x0001, %bx + mov $0x0E, %ah + int $0x10 /* display a char */ + pop %eax + loop 1b + popal + ret + +/**************************************************************************/ +#ifdef STAGE1_5 +notification_string: .string "Loading stage1.5 " +#else +notification_string: .string "Loading stage2 " +#endif + +notification_step: .string "." +notification_done: .string "\r\n" + +read_error_string: .string "Read error 0x" + +/* + * EBIOS disk address packet + */ + .align 8 +dapa: .byte 16 /* Packet size */ + .byte 0 /* reserved */ + .word 0 /* +2 Block count */ + .word 0 /* +4 Offset of buffer */ + .word 0 /* +6 Segment of buffer */ + .long 0 /* +8 LBA (LSW) */ + .long 0 /* +C LBA (MSW) */ + +BootDrive: + .byte 0xFF +MaxTransfer: + .word 16 /* Max sectors per transfer (32Kb) */ +RetryCount: + .byte 0 + + + . = _start + SECTOR_SIZE - 2 + + .word 0 diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 5902608..ad337cb 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -7,7 +7,8 @@ COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32 COMMON_LDFLAGS = -m32 -nostdlib # Images. -pkglib_IMAGES = boot.img diskboot.img kernel.img pxeboot.img lnxboot.img +pkglib_IMAGES = boot.img diskboot.img kernel.img pxeboot.img lnxboot.img \ + cdboot.img # For boot.img. boot_img_SOURCES = boot/i386/pc/boot.S @@ -29,6 +30,11 @@ lnxboot_img_SOURCES = boot/i386/pc/lnxboot.S lnxboot_img_ASFLAGS = $(COMMON_ASFLAGS) lnxboot_img_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-N,-Ttext,6000 +# For cdboot.img. +cdboot_img_SOURCES = boot/i386/pc/cdboot.S +cdboot_img_ASFLAGS = $(COMMON_ASFLAGS) +cdboot_img_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-N,-Ttext,7C00 + # For kernel.img. kernel_img_SOURCES = kern/i386/pc/startup.S kern/main.c kern/device.c \ kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index eb22e6d..01a67c4 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -26,12 +26,15 @@ #include <grub/err.h> #include <grub/term.h> +static int cd_start = 0xe0; +static int cd_count = 0; + static int grub_biosdisk_get_drive (const char *name) { unsigned long drive; - if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd') + if ((name[0] != 'f' && name[0] != 'h' && name[0] != 'c') || name[1] != 'd') goto fail; drive = grub_strtoul (name + 2, 0, 10); @@ -40,6 +43,8 @@ grub_biosdisk_get_drive (const char *name) if (name[0] == 'h') drive += 0x80; + else if (name[0] == 'c') + drive += cd_start; return (int) drive ; @@ -52,8 +57,11 @@ static int grub_biosdisk_call_hook (int (*hook) (const char *name), int drive) { char name[10]; - - grub_sprintf (name, (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80)); + + if (drive >= cd_start) + grub_sprintf (name, "cd%d", drive - cd_start); + else + grub_sprintf (name, (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80)); return hook (name); } @@ -82,7 +90,11 @@ grub_biosdisk_iterate (int (*hook) (const char *name)) if (grub_biosdisk_call_hook (hook, drive)) return 1; } - + + for (drive = cd_start; drive < cd_start + cd_count; drive++) + if (grub_biosdisk_call_hook (hook, drive)) + return 1; + return 0; } @@ -97,7 +109,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) if (drive < 0) return grub_errno; - disk->has_partitions = (drive & 0x80); + disk->has_partitions = ((drive & 0x80) && (drive < cd_start)); disk->id = drive; data = (struct grub_biosdisk_data *) grub_malloc (sizeof (*data)); @@ -106,8 +118,14 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) data->drive = drive; data->flags = 0; - - if (drive & 0x80) + + if (drive >= cd_start) + { + data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; + data->sectors = 32; + total_sectors = 9000000; /* TODO: get the correct size. */ + } + else if (drive & 0x80) { /* HDD */ int version; @@ -136,18 +154,21 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) } } - if (grub_biosdisk_get_diskinfo_standard (drive, - &data->cylinders, - &data->heads, - &data->sectors) != 0) + if (drive < cd_start) { - grub_free (data); - return grub_error (GRUB_ERR_BAD_DEVICE, "cannot get C/H/S values"); + if (grub_biosdisk_get_diskinfo_standard (drive, + &data->cylinders, + &data->heads, + &data->sectors) != 0) + { + grub_free (data); + return grub_error (GRUB_ERR_BAD_DEVICE, "cannot get C/H/S values"); + } + + if (! total_sectors) + total_sectors = data->cylinders * data->heads * data->sectors; } - if (! total_sectors) - total_sectors = data->cylinders * data->heads * data->sectors; - disk->total_sectors = total_sectors; disk->data = data; @@ -184,13 +205,22 @@ grub_biosdisk_rw (int cmd, grub_disk_t disk, dap->buffer = segment << 16; /* The format SEGMENT:ADDRESS. */ dap->block = sector; - if (grub_biosdisk_rw_int13_extensions (cmd + 0x42, data->drive, dap)) - { - /* Fall back to the CHS mode. */ - data->flags &= ~GRUB_BIOSDISK_FLAG_LBA; - disk->total_sectors = data->cylinders * data->heads * data->sectors; - return grub_biosdisk_rw (cmd, disk, sector, size, segment); + if (data->flags & GRUB_BIOSDISK_FLAG_CDROM) + { + dap->blocks >>= 2; + dap->block >>= 2; + + if (grub_biosdisk_rw_int13_extensions (cmd + 0x42, data->drive, dap)) + return grub_errno; } + else + if (grub_biosdisk_rw_int13_extensions (cmd + 0x42, data->drive, dap)) + { + /* Fall back to the CHS mode. */ + data->flags &= ~GRUB_BIOSDISK_FLAG_LBA; + disk->total_sectors = data->cylinders * data->heads * data->sectors; + return grub_biosdisk_rw (cmd, disk, sector, size, segment); + } } else { @@ -323,6 +353,8 @@ grub_disk_biosdisk_fini (void) GRUB_MOD_INIT(biosdisk) { + int drive, found = 0; + if (grub_disk_firmware_is_tainted) { grub_printf ("Firmware is marked as tainted, refusing to initialize.\n"); @@ -331,6 +363,23 @@ GRUB_MOD_INIT(biosdisk) grub_disk_firmware_fini = grub_disk_biosdisk_fini; grub_disk_dev_register (&grub_biosdisk_dev); + + for (drive = 0xe0; drive < 0xff; drive++) + { + if (grub_biosdisk_check_int13_extensions (drive)) + { + if (! found) + cd_start = drive; + found++; + } + else + { + if (found) + break; + } + } + + cd_count = found; } GRUB_MOD_FINI(biosdisk) diff --git a/include/grub/i386/pc/biosdisk.h b/include/grub/i386/pc/biosdisk.h index 3591c2b..8319135 100644 --- a/include/grub/i386/pc/biosdisk.h +++ b/include/grub/i386/pc/biosdisk.h @@ -23,6 +23,7 @@ #include <grub/types.h> #define GRUB_BIOSDISK_FLAG_LBA 1 +#define GRUB_BIOSDISK_FLAG_CDROM 2 struct grub_biosdisk_data { diff --git a/kern/i386/pc/init.c b/kern/i386/pc/init.c index acaf20b..1a4e9df 100644 --- a/kern/i386/pc/init.c +++ b/kern/i386/pc/init.c @@ -71,9 +71,12 @@ make_install_device (void) } else if (grub_install_dos_part != -2) { - grub_sprintf (dev, "(%cd%u", - (grub_boot_drive & 0x80) ? 'h' : 'f', - grub_boot_drive & 0x7f); + if (grub_boot_drive >= 0xe0) + grub_sprintf (dev, "(cd%u", grub_boot_drive - 0xe0); + else + grub_sprintf (dev, "(%cd%u", + (grub_boot_drive & 0x80) ? 'h' : 'f', + grub_boot_drive & 0x7f); if (grub_install_dos_part >= 0) grub_sprintf (dev + grub_strlen (dev), ",%u", grub_install_dos_part + 1); -- Bean _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org http://lists.gnu.org/mailman/listinfo/grub-devel