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__ */
127 # include <sys/dkio.h>
132 #endif /* HAVE_OPENDISK */
134 #define WITHOUT_LIBC_STUBS 1
138 /* Get the geometry of a drive DRIVE. */
140 get_drive_geometry (struct geometry
*geom
, char **map
, int drive
)
144 if (geom
->flags
== -1)
146 fd
= open (map
[drive
], O_RDONLY
);
152 /* XXX This is the default size. */
153 geom
->sector_size
= SECTOR_SIZE
;
155 #if defined(__linux__)
158 struct hd_geometry hdg
;
161 if (ioctl (fd
, HDIO_GETGEO
, &hdg
))
164 if (ioctl (fd
, BLKGETSIZE
, &nr
))
167 /* Got the geometry, so save it. */
168 geom
->cylinders
= hdg
.cylinders
;
169 geom
->heads
= hdg
.heads
;
170 geom
->sectors
= hdg
.sectors
;
171 geom
->total_sectors
= nr
;
176 #elif defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
177 # if defined(__FreeBSD_kernel__) && __FreeBSD_kernel_version >= 500040
178 /* kFreeBSD version 5 or later */
179 if (get_kfreebsd_version () >= 500040)
181 unsigned int sector_size
;
185 if(ioctl (fd
, DIOCGSECTORSIZE
, §or_size
) != 0)
188 if (ioctl (fd
, DIOCGMEDIASIZE
, &media_size
) != 0)
191 geom
->total_sectors
= media_size
/ sector_size
;
193 if (ioctl (fd
, DIOCGFWSECTORS
, &tmp
) == 0)
197 if (ioctl (fd
, DIOCGFWHEADS
, &tmp
) == 0)
199 else if (geom
->total_sectors
<= 63 * 1 * 1024)
201 else if (geom
->total_sectors
<= 63 * 16 * 1024)
206 geom
->cylinders
= (geom
->total_sectors
213 #endif /* defined(__FreeBSD_kernel__) && __FreeBSD_kernel_version >= 500040 */
215 /* kFreeBSD < 5, NetBSD or OpenBSD */
217 struct disklabel hdg
;
218 if (ioctl (fd
, DIOCGDINFO
, &hdg
))
221 geom
->cylinders
= hdg
.d_ncylinders
;
222 geom
->heads
= hdg
.d_ntracks
;
223 geom
->sectors
= hdg
.d_nsectors
;
224 geom
->total_sectors
= hdg
.d_secperunit
;
234 if (ioctl(fd
, DKIOCG_PHYGEOM
, &dkg
))
236 geom
->cylinders
= dkg
.dkg_ncyl
;
237 geom
->heads
= dkg
.dkg_nhead
;
238 geom
->sectors
= dkg
.dkg_nsect
;
239 geom
->total_sectors
= (unsigned long long)dkg
.dkg_ncyl
* dkg
.dkg_nhead
246 /* Notably, defined(__GNU__) */
247 # warning "Automatic detection of geometries will be performed only \
248 partially. This is not fatal."
255 /* FIXME: It would be nice to somehow compute fake C/H/S settings,
256 given a proper st_blocks size. */
259 geom
->cylinders
= DEFAULT_HD_CYLINDERS
;
260 geom
->heads
= DEFAULT_HD_HEADS
;
261 geom
->sectors
= DEFAULT_HD_SECTORS
;
265 geom
->cylinders
= DEFAULT_FD_CYLINDERS
;
266 geom
->heads
= DEFAULT_FD_HEADS
;
267 geom
->sectors
= DEFAULT_FD_SECTORS
;
270 /* Set the total sectors properly, if we can. */
271 if (! fstat (fd
, &st
) && st
.st_blocks
)
272 geom
->total_sectors
= st
.st_blocks
>> SECTOR_BITS
;
274 geom
->total_sectors
= (unsigned long long)geom
->cylinders
*
275 geom
->heads
* geom
->sectors
;
279 if (geom
->flags
== -1)
284 /* Check if we have devfs support. */
288 static int dev_devfsd_exists
= -1;
290 if (dev_devfsd_exists
< 0)
294 dev_devfsd_exists
= stat ("/dev/.devfsd", &st
) == 0;
297 return dev_devfsd_exists
;
299 #endif /* __linux__ */
301 /* These three functions are quite different among OSes. */
303 get_floppy_disk_name (char *name
, int unit
)
305 #if defined(__linux__)
308 sprintf (name
, "/dev/floppy/%d", unit
);
310 sprintf (name
, "/dev/fd%d", unit
);
311 #elif defined(__GNU__)
313 sprintf (name
, "/dev/fd%d", unit
);
314 #elif defined(__FreeBSD_kernel__)
316 if (get_kfreebsd_version () >= 400000)
317 sprintf (name
, "/dev/fd%d", unit
);
319 sprintf (name
, "/dev/rfd%d", unit
);
320 #elif defined(__NetBSD__)
322 /* opendisk() doesn't work for floppies. */
323 sprintf (name
, "/dev/rfd%da", unit
);
324 #elif defined(__OpenBSD__)
326 sprintf (name
, "/dev/rfd%dc", unit
);
327 #elif defined(__QNXNTO__)
329 sprintf (name
, "/dev/fd%d", unit
);
332 sprintf (name
, "/dev/rdiskette%d", unit
);
334 # warning "BIOS floppy drives cannot be guessed in your operating system."
335 /* Set NAME to a bogus string. */
341 get_ide_disk_name (char *name
, int unit
)
343 #if defined(__linux__)
345 sprintf (name
, "/dev/hd%c", unit
+ 'a');
346 #elif defined(__GNU__)
348 sprintf (name
, "/dev/hd%d", unit
);
349 #elif defined(__FreeBSD_kernel__)
351 if (get_kfreebsd_version () >= 400000)
352 sprintf (name
, "/dev/ad%d", unit
);
354 sprintf (name
, "/dev/rwd%d", unit
);
355 #elif defined(__NetBSD__) && defined(HAVE_OPENDISK)
360 sprintf (shortname
, "wd%d", unit
);
361 fd
= opendisk (shortname
, O_RDONLY
, name
,
362 16, /* length of NAME */
366 #elif defined(__OpenBSD__)
368 sprintf (name
, "/dev/rwd%dc", unit
);
369 #elif defined(__QNXNTO__)
371 /* Actually, QNX RTP doesn't distinguish IDE from SCSI, so this could
372 contain SCSI disks. */
373 sprintf (name
, "/dev/hd%d", unit
);
375 *name
= 0; /* FIXME */
377 # warning "BIOS IDE drives cannot be guessed in your operating system."
378 /* Set NAME to a bogus string. */
384 get_scsi_disk_name (char *name
, int unit
)
386 #if defined(__linux__)
388 sprintf (name
, "/dev/sd%c", unit
+ 'a');
389 #elif defined(__GNU__)
391 sprintf (name
, "/dev/sd%d", unit
);
392 #elif defined(__FreeBSD_kernel__)
394 if (get_kfreebsd_version () >= 400000)
395 sprintf (name
, "/dev/da%d", unit
);
397 sprintf (name
, "/dev/rda%d", unit
);
398 #elif defined(__NetBSD__) && defined(HAVE_OPENDISK)
403 sprintf (shortname
, "sd%d", unit
);
404 fd
= opendisk (shortname
, O_RDONLY
, name
,
405 16, /* length of NAME */
409 #elif defined(__OpenBSD__)
411 sprintf (name
, "/dev/rsd%dc", unit
);
412 #elif defined(__QNXNTO__)
414 /* QNX RTP doesn't distinguish SCSI from IDE, so it is better to
415 disable the detection of SCSI disks here. */
418 *name
= 0; /* FIXME */
420 # warning "BIOS SCSI drives cannot be guessed in your operating system."
421 /* Set NAME to a bogus string. */
428 get_dac960_disk_name (char *name
, int controller
, int drive
)
430 sprintf (name
, "/dev/rd/c%dd%d", controller
, drive
);
434 get_ataraid_disk_name (char *name
, int unit
)
436 sprintf (name
, "/dev/ataraid/d%c", unit
+ '0');
440 /* Check if DEVICE can be read. If an error occurs, return zero,
441 otherwise return non-zero. */
443 check_device (const char *device
)
448 /* If DEVICE is empty, just return 1. */
452 fp
= fopen (device
, "r");
460 /* At the moment, this finds only CDROMs, which can't be
461 read anyway, so leave it out. Code should be
462 reactivated if `removable disks' and CDROMs are
464 /* Accept it, it may be inserted. */
468 #endif /* ENOMEDIUM */
470 /* Break case and leave. */
473 /* Error opening the device. */
477 /* Make sure CD-ROMs don't get assigned a BIOS disk number
478 before SCSI disks! */
480 # ifdef CDROM_GET_CAPABILITY
481 if (ioctl (fileno (fp
), CDROM_GET_CAPABILITY
, 0) >= 0)
483 # else /* ! CDROM_GET_CAPABILITY */
484 /* Check if DEVICE is a CD-ROM drive by the HDIO_GETGEO ioctl. */
486 struct hd_geometry hdg
;
489 if (fstat (fileno (fp
), &st
))
492 /* If it is a block device and isn't a floppy, check if HDIO_GETGEO
494 if (S_ISBLK (st
.st_mode
)
495 && MAJOR (st
.st_rdev
) != FLOPPY_MAJOR
496 && ioctl (fileno (fp
), HDIO_GETGEO
, &hdg
))
499 # endif /* ! CDROM_GET_CAPABILITY */
500 #endif /* __linux__ */
502 #if defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
503 # ifdef CDIOCCLRDEBUG
504 if (ioctl (fileno (fp
), CDIOCCLRDEBUG
, 0) >= 0)
506 # endif /* CDIOCCLRDEBUG */
507 #endif /* __FreeBSD_kernel__ || __NetBSD__ || __OpenBSD__ */
509 /* Attempt to read the first sector. */
510 if (fread (buf
, 1, 512, fp
) != 512)
520 /* Read mapping information from FP, and write it to MAP. */
522 read_device_map (FILE *fp
, char **map
, const char *map_file
)
524 auto void show_error (int no
, const char *msg
);
525 auto void show_warning (int no
, const char *msg
, ...);
527 auto void show_error (int no
, const char *msg
)
529 fprintf (stderr
, "%s:%d: error: %s\n", map_file
, no
, msg
);
532 auto void show_warning (int no
, const char *msg
, ...)
537 fprintf (stderr
, "%s:%d: warning: ", map_file
, no
);
538 vfprintf (stderr
, msg
, ap
);
542 /* If there is the device map file, use the data in it instead of
544 char buf
[1024]; /* XXX */
547 while (fgets (buf
, sizeof (buf
), fp
))
553 /* Increase the number of lines. */
556 /* If the first character is '#', skip it. */
561 /* Skip leading spaces. */
562 while (*ptr
&& isspace (*ptr
))
565 /* Skip empty lines. */
571 show_error (line_number
, "No open parenthesis found");
576 if ((*ptr
!= 'f' && *ptr
!= 'h') || *(ptr
+ 1) != 'd')
578 show_error (line_number
, "Bad drive name");
586 drive
= strtoul (ptr
, &ptr
, 10);
589 show_error (line_number
, "Bad device number");
592 else if (drive
> 127)
594 show_warning (line_number
,
595 "Ignoring %cd%d due to a BIOS limitation",
596 is_floppy
? 'f' : 'h', drive
);
605 show_error (line_number
, "No close parenthesis found");
611 while (*ptr
&& isspace (*ptr
))
616 show_error (line_number
, "No filename found");
620 /* Terminate the filename. */
622 while (*eptr
&& ! isspace (*eptr
))
626 /* Multiple entries for a given drive is not allowed. */
629 show_error (line_number
, "Duplicated entry found");
633 map
[drive
] = strdup (ptr
);
640 /* Initialize the device map MAP. *MAP will be allocated from the heap
641 space. If MAP_FILE is not NULL, then read mappings from the file
642 MAP_FILE if it exists, otherwise, write guessed mappings to the file.
643 FLOPPY_DISKS is the number of floppy disk drives which will be probed.
644 If it is zero, don't probe any floppy at all. If it is one, probe one
645 floppy. If it is two, probe two floppies. And so on. */
647 init_device_map (char ***map
, const char *map_file
, int floppy_disks
)
655 *map
= malloc (NUM_DISKS
* sizeof (char *));
658 /* Probe devices for creating the device map. */
660 /* Initialize DEVICE_MAP. */
661 for (i
= 0; i
< NUM_DISKS
; i
++)
666 /* Open the device map file. */
667 fp
= fopen (map_file
, "r");
672 ret
= read_device_map (fp
, *map
, map_file
);
678 /* Print something so that the user does not think GRUB has been
681 "Probing devices to guess BIOS drives. "
682 "This may take a long time.\n");
685 /* Try to open the device map file to write the probed data. */
686 fp
= fopen (map_file
, "w");
689 for (i
= 0; i
< floppy_disks
; i
++)
693 get_floppy_disk_name (name
, i
);
694 /* In floppies, write the map, whether check_device succeeds
695 or not, because the user just does not insert floppies. */
697 fprintf (fp
, "(fd%d)\t%s\n", i
, name
);
699 if (check_device (name
))
701 (*map
)[i
] = strdup (name
);
715 /* Linux creates symlinks "/dev/discs/discN" for convenience.
716 The way to number disks is the same as GRUB's. */
717 sprintf (discn
, "/dev/discs/disc%d", num_hd
);
718 if (stat (discn
, &st
) < 0)
721 if (realpath (discn
, name
))
723 strcat (name
, "/disc");
724 (*map
)[num_hd
+ 0x80] = strdup (name
);
725 assert ((*map
)[num_hd
+ 0x80]);
727 /* If the device map file is opened, write the map. */
729 fprintf (fp
, "(hd%d)\t%s\n", num_hd
, name
);
735 /* OK, close the device map file if opened. */
741 #endif /* __linux__ */
744 for (i
= 0; i
< 8; i
++)
748 get_ide_disk_name (name
, i
);
749 if (check_device (name
))
751 (*map
)[num_hd
+ 0x80] = strdup (name
);
752 assert ((*map
)[num_hd
+ 0x80]);
754 /* If the device map file is opened, write the map. */
756 fprintf (fp
, "(hd%d)\t%s\n", num_hd
, name
);
764 for (i
= 0; i
< 8; i
++)
768 get_ataraid_disk_name (name
, i
);
769 if (check_device (name
))
771 (*map
)[num_hd
+ 0x80] = strdup (name
);
772 assert ((*map
)[num_hd
+ 0x80]);
774 /* If the device map file is opened, write the map. */
776 fprintf (fp
, "(hd%d)\t%s\n", num_hd
, name
);
781 #endif /* __linux__ */
783 /* The rest is SCSI disks. */
784 for (i
= 0; i
< 16; i
++)
788 get_scsi_disk_name (name
, i
);
789 if (check_device (name
))
791 (*map
)[num_hd
+ 0x80] = strdup (name
);
792 assert ((*map
)[num_hd
+ 0x80]);
794 /* If the device map file is opened, write the map. */
796 fprintf (fp
, "(hd%d)\t%s\n", num_hd
, name
);
803 /* This is for DAC960 - we have
804 /dev/rd/c<controller>d<logical drive>p<partition>.
806 DAC960 driver currently supports up to 8 controllers, 32 logical
807 drives, and 7 partitions. */
809 int controller
, drive
;
811 for (controller
= 0; controller
< 8; controller
++)
813 for (drive
= 0; drive
< 15; drive
++)
817 get_dac960_disk_name (name
, controller
, drive
);
818 if (check_device (name
))
820 (*map
)[num_hd
+ 0x80] = strdup (name
);
821 assert ((*map
)[num_hd
+ 0x80]);
823 /* If the device map file is opened, write the map. */
825 fprintf (fp
, "(hd%d)\t%s\n", num_hd
, name
);
832 #endif /* __linux__ */
834 /* OK, close the device map file if opened. */
841 /* Restore the memory consumed for MAP. */
843 restore_device_map (char **map
)
847 for (i
= 0; i
< NUM_DISKS
; i
++)
855 /* Linux-only functions, because Linux has a bug that the disk cache for
856 a whole disk is not consistent with the one for a partition of the
859 is_disk_device (char **map
, int drive
)
863 assert (map
[drive
] != 0);
864 assert (stat (map
[drive
], &st
) == 0);
865 /* For now, disk devices under Linux are all block devices. */
866 return S_ISBLK (st
.st_mode
);
870 write_to_partition (char **map
, int drive
, int partition
,
871 int sector
, int size
, const char *buf
)
873 char dev
[PATH_MAX
]; /* XXX */
876 if ((partition
& 0x00FF00) != 0x00FF00)
878 /* If the partition is a BSD partition, it is difficult to
879 obtain the representation in Linux. So don't support that. */
880 errnum
= ERR_DEV_VALUES
;
884 assert (map
[drive
] != 0);
886 strcpy (dev
, map
[drive
]);
889 if (strcmp (dev
+ strlen(dev
) - 5, "/disc") == 0)
890 strcpy (dev
+ strlen(dev
) - 5, "/part");
892 sprintf (dev
+ strlen(dev
), "%d", ((partition
>> 16) & 0xFF) + 1);
894 /* Open the partition. */
895 fd
= open (dev
, O_RDWR
);
898 errnum
= ERR_NO_PART
;
902 #if defined(__linux__) && (!defined(__GLIBC__) || \
903 ((__GLIBC__ < 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 1))))
904 /* Maybe libc doesn't have large file support. */
906 loff_t offset
, result
;
907 static int _llseek (uint filedes
, ulong hi
, ulong lo
,
908 loff_t
*res
, uint wh
);
909 _syscall5 (int, _llseek
, uint
, filedes
, ulong
, hi
, ulong
, lo
,
910 loff_t
*, res
, uint
, wh
);
912 offset
= (loff_t
) sector
* (loff_t
) SECTOR_SIZE
;
913 if (_llseek (fd
, offset
>> 32, offset
& 0xffffffff, &result
, SEEK_SET
))
915 errnum
= ERR_DEV_VALUES
;
921 off_t offset
= (off_t
) sector
* (off_t
) SECTOR_SIZE
;
923 if (lseek (fd
, offset
, SEEK_SET
) != offset
)
925 errnum
= ERR_DEV_VALUES
;
931 if (write (fd
, buf
, size
* SECTOR_SIZE
) != (size
* SECTOR_SIZE
))
938 sync (); /* Paranoia. */
943 #endif /* __linux__ */