/* device.c - Some helper functions for OS devices and BIOS drives */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005 Free Software Foundation, Inc.
*
* 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.
*/
/* Try to use glibc's transparant LFS support. */
/* lseek becomes synonymous with lseek64. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#ifdef __linux__
# if !defined(__GLIBC__) || \
/* Maybe libc doesn't have large file support. */
# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
# ifndef HDIO_GETGEO
/* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
defined. */
struct hd_geometry
{
unsigned char heads;
unsigned char sectors;
unsigned short cylinders;
unsigned long start;
};
# endif /* ! HDIO_GETGEO */
# ifndef FLOPPY_MAJOR
# endif /* ! FLOPPY_MAJOR */
# ifndef MAJOR
({ \
})
# endif /* ! MAJOR */
# ifndef CDROM_GET_CAPABILITY
# endif /* ! CDROM_GET_CAPABILITY */
# ifndef BLKGETSIZE
# endif /* ! BLKGETSIZE */
#endif /* __linux__ */
/* Use __FreeBSD_kernel__ instead of __FreeBSD__ for compatibility with
#if defined(__FreeBSD__) && ! defined(__FreeBSD_kernel__)
# define __FreeBSD_kernel__
#endif
#ifdef __FreeBSD_kernel__
/* Obtain version of kFreeBSD headers */
# include <osreldate.h>
# ifndef __FreeBSD_kernel_version
# endif
/* Runtime detection of kernel */
int
{
if (major >= 9)
major = 9;
if (major >= 5)
{
}
else
{
}
}
#endif /* __FreeBSD_kernel__ */
# include <sys/disklabel.h>
# if defined(__FreeBSD_kernel__)
# if __FreeBSD_kernel_version >= 500040
# endif
# endif /* __FreeBSD_kernel__ */
#endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */
#if defined(__sun)
#endif /* __sun */
#ifdef HAVE_OPENDISK
# include <util.h>
#endif /* HAVE_OPENDISK */
#include <shared.h>
#include <device.h>
/* Get the geometry of a drive DRIVE. */
void
{
int fd;
{
}
else
/* XXX This is the default size. */
#if defined(__linux__)
/* Linux */
{
unsigned long nr;
goto fail;
goto fail;
/* Got the geometry, so save it. */
goto success;
}
/* kFreeBSD version 5 or later */
if (get_kfreebsd_version () >= 500040)
{
unsigned int sector_size;
unsigned int tmp;
sector_size = 512;
goto fail;
else
else
goto success;
}
else
#endif /* defined(__FreeBSD_kernel__) && __FreeBSD_kernel_version >= 500040 */
/* kFreeBSD < 5, NetBSD or OpenBSD */
{
goto fail;
goto success;
}
/* Solaris */
{
goto fail;
goto success;
}
#else
/* Notably, defined(__GNU__) */
# warning "Automatic detection of geometries will be performed only \
partially. This is not fatal."
#endif
fail:
{
/* FIXME: It would be nice to somehow compute fake C/H/S settings,
given a proper st_blocks size. */
if (drive & 0x80)
{
}
else
{
}
/* Set the total sectors properly, if we can. */
else
}
}
#ifdef __linux__
/* Check if we have devfs support. */
static int
have_devfs (void)
{
if (dev_devfsd_exists < 0)
{
}
return dev_devfsd_exists;
}
#endif /* __linux__ */
/* These three functions are quite different among OSes. */
static void
{
#if defined(__linux__)
if (have_devfs ())
else
#elif defined(__FreeBSD_kernel__)
/* kFreeBSD */
if (get_kfreebsd_version () >= 400000)
else
#elif defined(__NetBSD__)
/* NetBSD */
/* opendisk() doesn't work for floppies. */
#elif defined(__OpenBSD__)
/* OpenBSD */
#elif defined(__QNXNTO__)
/* QNX RTP */
/* Solaris */
#else
# warning "BIOS floppy drives cannot be guessed in your operating system."
/* Set NAME to a bogus string. */
*name = 0;
#endif
}
static void
{
#if defined(__linux__)
#elif defined(__FreeBSD_kernel__)
/* kFreeBSD */
if (get_kfreebsd_version () >= 400000)
else
/* NetBSD */
char shortname[16];
int fd;
16, /* length of NAME */
0 /* char device */
);
#elif defined(__OpenBSD__)
/* OpenBSD */
#elif defined(__QNXNTO__)
/* QNX RTP */
/* Actually, QNX RTP doesn't distinguish IDE from SCSI, so this could
contain SCSI disks. */
*name = 0; /* FIXME */
#else
# warning "BIOS IDE drives cannot be guessed in your operating system."
/* Set NAME to a bogus string. */
*name = 0;
#endif
}
static void
{
#if defined(__linux__)
#elif defined(__FreeBSD_kernel__)
/* kFreeBSD */
if (get_kfreebsd_version () >= 400000)
else
/* NetBSD */
char shortname[16];
int fd;
16, /* length of NAME */
0 /* char device */
);
#elif defined(__OpenBSD__)
/* OpenBSD */
#elif defined(__QNXNTO__)
/* QNX RTP */
/* QNX RTP doesn't distinguish SCSI from IDE, so it is better to
disable the detection of SCSI disks here. */
*name = 0;
*name = 0; /* FIXME */
#else
# warning "BIOS SCSI drives cannot be guessed in your operating system."
/* Set NAME to a bogus string. */
*name = 0;
#endif
}
#ifdef __linux__
static void
{
}
static void
{
}
#endif
/* Check if DEVICE can be read. If an error occurs, return zero,
otherwise return non-zero. */
int
{
/* If DEVICE is empty, just return 1. */
if (*device == 0)
return 1;
if (! fp)
{
switch (errno)
{
#ifdef ENOMEDIUM
case ENOMEDIUM:
# if 0
/* At the moment, this finds only CDROMs, which can't be
read anyway, so leave it out. Code should be
reactivated if `removable disks' and CDROMs are
supported. */
/* Accept it, it may be inserted. */
return 1;
# endif
break;
#endif /* ENOMEDIUM */
default:
/* Break case and leave. */
break;
}
/* Error opening the device. */
return 0;
}
/* Make sure CD-ROMs don't get assigned a BIOS disk number
before SCSI disks! */
#ifdef __linux__
# ifdef CDROM_GET_CAPABILITY
return 0;
# else /* ! CDROM_GET_CAPABILITY */
/* Check if DEVICE is a CD-ROM drive by the HDIO_GETGEO ioctl. */
{
struct hd_geometry hdg;
return 0;
/* If it is a block device and isn't a floppy, check if HDIO_GETGEO
succeeds. */
return 0;
}
# endif /* ! CDROM_GET_CAPABILITY */
#endif /* __linux__ */
# ifdef CDIOCCLRDEBUG
return 0;
# endif /* CDIOCCLRDEBUG */
#endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */
/* Attempt to read the first sector. */
{
return 0;
}
return 1;
}
/* Read mapping information from FP, and write it to MAP. */
static int
{
{
}
{
}
/* If there is the device map file, use the data in it instead of
probing devices. */
int line_number = 0;
{
int drive;
int is_floppy = 0;
/* Increase the number of lines. */
line_number++;
/* If the first character is '#', skip it. */
if (buf[0] == '#')
continue;
/* Skip leading spaces. */
ptr++;
/* Skip empty lines. */
if (! *ptr)
continue;
if (*ptr != '(')
{
return 0;
}
ptr++;
{
return 0;
}
if (*ptr == 'f')
is_floppy = 1;
ptr += 2;
if (drive < 0)
{
return 0;
}
else if (drive > 127)
{
"Ignoring %cd%d due to a BIOS limitation",
continue;
}
if (! is_floppy)
drive += 0x80;
if (*ptr != ')')
{
return 0;
}
ptr++;
/* Skip spaces. */
ptr++;
if (! *ptr)
{
return 0;
}
/* Terminate the filename. */
eptr++;
*eptr = 0;
/* Multiple entries for a given drive is not allowed. */
{
return 0;
}
}
return 1;
}
/* Initialize the device map MAP. *MAP will be allocated from the heap
space. If MAP_FILE is not NULL, then read mappings from the file
MAP_FILE if it exists, otherwise, write guessed mappings to the file.
FLOPPY_DISKS is the number of floppy disk drives which will be probed.
If it is zero, don't probe any floppy at all. If it is one, probe one
floppy. If it is two, probe two floppies. And so on. */
int
{
int i;
int num_hd = 0;
/* Probe devices for creating the device map. */
/* Initialize DEVICE_MAP. */
for (i = 0; i < NUM_DISKS; i++)
(*map)[i] = 0;
if (map_file)
{
/* Open the device map file. */
if (fp)
{
int ret;
return ret;
}
}
/* Print something so that the user does not think GRUB has been
crashed. */
"Probing devices to guess BIOS drives. "
"This may take a long time.\n");
if (map_file)
/* Try to open the device map file to write the probed data. */
/* Floppies. */
for (i = 0; i < floppy_disks; i++)
{
get_floppy_disk_name (name, i);
/* In floppies, write the map, whether check_device succeeds
or not, because the user just does not insert floppies. */
if (fp)
if (check_device (name))
{
}
}
#ifdef __linux__
if (have_devfs ())
{
while (1)
{
The way to number disks is the same as GRUB's. */
break;
{
/* If the device map file is opened, write the map. */
if (fp)
}
num_hd++;
}
/* OK, close the device map file if opened. */
if (fp)
return 1;
}
#endif /* __linux__ */
/* IDE disks. */
for (i = 0; i < 8; i++)
{
get_ide_disk_name (name, i);
if (check_device (name))
{
/* If the device map file is opened, write the map. */
if (fp)
num_hd++;
}
}
#ifdef __linux__
/* ATARAID disks. */
for (i = 0; i < 8; i++)
{
get_ataraid_disk_name (name, i);
if (check_device (name))
{
/* If the device map file is opened, write the map. */
if (fp)
num_hd++;
}
}
#endif /* __linux__ */
/* The rest is SCSI disks. */
for (i = 0; i < 16; i++)
{
get_scsi_disk_name (name, i);
if (check_device (name))
{
/* If the device map file is opened, write the map. */
if (fp)
num_hd++;
}
}
#ifdef __linux__
/* This is for DAC960 - we have
DAC960 driver currently supports up to 8 controllers, 32 logical
drives, and 7 partitions. */
{
{
{
if (check_device (name))
{
/* If the device map file is opened, write the map. */
if (fp)
num_hd++;
}
}
}
}
#endif /* __linux__ */
/* OK, close the device map file if opened. */
if (fp)
return 1;
}
/* Restore the memory consumed for MAP. */
void
{
int i;
for (i = 0; i < NUM_DISKS; i++)
if (map[i])
}
#ifdef __linux__
/* Linux-only functions, because Linux has a bug that the disk cache for
a whole disk is not consistent with the one for a partition of the
disk. */
int
{
/* For now, disk devices under Linux are all block devices. */
}
int
{
int fd;
{
/* If the partition is a BSD partition, it is difficult to
obtain the representation in Linux. So don't support that. */
return 1;
}
if (have_devfs ())
{
}
/* Open the partition. */
if (fd < 0)
{
return 0;
}
/* Maybe libc doesn't have large file support. */
{
{
return 0;
}
}
#else
{
{
return 0;
}
}
#endif
{
return 0;
}
sync (); /* Paranoia. */
return 1;
}
#endif /* __linux__ */