remove all trailing whitespace
[grub2/phcoder/solaris.git] / disk / efi / efidisk.c
blobde848594a1861f400c9a0609cbb63be378b3d20d
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 grub_efi_disk_io_t *disk_io;
37 struct grub_efidisk_data *next;
40 /* GUIDs. */
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))
61 break;
64 p = grub_malloc (total_size);
65 if (! p)
66 return 0;
68 grub_memcpy (p, dp, total_size);
69 return p;
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))
79 return 0;
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))
86 return p;
89 /* Compare device paths. */
90 static int
91 compare_device_paths (const grub_efi_device_path_t *dp1,
92 const grub_efi_device_path_t *dp2)
94 if (! dp1 || ! dp2)
95 /* Return non-zero. */
96 return 1;
98 while (1)
100 grub_efi_uint8_t type1, type2;
101 grub_efi_uint8_t subtype1, subtype2;
102 grub_efi_uint16_t len1, len2;
103 int ret;
105 type1 = GRUB_EFI_DEVICE_PATH_TYPE (dp1);
106 type2 = GRUB_EFI_DEVICE_PATH_TYPE (dp2);
108 if (type1 != type2)
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);
120 if (len1 != len2)
121 return (int) len1 - (int) len2;
123 ret = grub_memcmp (dp1, dp2, len1);
124 if (ret != 0)
125 return ret;
127 if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1))
128 break;
130 dp1 = (grub_efi_device_path_t *) ((char *) dp1 + len1);
131 dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2);
134 return 0;
137 static struct grub_efidisk_data *
138 make_devices (void)
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,
147 0, &num_handles);
148 if (! handles)
149 return 0;
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);
161 if (! dp)
162 continue;
164 ldp = find_last_device_path (dp);
165 if (! ldp)
166 /* This is empty. Why? */
167 continue;
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);
173 if (! bio || ! dio)
174 /* This should not happen... Why? */
175 continue;
177 d = grub_malloc (sizeof (*d));
178 if (! d)
180 /* Uggh. */
181 grub_free (handles);
182 return 0;
185 d->handle = *handle;
186 d->device_path = dp;
187 d->last_device_path = ldp;
188 d->block_io = bio;
189 d->disk_io = dio;
190 d->next = devices;
191 devices = d;
194 grub_free (handles);
196 return devices;
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);
208 if (! dp)
209 return 0;
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);
215 ldp->length[1] = 0;
217 for (parent = devices; parent; parent = parent->next)
219 /* Ignore itself. */
220 if (parent == d)
221 continue;
223 if (compare_device_paths (parent->device_path, dp) == 0)
225 /* Found. */
226 if (! parent->last_device_path)
227 parent = 0;
229 break;
233 grub_free (dp);
234 return parent;
237 static int
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);
249 if (! dp)
250 return 0;
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);
256 ldp->length[1] = 0;
258 if (compare_device_paths (dp, d->device_path) == 0)
259 if (hook (p))
261 grub_free (dp);
262 return 1;
265 grub_free (dp);
268 return 0;
271 /* Add a device into a list of devices in an ascending order. */
272 static void
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))
280 int ret;
282 ret = compare_device_paths (find_last_device_path ((*p)->device_path),
283 find_last_device_path (d->device_path));
284 if (ret == 0)
285 ret = compare_device_paths ((*p)->device_path,
286 d->device_path);
287 if (ret == 0)
288 return;
289 else if (ret > 0)
290 break;
293 n = grub_malloc (sizeof (*n));
294 if (! n)
295 return;
297 grub_memcpy (n, d, sizeof (*n));
298 n->next = (*p);
299 (*p) = n;
302 /* Name the devices. */
303 static void
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;
314 if (! dp)
315 continue;
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:
324 is_hard_drive = 1;
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);
331 if (parent)
333 if (is_hard_drive)
335 #if 0
336 grub_printf ("adding a hard drive by a partition: ");
337 grub_print_device_path (parent->device_path);
338 #endif
339 add_device (&hd_devices, parent);
341 else
343 #if 0
344 grub_printf ("adding a cdrom by a partition: ");
345 grub_print_device_path (parent->device_path);
346 #endif
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;
356 break;
358 default:
359 /* For now, ignore the others. */
360 break;
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;
372 if (! dp)
373 continue;
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. */
380 #if 0
381 grub_printf ("adding a floppy by guessing: ");
382 grub_print_device_path (d->device_path);
383 #endif
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
389 CDROM drive. */
390 #if 0
391 grub_printf ("adding a cdrom by guessing: ");
392 grub_print_device_path (d->device_path);
393 #endif
394 add_device (&cd_devices, d);
396 else
398 /* The default is a hard drive. */
399 #if 0
400 grub_printf ("adding a hard drive by guessing: ");
401 grub_print_device_path (d->device_path);
402 #endif
403 add_device (&hd_devices, d);
408 static void
409 free_devices (struct grub_efidisk_data *devices)
411 struct grub_efidisk_data *p, *q;
413 for (p = devices; p; p = q)
415 q = p->next;
416 grub_free (p);
420 /* Enumerate all disks to name devices. */
421 static void
422 enumerate_disks (void)
424 struct grub_efidisk_data *devices;
426 devices = make_devices ();
427 if (! devices)
428 return;
430 name_devices (devices);
431 free_devices (devices);
434 static int
435 grub_efidisk_iterate (int (*hook) (const char *name))
437 struct grub_efidisk_data *d;
438 char buf[16];
439 int count;
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);
445 if (hook (buf))
446 return 1;
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);
453 if (hook (buf))
454 return 1;
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);
461 if (hook (buf))
462 return 1;
465 return 0;
468 static int
469 get_drive_number (const char *name)
471 unsigned long drive;
473 if ((name[0] != 'f' && name[0] != 'h' && name[0] != 'c') || name[1] != 'd')
474 goto fail;
476 drive = grub_strtoul (name + 2, 0, 10);
477 if (grub_errno != GRUB_ERR_NONE)
478 goto fail;
480 return (int) drive ;
482 fail:
483 grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a efidisk");
484 return -1;
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--)
495 if (num == 0)
496 return d;
498 return 0;
501 static grub_err_t
502 grub_efidisk_open (const char *name, struct grub_disk *disk)
504 int num;
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);
511 if (num < 0)
512 return grub_errno;
514 switch (name[0])
516 case 'f':
517 disk->has_partitions = 0;
518 d = get_device (fd_devices, num);
519 break;
520 case 'c':
521 /* FIXME: a CDROM should have partitions, but not implemented yet. */
522 disk->has_partitions = 0;
523 d = get_device (cd_devices, num);
524 break;
525 case 'h':
526 disk->has_partitions = 1;
527 d = get_device (hd_devices, num);
528 break;
529 default:
530 /* Never reach here. */
531 break;
534 if (! d)
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));
545 disk->data = d;
547 grub_dprintf ("efidisk", "opening %s succeeded\n", name);
549 return GRUB_ERR_NONE;
552 static void
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);
559 static grub_err_t
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;
569 d = disk->data;
570 dio = d->disk_io;
571 bio = d->block_io;
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,
580 buf);
581 if (status != GRUB_EFI_SUCCESS)
582 return grub_error (GRUB_ERR_READ_ERROR, "efidisk read error");
584 return GRUB_ERR_NONE;
587 static grub_err_t
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;
597 d = disk->data;
598 dio = d->disk_io;
599 bio = d->block_io;
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,
608 (void *) buf);
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 =
617 .name = "efidisk",
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,
624 .next = 0
627 void
628 grub_efidisk_init (void)
630 enumerate_disks ();
631 grub_disk_dev_register (&grub_efidisk_dev);
634 void
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. */
644 grub_efi_handle_t
645 grub_efidisk_get_device_handle (grub_disk_t disk)
647 struct grub_efidisk_data *d;
648 char type;
650 if (disk->dev->id != GRUB_DISK_DEVICE_EFIDISK_ID)
651 return 0;
653 d = disk->data;
654 type = disk->name[0];
656 switch (type)
658 case 'f':
659 /* This is the simplest case. */
660 return d->handle;
662 case 'c':
663 /* FIXME: probably this is not correct. */
664 return d->handle;
666 case 'h':
667 /* If this is the whole disk, just return its own data. */
668 if (! disk->partition)
669 return d->handle;
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))
692 handle = c->handle;
693 return 1;
696 return 0;
699 devices = make_devices ();
700 iterate_child_devices (devices, d, find_partition);
701 free_devices (devices);
703 if (handle != 0)
704 return handle;
706 break;
708 default:
709 break;
712 return 0;
715 char *
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);
721 if (! dp)
722 return 0;
724 ldp = find_last_device_path (dp);
725 if (! ldp)
726 return 0;
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;
735 char *device_name;
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)
744 grub_disk_t disk;
746 disk = grub_disk_open (name);
747 if (! disk)
748 return 1;
750 if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID)
752 struct grub_efidisk_data *d;
754 d = disk->data;
755 if (compare_device_paths (d->device_path, dup_dp) == 0)
757 parent = disk;
758 return 1;
762 grub_disk_close (disk);
763 return 0;
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);
774 return 1;
777 return 0;
780 /* It is necessary to duplicate the device path so that GRUB
781 can overwrite it. */
782 dup_dp = duplicate_device_path (dp);
783 if (! dup_dp)
784 return 0;
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);
793 grub_free (dup_dp);
795 if (! parent)
796 return 0;
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);
805 return 0;
808 device_name = grub_malloc (grub_strlen (parent->name) + 1
809 + grub_strlen (partition_name) + 1);
810 if (! device_name)
812 grub_free (partition_name);
813 grub_disk_close (parent);
814 return 0;
817 grub_sprintf (device_name, "%s,%s", parent->name, partition_name);
818 grub_free (partition_name);
819 grub_disk_close (parent);
820 return device_name;
822 else
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)
830 grub_disk_t disk;
832 disk = grub_disk_open (name);
833 if (! disk)
834 return 1;
836 if (disk->id == GRUB_DISK_DEVICE_EFIDISK_ID)
838 struct grub_efidisk_data *d;
840 d = disk->data;
841 if (compare_device_paths (d->device_path, dp) == 0)
843 device_name = grub_strdup (disk->name);
844 grub_disk_close (disk);
845 return 1;
849 grub_disk_close (disk);
850 return 0;
854 grub_efidisk_iterate (find_disk);
855 return device_name;
858 return 0;