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
[0] = sizeof (*ldp
);
163 for (parent
= devices
; parent
; parent
= parent
->next
)
169 if (grub_efi_compare_device_paths (parent
->device_path
, dp
) == 0)
178 iterate_child_devices (struct grub_efidisk_data
*devices
,
179 struct grub_efidisk_data
*d
,
180 int (*hook
) (struct grub_efidisk_data
*child
))
182 struct grub_efidisk_data
*p
;
184 for (p
= devices
; p
; p
= p
->next
)
186 grub_efi_device_path_t
*dp
, *ldp
;
188 dp
= duplicate_device_path (p
->device_path
);
192 ldp
= find_last_device_path (dp
);
193 ldp
->type
= GRUB_EFI_END_DEVICE_PATH_TYPE
;
194 ldp
->subtype
= GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE
;
195 ldp
->length
[0] = sizeof (*ldp
);
198 if (grub_efi_compare_device_paths (dp
, d
->device_path
) == 0)
211 /* Add a device into a list of devices in an ascending order. */
213 add_device (struct grub_efidisk_data
**devices
, struct grub_efidisk_data
*d
)
215 struct grub_efidisk_data
**p
;
216 struct grub_efidisk_data
*n
;
218 for (p
= devices
; *p
; p
= &((*p
)->next
))
222 ret
= grub_efi_compare_device_paths (find_last_device_path ((*p
)->device_path
),
223 find_last_device_path (d
->device_path
));
225 ret
= grub_efi_compare_device_paths ((*p
)->device_path
,
233 n
= grub_malloc (sizeof (*n
));
237 grub_memcpy (n
, d
, sizeof (*n
));
242 /* Name the devices. */
244 name_devices (struct grub_efidisk_data
*devices
)
246 struct grub_efidisk_data
*d
;
248 /* First, identify devices by media device paths. */
249 for (d
= devices
; d
; d
= d
->next
)
251 grub_efi_device_path_t
*dp
;
253 dp
= d
->last_device_path
;
257 if (GRUB_EFI_DEVICE_PATH_TYPE (dp
) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
)
259 int is_hard_drive
= 0;
261 switch (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp
))
263 case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE
:
265 /* Fall through by intention. */
266 case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE
:
268 struct grub_efidisk_data
*parent
, *parent2
;
270 parent
= find_parent_device (devices
, d
);
274 grub_printf ("skipping orphaned partition: ");
275 grub_efi_print_device_path (d
->device_path
);
279 parent2
= find_parent_device (devices
, parent
);
283 grub_printf ("skipping subpartition: ");
284 grub_efi_print_device_path (d
->device_path
);
286 /* Mark itself as used. */
287 d
->last_device_path
= 0;
290 if (!parent
->last_device_path
)
292 d
->last_device_path
= 0;
298 grub_printf ("adding a hard drive by a partition: ");
299 grub_efi_print_device_path (parent
->device_path
);
301 add_device (&hd_devices
, parent
);
306 grub_printf ("adding a cdrom by a partition: ");
307 grub_efi_print_device_path (parent
->device_path
);
309 add_device (&cd_devices
, parent
);
312 /* Mark the parent as used. */
313 parent
->last_device_path
= 0;
315 /* Mark itself as used. */
316 d
->last_device_path
= 0;
321 grub_printf ("skipping other type: ");
322 grub_efi_print_device_path (d
->device_path
);
324 /* For now, ignore the others. */
331 grub_printf ("skipping non-media: ");
332 grub_efi_print_device_path (d
->device_path
);
337 /* Let's see what can be added more. */
338 for (d
= devices
; d
; d
= d
->next
)
340 grub_efi_device_path_t
*dp
;
341 grub_efi_block_io_media_t
*m
;
343 dp
= d
->last_device_path
;
347 m
= d
->block_io
->media
;
348 if (m
->logical_partition
)
350 /* Only one partition in a non-media device. Assume that this
351 is a floppy drive. */
353 grub_printf ("adding a floppy by guessing: ");
354 grub_efi_print_device_path (d
->device_path
);
356 add_device (&fd_devices
, d
);
358 else if (m
->read_only
&& m
->block_size
> GRUB_DISK_SECTOR_SIZE
)
360 /* This check is too heuristic, but assume that this is a
363 grub_printf ("adding a cdrom by guessing: ");
364 grub_efi_print_device_path (d
->device_path
);
366 add_device (&cd_devices
, d
);
370 /* The default is a hard drive. */
372 grub_printf ("adding a hard drive by guessing: ");
373 grub_efi_print_device_path (d
->device_path
);
375 add_device (&hd_devices
, d
);
381 free_devices (struct grub_efidisk_data
*devices
)
383 struct grub_efidisk_data
*p
, *q
;
385 for (p
= devices
; p
; p
= q
)
392 /* Enumerate all disks to name devices. */
394 enumerate_disks (void)
396 struct grub_efidisk_data
*devices
;
398 devices
= make_devices ();
402 name_devices (devices
);
403 free_devices (devices
);
407 grub_efidisk_iterate (int (*hook
) (const char *name
),
408 grub_disk_pull_t pull
)
410 struct grub_efidisk_data
*d
;
416 case GRUB_DISK_PULL_NONE
:
417 for (d
= hd_devices
, count
= 0; d
; d
= d
->next
, count
++)
419 grub_snprintf (buf
, sizeof (buf
), "hd%d", count
);
420 grub_dprintf ("efidisk", "iterating %s\n", buf
);
425 case GRUB_DISK_PULL_REMOVABLE
:
426 for (d
= fd_devices
, count
= 0; d
; d
= d
->next
, count
++)
428 grub_snprintf (buf
, sizeof (buf
), "fd%d", count
);
429 grub_dprintf ("efidisk", "iterating %s\n", buf
);
434 for (d
= cd_devices
, count
= 0; d
; d
= d
->next
, count
++)
436 grub_snprintf (buf
, sizeof (buf
), "cd%d", count
);
437 grub_dprintf ("efidisk", "iterating %s\n", buf
);
450 get_drive_number (const char *name
)
454 if ((name
[0] != 'f' && name
[0] != 'h' && name
[0] != 'c') || name
[1] != 'd')
457 drive
= grub_strtoul (name
+ 2, 0, 10);
458 if (grub_errno
!= GRUB_ERR_NONE
)
464 grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "not a efidisk");
468 static struct grub_efidisk_data
*
469 get_device (struct grub_efidisk_data
*devices
, int num
)
471 struct grub_efidisk_data
*d
;
473 for (d
= devices
; d
&& num
; d
= d
->next
, num
--)
483 grub_efidisk_open (const char *name
, struct grub_disk
*disk
)
486 struct grub_efidisk_data
*d
= 0;
487 grub_efi_block_io_media_t
*m
;
489 grub_dprintf ("efidisk", "opening %s\n", name
);
491 num
= get_drive_number (name
);
498 d
= get_device (fd_devices
, num
);
501 d
= get_device (cd_devices
, num
);
504 d
= get_device (hd_devices
, num
);
507 /* Never reach here. */
512 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
, "no such device");
514 disk
->id
= ((num
<< 8) | name
[0]);
515 m
= d
->block_io
->media
;
516 /* FIXME: Probably it is better to store the block size in the disk,
517 and total sectors should be replaced with total blocks. */
518 grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n",
519 m
, (unsigned long long) m
->last_block
, m
->block_size
);
520 disk
->total_sectors
= m
->last_block
+ 1;
521 if (m
->block_size
& (m
->block_size
- 1) || !m
->block_size
)
522 return grub_error (GRUB_ERR_IO
, "invalid sector size %d",
524 for (disk
->log_sector_size
= 0;
525 (1U << disk
->log_sector_size
) < m
->block_size
;
526 disk
->log_sector_size
++);
529 grub_dprintf ("efidisk", "opening %s succeeded\n", name
);
531 return GRUB_ERR_NONE
;
535 grub_efidisk_close (struct grub_disk
*disk
__attribute__ ((unused
)))
537 /* EFI disks do not allocate extra memory, so nothing to do here. */
538 grub_dprintf ("efidisk", "closing %s\n", disk
->name
);
542 grub_efidisk_read (struct grub_disk
*disk
, grub_disk_addr_t sector
,
543 grub_size_t size
, char *buf
)
545 /* For now, use the disk io interface rather than the block io's. */
546 struct grub_efidisk_data
*d
;
547 grub_efi_block_io_t
*bio
;
548 grub_efi_status_t status
;
553 grub_dprintf ("efidisk",
554 "reading 0x%lx sectors at the sector 0x%llx from %s\n",
555 (unsigned long) size
, (unsigned long long) sector
, disk
->name
);
557 status
= efi_call_5 (bio
->read_blocks
, bio
, bio
->media
->media_id
,
558 (grub_efi_uint64_t
) sector
,
559 (grub_efi_uintn_t
) size
<< disk
->log_sector_size
,
561 if (status
!= GRUB_EFI_SUCCESS
)
562 return grub_error (GRUB_ERR_READ_ERROR
,
563 N_("failure reading sector 0x%llx from `%s'"),
564 (unsigned long long) sector
,
567 return GRUB_ERR_NONE
;
571 grub_efidisk_write (struct grub_disk
*disk
, grub_disk_addr_t sector
,
572 grub_size_t size
, const char *buf
)
574 /* For now, use the disk io interface rather than the block io's. */
575 struct grub_efidisk_data
*d
;
576 grub_efi_block_io_t
*bio
;
577 grub_efi_status_t status
;
582 grub_dprintf ("efidisk",
583 "writing 0x%lx sectors at the sector 0x%llx to %s\n",
584 (unsigned long) size
, (unsigned long long) sector
, disk
->name
);
586 status
= efi_call_5 (bio
->write_blocks
, bio
, bio
->media
->media_id
,
587 (grub_efi_uint64_t
) sector
,
588 (grub_efi_uintn_t
) size
<< disk
->log_sector_size
,
590 if (status
!= GRUB_EFI_SUCCESS
)
591 return grub_error (GRUB_ERR_WRITE_ERROR
,
592 N_("failure writing sector 0x%llx to `%s'"),
593 (unsigned long long) sector
, disk
->name
);
595 return GRUB_ERR_NONE
;
598 static struct grub_disk_dev grub_efidisk_dev
=
601 .id
= GRUB_DISK_DEVICE_EFIDISK_ID
,
602 .iterate
= grub_efidisk_iterate
,
603 .open
= grub_efidisk_open
,
604 .close
= grub_efidisk_close
,
605 .read
= grub_efidisk_read
,
606 .write
= grub_efidisk_write
,
611 grub_efidisk_init (void)
614 grub_disk_dev_register (&grub_efidisk_dev
);
618 grub_efidisk_fini (void)
620 free_devices (fd_devices
);
621 free_devices (hd_devices
);
622 free_devices (cd_devices
);
623 grub_disk_dev_unregister (&grub_efidisk_dev
);
626 /* Some utility functions to map GRUB devices with EFI devices. */
628 grub_efidisk_get_device_handle (grub_disk_t disk
)
630 struct grub_efidisk_data
*d
;
633 if (disk
->dev
->id
!= GRUB_DISK_DEVICE_EFIDISK_ID
)
637 type
= disk
->name
[0];
642 /* This is the simplest case. */
646 /* FIXME: probably this is not correct. */
650 /* If this is the whole disk, just return its own data. */
651 if (! disk
->partition
)
654 /* Otherwise, we must query the corresponding device to the firmware. */
656 struct grub_efidisk_data
*devices
;
657 grub_efi_handle_t handle
= 0;
658 auto int find_partition (struct grub_efidisk_data
*c
);
660 int find_partition (struct grub_efidisk_data
*c
)
662 grub_efi_hard_drive_device_path_t hd
;
664 grub_memcpy (&hd
, c
->last_device_path
, sizeof (hd
));
666 if ((GRUB_EFI_DEVICE_PATH_TYPE (c
->last_device_path
)
667 == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
)
668 && (GRUB_EFI_DEVICE_PATH_SUBTYPE (c
->last_device_path
)
669 == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE
)
670 && (grub_partition_get_start (disk
->partition
)
671 == hd
.partition_start
)
672 && (grub_partition_get_len (disk
->partition
)
673 == hd
.partition_size
))
682 devices
= make_devices ();
683 iterate_child_devices (devices
, d
, find_partition
);
684 free_devices (devices
);
698 #define NEEDED_BUFLEN sizeof ("XdXXXXXXXXXX")
700 get_diskname_from_path_real (const grub_efi_device_path_t
*path
,
701 struct grub_efidisk_data
*head
,
705 struct grub_efidisk_data
*d
;
706 for (d
= head
, count
= 0; d
; d
= d
->next
, count
++)
707 if (grub_efi_compare_device_paths (d
->device_path
, path
) == 0)
709 grub_snprintf (buf
, NEEDED_BUFLEN
- 1, "d%d", count
);
716 get_diskname_from_path (const grub_efi_device_path_t
*path
,
719 if (get_diskname_from_path_real (path
, hd_devices
, buf
+ 1))
725 if (get_diskname_from_path_real (path
, fd_devices
, buf
+ 1))
731 if (get_diskname_from_path_real (path
, cd_devices
, buf
+ 1))
740 grub_efidisk_get_device_name (grub_efi_handle_t
*handle
)
742 grub_efi_device_path_t
*dp
, *ldp
;
743 char device_name
[NEEDED_BUFLEN
];
745 dp
= grub_efi_get_device_path (handle
);
749 ldp
= find_last_device_path (dp
);
753 if (GRUB_EFI_DEVICE_PATH_TYPE (ldp
) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
754 && (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp
)
755 == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE
))
757 char *partition_name
= NULL
;
759 grub_efi_device_path_t
*dup_dp
, *dup_ldp
;
760 grub_efi_hard_drive_device_path_t hd
;
761 grub_disk_t parent
= 0;
763 auto int find_partition (grub_disk_t disk
, const grub_partition_t part
);
765 /* Find the identical partition. */
766 int find_partition (grub_disk_t disk
__attribute__ ((unused
)),
767 const grub_partition_t part
)
769 if (grub_partition_get_start (part
) == hd
.partition_start
770 && grub_partition_get_len (part
) == hd
.partition_size
)
772 partition_name
= grub_partition_get_name (part
);
779 /* It is necessary to duplicate the device path so that GRUB
781 dup_dp
= duplicate_device_path (dp
);
785 dup_ldp
= find_last_device_path (dup_dp
);
786 dup_ldp
->type
= GRUB_EFI_END_DEVICE_PATH_TYPE
;
787 dup_ldp
->subtype
= GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE
;
788 dup_ldp
->length
[0] = sizeof (*dup_ldp
);
789 dup_ldp
->length
[1] = 0;
791 if (!get_diskname_from_path (dup_dp
, device_name
))
793 parent
= grub_disk_open (device_name
);
799 /* Find a partition which matches the hard drive device path. */
800 grub_memcpy (&hd
, ldp
, sizeof (hd
));
801 if (hd
.partition_start
== 0
802 && hd
.partition_size
== grub_disk_get_size (parent
))
804 dev_name
= grub_strdup (parent
->name
);
808 grub_partition_iterate (parent
, find_partition
);
810 if (! partition_name
)
812 grub_disk_close (parent
);
816 dev_name
= grub_xasprintf ("%s,%s", parent
->name
, partition_name
);
817 grub_free (partition_name
);
819 grub_disk_close (parent
);
825 /* This should be an entire disk. */
826 if (!get_diskname_from_path (dp
, device_name
))
828 return grub_strdup (device_name
);