1
/* biosdisk.c - emulate biosdisk */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 1999,2000,2001,2002,2003,2004 Free Software Foundation, Inc.
6
* GRUB is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with GRUB; if not, write to the Free Software
18
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
#include <grub/machine/biosdisk.h>
22
#include <grub/disk.h>
23
#include <grub/partition.h>
24
#include <grub/pc_partition.h>
25
#include <grub/types.h>
27
#include <grub/util/misc.h>
35
#include <sys/types.h>
42
# include <sys/ioctl.h> /* ioctl */
43
# if !defined(__GLIBC__) || \
44
((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
45
/* Maybe libc doesn't have large file support. */
46
# include <linux/unistd.h> /* _llseek */
47
# endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
49
# define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
50
# endif /* ! BLKFLSBUF */
51
# include <sys/ioctl.h> /* ioctl */
53
# define HDIO_GETGEO 0x0301 /* get device geometry */
54
/* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
59
unsigned char sectors;
60
unsigned short cylinders;
63
# endif /* ! HDIO_GETGEO */
65
# define BLKGETSIZE _IO(0x12,96) /* return device size */
66
# endif /* ! BLKGETSIZE */
70
# endif /* ! MINORBITS */
71
# define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
74
# define FLOPPY_MAJOR 2
75
# endif /* ! FLOPPY_MAJOR */
78
# endif /* ! LOOP_MAJOR */
79
#endif /* __linux__ */
81
static char *map[256];
84
/* Check if we have devfs support. */
88
static int dev_devfsd_exists = -1;
90
if (dev_devfsd_exists < 0)
94
dev_devfsd_exists = stat ("/dev/.devfsd", &st) == 0;
97
return dev_devfsd_exists;
99
#endif /* __linux__ */
102
get_drive (const char *name)
107
if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd')
110
drive = strtoul (name + 2, &p, 10);
117
if (drive > sizeof (map) / sizeof (map[0]))
123
grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk");
128
call_hook (int (*hook) (const char *name), int drive)
132
sprintf (name, (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80));
137
grub_util_biosdisk_iterate (int (*hook) (const char *name))
141
for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
142
if (map[i] && call_hook (hook, i))
149
grub_util_biosdisk_open (const char *name, grub_disk_t disk)
154
drive = get_drive (name);
159
return grub_error (GRUB_ERR_BAD_DEVICE,
160
"no mapping exists for `%s'", name);
162
disk->has_partitions = (drive & 0x80);
171
fd = open (map[drive], O_RDONLY);
173
return grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", map[drive]);
175
if (fstat (fd, &st) < 0 || ! S_ISBLK (st.st_mode))
181
if (ioctl (fd, BLKGETSIZE, &nr))
188
disk->total_sectors = nr;
190
grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
192
return GRUB_ERR_NONE;
196
/* In GNU/Hurd, stat() will return the right size. */
197
#elif !defined (__GNU__)
198
# warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
200
if (stat (map[drive], &st) < 0)
201
return grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", map[drive]);
203
disk->total_sectors = st.st_size >> GRUB_DISK_SECTOR_BITS;
205
grub_util_info ("the size of %s is %lu", name, disk->total_sectors);
207
return GRUB_ERR_NONE;
212
linux_find_partition (char *dev, unsigned long sector)
214
size_t len = strlen (dev);
220
real_dev = xstrdup (dev);
222
if (have_devfs () && strcmp (real_dev + len - 5, "/disc") == 0)
224
p = real_dev + len - 4;
227
else if ((strncmp (real_dev + 5, "hd", 2) == 0
228
|| strncmp (real_dev + 5, "sd", 2) == 0)
229
&& real_dev[7] >= 'a' && real_dev[7] <= 'z')
234
else if (strncmp (real_dev + 5, "rd/c", 4) == 0)
236
p = strchr (real_dev + 9, 'd');
241
while (*p && isdigit (*p))
252
for (i = 1; i < 10000; i++)
255
struct hd_geometry hdg;
257
sprintf (p, format, i);
258
fd = open (real_dev, O_RDONLY);
265
if (ioctl (fd, HDIO_GETGEO, &hdg))
274
if (hdg.start == sector)
276
strcpy (dev, real_dev);
285
#endif /* __linux__ */
288
open_device (const grub_disk_t disk, unsigned long sector, int flags)
293
flags |= O_LARGEFILE;
303
/* Linux has a bug that the disk cache for a whole disk is not consistent
304
with the one for a partition of the disk. */
306
int is_partition = 0;
309
strcpy (dev, map[disk->id]);
310
if (disk->partition && strncmp (map[disk->id], "/dev/", 5) == 0)
311
is_partition = linux_find_partition (dev, disk->partition->start);
313
/* Open the partition. */
314
grub_util_info ("opening the device `%s'", dev);
315
fd = open (dev, flags);
318
grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", dev);
322
/* Make the buffer cache consistent with the physical disk. */
323
ioctl (fd, BLKFLSBUF, 0);
326
sector -= disk->partition->start;
328
#else /* ! __linux__ */
329
fd = open (map[disk->id], flags);
332
grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", map[disk->id]);
335
#endif /* ! __linux__ */
337
#if defined(__linux__) && (!defined(__GLIBC__) || \
338
((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
339
/* Maybe libc doesn't have large file support. */
341
loff_t offset, result;
342
static int _llseek (uint filedes, ulong hi, ulong lo,
343
loff_t *res, uint wh);
344
_syscall5 (int, _llseek, uint, filedes, ulong, hi, ulong, lo,
345
loff_t *, res, uint, wh);
347
offset = (loff_t) sector << GRUB_DISK_SECTOR_BITS;
348
if (_llseek (fd, offset >> 32, offset & 0xffffffff, &result, SEEK_SET))
350
grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id]);
357
off_t offset = (off_t) sector << GRUB_DISK_SECTOR_BITS;
359
if (lseek (fd, offset, SEEK_SET) != offset)
361
grub_error (GRUB_ERR_BAD_DEVICE, "cannot seek `%s'", map[disk->id]);
371
/* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
372
error occurs, otherwise return LEN. */
374
nread (int fd, char *buf, size_t len)
380
ssize_t ret = read (fd, buf, len);
397
/* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
398
error occurs, otherwise return LEN. */
400
nwrite (int fd, const char *buf, size_t len)
406
ssize_t ret = write (fd, buf, len);
424
grub_util_biosdisk_read (grub_disk_t disk, unsigned long sector,
425
unsigned long size, char *buf)
429
fd = open_device (disk, sector, O_RDONLY);
434
if (sector == 0 && size > 1)
436
/* Work around a bug in linux's ez remapping. Linux remaps all
437
sectors that are read together with the MBR in one read. It
438
should only remap the MBR, so we split the read in two
440
if (nread (fd, buf, GRUB_DISK_SECTOR_SIZE) != GRUB_DISK_SECTOR_SIZE)
442
grub_error (GRUB_ERR_READ_ERROR, "cannot read `%s'", map[disk->id]);
447
buf += GRUB_DISK_SECTOR_SIZE;
450
#endif /* __linux__ */
452
if (nread (fd, buf, size << GRUB_DISK_SECTOR_BITS)
453
!= (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
454
grub_error (GRUB_ERR_READ_ERROR, "cannot read from `%s'", map[disk->id]);
461
grub_util_biosdisk_write (grub_disk_t disk, unsigned long sector,
462
unsigned long size, const char *buf)
466
fd = open_device (disk, sector, O_WRONLY);
470
if (nwrite (fd, buf, size << GRUB_DISK_SECTOR_BITS)
471
!= (ssize_t) (size << GRUB_DISK_SECTOR_BITS))
472
grub_error (GRUB_ERR_WRITE_ERROR, "cannot write to `%s'", map[disk->id]);
478
static struct grub_disk_dev grub_util_biosdisk_dev =
481
.id = GRUB_DISK_DEVICE_BIOSDISK_ID,
482
.iterate = grub_util_biosdisk_iterate,
483
.open = grub_util_biosdisk_open,
485
.read = grub_util_biosdisk_read,
486
.write = grub_util_biosdisk_write,
491
read_device_map (const char *dev_map)
494
char buf[1024]; /* XXX */
496
auto void show_error (const char *msg);
498
void show_error (const char *msg)
500
grub_util_error ("%s:%d: %s", dev_map, lineno, msg);
503
fp = fopen (dev_map, "r");
505
grub_util_error ("Cannot open `%s'", dev_map);
507
while (fgets (buf, sizeof (buf), fp))
515
/* Skip leading spaces. */
516
while (*p && isspace (*p))
519
/* If the first character is `#' or NUL, skip this line. */
520
if (*p == '\0' || *p == '#')
524
show_error ("No open parenthesis found");
527
drive = get_drive (p);
528
if (drive < 0 || drive >= (int) (sizeof (map) / sizeof (map[0])))
529
show_error ("Bad device name");
533
show_error ("No close parenthesis found");
536
/* Skip leading spaces. */
537
while (*p && isspace (*p))
541
show_error ("No filename found");
543
/* NUL-terminate the filename. */
545
while (*e && ! isspace (*e))
549
/* Multiple entries for a given drive is not allowed. */
551
show_error ("Duplicated entry found");
554
/* On Linux, the devfs uses symbolic links horribly, and that
555
confuses the interface very much, so use realpath to expand
557
map[drive] = xmalloc (PATH_MAX);
558
if (! realpath (p, map[drive]))
559
grub_util_error ("Cannot get the real path of `%s'", p);
561
map[drive] = xstrdup (p);
569
grub_util_biosdisk_init (const char *dev_map)
571
read_device_map (dev_map);
572
grub_disk_dev_register (&grub_util_biosdisk_dev);
576
grub_util_biosdisk_fini (void)
580
for (i = 0; i < sizeof (map) / sizeof (map[0]); i++)
583
grub_disk_dev_unregister (&grub_util_biosdisk_dev);
587
make_device_name (int drive, int dos_part, int bsd_part)
592
sprintf (p, (drive & 0x80) ? "hd%d" : "fd%d", drive & ~0x80);
595
sprintf (p + strlen (p), ",%d", dos_part);
598
sprintf (p + strlen (p), ",%c", bsd_part + 'a');
604
get_os_disk (const char *os_dev)
608
#if defined(__linux__)
609
path = xmalloc (PATH_MAX);
610
if (! realpath (os_dev, path))
613
if (strncmp ("/dev/", path, 5) == 0)
619
/* If this is an IDE disk. */
620
if (strncmp ("/dev/ide/", p, 9) == 0)
622
p = strstr (p, "part");
629
/* If this is a SCSI disk. */
630
if (strncmp ("/dev/scsi/", p, 10) == 0)
632
p = strstr (p, "part");
640
/* If this is a DAC960 disk. */
641
if (strncmp ("rd/c", p, 4) == 0)
643
/* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
651
/* If this is an IDE disk or a SCSI disk. */
652
if ((strncmp ("hd", p, 2) == 0
653
|| strncmp ("sd", p, 2) == 0)
654
&& p[2] >= 'a' && p[2] <= 'z')
656
/* /dev/[hs]d[a-z][0-9]* */
664
#elif defined(__GNU__)
665
path = xstrdup (os_dev);
666
if (strncmp ("/dev/sd", path, 7) == 0 || strncmp ("/dev/hd", path, 7) == 0)
668
p = strchr (path, 's');
675
# warning "The function `get_os_disk' might not work on your OS correctly."
676
return xstrdup (os_dev);
681
find_drive (const char *os_dev)
686
os_disk = get_os_disk (os_dev);
690
for (i = 0; i < (int) (sizeof (map) / sizeof (map[0])); i++)
691
if (map[i] && strcmp (map[i], os_disk) == 0)
702
grub_util_biosdisk_get_grub_dev (const char *os_dev)
707
if (stat (os_dev, &st) < 0)
709
grub_error (GRUB_ERR_BAD_DEVICE, "cannot stat `%s'", os_dev);
713
drive = find_drive (os_dev);
716
grub_error (GRUB_ERR_BAD_DEVICE,
717
"no mapping exists for `%s'", os_dev);
721
if (! S_ISBLK (st.st_mode))
722
return make_device_name (drive, -1, -1);
724
#if defined(__linux__)
725
/* Linux counts partitions uniformly, whether a BSD partition or a DOS
726
partition, so mapping them to GRUB devices is not trivial.
727
Here, get the start sector of a partition by HDIO_GETGEO, and
728
compare it with each partition GRUB recognizes. */
733
struct hd_geometry hdg;
736
auto int find_partition (grub_disk_t disk,
737
const grub_partition_t partition);
739
int find_partition (grub_disk_t disk __attribute__ ((unused)),
740
const grub_partition_t partition)
742
struct grub_pc_partition *pcdata = 0;
744
if (strcmp (partition->partmap->name, "pc_partition_map") == 0)
745
pcdata = partition->data;
749
if (pcdata->bsd_part < 0)
750
grub_util_info ("DOS partition %d starts from %lu",
751
pcdata->dos_part, partition->start);
753
grub_util_info ("BSD partition %d,%c starts from %lu",
754
pcdata->dos_part, pcdata->bsd_part + 'a',
758
if (hdg.start == partition->start)
762
dos_part = pcdata->dos_part;
763
bsd_part = pcdata->bsd_part;
776
name = make_device_name (drive, -1, -1);
778
if (MAJOR (st.st_rdev) == FLOPPY_MAJOR)
781
fd = open (os_dev, O_RDONLY);
784
grub_error (GRUB_ERR_BAD_DEVICE, "cannot open `%s'", os_dev);
789
if (ioctl (fd, HDIO_GETGEO, &hdg))
791
grub_error (GRUB_ERR_BAD_DEVICE,
792
"cannot get geometry of `%s'", os_dev);
800
grub_util_info ("%s starts from %lu", os_dev, hdg.start);
805
grub_util_info ("opening the device %s", name);
806
disk = grub_disk_open (name);
812
if (grub_partition_iterate (disk, find_partition) != GRUB_ERR_NONE)
814
grub_disk_close (disk);
820
grub_disk_close (disk);
821
grub_error (GRUB_ERR_BAD_DEVICE,
822
"cannot find the partition of `%s'", os_dev);
826
return make_device_name (drive, dos_part, bsd_part);
829
#elif defined(__GNU__)
830
/* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
836
p = strrchr (os_dev, 's');
843
n = strtol (p, &q, 10);
844
if (p != q && n != LONG_MIN && n != LONG_MAX)
848
if (*q >= 'a' && *q <= 'g')
853
return make_device_name (drive, dos_part, bsd_part);
857
# warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
858
return make_device_name (drive, -1, -1);