grub-devel
[Top][All Lists]
Advanced

[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 20:43:29 +0800

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  <address@hidden>

        * 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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]