Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / disk / efi / efidisk.c
blobd9d788c411999b7e762563f92858f0e99f5478a4
1 /*
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>
21 #include <grub/mm.h>
22 #include <grub/types.h>
23 #include <grub/misc.h>
24 #include <grub/err.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;
39 /* GUID. */
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))
59 break;
62 p = grub_malloc (total_size);
63 if (! p)
64 return 0;
66 grub_memcpy (p, dp, total_size);
67 return p;
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))
77 return 0;
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))
84 return p;
87 static struct grub_efidisk_data *
88 make_devices (void)
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,
97 0, &num_handles);
98 if (! handles)
99 return 0;
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);
110 if (! dp)
111 continue;
113 ldp = find_last_device_path (dp);
114 if (! ldp)
115 /* This is empty. Why? */
116 continue;
118 bio = grub_efi_open_protocol (*handle, &block_io_guid,
119 GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
120 if (! bio)
121 /* This should not happen... Why? */
122 continue;
124 d = grub_malloc (sizeof (*d));
125 if (! d)
127 /* Uggh. */
128 grub_free (handles);
129 return 0;
132 d->handle = *handle;
133 d->device_path = dp;
134 d->last_device_path = ldp;
135 d->block_io = bio;
136 d->next = devices;
137 devices = d;
140 grub_free (handles);
142 return devices;
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);
154 if (! dp)
155 return 0;
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);
161 ldp->length[1] = 0;
163 for (parent = devices; parent; parent = parent->next)
165 /* Ignore itself. */
166 if (parent == d)
167 continue;
169 if (grub_efi_compare_device_paths (parent->device_path, dp) == 0)
170 break;
173 grub_free (dp);
174 return parent;
177 static int
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);
189 if (! dp)
190 return 0;
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);
196 ldp->length[1] = 0;
198 if (grub_efi_compare_device_paths (dp, d->device_path) == 0)
199 if (hook (p))
201 grub_free (dp);
202 return 1;
205 grub_free (dp);
208 return 0;
211 /* Add a device into a list of devices in an ascending order. */
212 static void
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))
220 int ret;
222 ret = grub_efi_compare_device_paths (find_last_device_path ((*p)->device_path),
223 find_last_device_path (d->device_path));
224 if (ret == 0)
225 ret = grub_efi_compare_device_paths ((*p)->device_path,
226 d->device_path);
227 if (ret == 0)
228 return;
229 else if (ret > 0)
230 break;
233 n = grub_malloc (sizeof (*n));
234 if (! n)
235 return;
237 grub_memcpy (n, d, sizeof (*n));
238 n->next = (*p);
239 (*p) = n;
242 /* Name the devices. */
243 static void
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;
254 if (! dp)
255 continue;
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:
264 is_hard_drive = 1;
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);
271 if (!parent)
273 #ifdef DEBUG_NAMES
274 grub_printf ("skipping orphaned partition: ");
275 grub_efi_print_device_path (d->device_path);
276 #endif
277 break;
279 parent2 = find_parent_device (devices, parent);
280 if (parent2)
282 #ifdef DEBUG_NAMES
283 grub_printf ("skipping subpartition: ");
284 grub_efi_print_device_path (d->device_path);
285 #endif
286 /* Mark itself as used. */
287 d->last_device_path = 0;
288 break;
290 if (!parent->last_device_path)
292 d->last_device_path = 0;
293 break;
295 if (is_hard_drive)
297 #ifdef DEBUG_NAMES
298 grub_printf ("adding a hard drive by a partition: ");
299 grub_efi_print_device_path (parent->device_path);
300 #endif
301 add_device (&hd_devices, parent);
303 else
305 #ifdef DEBUG_NAMES
306 grub_printf ("adding a cdrom by a partition: ");
307 grub_efi_print_device_path (parent->device_path);
308 #endif
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;
317 break;
319 default:
320 #ifdef DEBUG_NAMES
321 grub_printf ("skipping other type: ");
322 grub_efi_print_device_path (d->device_path);
323 #endif
324 /* For now, ignore the others. */
325 break;
328 else
330 #ifdef DEBUG_NAMES
331 grub_printf ("skipping non-media: ");
332 grub_efi_print_device_path (d->device_path);
333 #endif
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;
344 if (! dp)
345 continue;
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. */
352 #ifdef DEBUG_NAMES
353 grub_printf ("adding a floppy by guessing: ");
354 grub_efi_print_device_path (d->device_path);
355 #endif
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
361 CDROM drive. */
362 #ifdef DEBUG_NAMES
363 grub_printf ("adding a cdrom by guessing: ");
364 grub_efi_print_device_path (d->device_path);
365 #endif
366 add_device (&cd_devices, d);
368 else
370 /* The default is a hard drive. */
371 #ifdef DEBUG_NAMES
372 grub_printf ("adding a hard drive by guessing: ");
373 grub_efi_print_device_path (d->device_path);
374 #endif
375 add_device (&hd_devices, d);
380 static void
381 free_devices (struct grub_efidisk_data *devices)
383 struct grub_efidisk_data *p, *q;
385 for (p = devices; p; p = q)
387 q = p->next;
388 grub_free (p);
392 /* Enumerate all disks to name devices. */
393 static void
394 enumerate_disks (void)
396 struct grub_efidisk_data *devices;
398 devices = make_devices ();
399 if (! devices)
400 return;
402 name_devices (devices);
403 free_devices (devices);
406 static int
407 grub_efidisk_iterate (int (*hook) (const char *name),
408 grub_disk_pull_t pull)
410 struct grub_efidisk_data *d;
411 char buf[16];
412 int count;
414 switch (pull)
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);
421 if (hook (buf))
422 return 1;
424 break;
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);
430 if (hook (buf))
431 return 1;
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);
438 if (hook (buf))
439 return 1;
441 break;
442 default:
443 return 0;
446 return 0;
449 static int
450 get_drive_number (const char *name)
452 unsigned long drive;
454 if ((name[0] != 'f' && name[0] != 'h' && name[0] != 'c') || name[1] != 'd')
455 goto fail;
457 drive = grub_strtoul (name + 2, 0, 10);
458 if (grub_errno != GRUB_ERR_NONE)
459 goto fail;
461 return (int) drive ;
463 fail:
464 grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a efidisk");
465 return -1;
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--)
476 if (num == 0)
477 return d;
479 return 0;
482 static grub_err_t
483 grub_efidisk_open (const char *name, struct grub_disk *disk)
485 int num;
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);
492 if (num < 0)
493 return grub_errno;
495 switch (name[0])
497 case 'f':
498 d = get_device (fd_devices, num);
499 break;
500 case 'c':
501 d = get_device (cd_devices, num);
502 break;
503 case 'h':
504 d = get_device (hd_devices, num);
505 break;
506 default:
507 /* Never reach here. */
508 break;
511 if (! d)
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",
523 m->block_size);
524 for (disk->log_sector_size = 0;
525 (1U << disk->log_sector_size) < m->block_size;
526 disk->log_sector_size++);
527 disk->data = d;
529 grub_dprintf ("efidisk", "opening %s succeeded\n", name);
531 return GRUB_ERR_NONE;
534 static void
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);
541 static grub_err_t
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;
550 d = disk->data;
551 bio = d->block_io;
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,
560 buf);
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,
565 disk->name);
567 return GRUB_ERR_NONE;
570 static grub_err_t
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;
579 d = disk->data;
580 bio = d->block_io;
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,
589 (void *) buf);
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 =
600 .name = "efidisk",
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,
607 .next = 0
610 void
611 grub_efidisk_init (void)
613 enumerate_disks ();
614 grub_disk_dev_register (&grub_efidisk_dev);
617 void
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. */
627 grub_efi_handle_t
628 grub_efidisk_get_device_handle (grub_disk_t disk)
630 struct grub_efidisk_data *d;
631 char type;
633 if (disk->dev->id != GRUB_DISK_DEVICE_EFIDISK_ID)
634 return 0;
636 d = disk->data;
637 type = disk->name[0];
639 switch (type)
641 case 'f':
642 /* This is the simplest case. */
643 return d->handle;
645 case 'c':
646 /* FIXME: probably this is not correct. */
647 return d->handle;
649 case 'h':
650 /* If this is the whole disk, just return its own data. */
651 if (! disk->partition)
652 return d->handle;
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))
675 handle = c->handle;
676 return 1;
679 return 0;
682 devices = make_devices ();
683 iterate_child_devices (devices, d, find_partition);
684 free_devices (devices);
686 if (handle != 0)
687 return handle;
689 break;
691 default:
692 break;
695 return 0;
698 #define NEEDED_BUFLEN sizeof ("XdXXXXXXXXXX")
699 static inline int
700 get_diskname_from_path_real (const grub_efi_device_path_t *path,
701 struct grub_efidisk_data *head,
702 char *buf)
704 int count = 0;
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);
710 return 1;
712 return 0;
715 static inline int
716 get_diskname_from_path (const grub_efi_device_path_t *path,
717 char *buf)
719 if (get_diskname_from_path_real (path, hd_devices, buf + 1))
721 buf[0] = 'h';
722 return 1;
725 if (get_diskname_from_path_real (path, fd_devices, buf + 1))
727 buf[0] = 'f';
728 return 1;
731 if (get_diskname_from_path_real (path, cd_devices, buf + 1))
733 buf[0] = 'c';
734 return 1;
736 return 0;
739 char *
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);
746 if (! dp)
747 return 0;
749 ldp = find_last_device_path (dp);
750 if (! ldp)
751 return 0;
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;
758 char *dev_name;
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);
773 return 1;
776 return 0;
779 /* It is necessary to duplicate the device path so that GRUB
780 can overwrite it. */
781 dup_dp = duplicate_device_path (dp);
782 if (! dup_dp)
783 return 0;
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))
792 return 0;
793 parent = grub_disk_open (device_name);
794 grub_free (dup_dp);
796 if (! parent)
797 return 0;
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);
806 else
808 grub_partition_iterate (parent, find_partition);
810 if (! partition_name)
812 grub_disk_close (parent);
813 return 0;
816 dev_name = grub_xasprintf ("%s,%s", parent->name, partition_name);
817 grub_free (partition_name);
819 grub_disk_close (parent);
821 return dev_name;
823 else
825 /* This should be an entire disk. */
826 if (!get_diskname_from_path (dp, device_name))
827 return 0;
828 return grub_strdup (device_name);