2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2006,2007,2008 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 struct grub_efidisk_data
*next
;
40 static grub_efi_guid_t block_io_guid
= GRUB_EFI_BLOCK_IO_GUID
;
42 static struct grub_efidisk_data
*fd_devices
;
43 static struct grub_efidisk_data
*hd_devices
;
44 static struct grub_efidisk_data
*cd_devices
;
46 /* Duplicate a device path. */
47 static grub_efi_device_path_t
*
48 duplicate_device_path (const grub_efi_device_path_t
*dp
)
50 grub_efi_device_path_t
*p
;
51 grub_size_t total_size
= 0;
53 for (p
= (grub_efi_device_path_t
*) dp
;
55 p
= GRUB_EFI_NEXT_DEVICE_PATH (p
))
57 total_size
+= GRUB_EFI_DEVICE_PATH_LENGTH (p
);
58 if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (p
))
62 p
= grub_malloc (total_size
);
66 grub_memcpy (p
, dp
, total_size
);
70 /* Return the device path node right before the end node. */
71 static grub_efi_device_path_t
*
72 find_last_device_path (const grub_efi_device_path_t
*dp
)
74 grub_efi_device_path_t
*next
, *p
;
76 if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp
))
79 for (p
= (grub_efi_device_path_t
*) dp
, next
= GRUB_EFI_NEXT_DEVICE_PATH (p
);
80 ! GRUB_EFI_END_ENTIRE_DEVICE_PATH (next
);
81 p
= next
, next
= GRUB_EFI_NEXT_DEVICE_PATH (next
))
87 static struct grub_efidisk_data
*
90 grub_efi_uintn_t num_handles
;
91 grub_efi_handle_t
*handles
;
92 grub_efi_handle_t
*handle
;
93 struct grub_efidisk_data
*devices
= 0;
95 /* Find handles which support the disk io interface. */
96 handles
= grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL
, &block_io_guid
,
101 /* Make a linked list of devices. */
102 for (handle
= handles
; num_handles
--; handle
++)
104 grub_efi_device_path_t
*dp
;
105 grub_efi_device_path_t
*ldp
;
106 struct grub_efidisk_data
*d
;
107 grub_efi_block_io_t
*bio
;
109 dp
= grub_efi_get_device_path (*handle
);
113 ldp
= find_last_device_path (dp
);
115 /* This is empty. Why? */
118 bio
= grub_efi_open_protocol (*handle
, &block_io_guid
,
119 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
121 /* This should not happen... Why? */
124 d
= grub_malloc (sizeof (*d
));
134 d
->last_device_path
= ldp
;
145 /* Find the parent device. */
146 static struct grub_efidisk_data
*
147 find_parent_device (struct grub_efidisk_data
*devices
,
148 struct grub_efidisk_data
*d
)
150 grub_efi_device_path_t
*dp
, *ldp
;
151 struct grub_efidisk_data
*parent
;
153 dp
= duplicate_device_path (d
->device_path
);
157 ldp
= find_last_device_path (dp
);
158 ldp
->type
= GRUB_EFI_END_DEVICE_PATH_TYPE
;
159 ldp
->subtype
= GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE
;
160 ldp
->length
= sizeof (*ldp
);
162 for (parent
= devices
; parent
; parent
= parent
->next
)
168 if (grub_efi_compare_device_paths (parent
->device_path
, dp
) == 0)
177 is_child (struct grub_efidisk_data
*child
,
178 struct grub_efidisk_data
*parent
)
180 grub_efi_device_path_t
*dp
, *ldp
;
183 dp
= duplicate_device_path (child
->device_path
);
187 ldp
= find_last_device_path (dp
);
188 ldp
->type
= GRUB_EFI_END_DEVICE_PATH_TYPE
;
189 ldp
->subtype
= GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE
;
190 ldp
->length
= sizeof (*ldp
);
192 ret
= (grub_efi_compare_device_paths (dp
, parent
->device_path
) == 0);
197 #define FOR_CHILDREN(p, dev) for (p = dev; p; p = p->next) if (is_child (p, d))
199 /* Add a device into a list of devices in an ascending order. */
201 add_device (struct grub_efidisk_data
**devices
, struct grub_efidisk_data
*d
)
203 struct grub_efidisk_data
**p
;
204 struct grub_efidisk_data
*n
;
206 for (p
= devices
; *p
; p
= &((*p
)->next
))
210 ret
= grub_efi_compare_device_paths (find_last_device_path ((*p
)->device_path
),
211 find_last_device_path (d
->device_path
));
213 ret
= grub_efi_compare_device_paths ((*p
)->device_path
,
221 n
= grub_malloc (sizeof (*n
));
225 grub_memcpy (n
, d
, sizeof (*n
));
230 /* Name the devices. */
232 name_devices (struct grub_efidisk_data
*devices
)
234 struct grub_efidisk_data
*d
;
236 /* First, identify devices by media device paths. */
237 for (d
= devices
; d
; d
= d
->next
)
239 grub_efi_device_path_t
*dp
;
241 dp
= d
->last_device_path
;
245 if (GRUB_EFI_DEVICE_PATH_TYPE (dp
) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
)
247 int is_hard_drive
= 0;
249 switch (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp
))
251 case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE
:
253 /* Fall through by intention. */
254 case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE
:
256 struct grub_efidisk_data
*parent
, *parent2
;
258 parent
= find_parent_device (devices
, d
);
262 grub_printf ("skipping orphaned partition: ");
263 grub_efi_print_device_path (d
->device_path
);
267 parent2
= find_parent_device (devices
, parent
);
271 grub_printf ("skipping subpartition: ");
272 grub_efi_print_device_path (d
->device_path
);
274 /* Mark itself as used. */
275 d
->last_device_path
= 0;
278 if (!parent
->last_device_path
)
280 d
->last_device_path
= 0;
286 grub_printf ("adding a hard drive by a partition: ");
287 grub_efi_print_device_path (parent
->device_path
);
289 add_device (&hd_devices
, parent
);
294 grub_printf ("adding a cdrom by a partition: ");
295 grub_efi_print_device_path (parent
->device_path
);
297 add_device (&cd_devices
, parent
);
300 /* Mark the parent as used. */
301 parent
->last_device_path
= 0;
303 /* Mark itself as used. */
304 d
->last_device_path
= 0;
309 grub_printf ("skipping other type: ");
310 grub_efi_print_device_path (d
->device_path
);
312 /* For now, ignore the others. */
319 grub_printf ("skipping non-media: ");
320 grub_efi_print_device_path (d
->device_path
);
325 /* Let's see what can be added more. */
326 for (d
= devices
; d
; d
= d
->next
)
328 grub_efi_device_path_t
*dp
;
329 grub_efi_block_io_media_t
*m
;
332 dp
= d
->last_device_path
;
336 /* Ghosts proudly presented by Apple. */
337 if (GRUB_EFI_DEVICE_PATH_TYPE (dp
) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
338 && GRUB_EFI_DEVICE_PATH_SUBTYPE (dp
)
339 == GRUB_EFI_VENDOR_MEDIA_DEVICE_PATH_SUBTYPE
)
341 grub_efi_vendor_device_path_t
*vendor
= (grub_efi_vendor_device_path_t
*) dp
;
342 const struct grub_efi_guid apple
= GRUB_EFI_VENDOR_APPLE_GUID
;
344 if (vendor
->header
.length
== sizeof (*vendor
)
345 && grub_memcmp (&vendor
->vendor_guid
, &apple
,
346 sizeof (vendor
->vendor_guid
)) == 0
347 && find_parent_device (devices
, d
))
351 m
= d
->block_io
->media
;
352 if (GRUB_EFI_DEVICE_PATH_TYPE (dp
) == GRUB_EFI_ACPI_DEVICE_PATH_TYPE
353 && GRUB_EFI_DEVICE_PATH_SUBTYPE (dp
)
354 == GRUB_EFI_ACPI_DEVICE_PATH_SUBTYPE
)
356 grub_efi_acpi_device_path_t
*acpi
357 = (grub_efi_acpi_device_path_t
*) dp
;
358 /* Floppy EISA ID. */
359 if (acpi
->hid
== 0x60441d0 || acpi
->hid
== 0x70041d0
360 || acpi
->hid
== 0x70141d1)
366 grub_printf ("adding a floppy: ");
367 grub_efi_print_device_path (d
->device_path
);
369 add_device (&fd_devices
, d
);
371 else if (m
->read_only
&& m
->block_size
> GRUB_DISK_SECTOR_SIZE
)
373 /* This check is too heuristic, but assume that this is a
376 grub_printf ("adding a cdrom by guessing: ");
377 grub_efi_print_device_path (d
->device_path
);
379 add_device (&cd_devices
, d
);
383 /* The default is a hard drive. */
385 grub_printf ("adding a hard drive by guessing: ");
386 grub_efi_print_device_path (d
->device_path
);
388 add_device (&hd_devices
, d
);
394 free_devices (struct grub_efidisk_data
*devices
)
396 struct grub_efidisk_data
*p
, *q
;
398 for (p
= devices
; p
; p
= q
)
405 /* Enumerate all disks to name devices. */
407 enumerate_disks (void)
409 struct grub_efidisk_data
*devices
;
411 devices
= make_devices ();
415 name_devices (devices
);
416 free_devices (devices
);
420 grub_efidisk_iterate (grub_disk_dev_iterate_hook_t hook
, void *hook_data
,
421 grub_disk_pull_t pull
)
423 struct grub_efidisk_data
*d
;
429 case GRUB_DISK_PULL_NONE
:
430 for (d
= hd_devices
, count
= 0; d
; d
= d
->next
, count
++)
432 grub_snprintf (buf
, sizeof (buf
), "hd%d", count
);
433 grub_dprintf ("efidisk", "iterating %s\n", buf
);
434 if (hook (buf
, hook_data
))
438 case GRUB_DISK_PULL_REMOVABLE
:
439 for (d
= fd_devices
, count
= 0; d
; d
= d
->next
, count
++)
441 grub_snprintf (buf
, sizeof (buf
), "fd%d", count
);
442 grub_dprintf ("efidisk", "iterating %s\n", buf
);
443 if (hook (buf
, hook_data
))
447 for (d
= cd_devices
, count
= 0; d
; d
= d
->next
, count
++)
449 grub_snprintf (buf
, sizeof (buf
), "cd%d", count
);
450 grub_dprintf ("efidisk", "iterating %s\n", buf
);
451 if (hook (buf
, hook_data
))
463 get_drive_number (const char *name
)
467 if ((name
[0] != 'f' && name
[0] != 'h' && name
[0] != 'c') || name
[1] != 'd')
470 drive
= grub_strtoul (name
+ 2, 0, 10);
471 if (grub_errno
!= GRUB_ERR_NONE
)
477 grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "not a efidisk");
481 static struct grub_efidisk_data
*
482 get_device (struct grub_efidisk_data
*devices
, int num
)
484 struct grub_efidisk_data
*d
;
486 for (d
= devices
; d
&& num
; d
= d
->next
, num
--)
496 grub_efidisk_open (const char *name
, struct grub_disk
*disk
)
499 struct grub_efidisk_data
*d
= 0;
500 grub_efi_block_io_media_t
*m
;
502 grub_dprintf ("efidisk", "opening %s\n", name
);
504 num
= get_drive_number (name
);
511 d
= get_device (fd_devices
, num
);
514 d
= get_device (cd_devices
, num
);
517 d
= get_device (hd_devices
, num
);
520 /* Never reach here. */
525 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "no such device");
527 disk
->id
= ((num
<< GRUB_CHAR_BIT
) | name
[0]);
528 m
= d
->block_io
->media
;
529 /* FIXME: Probably it is better to store the block size in the disk,
530 and total sectors should be replaced with total blocks. */
531 grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n",
532 m
, (unsigned long long) m
->last_block
, m
->block_size
);
533 disk
->total_sectors
= m
->last_block
+ 1;
534 /* Don't increase this value due to bug in some EFI. */
535 disk
->max_agglomerate
= 0xa0000 >> (GRUB_DISK_CACHE_BITS
+ GRUB_DISK_SECTOR_BITS
);
536 if (m
->block_size
& (m
->block_size
- 1) || !m
->block_size
)
537 return grub_error (GRUB_ERR_IO
, "invalid sector size %d",
539 for (disk
->log_sector_size
= 0;
540 (1U << disk
->log_sector_size
) < m
->block_size
;
541 disk
->log_sector_size
++);
544 grub_dprintf ("efidisk", "opening %s succeeded\n", name
);
546 return GRUB_ERR_NONE
;
550 grub_efidisk_close (struct grub_disk
*disk
__attribute__ ((unused
)))
552 /* EFI disks do not allocate extra memory, so nothing to do here. */
553 grub_dprintf ("efidisk", "closing %s\n", disk
->name
);
556 static grub_efi_status_t
557 grub_efidisk_readwrite (struct grub_disk
*disk
, grub_disk_addr_t sector
,
558 grub_size_t size
, char *buf
, int wr
)
560 struct grub_efidisk_data
*d
;
561 grub_efi_block_io_t
*bio
;
566 return efi_call_5 ((wr
? bio
->write_blocks
: bio
->read_blocks
), bio
,
567 bio
->media
->media_id
,
568 (grub_efi_uint64_t
) sector
,
569 (grub_efi_uintn_t
) size
<< disk
->log_sector_size
,
574 grub_efidisk_read (struct grub_disk
*disk
, grub_disk_addr_t sector
,
575 grub_size_t size
, char *buf
)
577 grub_efi_status_t status
;
579 grub_dprintf ("efidisk",
580 "reading 0x%lx sectors at the sector 0x%llx from %s\n",
581 (unsigned long) size
, (unsigned long long) sector
, disk
->name
);
583 status
= grub_efidisk_readwrite (disk
, sector
, size
, buf
, 0);
585 if (status
!= GRUB_EFI_SUCCESS
)
586 return grub_error (GRUB_ERR_READ_ERROR
,
587 N_("failure reading sector 0x%llx from `%s'"),
588 (unsigned long long) sector
,
591 return GRUB_ERR_NONE
;
595 grub_efidisk_write (struct grub_disk
*disk
, grub_disk_addr_t sector
,
596 grub_size_t size
, const char *buf
)
598 grub_efi_status_t status
;
600 grub_dprintf ("efidisk",
601 "writing 0x%lx sectors at the sector 0x%llx to %s\n",
602 (unsigned long) size
, (unsigned long long) sector
, disk
->name
);
604 status
= grub_efidisk_readwrite (disk
, sector
, size
, (char *) buf
, 1);
606 if (status
!= GRUB_EFI_SUCCESS
)
607 return grub_error (GRUB_ERR_WRITE_ERROR
,
608 N_("failure writing sector 0x%llx to `%s'"),
609 (unsigned long long) sector
, disk
->name
);
611 return GRUB_ERR_NONE
;
614 static struct grub_disk_dev grub_efidisk_dev
=
617 .id
= GRUB_DISK_DEVICE_EFIDISK_ID
,
618 .iterate
= grub_efidisk_iterate
,
619 .open
= grub_efidisk_open
,
620 .close
= grub_efidisk_close
,
621 .read
= grub_efidisk_read
,
622 .write
= grub_efidisk_write
,
627 grub_efidisk_fini (void)
629 free_devices (fd_devices
);
630 free_devices (hd_devices
);
631 free_devices (cd_devices
);
635 grub_disk_dev_unregister (&grub_efidisk_dev
);
639 grub_efidisk_init (void)
641 grub_disk_firmware_fini
= grub_efidisk_fini
;
644 grub_disk_dev_register (&grub_efidisk_dev
);
647 /* Some utility functions to map GRUB devices with EFI devices. */
649 grub_efidisk_get_device_handle (grub_disk_t disk
)
651 struct grub_efidisk_data
*d
;
654 if (disk
->dev
->id
!= GRUB_DISK_DEVICE_EFIDISK_ID
)
658 type
= disk
->name
[0];
663 /* This is the simplest case. */
667 /* FIXME: probably this is not correct. */
671 /* If this is the whole disk, just return its own data. */
672 if (! disk
->partition
)
675 /* Otherwise, we must query the corresponding device to the firmware. */
677 struct grub_efidisk_data
*devices
;
678 grub_efi_handle_t handle
= 0;
679 struct grub_efidisk_data
*c
;
681 devices
= make_devices ();
682 FOR_CHILDREN (c
, devices
)
684 grub_efi_hard_drive_device_path_t
*hd
;
686 hd
= (grub_efi_hard_drive_device_path_t
*) c
->last_device_path
;
688 if ((GRUB_EFI_DEVICE_PATH_TYPE (c
->last_device_path
)
689 == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
)
690 && (GRUB_EFI_DEVICE_PATH_SUBTYPE (c
->last_device_path
)
691 == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE
)
692 && (grub_partition_get_start (disk
->partition
)
693 == (hd
->partition_start
<< (disk
->log_sector_size
694 - GRUB_DISK_SECTOR_BITS
)))
695 && (grub_partition_get_len (disk
->partition
)
696 == (hd
->partition_size
<< (disk
->log_sector_size
697 - GRUB_DISK_SECTOR_BITS
))))
704 free_devices (devices
);
718 #define NEEDED_BUFLEN sizeof ("XdXXXXXXXXXX")
720 get_diskname_from_path_real (const grub_efi_device_path_t
*path
,
721 struct grub_efidisk_data
*head
,
725 struct grub_efidisk_data
*d
;
726 for (d
= head
, count
= 0; d
; d
= d
->next
, count
++)
727 if (grub_efi_compare_device_paths (d
->device_path
, path
) == 0)
729 grub_snprintf (buf
, NEEDED_BUFLEN
- 1, "d%d", count
);
736 get_diskname_from_path (const grub_efi_device_path_t
*path
,
739 if (get_diskname_from_path_real (path
, hd_devices
, buf
+ 1))
745 if (get_diskname_from_path_real (path
, fd_devices
, buf
+ 1))
751 if (get_diskname_from_path_real (path
, cd_devices
, buf
+ 1))
759 /* Context for grub_efidisk_get_device_name. */
760 struct grub_efidisk_get_device_name_ctx
762 char *partition_name
;
763 grub_efi_hard_drive_device_path_t
*hd
;
766 /* Helper for grub_efidisk_get_device_name.
767 Find the identical partition. */
769 grub_efidisk_get_device_name_iter (grub_disk_t disk
,
770 const grub_partition_t part
, void *data
)
772 struct grub_efidisk_get_device_name_ctx
*ctx
= data
;
774 if (grub_partition_get_start (part
)
775 == (ctx
->hd
->partition_start
<< (disk
->log_sector_size
776 - GRUB_DISK_SECTOR_BITS
))
777 && grub_partition_get_len (part
)
778 == (ctx
->hd
->partition_size
<< (disk
->log_sector_size
779 - GRUB_DISK_SECTOR_BITS
)))
781 ctx
->partition_name
= grub_partition_get_name (part
);
789 grub_efidisk_get_device_name (grub_efi_handle_t
*handle
)
791 grub_efi_device_path_t
*dp
, *ldp
;
792 char device_name
[NEEDED_BUFLEN
];
794 dp
= grub_efi_get_device_path (handle
);
798 ldp
= find_last_device_path (dp
);
802 if (GRUB_EFI_DEVICE_PATH_TYPE (ldp
) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
803 && (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp
) == GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE
804 || GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp
) == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE
))
806 struct grub_efidisk_get_device_name_ctx ctx
;
808 grub_efi_device_path_t
*dup_dp
;
809 grub_disk_t parent
= 0;
811 /* It is necessary to duplicate the device path so that GRUB
813 dup_dp
= duplicate_device_path (dp
);
819 grub_efi_device_path_t
*dup_ldp
;
820 dup_ldp
= find_last_device_path (dup_dp
);
821 if (!(GRUB_EFI_DEVICE_PATH_TYPE (dup_ldp
) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
822 && (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp
) == GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE
823 || GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp
) == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE
)))
826 dup_ldp
->type
= GRUB_EFI_END_DEVICE_PATH_TYPE
;
827 dup_ldp
->subtype
= GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE
;
828 dup_ldp
->length
= sizeof (*dup_ldp
);
831 if (!get_diskname_from_path (dup_dp
, device_name
))
837 parent
= grub_disk_open (device_name
);
843 /* Find a partition which matches the hard drive device path. */
844 ctx
.partition_name
= NULL
;
845 ctx
.hd
= (grub_efi_hard_drive_device_path_t
*) ldp
;
846 if (ctx
.hd
->partition_start
== 0
847 && (ctx
.hd
->partition_size
<< (parent
->log_sector_size
848 - GRUB_DISK_SECTOR_BITS
))
849 == grub_disk_get_size (parent
))
851 dev_name
= grub_strdup (parent
->name
);
855 grub_partition_iterate (parent
, grub_efidisk_get_device_name_iter
,
858 if (! ctx
.partition_name
)
860 /* No partition found. In most cases partition is embed in
861 the root path anyway, so this is not critical.
862 This happens only if partition is on partmap that GRUB
863 doesn't need to access root.
865 grub_disk_close (parent
);
866 return grub_strdup (device_name
);
869 dev_name
= grub_xasprintf ("%s,%s", parent
->name
,
871 grub_free (ctx
.partition_name
);
873 grub_disk_close (parent
);
877 /* This may be guessed device - floppy, cdrom or entire disk. */
878 if (!get_diskname_from_path (dp
, device_name
))
880 return grub_strdup (device_name
);