2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2006,2007 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/disk.h>
20 #include <grub/partition.h>
22 #include <grub/types.h>
23 #include <grub/misc.h>
25 #include <grub/term.h>
26 #include <grub/efi/api.h>
27 #include <grub/efi/efi.h>
28 #include <grub/efi/disk.h>
30 struct grub_efidisk_data
32 grub_efi_handle_t handle
;
33 grub_efi_device_path_t
*device_path
;
34 grub_efi_device_path_t
*last_device_path
;
35 grub_efi_block_io_t
*block_io
;
36 grub_efi_disk_io_t
*disk_io
;
37 struct grub_efidisk_data
*next
;
41 static grub_efi_guid_t disk_io_guid
= GRUB_EFI_DISK_IO_GUID
;
42 static grub_efi_guid_t block_io_guid
= GRUB_EFI_BLOCK_IO_GUID
;
44 static struct grub_efidisk_data
*fd_devices
;
45 static struct grub_efidisk_data
*hd_devices
;
46 static struct grub_efidisk_data
*cd_devices
;
48 /* Duplicate a device path. */
49 static grub_efi_device_path_t
*
50 duplicate_device_path (const grub_efi_device_path_t
*dp
)
52 grub_efi_device_path_t
*p
;
53 grub_size_t total_size
= 0;
55 for (p
= (grub_efi_device_path_t
*) dp
;
57 p
= GRUB_EFI_NEXT_DEVICE_PATH (p
))
59 total_size
+= GRUB_EFI_DEVICE_PATH_LENGTH (p
);
60 if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (p
))
64 p
= grub_malloc (total_size
);
68 grub_memcpy (p
, dp
, total_size
);
72 /* Return the device path node right before the end node. */
73 static grub_efi_device_path_t
*
74 find_last_device_path (const grub_efi_device_path_t
*dp
)
76 grub_efi_device_path_t
*next
, *p
;
78 if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp
))
81 for (p
= (grub_efi_device_path_t
*) dp
, next
= GRUB_EFI_NEXT_DEVICE_PATH (p
);
82 ! GRUB_EFI_END_ENTIRE_DEVICE_PATH (next
);
83 p
= next
, next
= GRUB_EFI_NEXT_DEVICE_PATH (next
))
89 /* Compare device paths. */
91 compare_device_paths (const grub_efi_device_path_t
*dp1
,
92 const grub_efi_device_path_t
*dp2
)
95 /* Return non-zero. */
100 grub_efi_uint8_t type1
, type2
;
101 grub_efi_uint8_t subtype1
, subtype2
;
102 grub_efi_uint16_t len1
, len2
;
105 type1
= GRUB_EFI_DEVICE_PATH_TYPE (dp1
);
106 type2
= GRUB_EFI_DEVICE_PATH_TYPE (dp2
);
109 return (int) type2
- (int) type1
;
111 subtype1
= GRUB_EFI_DEVICE_PATH_SUBTYPE (dp1
);
112 subtype2
= GRUB_EFI_DEVICE_PATH_SUBTYPE (dp2
);
114 if (subtype1
!= subtype2
)
115 return (int) subtype1
- (int) subtype2
;
117 len1
= GRUB_EFI_DEVICE_PATH_LENGTH (dp1
);
118 len2
= GRUB_EFI_DEVICE_PATH_LENGTH (dp2
);
121 return (int) len1
- (int) len2
;
123 ret
= grub_memcmp (dp1
, dp2
, len1
);
127 if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1
))
130 dp1
= (grub_efi_device_path_t
*) ((char *) dp1
+ len1
);
131 dp2
= (grub_efi_device_path_t
*) ((char *) dp2
+ len2
);
137 static struct grub_efidisk_data
*
140 grub_efi_uintn_t num_handles
;
141 grub_efi_handle_t
*handles
;
142 grub_efi_handle_t
*handle
;
143 struct grub_efidisk_data
*devices
= 0;
145 /* Find handles which support the disk io interface. */
146 handles
= grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL
, &disk_io_guid
,
151 /* Make a linked list of devices. */
152 for (handle
= handles
; num_handles
--; handle
++)
154 grub_efi_device_path_t
*dp
;
155 grub_efi_device_path_t
*ldp
;
156 struct grub_efidisk_data
*d
;
157 grub_efi_block_io_t
*bio
;
158 grub_efi_disk_io_t
*dio
;
160 dp
= grub_efi_get_device_path (*handle
);
164 ldp
= find_last_device_path (dp
);
166 /* This is empty. Why? */
169 bio
= grub_efi_open_protocol (*handle
, &block_io_guid
,
170 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
171 dio
= grub_efi_open_protocol (*handle
, &disk_io_guid
,
172 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
174 /* This should not happen... Why? */
177 d
= grub_malloc (sizeof (*d
));
187 d
->last_device_path
= ldp
;
199 /* Find the parent device. */
200 static struct grub_efidisk_data
*
201 find_parent_device (struct grub_efidisk_data
*devices
,
202 struct grub_efidisk_data
*d
)
204 grub_efi_device_path_t
*dp
, *ldp
;
205 struct grub_efidisk_data
*parent
;
207 dp
= duplicate_device_path (d
->device_path
);
211 ldp
= find_last_device_path (dp
);
212 ldp
->type
= GRUB_EFI_END_DEVICE_PATH_TYPE
;
213 ldp
->subtype
= GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE
;
214 ldp
->length
[0] = sizeof (*ldp
);
217 for (parent
= devices
; parent
; parent
= parent
->next
)
223 if (compare_device_paths (parent
->device_path
, dp
) == 0)
226 if (! parent
->last_device_path
)
238 iterate_child_devices (struct grub_efidisk_data
*devices
,
239 struct grub_efidisk_data
*d
,
240 int (*hook
) (struct grub_efidisk_data
*child
))
242 struct grub_efidisk_data
*p
;
244 for (p
= devices
; p
; p
= p
->next
)
246 grub_efi_device_path_t
*dp
, *ldp
;
248 dp
= duplicate_device_path (p
->device_path
);
252 ldp
= find_last_device_path (dp
);
253 ldp
->type
= GRUB_EFI_END_DEVICE_PATH_TYPE
;
254 ldp
->subtype
= GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE
;
255 ldp
->length
[0] = sizeof (*ldp
);
258 if (compare_device_paths (dp
, d
->device_path
) == 0)
271 /* Add a device into a list of devices in an ascending order. */
273 add_device (struct grub_efidisk_data
**devices
, struct grub_efidisk_data
*d
)
275 struct grub_efidisk_data
**p
;
276 struct grub_efidisk_data
*n
;
278 for (p
= devices
; *p
; p
= &((*p
)->next
))
282 ret
= compare_device_paths (find_last_device_path ((*p
)->device_path
),
283 find_last_device_path (d
->device_path
));
285 ret
= compare_device_paths ((*p
)->device_path
,
293 n
= grub_malloc (sizeof (*n
));
297 grub_memcpy (n
, d
, sizeof (*n
));
302 /* Name the devices. */
304 name_devices (struct grub_efidisk_data
*devices
)
306 struct grub_efidisk_data
*d
;
308 /* First, identify devices by media device paths. */
309 for (d
= devices
; d
; d
= d
->next
)
311 grub_efi_device_path_t
*dp
;
313 dp
= d
->last_device_path
;
317 if (GRUB_EFI_DEVICE_PATH_TYPE (dp
) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
)
319 int is_hard_drive
= 0;
321 switch (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp
))
323 case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE
:
325 /* Fall through by intention. */
326 case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE
:
328 struct grub_efidisk_data
*parent
;
330 parent
= find_parent_device (devices
, d
);
336 grub_printf ("adding a hard drive by a partition: ");
337 grub_print_device_path (parent
->device_path
);
339 add_device (&hd_devices
, parent
);
344 grub_printf ("adding a cdrom by a partition: ");
345 grub_print_device_path (parent
->device_path
);
347 add_device (&cd_devices
, parent
);
350 /* Mark the parent as used. */
351 parent
->last_device_path
= 0;
354 /* Mark itself as used. */
355 d
->last_device_path
= 0;
359 /* For now, ignore the others. */
365 /* Let's see what can be added more. */
366 for (d
= devices
; d
; d
= d
->next
)
368 grub_efi_device_path_t
*dp
;
369 grub_efi_block_io_media_t
*m
;
371 dp
= d
->last_device_path
;
375 m
= d
->block_io
->media
;
376 if (m
->logical_partition
)
378 /* Only one partition in a non-media device. Assume that this
379 is a floppy drive. */
381 grub_printf ("adding a floppy by guessing: ");
382 grub_print_device_path (d
->device_path
);
384 add_device (&fd_devices
, d
);
386 else if (m
->read_only
&& m
->block_size
> GRUB_DISK_SECTOR_SIZE
)
388 /* This check is too heuristic, but assume that this is a
391 grub_printf ("adding a cdrom by guessing: ");
392 grub_print_device_path (d
->device_path
);
394 add_device (&cd_devices
, d
);
398 /* The default is a hard drive. */
400 grub_printf ("adding a hard drive by guessing: ");
401 grub_print_device_path (d
->device_path
);
403 add_device (&hd_devices
, d
);
409 free_devices (struct grub_efidisk_data
*devices
)
411 struct grub_efidisk_data
*p
, *q
;
413 for (p
= devices
; p
; p
= q
)
420 /* Enumerate all disks to name devices. */
422 enumerate_disks (void)
424 struct grub_efidisk_data
*devices
;
426 devices
= make_devices ();
430 name_devices (devices
);
431 free_devices (devices
);
435 grub_efidisk_iterate (int (*hook
) (const char *name
))
437 struct grub_efidisk_data
*d
;
441 for (d
= fd_devices
, count
= 0; d
; d
= d
->next
, count
++)
443 grub_sprintf (buf
, "fd%d", count
);
444 grub_dprintf ("efidisk", "iterating %s\n", buf
);
449 for (d
= hd_devices
, count
= 0; d
; d
= d
->next
, count
++)
451 grub_sprintf (buf
, "hd%d", count
);
452 grub_dprintf ("efidisk", "iterating %s\n", buf
);
457 for (d
= cd_devices
, count
= 0; d
; d
= d
->next
, count
++)
459 grub_sprintf (buf
, "cd%d", count
);
460 grub_dprintf ("efidisk", "iterating %s\n", buf
);
469 get_drive_number (const char *name
)
473 if ((name
[0] != 'f' && name
[0] != 'h' && name
[0] != 'c') || name
[1] != 'd')
476 drive
= grub_strtoul (name
+ 2, 0, 10);
477 if (grub_errno
!= GRUB_ERR_NONE
)
483 grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "not a efidisk");
487 static struct grub_efidisk_data
*
488 get_device (struct grub_efidisk_data
*devices
, int num
)
490 struct grub_efidisk_data
*d
;
492 for (d
= devices
; d
&& num
; d
= d
->next
, num
--)
502 grub_efidisk_open (const char *name
, struct grub_disk
*disk
)
505 struct grub_efidisk_data
*d
= 0;
506 grub_efi_block_io_media_t
*m
;
508 grub_dprintf ("efidisk", "opening %s\n", name
);
510 num
= get_drive_number (name
);
517 disk
->has_partitions
= 0;
518 d
= get_device (fd_devices
, num
);
521 /* FIXME: a CDROM should have partitions, but not implemented yet. */
522 disk
->has_partitions
= 0;
523 d
= get_device (cd_devices
, num
);
526 disk
->has_partitions
= 1;
527 d
= get_device (hd_devices
, num
);
530 /* Never reach here. */
535 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "no such device");
537 disk
->id
= ((num
<< 8) | name
[0]);
538 m
= d
->block_io
->media
;
539 /* FIXME: Probably it is better to store the block size in the disk,
540 and total sectors should be replaced with total blocks. */
541 grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n",
542 m
, (unsigned long long) m
->last_block
, m
->block_size
);
543 disk
->total_sectors
= (m
->last_block
544 * (m
->block_size
>> GRUB_DISK_SECTOR_BITS
));
547 grub_dprintf ("efidisk", "opening %s succeeded\n", name
);
549 return GRUB_ERR_NONE
;
553 grub_efidisk_close (struct grub_disk
*disk
__attribute__ ((unused
)))
555 /* EFI disks do not allocate extra memory, so nothing to do here. */
556 grub_dprintf ("efidisk", "closing %s\n", disk
->name
);
560 grub_efidisk_read (struct grub_disk
*disk
, grub_disk_addr_t sector
,
561 grub_size_t size
, char *buf
)
563 /* For now, use the disk io interface rather than the block io's. */
564 struct grub_efidisk_data
*d
;
565 grub_efi_disk_io_t
*dio
;
566 grub_efi_block_io_t
*bio
;
567 grub_efi_status_t status
;
573 grub_dprintf ("efidisk",
574 "reading 0x%lx sectors at the sector 0x%llx from %s\n",
575 (unsigned long) size
, (unsigned long long) sector
, disk
->name
);
577 status
= efi_call_5 (dio
->read
, dio
, bio
->media
->media_id
,
578 (grub_efi_uint64_t
) sector
<< GRUB_DISK_SECTOR_BITS
,
579 (grub_efi_uintn_t
) size
<< GRUB_DISK_SECTOR_BITS
,
581 if (status
!= GRUB_EFI_SUCCESS
)
582 return grub_error (GRUB_ERR_READ_ERROR
, "efidisk read error");
584 return GRUB_ERR_NONE
;
588 grub_efidisk_write (struct grub_disk
*disk
, grub_disk_addr_t sector
,
589 grub_size_t size
, const char *buf
)
591 /* For now, use the disk io interface rather than the block io's. */
592 struct grub_efidisk_data
*d
;
593 grub_efi_disk_io_t
*dio
;
594 grub_efi_block_io_t
*bio
;
595 grub_efi_status_t status
;
601 grub_dprintf ("efidisk",
602 "writing 0x%lx sectors at the sector 0x%llx to %s\n",
603 (unsigned long) size
, (unsigned long long) sector
, disk
->name
);
605 status
= efi_call_5 (dio
->write
, dio
, bio
->media
->media_id
,
606 (grub_efi_uint64_t
) sector
<< GRUB_DISK_SECTOR_BITS
,
607 (grub_efi_uintn_t
) size
<< GRUB_DISK_SECTOR_BITS
,
609 if (status
!= GRUB_EFI_SUCCESS
)
610 return grub_error (GRUB_ERR_WRITE_ERROR
, "efidisk write error");
612 return GRUB_ERR_NONE
;
615 static struct grub_disk_dev grub_efidisk_dev
=
618 .id
= GRUB_DISK_DEVICE_EFIDISK_ID
,
619 .iterate
= grub_efidisk_iterate
,
620 .open
= grub_efidisk_open
,
621 .close
= grub_efidisk_close
,
622 .read
= grub_efidisk_read
,
623 .write
= grub_efidisk_write
,
628 grub_efidisk_init (void)
631 grub_disk_dev_register (&grub_efidisk_dev
);
635 grub_efidisk_fini (void)
637 free_devices (fd_devices
);
638 free_devices (hd_devices
);
639 free_devices (cd_devices
);
640 grub_disk_dev_unregister (&grub_efidisk_dev
);
643 /* Some utility functions to map GRUB devices with EFI devices. */
645 grub_efidisk_get_device_handle (grub_disk_t disk
)
647 struct grub_efidisk_data
*d
;
650 if (disk
->dev
->id
!= GRUB_DISK_DEVICE_EFIDISK_ID
)
654 type
= disk
->name
[0];
659 /* This is the simplest case. */
663 /* FIXME: probably this is not correct. */
667 /* If this is the whole disk, just return its own data. */
668 if (! disk
->partition
)
671 /* Otherwise, we must query the corresponding device to the firmware. */
673 struct grub_efidisk_data
*devices
;
674 grub_efi_handle_t handle
= 0;
675 auto int find_partition (struct grub_efidisk_data
*c
);
677 int find_partition (struct grub_efidisk_data
*c
)
679 grub_efi_hard_drive_device_path_t hd
;
681 grub_memcpy (&hd
, c
->last_device_path
, sizeof (hd
));
683 if ((GRUB_EFI_DEVICE_PATH_TYPE (c
->last_device_path
)
684 == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
)
685 && (GRUB_EFI_DEVICE_PATH_SUBTYPE (c
->last_device_path
)
686 == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE
)
687 && (grub_partition_get_start (disk
->partition
)
688 == hd
.partition_start
)
689 && (grub_partition_get_len (disk
->partition
)
690 == hd
.partition_size
))
699 devices
= make_devices ();
700 iterate_child_devices (devices
, d
, find_partition
);
701 free_devices (devices
);
716 grub_efidisk_get_device_name (grub_efi_handle_t
*handle
)
718 grub_efi_device_path_t
*dp
, *ldp
;
720 dp
= grub_efi_get_device_path (handle
);
724 ldp
= find_last_device_path (dp
);
728 if (GRUB_EFI_DEVICE_PATH_TYPE (ldp
) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
729 && (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp
)
730 == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE
))
732 /* This is a hard disk partition. */
733 grub_disk_t parent
= 0;
734 char *partition_name
= 0;
736 grub_efi_device_path_t
*dup_dp
, *dup_ldp
;
737 grub_efi_hard_drive_device_path_t hd
;
738 auto int find_parent_disk (const char *name
);
739 auto int find_partition (grub_disk_t disk
, const grub_partition_t part
);
741 /* Find the disk which is the parent of a given hard disk partition. */
742 int find_parent_disk (const char *name
)
746 disk
= grub_disk_open (name
);
750 if (disk
->dev
->id
== GRUB_DISK_DEVICE_EFIDISK_ID
)
752 struct grub_efidisk_data
*d
;
755 if (compare_device_paths (d
->device_path
, dup_dp
) == 0)
762 grub_disk_close (disk
);
766 /* Find the identical partition. */
767 int find_partition (grub_disk_t disk
__attribute__ ((unused
)),
768 const grub_partition_t part
)
770 if (grub_partition_get_start (part
) == hd
.partition_start
771 && grub_partition_get_len (part
) == hd
.partition_size
)
773 partition_name
= grub_partition_get_name (part
);
780 /* It is necessary to duplicate the device path so that GRUB
782 dup_dp
= duplicate_device_path (dp
);
786 dup_ldp
= find_last_device_path (dup_dp
);
787 dup_ldp
->type
= GRUB_EFI_END_DEVICE_PATH_TYPE
;
788 dup_ldp
->subtype
= GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE
;
789 dup_ldp
->length
[0] = sizeof (*dup_ldp
);
790 dup_ldp
->length
[1] = 0;
792 grub_efidisk_iterate (find_parent_disk
);
798 /* Find a partition which matches the hard drive device path. */
799 grub_memcpy (&hd
, ldp
, sizeof (hd
));
800 grub_partition_iterate (parent
, find_partition
);
802 if (! partition_name
)
804 grub_disk_close (parent
);
808 device_name
= grub_malloc (grub_strlen (parent
->name
) + 1
809 + grub_strlen (partition_name
) + 1);
812 grub_free (partition_name
);
813 grub_disk_close (parent
);
817 grub_sprintf (device_name
, "%s,%s", parent
->name
, partition_name
);
818 grub_free (partition_name
);
819 grub_disk_close (parent
);
824 /* This should be an entire disk. */
825 auto int find_disk (const char *name
);
826 char *device_name
= 0;
828 int find_disk (const char *name
)
832 disk
= grub_disk_open (name
);
836 if (disk
->id
== GRUB_DISK_DEVICE_EFIDISK_ID
)
838 struct grub_efidisk_data
*d
;
841 if (compare_device_paths (d
->device_path
, dp
) == 0)
843 device_name
= grub_strdup (disk
->name
);
844 grub_disk_close (disk
);
849 grub_disk_close (disk
);
854 grub_efidisk_iterate (find_disk
);