Hi, ok, this is the new patch, the cdboot function is implemented in separate file cdboot.S. To create a eltorito boot file, use:
cat cdboot.img core.img > grub2cd.bin 2008-01-31 Bean <[EMAIL PROTECTED]> * conf/i386-pc.rmk (pkglib_IMAGES): Add cdboot.img. (cdboot_img_SOURCES): New variable. (cdboot_img_ASFLAGS): New variable. (cdboot_img_LDFLAGS): New variable. * boot/i386/pc/cdboot.S: New file. * disk/i386/pc/biosdisk.c (cd_start): New variable. (cd_count): Likewise. (grub_biosdisk_get_drive): Add support for cd device. (grub_biosdisk_call_hook): Likewise. (grub_biosdisk_iterate): Likewise. (grub_biosdisk_open): Likewise. (GRUB_BIOSDISK_CDROM_RETRY_COUNT): New macro. (grub_biosdisk_rw): Support reading from cd device. (GRUB_MOD_INIT): Iterate cd devices. * include/grub/i386/pc/biosdisk.h (GRUB_BIOSDISK_FLAG_CDROM): New macro. (GRUB_BIOSDISK_MACHINE_CDROM_START): Likewise. (GRUB_BIOSDISK_MACHINE_CDROM_END): Likewise. * kern/i386/pc/init.c (make_install_device): Check for cd device. diff --git a/boot/i386/pc/cdboot.S b/boot/i386/pc/cdboot.S new file mode 100644 index 0000000..430496f --- /dev/null +++ b/boot/i386/pc/cdboot.S @@ -0,0 +1,171 @@ +/* -*-Asm-*- */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * GRUB 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 3 of the License, or + * (at your option) any later version. + * + * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <grub/boot.h> +#include <grub/machine/boot.h> +#include <grub/machine/kernel.h> +#include <multiboot.h> + + .file "cdboot.S" + +#define CODE_ADDR 0x6000 +#define DATA_ADDR ((GRUB_BOOT_MACHINE_KERNEL_ADDR) + 0x200) + +#define CDSEC_SHIFT 11 +#define CDBLK_LENG 16 + + .text + + .code16 + + .globl start, _start + +data_start: + call data_next + +data_next: + jmp 1f + + . = data_start + 8 + +bi_pvd: + .long 0 /* LBA of primary volume descript. */ +bi_file: + .long 0 /* LBA of boot file. */ +bi_length: + .long 0 /* Length of boot file. */ +bi_csum: + .long 0 /* Checksum of boot file */ +bi_reserved: + .space (10*4) /* Reserved */ + +1: + popw %bx + + /* Boot from CDROM. */ + + xorw %ax, %ax + movw %ax, %ss + movw $(CODE_ADDR), %sp + movw %ax, %ds + movw %ax, %es + + movw $(0x7C00 + err_noboot_msg - data_start), %si + movl %cs: bi_length - data_next(%bx), %ecx + orl %ecx, %ecx + jz fail + + addl $((1 << CDSEC_SHIFT) - 1), %ecx + shrl $CDSEC_SHIFT, %ecx + + movl %cs: bi_file - data_next(%bx), %esi + + call read_cdrom + + ljmp $(DATA_ADDR >> 4), $0 + +/* + * Parameters: + * esi: start sector + * ecx: number of sectors + */ +read_cdrom: + xorl %eax, %eax + + /* Number of blocks to read. */ + pushw $CDBLK_LENG + + /* Block number. */ + pushl %eax + pushl %esi + + /* Buffer address. */ + pushw $((DATA_ADDR - 0x400)>> 4) + pushl %eax + pushw $0x10 + + xorl %edi, %edi + movw %sp, %si + +1: + movw 0x10(%si), %di + cmpl %ecx, %edi + jbe 2f + movl %ecx, %edi + +2: + mov %di, 2(%si) + + pushl %ecx + + movb $0x42, %ah + int $0x13 + + jnc 3f + + movb $0x42, %ah /* Try again. */ + int $0x13 + + jnc 3f + +2: + shrw $1, %di /* Reduce transfer size. */ + jz cdrom_fail + movw %di, 0x10(%si) + movw %di, 2(%si) + movb $0x42, %ah + int $0x13 + jc 2b + +3: + + movw %di, %ax + shlw $(CDSEC_SHIFT - 4), %ax + addw %ax, 6(%si) + addl %edi, 8(%si) + + popl %ecx + subl %edi, %ecx + jnz 1b + + addw $0x12, %sp + ret + +cdrom_fail: + movw $(0x7C00 + err_cdfail_msg - data_start), %si + +fail: + movb $0x0e, %ah + xorw %bx, %bx +1: + lodsb (%si), %al + int $0x10 + cmpb $0, %al + jne 1b +1: jmp 1b + +err_noboot_msg: + .ascii "no boot info\0" + +err_cdfail_msg: + .ascii "cdrom read fails\0" + + . = data_start + 0x1FF + + .byte 0 diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index fb435bc..2e4005d 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..ddcc666 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 = GRUB_BIOSDISK_MACHINE_CDROM_START; +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 ; @@ -53,7 +58,10 @@ 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; @@ -164,6 +185,8 @@ grub_biosdisk_close (grub_disk_t disk) #define GRUB_BIOSDISK_READ 0 #define GRUB_BIOSDISK_WRITE 1 +#define GRUB_BIOSDISK_CDROM_RETRY_COUNT 3 + static grub_err_t grub_biosdisk_rw (int cmd, grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, @@ -184,13 +207,31 @@ 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) + { + int i; + + if (cmd) + return grub_error (GRUB_ERR_WRITE_ERROR, "can\'t write to cdrom"); + + dap->blocks = (dap->blocks + 3) >> 2; + dap->block >>= 2; + + for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++) + if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap)) + break; + + if (i == GRUB_BIOSDISK_CDROM_RETRY_COUNT) + return grub_error (GRUB_ERR_READ_ERROR, "cdrom read error"); } + 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 +364,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 +374,24 @@ GRUB_MOD_INIT(biosdisk) grub_disk_firmware_fini = grub_disk_biosdisk_fini; grub_disk_dev_register (&grub_biosdisk_dev); + + for (drive = GRUB_BIOSDISK_MACHINE_CDROM_START; + drive < GRUB_BIOSDISK_MACHINE_CDROM_END; 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..2b67cbf 100644 --- a/include/grub/i386/pc/biosdisk.h +++ b/include/grub/i386/pc/biosdisk.h @@ -23,6 +23,10 @@ #include <grub/types.h> #define GRUB_BIOSDISK_FLAG_LBA 1 +#define GRUB_BIOSDISK_FLAG_CDROM 2 + +#define GRUB_BIOSDISK_MACHINE_CDROM_START 0xe0 +#define GRUB_BIOSDISK_MACHINE_CDROM_END 0xf0 struct grub_biosdisk_data { diff --git a/kern/i386/pc/init.c b/kern/i386/pc/init.c index acaf20b..908e2d7 100644 --- a/kern/i386/pc/init.c +++ b/kern/i386/pc/init.c @@ -22,6 +22,7 @@ #include <grub/machine/memory.h> #include <grub/machine/console.h> #include <grub/machine/kernel.h> +#include <grub/machine/biosdisk.h> #include <grub/types.h> #include <grub/err.h> #include <grub/dl.h> @@ -71,9 +72,13 @@ 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 >= GRUB_BIOSDISK_MACHINE_CDROM_START) + grub_sprintf (dev, "(cd%u", + grub_boot_drive - GRUB_BIOSDISK_MACHINE_CDROM_START); + 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