[Top][All Lists]
[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
- [PATCH] eltorito cdrom boot,
Bean <=