[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] eltorito cdrom boot
From: |
Bean |
Subject: |
Re: [PATCH] eltorito cdrom boot |
Date: |
Thu, 31 Jan 2008 16:38:15 +0800 |
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 <address@hidden>
* 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