All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] eltorito cdrom boot
@ 2008-01-29 19:22 Bean
  2008-01-29 20:33 ` Robert Millan
  2008-01-29 20:58 ` Marco Gerards
  0 siblings, 2 replies; 15+ messages in thread
From: Bean @ 2008-01-29 19:22 UTC (permalink / raw)
  To: The development of GRUB 2

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



^ permalink raw reply related	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2008-02-03  8:27 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-01-29 19:22 [PATCH] eltorito cdrom boot Bean
2008-01-29 20:33 ` Robert Millan
2008-01-29 20:58 ` Marco Gerards
2008-01-30 15:13   ` Bean
2008-01-31  8:38     ` Bean
2008-01-31 11:09       ` Robert Millan
2008-01-31 11:36         ` Yoshinori K. Okuji
2008-01-31 12:43           ` Bean
2008-02-02 14:19             ` Bean
2008-02-02 16:33               ` Robert Millan
2008-02-02 17:16                 ` Bean
2008-02-02 17:24                   ` Robert Millan
2008-02-03  8:27                     ` Bean
2008-01-31 12:47         ` Robert Millan
2008-01-31 13:04           ` Bean

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.