mirror of
https://git.savannah.gnu.org/git/grub.git
synced 2026-10-23 07:39:22 +00:00
* stage2/char_io.c [!GRUB_UTIL] (memcpy): New function. It is defined as an alias of grub_memmove. This is copied from GRUB 2. * stage2/disk_io.c (print_completions): Simplified conditionals to make it easier to edit the file with Emacs. Added support for (cd). (set_device): Likewise. * stage2/common.c (init_bios_info): Check if BOOT_DRIVE is a CDROM drive. If it is true, set CDROM_DRIVE to BOOT_DRIVE. (cdrom_drive): New variable. From Leonid Lisovskiy <lly@pisem.net>: * stage2/start_eltorito.S: New file. * stage2/stage2.c (run_menu): Use GRUB_INVALID_DRIVE instead of 0xFF. * stage2/shared.h (STAGE2_ID_ISO9660_STAGE1_5): New macro. [FSYS_ISO9660] (STAGE2_ID): Set to STAGE2_ID_ISO9660_STAGE1_5. (struct geometry): Added a new member ``sector_size''. (BIOSDISK_FLAG_CDROM): New macro. (cdrom_drive): Declared. * stage2/fsys_iso9660.c: New file. * stage2/iso9660.h: Likewise. * stage2/filesys.h (FSYS_ISO9660_NUM): New macro. [FSYS_ISO9660] (iso9660_mount): Declared. [FSYS_ISO9660] (iso9660_read): Likewise. [FSYS_ISO9660] (iso9660_dir): Likewise. (NUM_FSYS): Added FSYS_ISO9660_NUM. * stage2/disk_io.c (fsys_table) [FSYS_ISO9660]: Added iso9660. (current_drive): Use GRUB_INVALID_DRIVE. (log2): New function. (rawread): Use BUF_GEOM.SECTOR_SIZE instead of SECTOR_SIZE. Change the type of BUFADDR from int to char *. Use a virtual track to make sure that one track fits in the buffer. (sane_partition): Allow CURRENT_DRIVE to be CDROM_DRIVE, because a bios drive for a CD-ROM is often assigned to greater than 0x88. (set_device): Use GRUB_INVALID_DRIVE instead of 0xFF. (setup_part): Likewise. * stage2/cmdline.c (init_cmdline): Use GRUB_INVALID_DRIVE. * stage2/builtins.c (install_func): Use GRUB_INVALID_DRIVE. (setup_func): Added iso9660_stage1_5. * stage2/bios.c (biosdisk): Don't fall back to the CHS mode if the drive is a CDROM. (get_cdinfo): New function. (get_diskinfo): Call get_cdinfo if the drive is greater than or equal to 0x88 or the drive supports LBA. Set the sector size to SECTOR_SIZE if it is not a CD-ROM. * stage2/asm.S (biosdisk_int13_extensions): Take a word argument AX instead of a byte argument AH. (get_diskinfo_int13_extensions): Removed. * stage2/Makefile.am (noinst_HEADERS): Added iso9660.h. (libgrub_a_SOURCES): Added fsys_iso9660.c. (libgrub_a_CFLAGS): Added -DFSYS_ISO9660=1. (pkgdata_DATA): Added iso9660_stage1_5 and stage2_eltorito. (noinst_PROGRAMS): Added iso9660_stage1_5.exec and start_eltorito.exec. (noinst_DATA): Added start_eltorito. (pre_stage2_exec_SOURCES): Added fsys_iso9660.c. (START_ELTORITO_LINK): New variable. (start_eltorito_exec_SOURCES): Likewise. (start_eltorito_exec_CCASFLAGS): Likewise. (start_eltorito_exec_LDFLAGS): Likewise. (start_eltorito_exec-start.$(OBJEXT)): New dependency. (stage2_eltorito): New target. (iso9660_stage1_5_exec_SOURCES): New variable. (iso9660_stage1_5_exec_CFLAGS): Likewise. (iso9660_stage1_5_exec_CCASFLAGS): Likewise. (iso9660_stage1_5_exec_LDFLAGS): Likewise. * stage1/stage1.h (GRUB_INVALID_DRIVE): New macro. * stage1/stage1.S (boot_drive): Use the macro GRUB_INVALID_DRIVE. (real_start): Likewise. * lib/device.c (get_drive_geometry): Set GEOM->SECTOR_SIZE to SECTOR_SIZE by default. * configure.ac (--disable-iso9660): New option.
318 lines
9.3 KiB
C
318 lines
9.3 KiB
C
/* bios.c - implement C part of low-level BIOS disk input and output */
|
|
/*
|
|
* GRUB -- GRand Unified Bootloader
|
|
* Copyright (C) 1999,2000,2003,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.
|
|
*/
|
|
|
|
#include "shared.h"
|
|
|
|
|
|
/* These are defined in asm.S, and never be used elsewhere, so declare the
|
|
prototypes here. */
|
|
extern int biosdisk_int13_extensions (int ax, int drive, void *dap);
|
|
extern int biosdisk_standard (int ah, int drive,
|
|
int coff, int hoff, int soff,
|
|
int nsec, int segment);
|
|
extern int check_int13_extensions (int drive);
|
|
extern int get_diskinfo_standard (int drive,
|
|
unsigned long *cylinders,
|
|
unsigned long *heads,
|
|
unsigned long *sectors);
|
|
#if 0
|
|
extern int get_diskinfo_floppy (int drive,
|
|
unsigned long *cylinders,
|
|
unsigned long *heads,
|
|
unsigned long *sectors);
|
|
#endif
|
|
|
|
|
|
/* Read/write NSEC sectors starting from SECTOR in DRIVE disk with GEOMETRY
|
|
from/into SEGMENT segment. If READ is BIOSDISK_READ, then read it,
|
|
else if READ is BIOSDISK_WRITE, then write it. If an geometry error
|
|
occurs, return BIOSDISK_ERROR_GEOMETRY, and if other error occurs, then
|
|
return the error number. Otherwise, return 0. */
|
|
int
|
|
biosdisk (int read, int drive, struct geometry *geometry,
|
|
int sector, int nsec, int segment)
|
|
{
|
|
int err;
|
|
|
|
if (geometry->flags & BIOSDISK_FLAG_LBA_EXTENSION)
|
|
{
|
|
struct disk_address_packet
|
|
{
|
|
unsigned char length;
|
|
unsigned char reserved;
|
|
unsigned short blocks;
|
|
unsigned long buffer;
|
|
unsigned long long block;
|
|
} __attribute__ ((packed)) dap;
|
|
|
|
/* XXX: Don't check the geometry by default, because some buggy
|
|
BIOSes don't return the number of total sectors correctly,
|
|
even if they have working LBA support. Hell. */
|
|
#ifdef NO_BUGGY_BIOS_IN_THE_WORLD
|
|
if (sector >= geometry->total_sectors)
|
|
return BIOSDISK_ERROR_GEOMETRY;
|
|
#endif /* NO_BUGGY_BIOS_IN_THE_WORLD */
|
|
|
|
/* FIXME: sizeof (DAP) must be 0x10. Should assert that the compiler
|
|
can't add any padding. */
|
|
dap.length = sizeof (dap);
|
|
dap.block = sector;
|
|
dap.blocks = nsec;
|
|
dap.reserved = 0;
|
|
/* This is undocumented part. The address is formated in
|
|
SEGMENT:ADDRESS. */
|
|
dap.buffer = segment << 16;
|
|
|
|
err = biosdisk_int13_extensions ((read + 0x42) << 8, drive, &dap);
|
|
|
|
/* #undef NO_INT13_FALLBACK */
|
|
#ifndef NO_INT13_FALLBACK
|
|
if (err)
|
|
{
|
|
if (geometry->flags & BIOSDISK_FLAG_CDROM)
|
|
return err;
|
|
|
|
geometry->flags &= ~BIOSDISK_FLAG_LBA_EXTENSION;
|
|
geometry->total_sectors = (geometry->cylinders
|
|
* geometry->heads
|
|
* geometry->sectors);
|
|
return biosdisk (read, drive, geometry, sector, nsec, segment);
|
|
}
|
|
#endif /* ! NO_INT13_FALLBACK */
|
|
|
|
}
|
|
else
|
|
{
|
|
int cylinder_offset, head_offset, sector_offset;
|
|
int head;
|
|
|
|
/* SECTOR_OFFSET is counted from one, while HEAD_OFFSET and
|
|
CYLINDER_OFFSET are counted from zero. */
|
|
sector_offset = sector % geometry->sectors + 1;
|
|
head = sector / geometry->sectors;
|
|
head_offset = head % geometry->heads;
|
|
cylinder_offset = head / geometry->heads;
|
|
|
|
if (cylinder_offset >= geometry->cylinders)
|
|
return BIOSDISK_ERROR_GEOMETRY;
|
|
|
|
err = biosdisk_standard (read + 0x02, drive,
|
|
cylinder_offset, head_offset, sector_offset,
|
|
nsec, segment);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
/* Check bootable CD-ROM emulation status. */
|
|
static int
|
|
get_cdinfo (int drive, struct geometry *geometry)
|
|
{
|
|
int err;
|
|
struct iso_spec_packet
|
|
{
|
|
unsigned char size;
|
|
unsigned char media_type;
|
|
unsigned char drive_no;
|
|
unsigned char controller_no;
|
|
unsigned long image_lba;
|
|
unsigned short device_spec;
|
|
unsigned short cache_seg;
|
|
unsigned short load_seg;
|
|
unsigned short length_sec512;
|
|
unsigned char cylinders;
|
|
unsigned char sectors;
|
|
unsigned char heads;
|
|
|
|
unsigned char dummy[16];
|
|
} __attribute__ ((packed)) cdrp;
|
|
|
|
grub_memset (&cdrp, 0, sizeof (cdrp));
|
|
cdrp.size = sizeof (cdrp) - sizeof (cdrp.dummy);
|
|
err = biosdisk_int13_extensions (0x4B01, drive, &cdrp);
|
|
if (! err && cdrp.drive_no == drive)
|
|
{
|
|
if ((cdrp.media_type & 0x0F) == 0)
|
|
{
|
|
/* No emulation bootable CD-ROM */
|
|
geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION | BIOSDISK_FLAG_CDROM;
|
|
geometry->cylinders = 0;
|
|
geometry->heads = 1;
|
|
geometry->sectors = 15;
|
|
geometry->sector_size = 2048;
|
|
geometry->total_sectors = MAXINT;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
/* Floppy or hard-disk emulation */
|
|
geometry->cylinders
|
|
= ((unsigned int) cdrp.cylinders
|
|
+ (((unsigned int) (cdrp.sectors & 0xC0)) << 2));
|
|
geometry->heads = cdrp.heads;
|
|
geometry->sectors = cdrp.sectors & 0x3F;
|
|
geometry->sector_size = SECTOR_SIZE;
|
|
geometry->total_sectors = (geometry->cylinders
|
|
* geometry->heads
|
|
* geometry->sectors);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Return the geometry of DRIVE in GEOMETRY. If an error occurs, return
|
|
non-zero, otherwise zero. */
|
|
int
|
|
get_diskinfo (int drive, struct geometry *geometry)
|
|
{
|
|
int err;
|
|
|
|
/* Clear the flags. */
|
|
geometry->flags = 0;
|
|
|
|
if (drive & 0x80)
|
|
{
|
|
/* hard disk or CD-ROM */
|
|
int version;
|
|
unsigned long total_sectors = 0;
|
|
|
|
version = check_int13_extensions (drive);
|
|
|
|
if (drive >= 0x88 || version)
|
|
{
|
|
/* Possible CD-ROM - check the status. */
|
|
if (get_cdinfo (drive, geometry))
|
|
return 0;
|
|
}
|
|
|
|
if (version)
|
|
{
|
|
struct drive_parameters
|
|
{
|
|
unsigned short size;
|
|
unsigned short flags;
|
|
unsigned long cylinders;
|
|
unsigned long heads;
|
|
unsigned long sectors;
|
|
unsigned long long total_sectors;
|
|
unsigned short bytes_per_sector;
|
|
/* ver 2.0 or higher */
|
|
unsigned long EDD_configuration_parameters;
|
|
/* ver 3.0 or higher */
|
|
unsigned short signature_dpi;
|
|
unsigned char length_dpi;
|
|
unsigned char reserved[3];
|
|
unsigned char name_of_host_bus[4];
|
|
unsigned char name_of_interface_type[8];
|
|
unsigned char interface_path[8];
|
|
unsigned char device_path[8];
|
|
unsigned char reserved2;
|
|
unsigned char checksum;
|
|
|
|
/* XXX: This is necessary, because the BIOS of Thinkpad X20
|
|
writes a garbage to the tail of drive parameters,
|
|
regardless of a size specified in a caller. */
|
|
unsigned char dummy[16];
|
|
} __attribute__ ((packed)) drp;
|
|
|
|
/* It is safe to clear out DRP. */
|
|
grub_memset (&drp, 0, sizeof (drp));
|
|
|
|
/* PhoenixBIOS 4.0 Revision 6.0 for ZF Micro might understand
|
|
the greater buffer size for the "get drive parameters" int
|
|
0x13 call in its own way. Supposedly the BIOS assumes even
|
|
bigger space is available and thus corrupts the stack.
|
|
This is why we specify the exactly necessary size of 0x42
|
|
bytes. */
|
|
drp.size = sizeof (drp) - sizeof (drp.dummy);
|
|
|
|
err = biosdisk_int13_extensions (0x4800, drive, &drp);
|
|
if (! err)
|
|
{
|
|
/* Set the LBA flag. */
|
|
geometry->flags = BIOSDISK_FLAG_LBA_EXTENSION;
|
|
|
|
/* I'm not sure if GRUB should check the bit 1 of DRP.FLAGS,
|
|
so I omit the check for now. - okuji */
|
|
/* if (drp.flags & (1 << 1)) */
|
|
|
|
/* FIXME: when the 2TB limit becomes critical, we must
|
|
change the type of TOTAL_SECTORS to unsigned long
|
|
long. */
|
|
if (drp.total_sectors)
|
|
total_sectors = drp.total_sectors & ~0L;
|
|
else
|
|
/* Some buggy BIOSes doesn't return the total sectors
|
|
correctly but returns zero. So if it is zero, compute
|
|
it by C/H/S returned by the LBA BIOS call. */
|
|
total_sectors = drp.cylinders * drp.heads * drp.sectors;
|
|
}
|
|
}
|
|
|
|
/* Don't pass GEOMETRY directly, but pass each element instead,
|
|
so that we can change the structure easily. */
|
|
err = get_diskinfo_standard (drive,
|
|
&geometry->cylinders,
|
|
&geometry->heads,
|
|
&geometry->sectors);
|
|
if (err)
|
|
return err;
|
|
|
|
if (! total_sectors)
|
|
{
|
|
total_sectors = (geometry->cylinders
|
|
* geometry->heads
|
|
* geometry->sectors);
|
|
}
|
|
geometry->total_sectors = total_sectors;
|
|
geometry->sector_size = SECTOR_SIZE;
|
|
}
|
|
else
|
|
{
|
|
/* floppy disk */
|
|
|
|
/* First, try INT 13 AH=8h call. */
|
|
err = get_diskinfo_standard (drive,
|
|
&geometry->cylinders,
|
|
&geometry->heads,
|
|
&geometry->sectors);
|
|
|
|
#if 0
|
|
/* If fails, then try floppy-specific probe routine. */
|
|
if (err)
|
|
err = get_diskinfo_floppy (drive,
|
|
&geometry->cylinders,
|
|
&geometry->heads,
|
|
&geometry->sectors);
|
|
#endif
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
geometry->total_sectors = (geometry->cylinders
|
|
* geometry->heads
|
|
* geometry->sectors);
|
|
geometry->sector_size = SECTOR_SIZE;
|
|
}
|
|
|
|
return 0;
|
|
}
|