grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] eltorito cdrom boot


From: Bean
Subject: [PATCH] eltorito cdrom boot
Date: Wed, 30 Jan 2008 03:22:58 +0800

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




reply via email to

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