Changes in this new version: Fix a bug in lnxboot. Use macro for magic values. Try 3 times when reading from cdrom.
2008-01-31 Bean <[EMAIL PROTECTED]> * boot/i396/pc/lnxboot.S (data_start): Add cdrom boot support. * 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/lnxboot.S b/boot/i386/pc/lnxboot.S index 6a4de8d..99f896f 100644 --- a/boot/i386/pc/lnxboot.S +++ b/boot/i386/pc/lnxboot.S @@ -30,6 +30,9 @@ #define BLCK_LENG 0x4000 +#define CDSEC_SHIFT 11 +#define CDBLK_LENG 16 + .text .code16 @@ -38,7 +41,122 @@ data_start: xorl %ebp, %ebp - jmp linux_next + 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 + + cmpb $0xe0, %dl + jb linux_next + + movl %cs: bi_length - data_next(%bx), %ecx + orl %ecx, %ecx + jz linux_next + + /* Boot from CDROM. */ + + xorw %ax, %ax + movw %ax, %ss + movw $(CODE_ADDR), %sp + movw %ax, %ds + movw %ax, %es + + 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 - CODE_LENG - 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 + jmp fail . = data_start + 0x1F1 @@ -142,8 +260,7 @@ normalize: real_code: subw $0x20, %ax movw %ax, %ds - movw (setup_sects - data_start), %cx - shlw $7, %cx + movw $((CODE_LENG) >> 2), %cx /* Setup stack. */ @@ -286,6 +403,9 @@ fail: err_int15_msg: .ascii "move memory fails\0" +err_cdfail_msg: + .ascii "cdrom read fails\0" + . = (. & (~0x1FF)) + 0x1FF .byte 0 diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index eb22e6d..9c0ecc5 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