1 /* device.c - Some helper functions for OS devices and BIOS drives */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 1999,2000,2001,2002,2003,2004,2005 Free Software Foundation, Inc.
6 * This program 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 /* Try to use glibc's transparant LFS support. */
22 #define _LARGEFILE_SOURCE 1
23 /* lseek becomes synonymous with lseek64. */
24 #define _FILE_OFFSET_BITS 64
32 #include <sys/types.h>
40 # if !defined(__GLIBC__) || \
41 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1)))
42 /* Maybe libc doesn't have large file support. */
43 # include <linux/unistd.h> /* _llseek */
44 # endif /* (GLIBC < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR < 1)) */
45 # include <sys/ioctl.h> /* ioctl */
47 # define HDIO_GETGEO 0x0301 /* get device geometry */
48 /* If HDIO_GETGEO is not defined, it is unlikely that hd_geometry is
53 unsigned char sectors
;
54 unsigned short cylinders
;
57 # endif /* ! HDIO_GETGEO */
59 # define FLOPPY_MAJOR 2 /* the major number for floppy */
60 # endif /* ! FLOPPY_MAJOR */
64 unsigned long long __dev = (dev); \
65 (unsigned) ((__dev >> 8) & 0xfff) \
66 | ((unsigned int) (__dev >> 32) & ~0xfff); \
69 # ifndef CDROM_GET_CAPABILITY
70 # define CDROM_GET_CAPABILITY 0x5331 /* get capabilities */
71 # endif /* ! CDROM_GET_CAPABILITY */
73 # define BLKGETSIZE _IO(0x12,96) /* return device size */
74 # endif /* ! BLKGETSIZE */
75 #endif /* __linux__ */
77 /* Use __FreeBSD_kernel__ instead of __FreeBSD__ for compatibility with
78 kFreeBSD-based non-FreeBSD systems (e.g. GNU/kFreeBSD) */
79 #if defined(__FreeBSD__) && ! defined(__FreeBSD_kernel__)
80 # define __FreeBSD_kernel__
82 #ifdef __FreeBSD_kernel__
83 /* Obtain version of kFreeBSD headers */
84 # include <osreldate.h>
85 # ifndef __FreeBSD_kernel_version
86 # define __FreeBSD_kernel_version __FreeBSD_version
89 /* Runtime detection of kernel */
90 # include <sys/utsname.h>
92 get_kfreebsd_version ()
95 int major
; int minor
, v
[2];
98 sscanf (uts
.release
, "%d.%d", &major
, &minor
);
104 v
[0] = minor
/10; v
[1] = minor
%10;
108 v
[0] = minor
%10; v
[1] = minor
/10;
110 return major
*100000+v
[0]*10000+v
[1]*1000;
112 #endif /* __FreeBSD_kernel__ */
114 #if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
115 # include <sys/ioctl.h> /* ioctl */
116 # include <sys/disklabel.h>
117 # include <sys/cdio.h> /* CDIOCCLRDEBUG */
118 # if defined(__FreeBSD_kernel__)
119 # include <sys/param.h>
120 # if __FreeBSD_kernel_version >= 500040
121 # include <sys/disk.h>
123 # endif /* __FreeBSD_kernel__ */
124 #endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */
128 #endif /* HAVE_OPENDISK */
130 #define WITHOUT_LIBC_STUBS 1
134 /* Get the geometry of a drive DRIVE. */
136 get_drive_geometry (struct geometry
*geom
, char **map
, int drive
)
140 if (geom
->flags
== -1)
142 fd
= open (map
[drive
], O_RDONLY
);
148 /* XXX This is the default size. */
149 geom
->sector_size
= SECTOR_SIZE
;
151 #if defined(__linux__)
154 struct hd_geometry hdg
;
157 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
160 if (ioctl (fd
, BLKGETSIZE
, &nr
))
163 /* Got the geometry, so save it. */
164 geom
->cylinders
= hdg
.cylinders
;
165 geom
->heads
= hdg
.heads
;
166 geom
->sectors
= hdg
.sectors
;
167 geom
->total_sectors
= nr
;
172 #elif defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
173 # if defined(__FreeBSD_kernel__) && __FreeBSD_kernel_version >= 500040
174 /* kFreeBSD version 5 or later */
175 if (get_kfreebsd_version () >= 500040)
177 unsigned int sector_size
;
181 if(ioctl (fd
, DIOCGSECTORSIZE
, §or_size
) != 0)
184 if (ioctl (fd
, DIOCGMEDIASIZE
, &media_size
) != 0)
187 geom
->total_sectors
= media_size
/ sector_size
;
189 if (ioctl (fd
, DIOCGFWSECTORS
, &tmp
) == 0)
193 if (ioctl (fd
, DIOCGFWHEADS
, &tmp
) == 0)
195 else if (geom
->total_sectors
<= 63 * 1 * 1024)
197 else if (geom
->total_sectors
<= 63 * 16 * 1024)
202 geom
->cylinders
= (geom
->total_sectors
209 #endif /* defined(__FreeBSD_kernel__) && __FreeBSD_kernel_version >= 500040 */
211 /* kFreeBSD < 5, NetBSD or OpenBSD */
213 struct disklabel hdg
;
214 if (ioctl (fd
, DIOCGDINFO
, &hdg
))
217 geom
->cylinders
= hdg
.d_ncylinders
;
218 geom
->heads
= hdg
.d_ntracks
;
219 geom
->sectors
= hdg
.d_nsectors
;
220 geom
->total_sectors
= hdg
.d_secperunit
;
226 /* Notably, defined(__GNU__) */
227 # warning "Automatic detection of geometries will be performed only \
228 partially. This is not fatal."
235 /* FIXME: It would be nice to somehow compute fake C/H/S settings,
236 given a proper st_blocks size. */
239 geom
->cylinders
= DEFAULT_HD_CYLINDERS
;
240 geom
->heads
= DEFAULT_HD_HEADS
;
241 geom
->sectors
= DEFAULT_HD_SECTORS
;
245 geom
->cylinders
= DEFAULT_FD_CYLINDERS
;
246 geom
->heads
= DEFAULT_FD_HEADS
;
247 geom
->sectors
= DEFAULT_FD_SECTORS
;
250 /* Set the total sectors properly, if we can. */
251 if (! fstat (fd
, &st
) && st
.st_size
)
252 geom
->total_sectors
= st
.st_size
>> SECTOR_BITS
;
254 geom
->total_sectors
= geom
->cylinders
* geom
->heads
* geom
->sectors
;
258 if (geom
->flags
== -1)
263 /* Check if we have devfs support. */
267 static int dev_devfsd_exists
= -1;
269 if (dev_devfsd_exists
< 0)
273 dev_devfsd_exists
= stat ("/dev/.devfsd", &st
) == 0;
276 return dev_devfsd_exists
;
278 #endif /* __linux__ */
280 /* These three functions are quite different among OSes. */
282 get_floppy_disk_name (char *name
, int unit
)
284 #if defined(__linux__)
287 sprintf (name
, "/dev/floppy/%d", unit
);
289 sprintf (name
, "/dev/fd%d", unit
);
290 #elif defined(__GNU__)
292 sprintf (name
, "/dev/fd%d", unit
);
293 #elif defined(__FreeBSD_kernel__)
295 if (get_kfreebsd_version () >= 400000)
296 sprintf (name
, "/dev/fd%d", unit
);
298 sprintf (name
, "/dev/rfd%d", unit
);
299 #elif defined(__NetBSD__)
301 /* opendisk() doesn't work for floppies. */
302 sprintf (name
, "/dev/rfd%da", unit
);
303 #elif defined(__OpenBSD__)
305 sprintf (name
, "/dev/rfd%dc", unit
);
306 #elif defined(__QNXNTO__)
308 sprintf (name
, "/dev/fd%d", unit
);
310 # warning "BIOS floppy drives cannot be guessed in your operating system."
311 /* Set NAME to a bogus string. */
317 get_ide_disk_name (char *name
, int unit
)
319 #if defined(__linux__)
321 sprintf (name
, "/dev/hd%c", unit
+ 'a');
322 #elif defined(__GNU__)
324 sprintf (name
, "/dev/hd%d", unit
);
325 #elif defined(__FreeBSD_kernel__)
327 if (get_kfreebsd_version () >= 400000)
328 sprintf (name
, "/dev/ad%d", unit
);
330 sprintf (name
, "/dev/rwd%d", unit
);
331 #elif defined(__NetBSD__) && defined(HAVE_OPENDISK)
336 sprintf (shortname
, "wd%d", unit
);
337 fd
= opendisk (shortname
, O_RDONLY
, name
,
338 16, /* length of NAME */
342 #elif defined(__OpenBSD__)
344 sprintf (name
, "/dev/rwd%dc", unit
);
345 #elif defined(__QNXNTO__)
347 /* Actually, QNX RTP doesn't distinguish IDE from SCSI, so this could
348 contain SCSI disks. */
349 sprintf (name
, "/dev/hd%d", unit
);
351 # warning "BIOS IDE drives cannot be guessed in your operating system."
352 /* Set NAME to a bogus string. */
358 get_scsi_disk_name (char *name
, int unit
)
360 #if defined(__linux__)
362 sprintf (name
, "/dev/sd%c", unit
+ 'a');
363 #elif defined(__GNU__)
365 sprintf (name
, "/dev/sd%d", unit
);
366 #elif defined(__FreeBSD_kernel__)
368 if (get_kfreebsd_version () >= 400000)
369 sprintf (name
, "/dev/da%d", unit
);
371 sprintf (name
, "/dev/rda%d", unit
);
372 #elif defined(__NetBSD__) && defined(HAVE_OPENDISK)
377 sprintf (shortname
, "sd%d", unit
);
378 fd
= opendisk (shortname
, O_RDONLY
, name
,
379 16, /* length of NAME */
383 #elif defined(__OpenBSD__)
385 sprintf (name
, "/dev/rsd%dc", unit
);
386 #elif defined(__QNXNTO__)
388 /* QNX RTP doesn't distinguish SCSI from IDE, so it is better to
389 disable the detection of SCSI disks here. */
392 # warning "BIOS SCSI drives cannot be guessed in your operating system."
393 /* Set NAME to a bogus string. */
400 get_dac960_disk_name (char *name
, int controller
, int drive
)
402 sprintf (name
, "/dev/rd/c%dd%d", controller
, drive
);
406 get_ataraid_disk_name (char *name
, int unit
)
408 sprintf (name
, "/dev/ataraid/d%c", unit
+ '0');
412 /* Check if DEVICE can be read. If an error occurs, return zero,
413 otherwise return non-zero. */
415 check_device (const char *device
)
420 /* If DEVICE is empty, just return 1. */
424 fp
= fopen (device
, "r");
432 /* At the moment, this finds only CDROMs, which can't be
433 read anyway, so leave it out. Code should be
434 reactivated if `removable disks' and CDROMs are
436 /* Accept it, it may be inserted. */
440 #endif /* ENOMEDIUM */
442 /* Break case and leave. */
445 /* Error opening the device. */
449 /* Make sure CD-ROMs don't get assigned a BIOS disk number
450 before SCSI disks! */
452 # ifdef CDROM_GET_CAPABILITY
453 if (ioctl (fileno (fp
), CDROM_GET_CAPABILITY
, 0) >= 0)
455 # else /* ! CDROM_GET_CAPABILITY */
456 /* Check if DEVICE is a CD-ROM drive by the HDIO_GETGEO ioctl. */
458 struct hd_geometry hdg
;
461 if (fstat (fileno (fp
), &st
))
464 /* If it is a block device and isn't a floppy, check if HDIO_GETGEO
466 if (S_ISBLK (st
.st_mode
)
467 && MAJOR (st
.st_rdev
) != FLOPPY_MAJOR
468 && ioctl (fileno (fp
), HDIO_GETGEO
, &hdg
))
471 # endif /* ! CDROM_GET_CAPABILITY */
472 #endif /* __linux__ */
474 #if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
475 # ifdef CDIOCCLRDEBUG
476 if (ioctl (fileno (fp
), CDIOCCLRDEBUG
, 0) >= 0)
478 # endif /* CDIOCCLRDEBUG */
479 #endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */
481 /* Attempt to read the first sector. */
482 if (fread (buf
, 1, 512, fp
) != 512)
492 /* Read mapping information from FP, and write it to MAP. */
494 read_device_map (FILE *fp
, char **map
, const char *map_file
)
496 auto void show_error (int no
, const char *msg
);
497 auto void show_warning (int no
, const char *msg
, ...);
499 auto void show_error (int no
, const char *msg
)
501 fprintf (stderr
, "%s:%d: error: %s\n", map_file
, no
, msg
);
504 auto void show_warning (int no
, const char *msg
, ...)
509 fprintf (stderr
, "%s:%d: warning: ", map_file
, no
);
510 vfprintf (stderr
, msg
, ap
);
514 /* If there is the device map file, use the data in it instead of
516 char buf
[1024]; /* XXX */
519 while (fgets (buf
, sizeof (buf
), fp
))
525 /* Increase the number of lines. */
528 /* If the first character is '#', skip it. */
533 /* Skip leading spaces. */
534 while (*ptr
&& isspace (*ptr
))
537 /* Skip empty lines. */
543 show_error (line_number
, "No open parenthesis found");
548 if ((*ptr
!= 'f' && *ptr
!= 'h') || *(ptr
+ 1) != 'd')
550 show_error (line_number
, "Bad drive name");
558 drive
= strtoul (ptr
, &ptr
, 10);
561 show_error (line_number
, "Bad device number");
564 else if (drive
> 127)
566 show_warning (line_number
,
567 "Ignoring %cd%d due to a BIOS limitation",
568 is_floppy
? 'f' : 'h', drive
);
577 show_error (line_number
, "No close parenthesis found");
583 while (*ptr
&& isspace (*ptr
))
588 show_error (line_number
, "No filename found");
592 /* Terminate the filename. */
594 while (*eptr
&& ! isspace (*eptr
))
598 /* Multiple entries for a given drive is not allowed. */
601 show_error (line_number
, "Duplicated entry found");
605 map
[drive
] = strdup (ptr
);
612 /* Initialize the device map MAP. *MAP will be allocated from the heap
613 space. If MAP_FILE is not NULL, then read mappings from the file
614 MAP_FILE if it exists, otherwise, write guessed mappings to the file.
615 FLOPPY_DISKS is the number of floppy disk drives which will be probed.
616 If it is zero, don't probe any floppy at all. If it is one, probe one
617 floppy. If it is two, probe two floppies. And so on. */
619 init_device_map (char ***map
, const char *map_file
, int floppy_disks
)
627 *map
= malloc (NUM_DISKS
* sizeof (char *));
630 /* Probe devices for creating the device map. */
632 /* Initialize DEVICE_MAP. */
633 for (i
= 0; i
< NUM_DISKS
; i
++)
638 /* Open the device map file. */
639 fp
= fopen (map_file
, "r");
644 ret
= read_device_map (fp
, *map
, map_file
);
650 /* Print something so that the user does not think GRUB has been
653 "Probing devices to guess BIOS drives. "
654 "This may take a long time.\n");
657 /* Try to open the device map file to write the probed data. */
658 fp
= fopen (map_file
, "w");
661 for (i
= 0; i
< floppy_disks
; i
++)
665 get_floppy_disk_name (name
, i
);
666 /* In floppies, write the map, whether check_device succeeds
667 or not, because the user just does not insert floppies. */
669 fprintf (fp
, "(fd%d)\t%s\n", i
, name
);
671 if (check_device (name
))
673 (*map
)[i
] = strdup (name
);
687 /* Linux creates symlinks "/dev/discs/discN" for convenience.
688 The way to number disks is the same as GRUB's. */
689 sprintf (discn
, "/dev/discs/disc%d", num_hd
);
690 if (stat (discn
, &st
) < 0)
693 if (realpath (discn
, name
))
695 strcat (name
, "/disc");
696 (*map
)[num_hd
+ 0x80] = strdup (name
);
697 assert ((*map
)[num_hd
+ 0x80]);
699 /* If the device map file is opened, write the map. */
701 fprintf (fp
, "(hd%d)\t%s\n", num_hd
, name
);
707 /* OK, close the device map file if opened. */
713 #endif /* __linux__ */
716 for (i
= 0; i
< 8; i
++)
720 get_ide_disk_name (name
, i
);
721 if (check_device (name
))
723 (*map
)[num_hd
+ 0x80] = strdup (name
);
724 assert ((*map
)[num_hd
+ 0x80]);
726 /* If the device map file is opened, write the map. */
728 fprintf (fp
, "(hd%d)\t%s\n", num_hd
, name
);
736 for (i
= 0; i
< 8; i
++)
740 get_ataraid_disk_name (name
, i
);
741 if (check_device (name
))
743 (*map
)[num_hd
+ 0x80] = strdup (name
);
744 assert ((*map
)[num_hd
+ 0x80]);
746 /* If the device map file is opened, write the map. */
748 fprintf (fp
, "(hd%d)\t%s\n", num_hd
, name
);
753 #endif /* __linux__ */
755 /* The rest is SCSI disks. */
756 for (i
= 0; i
< 16; i
++)
760 get_scsi_disk_name (name
, i
);
761 if (check_device (name
))
763 (*map
)[num_hd
+ 0x80] = strdup (name
);
764 assert ((*map
)[num_hd
+ 0x80]);
766 /* If the device map file is opened, write the map. */
768 fprintf (fp
, "(hd%d)\t%s\n", num_hd
, name
);
775 /* This is for DAC960 - we have
776 /dev/rd/c<controller>d<logical drive>p<partition>.
778 DAC960 driver currently supports up to 8 controllers, 32 logical
779 drives, and 7 partitions. */
781 int controller
, drive
;
783 for (controller
= 0; controller
< 8; controller
++)
785 for (drive
= 0; drive
< 15; drive
++)
789 get_dac960_disk_name (name
, controller
, drive
);
790 if (check_device (name
))
792 (*map
)[num_hd
+ 0x80] = strdup (name
);
793 assert ((*map
)[num_hd
+ 0x80]);
795 /* If the device map file is opened, write the map. */
797 fprintf (fp
, "(hd%d)\t%s\n", num_hd
, name
);
804 #endif /* __linux__ */
806 /* OK, close the device map file if opened. */
813 /* Restore the memory consumed for MAP. */
815 restore_device_map (char **map
)
819 for (i
= 0; i
< NUM_DISKS
; i
++)
827 /* Linux-only functions, because Linux has a bug that the disk cache for
828 a whole disk is not consistent with the one for a partition of the
831 is_disk_device (char **map
, int drive
)
835 assert (map
[drive
] != 0);
836 assert (stat (map
[drive
], &st
) == 0);
837 /* For now, disk devices under Linux are all block devices. */
838 return S_ISBLK (st
.st_mode
);
842 write_to_partition (char **map
, int drive
, int partition
,
843 int sector
, int size
, const char *buf
)
845 char dev
[PATH_MAX
]; /* XXX */
848 if ((partition
& 0x00FF00) != 0x00FF00)
850 /* If the partition is a BSD partition, it is difficult to
851 obtain the representation in Linux. So don't support that. */
852 errnum
= ERR_DEV_VALUES
;
856 assert (map
[drive
] != 0);
858 strcpy (dev
, map
[drive
]);
861 if (strcmp (dev
+ strlen(dev
) - 5, "/disc") == 0)
862 strcpy (dev
+ strlen(dev
) - 5, "/part");
864 sprintf (dev
+ strlen(dev
), "%d", ((partition
>> 16) & 0xFF) + 1);
866 /* Open the partition. */
867 fd
= open (dev
, O_RDWR
);
870 errnum
= ERR_NO_PART
;
874 #if defined(__linux__) && (!defined(__GLIBC__) || \
875 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
876 /* Maybe libc doesn't have large file support. */
878 loff_t offset
, result
;
879 static int _llseek (uint filedes
, ulong hi
, ulong lo
,
880 loff_t
*res
, uint wh
);
881 _syscall5 (int, _llseek
, uint
, filedes
, ulong
, hi
, ulong
, lo
,
882 loff_t
*, res
, uint
, wh
);
884 offset
= (loff_t
) sector
* (loff_t
) SECTOR_SIZE
;
885 if (_llseek (fd
, offset
>> 32, offset
& 0xffffffff, &result
, SEEK_SET
))
887 errnum
= ERR_DEV_VALUES
;
893 off_t offset
= (off_t
) sector
* (off_t
) SECTOR_SIZE
;
895 if (lseek (fd
, offset
, SEEK_SET
) != offset
)
897 errnum
= ERR_DEV_VALUES
;
903 if (write (fd
, buf
, size
* SECTOR_SIZE
) != (size
* SECTOR_SIZE
))
910 sync (); /* Paranoia. */
915 #endif /* __linux__ */