source: grub-pc/trunk/fuentes/.pc/efidisk-device-path-helpers-for-efinet.patch/grub-core/disk/efi/efidisk.c @ 22

Last change on this file since 22 was 22, checked in by mabarracus, 4 years ago

updated version and apply net.ifnames=0 into debian/rules

File size: 21.4 KB
Line 
1/*
2 *  GRUB  --  GRand Unified Bootloader
3 *  Copyright (C) 2006,2007,2008  Free Software Foundation, Inc.
4 *
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.
9 *
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.
14 *
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/>.
17 */
18
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>
29
30struct grub_efidisk_data
31{
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;
37};
38
39/* GUID.  */
40static grub_efi_guid_t block_io_guid = GRUB_EFI_BLOCK_IO_GUID;
41
42static struct grub_efidisk_data *fd_devices;
43static struct grub_efidisk_data *hd_devices;
44static struct grub_efidisk_data *cd_devices;
45
46/* Duplicate a device path.  */
47static grub_efi_device_path_t *
48duplicate_device_path (const grub_efi_device_path_t *dp)
49{
50  grub_efi_device_path_t *p;
51  grub_size_t total_size = 0;
52
53  for (p = (grub_efi_device_path_t *) dp;
54       ;
55       p = GRUB_EFI_NEXT_DEVICE_PATH (p))
56    {
57      total_size += GRUB_EFI_DEVICE_PATH_LENGTH (p);
58      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (p))
59        break;
60    }
61
62  p = grub_malloc (total_size);
63  if (! p)
64    return 0;
65
66  grub_memcpy (p, dp, total_size);
67  return p;
68}
69
70/* Return the device path node right before the end node.  */
71static grub_efi_device_path_t *
72find_last_device_path (const grub_efi_device_path_t *dp)
73{
74  grub_efi_device_path_t *next, *p;
75
76  if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
77    return 0;
78
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))
82    ;
83
84  return p;
85}
86
87static struct grub_efidisk_data *
88make_devices (void)
89{
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;
94
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;
100
101  /* Make a linked list of devices.  */
102  for (handle = handles; num_handles--; handle++)
103    {
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;
108
109      dp = grub_efi_get_device_path (*handle);
110      if (! dp)
111        continue;
112
113      ldp = find_last_device_path (dp);
114      if (! ldp)
115        /* This is empty. Why?  */
116        continue;
117
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;
123
124      d = grub_malloc (sizeof (*d));
125      if (! d)
126        {
127          /* Uggh.  */
128          grub_free (handles);
129          return 0;
130        }
131
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;
138    }
139
140  grub_free (handles);
141
142  return devices;
143}
144
145/* Find the parent device.  */
146static struct grub_efidisk_data *
147find_parent_device (struct grub_efidisk_data *devices,
148                    struct grub_efidisk_data *d)
149{
150  grub_efi_device_path_t *dp, *ldp;
151  struct grub_efidisk_data *parent;
152
153  dp = duplicate_device_path (d->device_path);
154  if (! dp)
155    return 0;
156
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);
161
162  for (parent = devices; parent; parent = parent->next)
163    {
164      /* Ignore itself.  */
165      if (parent == d)
166        continue;
167
168      if (grub_efi_compare_device_paths (parent->device_path, dp) == 0)
169        break;
170    }
171
172  grub_free (dp);
173  return parent;
174}
175
176static int
177is_child (struct grub_efidisk_data *child,
178          struct grub_efidisk_data *parent)
179{
180  grub_efi_device_path_t *dp, *ldp;
181  int ret;
182
183  dp = duplicate_device_path (child->device_path);
184  if (! dp)
185    return 0;
186
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);
191
192  ret = (grub_efi_compare_device_paths (dp, parent->device_path) == 0);
193  grub_free (dp);
194  return ret;
195}
196
197#define FOR_CHILDREN(p, dev) for (p = dev; p; p = p->next) if (is_child (p, d))
198
199/* Add a device into a list of devices in an ascending order.  */
200static void
201add_device (struct grub_efidisk_data **devices, struct grub_efidisk_data *d)
202{
203  struct grub_efidisk_data **p;
204  struct grub_efidisk_data *n;
205
206  for (p = devices; *p; p = &((*p)->next))
207    {
208      int ret;
209
210      ret = grub_efi_compare_device_paths (find_last_device_path ((*p)->device_path),
211                                           find_last_device_path (d->device_path));
212      if (ret == 0)
213        ret = grub_efi_compare_device_paths ((*p)->device_path,
214                                             d->device_path);
215      if (ret == 0)
216        return;
217      else if (ret > 0)
218        break;
219    }
220
221  n = grub_malloc (sizeof (*n));
222  if (! n)
223    return;
224
225  grub_memcpy (n, d, sizeof (*n));
226  n->next = (*p);
227  (*p) = n;
228}
229
230/* Name the devices.  */
231static void
232name_devices (struct grub_efidisk_data *devices)
233{
234  struct grub_efidisk_data *d;
235
236  /* First, identify devices by media device paths.  */
237  for (d = devices; d; d = d->next)
238    {
239      grub_efi_device_path_t *dp;
240
241      dp = d->last_device_path;
242      if (! dp)
243        continue;
244
245      if (GRUB_EFI_DEVICE_PATH_TYPE (dp) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE)
246        {
247          int is_hard_drive = 0;
248
249          switch (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp))
250            {
251            case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE:
252              is_hard_drive = 1;
253              /* Fall through by intention.  */
254            case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE:
255              {
256                struct grub_efidisk_data *parent, *parent2;
257
258                parent = find_parent_device (devices, d);
259                if (!parent)
260                  {
261#ifdef DEBUG_NAMES
262                    grub_printf ("skipping orphaned partition: ");
263                    grub_efi_print_device_path (d->device_path);
264#endif
265                    break;
266                  }
267                parent2 = find_parent_device (devices, parent);
268                if (parent2)
269                  {
270#ifdef DEBUG_NAMES
271                    grub_printf ("skipping subpartition: ");
272                    grub_efi_print_device_path (d->device_path);
273#endif
274                    /* Mark itself as used.  */
275                    d->last_device_path = 0;
276                    break;
277                  }
278                if (!parent->last_device_path)
279                  {
280                    d->last_device_path = 0;
281                    break;
282                  }
283                if (is_hard_drive)
284                  {
285#ifdef DEBUG_NAMES
286                    grub_printf ("adding a hard drive by a partition: ");
287                    grub_efi_print_device_path (parent->device_path);
288#endif
289                    add_device (&hd_devices, parent);
290                  }
291                else
292                  {
293#ifdef DEBUG_NAMES
294                    grub_printf ("adding a cdrom by a partition: ");
295                    grub_efi_print_device_path (parent->device_path);
296#endif
297                    add_device (&cd_devices, parent);
298                  }
299
300                /* Mark the parent as used.  */
301                parent->last_device_path = 0;
302              }
303              /* Mark itself as used.  */
304              d->last_device_path = 0;
305              break;
306
307            default:
308#ifdef DEBUG_NAMES
309              grub_printf ("skipping other type: ");
310              grub_efi_print_device_path (d->device_path);
311#endif
312              /* For now, ignore the others.  */
313              break;
314            }
315        }
316      else
317        {
318#ifdef DEBUG_NAMES
319          grub_printf ("skipping non-media: ");
320          grub_efi_print_device_path (d->device_path);
321#endif
322        }
323    }
324
325  /* Let's see what can be added more.  */
326  for (d = devices; d; d = d->next)
327    {
328      grub_efi_device_path_t *dp;
329      grub_efi_block_io_media_t *m;
330      int is_floppy = 0;
331
332      dp = d->last_device_path;
333      if (! dp)
334        continue;
335
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)
340        {
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;
343
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))
348            continue;
349        }
350
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)
355        {
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)
361            is_floppy = 1;
362        }
363      if (is_floppy)
364        {
365#ifdef DEBUG_NAMES
366          grub_printf ("adding a floppy: ");
367          grub_efi_print_device_path (d->device_path);
368#endif
369          add_device (&fd_devices, d);
370        }
371      else if (m->read_only && m->block_size > GRUB_DISK_SECTOR_SIZE)
372        {
373          /* This check is too heuristic, but assume that this is a
374             CDROM drive.  */
375#ifdef DEBUG_NAMES
376          grub_printf ("adding a cdrom by guessing: ");
377          grub_efi_print_device_path (d->device_path);
378#endif
379          add_device (&cd_devices, d);
380        }
381      else
382        {
383          /* The default is a hard drive.  */
384#ifdef DEBUG_NAMES
385          grub_printf ("adding a hard drive by guessing: ");
386          grub_efi_print_device_path (d->device_path);
387#endif
388          add_device (&hd_devices, d);
389        }
390    }
391}
392
393static void
394free_devices (struct grub_efidisk_data *devices)
395{
396  struct grub_efidisk_data *p, *q;
397
398  for (p = devices; p; p = q)
399    {
400      q = p->next;
401      grub_free (p);
402    }
403}
404
405/* Enumerate all disks to name devices.  */
406static void
407enumerate_disks (void)
408{
409  struct grub_efidisk_data *devices;
410
411  devices = make_devices ();
412  if (! devices)
413    return;
414
415  name_devices (devices);
416  free_devices (devices);
417}
418
419static int
420grub_efidisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
421                      grub_disk_pull_t pull)
422{
423  struct grub_efidisk_data *d;
424  char buf[16];
425  int count;
426
427  switch (pull)
428    {
429    case GRUB_DISK_PULL_NONE:
430      for (d = hd_devices, count = 0; d; d = d->next, count++)
431        {
432          grub_snprintf (buf, sizeof (buf), "hd%d", count);
433          grub_dprintf ("efidisk", "iterating %s\n", buf);
434          if (hook (buf, hook_data))
435            return 1;
436        }
437      break;
438    case GRUB_DISK_PULL_REMOVABLE:
439      for (d = fd_devices, count = 0; d; d = d->next, count++)
440        {
441          grub_snprintf (buf, sizeof (buf), "fd%d", count);
442          grub_dprintf ("efidisk", "iterating %s\n", buf);
443          if (hook (buf, hook_data))
444            return 1;
445        }
446
447      for (d = cd_devices, count = 0; d; d = d->next, count++)
448        {
449          grub_snprintf (buf, sizeof (buf), "cd%d", count);
450          grub_dprintf ("efidisk", "iterating %s\n", buf);
451          if (hook (buf, hook_data))
452            return 1;
453        }
454      break;
455    default:
456      return 0;
457    }
458
459  return 0;
460}
461
462static int
463get_drive_number (const char *name)
464{
465  unsigned long drive;
466
467  if ((name[0] != 'f' && name[0] != 'h' && name[0] != 'c') || name[1] != 'd')
468    goto fail;
469
470  drive = grub_strtoul (name + 2, 0, 10);
471  if (grub_errno != GRUB_ERR_NONE)
472    goto fail;
473
474  return (int) drive ;
475
476 fail:
477  grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a efidisk");
478  return -1;
479}
480
481static struct grub_efidisk_data *
482get_device (struct grub_efidisk_data *devices, int num)
483{
484  struct grub_efidisk_data *d;
485
486  for (d = devices; d && num; d = d->next, num--)
487    ;
488
489  if (num == 0)
490    return d;
491
492  return 0;
493}
494
495static grub_err_t
496grub_efidisk_open (const char *name, struct grub_disk *disk)
497{
498  int num;
499  struct grub_efidisk_data *d = 0;
500  grub_efi_block_io_media_t *m;
501
502  grub_dprintf ("efidisk", "opening %s\n", name);
503
504  num = get_drive_number (name);
505  if (num < 0)
506    return grub_errno;
507
508  switch (name[0])
509    {
510    case 'f':
511      d = get_device (fd_devices, num);
512      break;
513    case 'c':
514      d = get_device (cd_devices, num);
515      break;
516    case 'h':
517      d = get_device (hd_devices, num);
518      break;
519    default:
520      /* Never reach here.  */
521      break;
522    }
523
524  if (! d)
525    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such device");
526
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",
538                       m->block_size);
539  for (disk->log_sector_size = 0;
540       (1U << disk->log_sector_size) < m->block_size;
541       disk->log_sector_size++);
542  disk->data = d;
543
544  grub_dprintf ("efidisk", "opening %s succeeded\n", name);
545
546  return GRUB_ERR_NONE;
547}
548
549static void
550grub_efidisk_close (struct grub_disk *disk __attribute__ ((unused)))
551{
552  /* EFI disks do not allocate extra memory, so nothing to do here.  */
553  grub_dprintf ("efidisk", "closing %s\n", disk->name);
554}
555
556static grub_efi_status_t
557grub_efidisk_readwrite (struct grub_disk *disk, grub_disk_addr_t sector,
558                        grub_size_t size, char *buf, int wr)
559{
560  struct grub_efidisk_data *d;
561  grub_efi_block_io_t *bio;
562
563  d = disk->data;
564  bio = d->block_io;
565
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,
570                     buf);
571}
572
573static grub_err_t
574grub_efidisk_read (struct grub_disk *disk, grub_disk_addr_t sector,
575                   grub_size_t size, char *buf)
576{
577  grub_efi_status_t status;
578
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);
582
583  status = grub_efidisk_readwrite (disk, sector, size, buf, 0);
584
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,
589                       disk->name);
590
591  return GRUB_ERR_NONE;
592}
593
594static grub_err_t
595grub_efidisk_write (struct grub_disk *disk, grub_disk_addr_t sector,
596                    grub_size_t size, const char *buf)
597{
598  grub_efi_status_t status;
599
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);
603
604  status = grub_efidisk_readwrite (disk, sector, size, (char *) buf, 1);
605
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);
610
611  return GRUB_ERR_NONE;
612}
613
614static struct grub_disk_dev grub_efidisk_dev =
615  {
616    .name = "efidisk",
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,
623    .next = 0
624  };
625
626void
627grub_efidisk_fini (void)
628{
629  free_devices (fd_devices);
630  free_devices (hd_devices);
631  free_devices (cd_devices);
632  fd_devices = 0;
633  hd_devices = 0;
634  cd_devices = 0;
635  grub_disk_dev_unregister (&grub_efidisk_dev);
636}
637
638void
639grub_efidisk_init (void)
640{
641  grub_disk_firmware_fini = grub_efidisk_fini;
642
643  enumerate_disks ();
644  grub_disk_dev_register (&grub_efidisk_dev);
645}
646
647/* Some utility functions to map GRUB devices with EFI devices.  */
648grub_efi_handle_t
649grub_efidisk_get_device_handle (grub_disk_t disk)
650{
651  struct grub_efidisk_data *d;
652  char type;
653
654  if (disk->dev->id != GRUB_DISK_DEVICE_EFIDISK_ID)
655    return 0;
656
657  d = disk->data;
658  type = disk->name[0];
659
660  switch (type)
661    {
662    case 'f':
663      /* This is the simplest case.  */
664      return d->handle;
665
666    case 'c':
667      /* FIXME: probably this is not correct.  */
668      return d->handle;
669
670    case 'h':
671      /* If this is the whole disk, just return its own data.  */
672      if (! disk->partition)
673        return d->handle;
674
675      /* Otherwise, we must query the corresponding device to the firmware.  */
676      {
677        struct grub_efidisk_data *devices;
678        grub_efi_handle_t handle = 0;
679        struct grub_efidisk_data *c;
680
681        devices = make_devices ();
682        FOR_CHILDREN (c, devices)
683          {
684            grub_efi_hard_drive_device_path_t *hd;
685
686            hd = (grub_efi_hard_drive_device_path_t *) c->last_device_path;
687
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))))
698              {
699                handle = c->handle;
700                break;
701              }
702          }
703
704        free_devices (devices);
705
706        if (handle != 0)
707          return handle;
708      }
709      break;
710
711    default:
712      break;
713    }
714
715  return 0;
716}
717
718#define NEEDED_BUFLEN sizeof ("XdXXXXXXXXXX")
719static inline int
720get_diskname_from_path_real (const grub_efi_device_path_t *path,
721                             struct grub_efidisk_data *head,
722                             char *buf)
723{
724  int count = 0;
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)
728      {
729        grub_snprintf (buf, NEEDED_BUFLEN - 1, "d%d", count);
730        return 1;
731      }
732  return 0;
733}
734
735static inline int
736get_diskname_from_path (const grub_efi_device_path_t *path,
737                        char *buf)
738{
739  if (get_diskname_from_path_real (path, hd_devices, buf + 1))
740    {
741      buf[0] = 'h';
742      return 1;
743    }
744
745  if (get_diskname_from_path_real (path, fd_devices, buf + 1))
746    {
747      buf[0] = 'f';
748      return 1;
749    }
750
751  if (get_diskname_from_path_real (path, cd_devices, buf + 1))
752    {
753      buf[0] = 'c';
754      return 1;
755    }
756  return 0;
757}
758
759/* Context for grub_efidisk_get_device_name.  */
760struct grub_efidisk_get_device_name_ctx
761{
762  char *partition_name;
763  grub_efi_hard_drive_device_path_t *hd;
764};
765
766/* Helper for grub_efidisk_get_device_name.
767   Find the identical partition.  */
768static int
769grub_efidisk_get_device_name_iter (grub_disk_t disk,
770                                   const grub_partition_t part, void *data)
771{
772  struct grub_efidisk_get_device_name_ctx *ctx = data;
773
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)))
780    {
781      ctx->partition_name = grub_partition_get_name (part);
782      return 1;
783    }
784
785  return 0;
786}
787
788char *
789grub_efidisk_get_device_name (grub_efi_handle_t *handle)
790{
791  grub_efi_device_path_t *dp, *ldp;
792  char device_name[NEEDED_BUFLEN];
793
794  dp = grub_efi_get_device_path (handle);
795  if (! dp)
796    return 0;
797
798  ldp = find_last_device_path (dp);
799  if (! ldp)
800    return 0;
801
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))
805    {
806      int is_cdrom = 0;
807      struct grub_efidisk_get_device_name_ctx ctx;
808      char *dev_name;
809      grub_efi_device_path_t *dup_dp;
810      grub_disk_t parent = 0;
811
812      /* It is necessary to duplicate the device path so that GRUB
813         can overwrite it.  */
814      dup_dp = duplicate_device_path (dp);
815      if (! dup_dp)
816        return 0;
817
818      while (1)
819        {
820          grub_efi_device_path_t *dup_ldp;
821          dup_ldp = find_last_device_path (dup_dp);
822          if (!(GRUB_EFI_DEVICE_PATH_TYPE (dup_ldp) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
823                && (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE
824                    || GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE)))
825            break;
826
827          if (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE)
828            is_cdrom = 1;
829
830          dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
831          dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
832          dup_ldp->length = sizeof (*dup_ldp);
833        }
834
835      if (!get_diskname_from_path (dup_dp, device_name))
836        {
837          grub_free (dup_dp);
838          return 0;
839        }
840
841      parent = grub_disk_open (device_name);
842      grub_free (dup_dp);
843
844      if (! parent)
845        return 0;
846
847      /* Find a partition which matches the hard drive device path.  */
848      ctx.partition_name = NULL;
849      ctx.hd = (grub_efi_hard_drive_device_path_t *) ldp;
850      if (ctx.hd->partition_start == 0
851          && (ctx.hd->partition_size << (parent->log_sector_size
852                                         - GRUB_DISK_SECTOR_BITS))
853          == grub_disk_get_size (parent))
854        {
855          dev_name = grub_strdup (parent->name);
856        }
857      else
858        {
859          grub_partition_iterate (parent, grub_efidisk_get_device_name_iter,
860                                  &ctx);
861
862          if (! ctx.partition_name)
863            {
864              grub_disk_close (parent);
865              if (is_cdrom)
866                return grub_strdup (device_name);
867              return 0;
868            }
869
870          dev_name = grub_xasprintf ("%s,%s", parent->name,
871                                     ctx.partition_name);
872          grub_free (ctx.partition_name);
873        }
874      grub_disk_close (parent);
875
876      return dev_name;
877    }
878  /* This may be guessed device - floppy, cdrom or entire disk.  */
879  if (!get_diskname_from_path (dp, device_name))
880    return 0;
881  return grub_strdup (device_name);
882}
Note: See TracBrowser for help on using the repository browser.