1 /* biosdisk.c - emulate biosdisk */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2004,2006,2007,2008 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 3 of the License, or
9 * (at your option) any later version.
11 * GRUB 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, see <http://www.gnu.org/licenses/>.
20 #include <grub/disk.h>
21 #include <grub/partition.h>
22 #include <grub/pc_partition.h>
23 #include <grub/types.h>
25 #include <grub/util/misc.h>
26 #include <grub/util/biosdisk.h>
34 #include <sys/types.h>
41 # include <sys/ioctl.h> /* ioctl */
42 # if !defined(__GLIBC__) || \
43 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
44 /* Maybe libc doesn't have large file support. */
45 # include <linux/unistd.h> /* _llseek */
46 # endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
48 # define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */
49 # endif /* ! BLKFLSBUF */
50 # include <sys/ioctl.h> /* ioctl */
52 # define HDIO_GETGEO 0x0301 /* get device geometry */
53 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
58 unsigned char sectors
;
59 unsigned short cylinders
;
62 # endif /* ! HDIO_GETGEO */
64 # define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size */
65 # endif /* ! BLKGETSIZE64 */
69 # endif /* ! MINORBITS */
70 # define MAJOR(dev) ((unsigned) ((dev) >> MINORBITS))
73 # define FLOPPY_MAJOR 2
74 # endif /* ! FLOPPY_MAJOR */
77 # endif /* ! LOOP_MAJOR */
78 #endif /* __linux__ */
81 # include <sys/ioctl.h>
82 # include <cygwin/fs.h> /* BLKGETSIZE64 */
83 # include <cygwin/hdreg.h> /* HDIO_GETGEO */
84 # define MAJOR(dev) ((unsigned) ((dev) >> 16))
85 # define FLOPPY_MAJOR 2
95 /* Check if we have devfs support. */
99 static int dev_devfsd_exists
= -1;
101 if (dev_devfsd_exists
< 0)
105 dev_devfsd_exists
= stat ("/dev/.devfsd", &st
) == 0;
108 return dev_devfsd_exists
;
110 #endif /* __linux__ */
113 find_grub_drive (const char *name
)
119 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
120 if (map
[i
].drive
&& ! strcmp (map
[i
].drive
, name
))
130 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
138 grub_util_biosdisk_iterate (int (*hook
) (const char *name
))
142 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
143 if (map
[i
].drive
&& hook (map
[i
].drive
))
150 grub_util_biosdisk_open (const char *name
, grub_disk_t disk
)
155 drive
= find_grub_drive (name
);
157 return grub_error (GRUB_ERR_BAD_DEVICE
,
158 "no mapping exists for `%s'", name
);
160 disk
->has_partitions
= 1;
164 #if defined(__linux__) || defined(__CYGWIN__)
166 unsigned long long nr
;
169 fd
= open (map
[drive
].device
, O_RDONLY
);
171 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s' while attempting to get disk size", map
[drive
].device
);
173 if (fstat (fd
, &st
) < 0 || ! S_ISBLK (st
.st_mode
))
179 if (ioctl (fd
, BLKGETSIZE64
, &nr
))
186 disk
->total_sectors
= nr
/ 512;
189 grub_util_error ("unaligned device size");
191 grub_util_info ("the size of %s is %llu", name
, disk
->total_sectors
);
193 return GRUB_ERR_NONE
;
197 /* In GNU/Hurd, stat() will return the right size. */
198 #elif !defined (__GNU__)
199 # warning "No special routine to get the size of a block device is implemented for your OS. This is not possibly fatal."
201 if (stat (map
[drive
].device
, &st
) < 0)
202 return grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", map
[drive
].device
);
204 disk
->total_sectors
= st
.st_size
>> GRUB_DISK_SECTOR_BITS
;
206 grub_util_info ("the size of %s is %lu", name
, disk
->total_sectors
);
208 return GRUB_ERR_NONE
;
213 linux_find_partition (char *dev
, unsigned long sector
)
215 size_t len
= strlen (dev
);
219 char real_dev
[PATH_MAX
];
221 strcpy(real_dev
, dev
);
223 if (have_devfs () && strcmp (real_dev
+ len
- 5, "/disc") == 0)
225 p
= real_dev
+ len
- 4;
228 else if (real_dev
[len
- 1] >= '0' && real_dev
[len
- 1] <= '9')
239 for (i
= 1; i
< 10000; i
++)
242 struct hd_geometry hdg
;
244 sprintf (p
, format
, i
);
245 fd
= open (real_dev
, O_RDONLY
);
249 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
257 if (hdg
.start
== sector
)
259 strcpy (dev
, real_dev
);
266 #endif /* __linux__ */
269 open_device (const grub_disk_t disk
, grub_disk_addr_t sector
, int flags
)
274 flags
|= O_LARGEFILE
;
284 /* Linux has a bug that the disk cache for a whole disk is not consistent
285 with the one for a partition of the disk. */
287 int is_partition
= 0;
290 strcpy (dev
, map
[disk
->id
].device
);
291 if (disk
->partition
&& strncmp (map
[disk
->id
].device
, "/dev/", 5) == 0)
292 is_partition
= linux_find_partition (dev
, disk
->partition
->start
);
294 /* Open the partition. */
295 grub_util_info ("opening the device `%s' in open_device()", dev
);
296 fd
= open (dev
, flags
);
299 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s'", dev
);
303 /* Make the buffer cache consistent with the physical disk. */
304 ioctl (fd
, BLKFLSBUF
, 0);
307 sector
-= disk
->partition
->start
;
309 #else /* ! __linux__ */
310 fd
= open (map
[disk
->id
].device
, flags
);
313 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s' in open_device()", map
[disk
->id
].device
);
316 #endif /* ! __linux__ */
318 #if defined(__linux__) && (!defined(__GLIBC__) || \
319 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
320 /* Maybe libc doesn't have large file support. */
322 loff_t offset
, result
;
323 static int _llseek (uint filedes
, ulong hi
, ulong lo
,
324 loff_t
*res
, uint wh
);
325 _syscall5 (int, _llseek
, uint
, filedes
, ulong
, hi
, ulong
, lo
,
326 loff_t
*, res
, uint
, wh
);
328 offset
= (loff_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
329 if (_llseek (fd
, offset
>> 32, offset
& 0xffffffff, &result
, SEEK_SET
))
331 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
].device
);
338 off_t offset
= (off_t
) sector
<< GRUB_DISK_SECTOR_BITS
;
340 if (lseek (fd
, offset
, SEEK_SET
) != offset
)
342 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot seek `%s'", map
[disk
->id
].device
);
352 /* Read LEN bytes from FD in BUF. Return less than or equal to zero if an
353 error occurs, otherwise return LEN. */
355 nread (int fd
, char *buf
, size_t len
)
361 ssize_t ret
= read (fd
, buf
, len
);
378 /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an
379 error occurs, otherwise return LEN. */
381 nwrite (int fd
, const char *buf
, size_t len
)
387 ssize_t ret
= write (fd
, buf
, len
);
405 grub_util_biosdisk_read (grub_disk_t disk
, grub_disk_addr_t sector
,
406 grub_size_t size
, char *buf
)
410 fd
= open_device (disk
, sector
, O_RDONLY
);
415 if (sector
== 0 && size
> 1)
417 /* Work around a bug in Linux ez remapping. Linux remaps all
418 sectors that are read together with the MBR in one read. It
419 should only remap the MBR, so we split the read in two
421 if (nread (fd
, buf
, GRUB_DISK_SECTOR_SIZE
) != GRUB_DISK_SECTOR_SIZE
)
423 grub_error (GRUB_ERR_READ_ERROR
, "cannot read `%s'", map
[disk
->id
].device
);
428 buf
+= GRUB_DISK_SECTOR_SIZE
;
431 #endif /* __linux__ */
433 if (nread (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
434 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
435 grub_error (GRUB_ERR_READ_ERROR
, "cannot read from `%s'", map
[disk
->id
].device
);
442 grub_util_biosdisk_write (grub_disk_t disk
, grub_disk_addr_t sector
,
443 grub_size_t size
, const char *buf
)
447 fd
= open_device (disk
, sector
, O_WRONLY
);
451 if (nwrite (fd
, buf
, size
<< GRUB_DISK_SECTOR_BITS
)
452 != (ssize_t
) (size
<< GRUB_DISK_SECTOR_BITS
))
453 grub_error (GRUB_ERR_WRITE_ERROR
, "cannot write to `%s'", map
[disk
->id
].device
);
459 static struct grub_disk_dev grub_util_biosdisk_dev
=
462 .id
= GRUB_DISK_DEVICE_BIOSDISK_ID
,
463 .iterate
= grub_util_biosdisk_iterate
,
464 .open
= grub_util_biosdisk_open
,
466 .read
= grub_util_biosdisk_read
,
467 .write
= grub_util_biosdisk_write
,
472 read_device_map (const char *dev_map
)
475 char buf
[1024]; /* XXX */
479 auto void show_error (const char *msg
);
480 void show_error (const char *msg
)
482 grub_util_error ("%s:%d: %s", dev_map
, lineno
, msg
);
485 fp
= fopen (dev_map
, "r");
487 grub_util_error ("Cannot open `%s'", dev_map
);
489 while (fgets (buf
, sizeof (buf
), fp
))
497 /* Skip leading spaces. */
498 while (*p
&& isspace (*p
))
501 /* If the first character is `#' or NUL, skip this line. */
502 if (*p
== '\0' || *p
== '#')
506 show_error ("No open parenthesis found");
509 /* Find a free slot. */
510 drive
= find_free_slot ();
512 show_error ("Map table size exceeded");
517 show_error ("No close parenthesis found");
519 map
[drive
].drive
= xmalloc (p
- e
+ sizeof ('\0'));
520 strncpy (map
[drive
].drive
, e
, p
- e
+ sizeof ('\0'));
521 map
[drive
].drive
[p
- e
] = '\0';
524 /* Skip leading spaces. */
525 while (*p
&& isspace (*p
))
529 show_error ("No filename found");
531 /* NUL-terminate the filename. */
533 while (*e
&& ! isspace (*e
))
537 if (stat (p
, &st
) == -1)
539 free (map
[drive
].drive
);
540 map
[drive
].drive
= NULL
;
541 grub_util_info ("Cannot stat `%s', skipping", p
);
546 /* On Linux, the devfs uses symbolic links horribly, and that
547 confuses the interface very much, so use realpath to expand
549 map
[drive
].device
= xmalloc (PATH_MAX
);
550 if (! realpath (p
, map
[drive
].device
))
551 grub_util_error ("Cannot get the real path of `%s'", p
);
553 map
[drive
].device
= xstrdup (p
);
561 grub_util_biosdisk_init (const char *dev_map
)
563 read_device_map (dev_map
);
564 grub_disk_dev_register (&grub_util_biosdisk_dev
);
568 grub_util_biosdisk_fini (void)
572 for (i
= 0; i
< sizeof (map
) / sizeof (map
[0]); i
++)
577 free (map
[i
].device
);
578 map
[i
].drive
= map
[i
].device
= NULL
;
581 grub_disk_dev_unregister (&grub_util_biosdisk_dev
);
585 make_device_name (int drive
, int dos_part
, int bsd_part
)
590 sprintf (p
, "%s", map
[drive
].drive
);
593 sprintf (p
+ strlen (p
), ",%d", dos_part
+ 1);
596 sprintf (p
+ strlen (p
), ",%c", bsd_part
+ 'a');
602 convert_system_partition_to_system_disk (const char *os_dev
)
604 #if defined(__linux__)
605 char *path
= xmalloc (PATH_MAX
);
606 if (! realpath (os_dev
, path
))
609 if (strncmp ("/dev/", path
, 5) == 0)
613 /* If this is an IDE disk. */
614 if (strncmp ("ide/", p
, 4) == 0)
616 p
= strstr (p
, "part");
623 /* If this is a SCSI disk. */
624 if (strncmp ("scsi/", p
, 5) == 0)
626 p
= strstr (p
, "part");
633 /* If this is a DAC960 disk. */
634 if (strncmp ("rd/c", p
, 4) == 0)
636 /* /dev/rd/c[0-9]+d[0-9]+(p[0-9]+)? */
644 /* If this is a CCISS disk. */
645 if (strncmp ("cciss/c", p
, sizeof ("cciss/c") - 1) == 0)
647 /* /dev/cciss/c[0-9]+d[0-9]+(p[0-9]+)? */
655 /* If this is a Compaq Intelligent Drive Array. */
656 if (strncmp ("ida/c", p
, sizeof ("ida/c") - 1) == 0)
658 /* /dev/ida/c[0-9]+d[0-9]+(p[0-9]+)? */
666 /* If this is an I2O disk. */
667 if (strncmp ("i2o/hd", p
, sizeof ("i2o/hd") - 1) == 0)
669 /* /dev/i2o/hd[a-z]([0-9]+)? */
670 p
[sizeof ("i2o/hda") - 1] = '\0';
674 /* If this is a MultiMediaCard (MMC). */
675 if (strncmp ("mmcblk", p
, sizeof ("mmcblk") - 1) == 0)
677 /* /dev/mmcblk[0-9]+(p[0-9]+)? */
685 /* If this is an IDE, SCSI or Virtio disk. */
686 if ((strncmp ("hd", p
, 2) == 0
687 || strncmp ("vd", p
, 2) == 0
688 || strncmp ("sd", p
, 2) == 0)
689 && p
[2] >= 'a' && p
[2] <= 'z')
691 /* /dev/[hsv]d[a-z][0-9]* */
696 /* If this is a Xen virtual block device. */
697 if ((strncmp ("xvd", p
, 3) == 0) && p
[3] >= 'a' && p
[3] <= 'z')
699 /* /dev/xvd[a-z][0-9]* */
707 #elif defined(__GNU__)
708 char *path
= xstrdup (os_dev
);
709 if (strncmp ("/dev/sd", path
, 7) == 0 || strncmp ("/dev/hd", path
, 7) == 0)
711 char *p
= strchr (path
+ 7, 's');
717 #elif defined(__CYGWIN__)
718 char *path
= xstrdup (os_dev
);
719 if (strncmp ("/dev/sd", path
, 7) == 0 && 'a' <= path
[7] && path
[7] <= 'z')
724 # warning "The function `convert_system_partition_to_system_disk' might not work on your OS correctly."
725 return xstrdup (os_dev
);
730 find_system_device (const char *os_dev
)
735 os_disk
= convert_system_partition_to_system_disk (os_dev
);
739 for (i
= 0; i
< (int) (sizeof (map
) / sizeof (map
[0])); i
++)
740 if (map
[i
].device
&& strcmp (map
[i
].device
, os_disk
) == 0)
751 grub_util_biosdisk_get_grub_dev (const char *os_dev
)
756 if (stat (os_dev
, &st
) < 0)
758 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot stat `%s'", os_dev
);
762 drive
= find_system_device (os_dev
);
765 grub_error (GRUB_ERR_BAD_DEVICE
,
766 "no mapping exists for `%s'", os_dev
);
770 if (! S_ISBLK (st
.st_mode
))
771 return make_device_name (drive
, -1, -1);
773 #if defined(__linux__) || defined(__CYGWIN__)
774 /* Linux counts partitions uniformly, whether a BSD partition or a DOS
775 partition, so mapping them to GRUB devices is not trivial.
776 Here, get the start sector of a partition by HDIO_GETGEO, and
777 compare it with each partition GRUB recognizes.
779 Cygwin /dev/sdXN emulation uses Windows partition mapping. It
780 does not count the extended partition and missing primary
781 partitions. Use same method as on Linux here. */
786 struct hd_geometry hdg
;
789 auto int find_partition (grub_disk_t disk
,
790 const grub_partition_t partition
);
792 int find_partition (grub_disk_t disk
__attribute__ ((unused
)),
793 const grub_partition_t partition
)
795 struct grub_pc_partition
*pcdata
= NULL
;
797 if (strcmp (partition
->partmap
->name
, "pc_partition_map") == 0)
798 pcdata
= partition
->data
;
802 if (pcdata
->bsd_part
< 0)
803 grub_util_info ("DOS partition %d starts from %lu",
804 pcdata
->dos_part
, partition
->start
);
806 grub_util_info ("BSD partition %d,%c starts from %lu",
807 pcdata
->dos_part
, pcdata
->bsd_part
+ 'a',
812 grub_util_info ("Partition %d starts from %lu",
813 partition
->index
, partition
->start
);
816 if (hdg
.start
== partition
->start
)
820 dos_part
= pcdata
->dos_part
;
821 bsd_part
= pcdata
->bsd_part
;
825 dos_part
= partition
->index
;
834 name
= make_device_name (drive
, -1, -1);
836 if (MAJOR (st
.st_rdev
) == FLOPPY_MAJOR
)
839 fd
= open (os_dev
, O_RDONLY
);
842 grub_error (GRUB_ERR_BAD_DEVICE
, "cannot open `%s' while attempting to get disk geometry", os_dev
);
847 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
849 grub_error (GRUB_ERR_BAD_DEVICE
,
850 "cannot get geometry of `%s'", os_dev
);
858 grub_util_info ("%s starts from %lu", os_dev
, hdg
.start
);
863 grub_util_info ("opening the device %s", name
);
864 disk
= grub_disk_open (name
);
870 grub_partition_iterate (disk
, find_partition
);
871 if (grub_errno
!= GRUB_ERR_NONE
)
873 grub_disk_close (disk
);
879 grub_disk_close (disk
);
880 grub_error (GRUB_ERR_BAD_DEVICE
,
881 "cannot find the partition of `%s'", os_dev
);
885 return make_device_name (drive
, dos_part
, bsd_part
);
888 #elif defined(__GNU__)
889 /* GNU uses "/dev/[hs]d[0-9]+(s[0-9]+[a-z]?)?". */
895 p
= strrchr (os_dev
, 's');
902 n
= strtol (p
, &q
, 10);
903 if (p
!= q
&& n
!= LONG_MIN
&& n
!= LONG_MAX
)
907 if (*q
>= 'a' && *q
<= 'g')
912 return make_device_name (drive
, dos_part
, bsd_part
);
916 # warning "The function `grub_util_biosdisk_get_grub_dev' might not work on your OS correctly."
917 return make_device_name (drive
, -1, -1);