~ubuntu-branches/debian/sid/grub2/sid-200907171837

« back to all changes in this revision

Viewing changes to disk/efi/efidisk.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2006-06-10 19:57:01 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20060610195701-2khcfacexb229tq4
Tags: 1.94-3
Fix FTBFS in amd64. Closes: 372548

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  GRUB  --  GRand Unified Bootloader
 
3
 *  Copyright (C) 2006  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 2 of the License, or
 
8
 *  (at your option) any later version.
 
9
 *
 
10
 *  This program 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, write to the Free Software
 
17
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
18
 */
 
19
 
 
20
#include <grub/disk.h>
 
21
#include <grub/partition.h>
 
22
#include <grub/mm.h>
 
23
#include <grub/types.h>
 
24
#include <grub/misc.h>
 
25
#include <grub/err.h>
 
26
#include <grub/term.h>
 
27
#include <grub/efi/api.h>
 
28
#include <grub/efi/efi.h>
 
29
#include <grub/efi/disk.h>
 
30
 
 
31
struct grub_efidisk_data
 
32
{
 
33
  grub_efi_handle_t handle;
 
34
  grub_efi_device_path_t *device_path;
 
35
  grub_efi_device_path_t *last_device_path;
 
36
  grub_efi_block_io_t *block_io;
 
37
  grub_efi_disk_io_t *disk_io;
 
38
  struct grub_efidisk_data *next;
 
39
};
 
40
 
 
41
/* GUIDs.  */
 
42
static grub_efi_guid_t disk_io_guid = GRUB_EFI_DISK_IO_GUID;
 
43
static grub_efi_guid_t block_io_guid = GRUB_EFI_BLOCK_IO_GUID;
 
44
 
 
45
static struct grub_efidisk_data *fd_devices;
 
46
static struct grub_efidisk_data *hd_devices;
 
47
static struct grub_efidisk_data *cd_devices;
 
48
 
 
49
/* Duplicate a device path.  */
 
50
static grub_efi_device_path_t *
 
51
duplicate_device_path (const grub_efi_device_path_t *dp)
 
52
{
 
53
  grub_efi_device_path_t *p;
 
54
  grub_size_t total_size = 0;
 
55
  
 
56
  for (p = (grub_efi_device_path_t *) dp;
 
57
       ;
 
58
       p = GRUB_EFI_NEXT_DEVICE_PATH (p))
 
59
    {
 
60
      total_size += GRUB_EFI_DEVICE_PATH_LENGTH (p);
 
61
      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (p))
 
62
        break;
 
63
    }
 
64
 
 
65
  p = grub_malloc (total_size);
 
66
  if (! p)
 
67
    return 0;
 
68
 
 
69
  grub_memcpy (p, dp, total_size);
 
70
  return p;
 
71
}
 
72
 
 
73
/* Return the device path node right before the end node.  */
 
74
static grub_efi_device_path_t *
 
75
find_last_device_path (const grub_efi_device_path_t *dp)
 
76
{
 
77
  grub_efi_device_path_t *next, *p;
 
78
 
 
79
  if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
 
80
    return 0;
 
81
 
 
82
  for (p = (grub_efi_device_path_t *) dp, next = GRUB_EFI_NEXT_DEVICE_PATH (p);
 
83
       ! GRUB_EFI_END_ENTIRE_DEVICE_PATH (next);
 
84
       p = next, next = GRUB_EFI_NEXT_DEVICE_PATH (next))
 
85
    ;
 
86
 
 
87
  return p;
 
88
}
 
89
 
 
90
/* Compare device paths.  */
 
91
static int
 
92
compare_device_paths (const grub_efi_device_path_t *dp1,
 
93
                      const grub_efi_device_path_t *dp2)
 
94
{
 
95
  if (! dp1 || ! dp2)
 
96
    /* Return non-zero.  */
 
97
    return 1;
 
98
  
 
99
  while (1)
 
100
    {
 
101
      grub_efi_uint8_t type1, type2;
 
102
      grub_efi_uint8_t subtype1, subtype2;
 
103
      grub_efi_uint16_t len1, len2;
 
104
      int ret;
 
105
      
 
106
      type1 = GRUB_EFI_DEVICE_PATH_TYPE (dp1);
 
107
      type2 = GRUB_EFI_DEVICE_PATH_TYPE (dp2);
 
108
 
 
109
      if (type1 != type2)
 
110
        return (int) type2 - (int) type1;
 
111
 
 
112
      subtype1 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp1);
 
113
      subtype2 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp2);
 
114
      
 
115
      if (subtype1 != subtype2)
 
116
        return (int) subtype1 - (int) subtype2;
 
117
 
 
118
      len1 = GRUB_EFI_DEVICE_PATH_LENGTH (dp1);
 
119
      len2 = GRUB_EFI_DEVICE_PATH_LENGTH (dp2);
 
120
 
 
121
      if (len1 != len2)
 
122
        return (int) len1 - (int) len2;
 
123
 
 
124
      ret = grub_memcmp (dp1, dp2, len1);
 
125
      if (ret != 0)
 
126
        return ret;
 
127
 
 
128
      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1))
 
129
        break;
 
130
 
 
131
      dp1 = (grub_efi_device_path_t *) ((char *) dp1 + len1);
 
132
      dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2);
 
133
    }
 
134
 
 
135
  return 0;
 
136
}
 
137
 
 
138
static struct grub_efidisk_data *
 
139
make_devices (void)
 
140
{
 
141
  grub_efi_uintn_t num_handles;
 
142
  grub_efi_handle_t *handles;
 
143
  grub_efi_handle_t *handle;
 
144
  struct grub_efidisk_data *devices = 0;
 
145
  
 
146
  /* Find handles which support the disk io interface.  */
 
147
  handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &disk_io_guid,
 
148
                                    0, &num_handles);
 
149
  if (! handles)
 
150
    return 0;
 
151
 
 
152
  /* Make a linked list of devices.  */
 
153
  for (handle = handles; num_handles--; handle++)
 
154
    {
 
155
      grub_efi_device_path_t *dp;
 
156
      grub_efi_device_path_t *ldp;
 
157
      struct grub_efidisk_data *d;
 
158
      grub_efi_block_io_t *bio;
 
159
      grub_efi_disk_io_t *dio;
 
160
      
 
161
      dp = grub_efi_get_device_path (*handle);
 
162
      if (! dp)
 
163
        continue;
 
164
 
 
165
      ldp = find_last_device_path (dp);
 
166
      if (! ldp)
 
167
        /* This is empty. Why?  */
 
168
        continue;
 
169
      
 
170
      bio = grub_efi_open_protocol (*handle, &block_io_guid,
 
171
                                    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 
172
      dio = grub_efi_open_protocol (*handle, &disk_io_guid,
 
173
                                    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 
174
      if (! bio || ! dio)
 
175
        /* This should not happen... Why?  */
 
176
        continue;
 
177
        
 
178
      d = grub_malloc (sizeof (*d));
 
179
      if (! d)
 
180
        {
 
181
          /* Uggh.  */
 
182
          grub_free (handles);
 
183
          return 0;
 
184
        }
 
185
 
 
186
      d->handle = *handle;
 
187
      d->device_path = dp;
 
188
      d->last_device_path = ldp;
 
189
      d->block_io = bio;
 
190
      d->disk_io = dio;
 
191
      d->next = devices;
 
192
      devices = d;
 
193
    }
 
194
 
 
195
  grub_free (handles);
 
196
  
 
197
  return devices;
 
198
}
 
199
 
 
200
/* Find the parent device.  */
 
201
static struct grub_efidisk_data *
 
202
find_parent_device (struct grub_efidisk_data *devices,
 
203
                    struct grub_efidisk_data *d)
 
204
{
 
205
  grub_efi_device_path_t *dp, *ldp;
 
206
  struct grub_efidisk_data *parent;
 
207
  
 
208
  dp = duplicate_device_path (d->device_path);
 
209
  if (! dp)
 
210
    return 0;
 
211
 
 
212
  ldp = find_last_device_path (dp);
 
213
  ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
 
214
  ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
 
215
  ldp->length[0] = sizeof (*ldp);
 
216
  ldp->length[1] = 0;
 
217
 
 
218
  for (parent = devices; parent; parent = parent->next)
 
219
    {
 
220
      /* Ignore itself.  */
 
221
      if (parent == d)
 
222
        continue;
 
223
      
 
224
      if (compare_device_paths (parent->device_path, dp) == 0)
 
225
        {
 
226
          /* Found.  */
 
227
          if (! parent->last_device_path)
 
228
            parent = 0;
 
229
          
 
230
          break;
 
231
        }
 
232
    }
 
233
 
 
234
  grub_free (dp);
 
235
  return parent;
 
236
}
 
237
 
 
238
static int
 
239
iterate_child_devices (struct grub_efidisk_data *devices,
 
240
                       struct grub_efidisk_data *d,
 
241
                       int (*hook) (struct grub_efidisk_data *child))
 
242
{
 
243
  struct grub_efidisk_data *p;
 
244
  
 
245
  for (p = devices; p; p = p->next)
 
246
    {
 
247
      grub_efi_device_path_t *dp, *ldp;
 
248
 
 
249
      dp = duplicate_device_path (p->device_path);
 
250
      if (! dp)
 
251
        return 0;
 
252
      
 
253
      ldp = find_last_device_path (dp);
 
254
      ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
 
255
      ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
 
256
      ldp->length[0] = sizeof (*ldp);
 
257
      ldp->length[1] = 0;
 
258
      
 
259
      if (compare_device_paths (dp, d->device_path) == 0)
 
260
        if (hook (p))
 
261
          {
 
262
            grub_free (dp);
 
263
            return 1;
 
264
          }
 
265
 
 
266
      grub_free (dp);
 
267
    }
 
268
 
 
269
  return 0;
 
270
}
 
271
 
 
272
/* Add a device into a list of devices in an ascending order.  */
 
273
static void
 
274
add_device (struct grub_efidisk_data **devices, struct grub_efidisk_data *d)
 
275
{
 
276
  struct grub_efidisk_data **p;
 
277
  struct grub_efidisk_data *n;
 
278
 
 
279
  for (p = devices; *p; p = &((*p)->next))
 
280
    {
 
281
      int ret;
 
282
 
 
283
      ret = compare_device_paths (find_last_device_path ((*p)->device_path),
 
284
                                  find_last_device_path (d->device_path));
 
285
      if (ret == 0)
 
286
        ret = compare_device_paths ((*p)->device_path,
 
287
                                    d->device_path);
 
288
      if (ret == 0)
 
289
        return;
 
290
      else if (ret > 0)
 
291
        break;
 
292
    }
 
293
 
 
294
  n = grub_malloc (sizeof (*n));
 
295
  if (! n)
 
296
    return;
 
297
 
 
298
  grub_memcpy (n, d, sizeof (*n));
 
299
  n->next = (*p);
 
300
  (*p) = n;
 
301
}
 
302
 
 
303
/* Name the devices.  */
 
304
static void
 
305
name_devices (struct grub_efidisk_data *devices)
 
306
{
 
307
  struct grub_efidisk_data *d;
 
308
  
 
309
  /* First, identify devices by media device paths.  */
 
310
  for (d = devices; d; d = d->next)
 
311
    {
 
312
      grub_efi_device_path_t *dp;
 
313
 
 
314
      dp = d->last_device_path;
 
315
      if (! dp)
 
316
        continue;
 
317
      
 
318
      if (GRUB_EFI_DEVICE_PATH_TYPE (dp) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE)
 
319
        {
 
320
          int is_hard_drive = 0;
 
321
          
 
322
          switch (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp))
 
323
            {
 
324
            case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE:
 
325
              is_hard_drive = 1;
 
326
              /* Fall through by intention.  */
 
327
            case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE:
 
328
              {
 
329
                struct grub_efidisk_data *parent;
 
330
 
 
331
                parent = find_parent_device (devices, d);
 
332
                if (parent)
 
333
                  {
 
334
                    if (is_hard_drive)
 
335
                      {
 
336
#if 0
 
337
                        grub_printf ("adding a hard drive by a partition: ");
 
338
                        grub_print_device_path (parent->device_path);
 
339
#endif
 
340
                        add_device (&hd_devices, parent);
 
341
                      }
 
342
                    else
 
343
                      {
 
344
#if 0
 
345
                        grub_printf ("adding a cdrom by a partition: ");
 
346
                        grub_print_device_path (parent->device_path);
 
347
#endif
 
348
                        add_device (&cd_devices, parent);
 
349
                      }
 
350
                    
 
351
                    /* Mark the parent as used.  */
 
352
                    parent->last_device_path = 0;
 
353
                  }
 
354
              }
 
355
              /* Mark itself as used.  */
 
356
              d->last_device_path = 0;
 
357
              break;
 
358
 
 
359
            default:
 
360
              /* For now, ignore the others.  */
 
361
              break;
 
362
            }
 
363
        }
 
364
    }
 
365
 
 
366
  /* Let's see what can be added more.  */
 
367
  for (d = devices; d; d = d->next)
 
368
    {
 
369
      grub_efi_device_path_t *dp;
 
370
      grub_efi_block_io_media_t *m;
 
371
      
 
372
      dp = d->last_device_path;
 
373
      if (! dp)
 
374
        continue;
 
375
 
 
376
      m = d->block_io->media;
 
377
      if (m->logical_partition)
 
378
        {
 
379
          /* Only one partition in a non-media device. Assume that this
 
380
             is a floppy drive.  */
 
381
#if 0
 
382
          grub_printf ("adding a floppy by guessing: ");
 
383
          grub_print_device_path (d->device_path);
 
384
#endif
 
385
          add_device (&fd_devices, d);
 
386
        }
 
387
      else if (m->read_only && m->block_size > GRUB_DISK_SECTOR_SIZE)
 
388
        {
 
389
          /* This check is too heuristic, but assume that this is a
 
390
             CDROM drive.  */
 
391
#if 0
 
392
          grub_printf ("adding a cdrom by guessing: ");
 
393
          grub_print_device_path (d->device_path);
 
394
#endif
 
395
          add_device (&cd_devices, d);
 
396
        }
 
397
      else
 
398
        {
 
399
          /* The default is a hard drive.  */
 
400
#if 0
 
401
          grub_printf ("adding a hard drive by guessing: ");
 
402
          grub_print_device_path (d->device_path);
 
403
#endif
 
404
          add_device (&hd_devices, d);
 
405
        }
 
406
    }
 
407
}
 
408
 
 
409
static void
 
410
free_devices (struct grub_efidisk_data *devices)
 
411
{
 
412
  struct grub_efidisk_data *p, *q;
 
413
  
 
414
  for (p = devices; p; p = q)
 
415
    {
 
416
      q = p->next;
 
417
      grub_free (p);
 
418
    }
 
419
}
 
420
 
 
421
/* Enumerate all disks to name devices.  */
 
422
static void
 
423
enumerate_disks (void)
 
424
{
 
425
  struct grub_efidisk_data *devices;
 
426
  
 
427
  devices = make_devices ();
 
428
  if (! devices)
 
429
    return;
 
430
  
 
431
  name_devices (devices);
 
432
  free_devices (devices);
 
433
}
 
434
 
 
435
static int
 
436
grub_efidisk_iterate (int (*hook) (const char *name))
 
437
{
 
438
  struct grub_efidisk_data *d;
 
439
  char buf[16];
 
440
  int count;
 
441
  
 
442
  for (d = fd_devices, count = 0; d; d = d->next, count++)
 
443
    {
 
444
      grub_sprintf (buf, "fd%d", count);
 
445
      grub_dprintf ("efidisk", "iterating %s\n", buf);
 
446
      if (hook (buf))
 
447
        return 1;
 
448
    }
 
449
  
 
450
  for (d = hd_devices, count = 0; d; d = d->next, count++)
 
451
    {
 
452
      grub_sprintf (buf, "hd%d", count);
 
453
      grub_dprintf ("efidisk", "iterating %s\n", buf);
 
454
      if (hook (buf))
 
455
        return 1;
 
456
    }
 
457
  
 
458
  for (d = cd_devices, count = 0; d; d = d->next, count++)
 
459
    {
 
460
      grub_sprintf (buf, "cd%d", count);
 
461
      grub_dprintf ("efidisk", "iterating %s\n", buf);
 
462
      if (hook (buf))
 
463
        return 1;
 
464
    }
 
465
 
 
466
  return 0;
 
467
}
 
468
 
 
469
static int
 
470
get_drive_number (const char *name)
 
471
{
 
472
  unsigned long drive;
 
473
 
 
474
  if ((name[0] != 'f' && name[0] != 'h' && name[0] != 'c') || name[1] != 'd')
 
475
    goto fail;
 
476
 
 
477
  drive = grub_strtoul (name + 2, 0, 10);
 
478
  if (grub_errno != GRUB_ERR_NONE)
 
479
    goto fail;
 
480
 
 
481
  return (int) drive ;
 
482
 
 
483
 fail:
 
484
  grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a efidisk");
 
485
  return -1;
 
486
}
 
487
 
 
488
static struct grub_efidisk_data *
 
489
get_device (struct grub_efidisk_data *devices, int num)
 
490
{
 
491
  struct grub_efidisk_data *d;
 
492
 
 
493
  for (d = devices; d && num; d = d->next, num--)
 
494
    ;
 
495
 
 
496
  if (num == 0)
 
497
    return d;
 
498
 
 
499
  return 0;
 
500
}
 
501
 
 
502
static grub_err_t
 
503
grub_efidisk_open (const char *name, struct grub_disk *disk)
 
504
{
 
505
  int num;
 
506
  struct grub_efidisk_data *d = 0;
 
507
  grub_efi_block_io_media_t *m;
 
508
 
 
509
  grub_dprintf ("efidisk", "opening %s\n", name);
 
510
  
 
511
  num = get_drive_number (name);
 
512
  if (num < 0)
 
513
    return grub_errno;
 
514
 
 
515
  switch (name[0])
 
516
    {
 
517
    case 'f':
 
518
      disk->has_partitions = 0;
 
519
      d = get_device (fd_devices, num);
 
520
      break;
 
521
    case 'c':
 
522
      /* FIXME: a CDROM should have partitions, but not implemented yet.  */
 
523
      disk->has_partitions = 0;
 
524
      d = get_device (cd_devices, num);
 
525
      break;
 
526
    case 'h':
 
527
      disk->has_partitions = 1;
 
528
      d = get_device (hd_devices, num);
 
529
      break;
 
530
    default:
 
531
      /* Never reach here.  */
 
532
      break;
 
533
    }
 
534
 
 
535
  if (! d)
 
536
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such device");
 
537
 
 
538
  disk->id = ((num << 8) | name[0]);
 
539
  m = d->block_io->media;
 
540
  /* FIXME: Probably it is better to store the block size in the disk,
 
541
     and total sectors should be replaced with total blocks.  */
 
542
  grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n",
 
543
                m, m->last_block, m->block_size);
 
544
  disk->total_sectors = (m->last_block
 
545
                         * (m->block_size >> GRUB_DISK_SECTOR_BITS));
 
546
  disk->data = d;
 
547
 
 
548
  grub_dprintf ("efidisk", "opening %s succeeded\n", name);
 
549
 
 
550
  return GRUB_ERR_NONE;
 
551
}
 
552
 
 
553
static void
 
554
grub_efidisk_close (struct grub_disk *disk __attribute__ ((unused)))
 
555
{
 
556
  /* EFI disks do not allocate extra memory, so nothing to do here.  */
 
557
  grub_dprintf ("efidisk", "closing %s\n", disk->name);
 
558
}
 
559
 
 
560
static grub_err_t
 
561
grub_efidisk_read (struct grub_disk *disk, unsigned long sector,
 
562
                   unsigned long size, char *buf)
 
563
{
 
564
  /* For now, use the disk io interface rather than the block io's.  */
 
565
  struct grub_efidisk_data *d;
 
566
  grub_efi_disk_io_t *dio;
 
567
  grub_efi_block_io_t *bio;
 
568
  grub_efi_status_t status;
 
569
  
 
570
  d = disk->data;
 
571
  dio = d->disk_io;
 
572
  bio = d->block_io;
 
573
 
 
574
  grub_dprintf ("efidisk", "reading 0x%lx sectors at the sector 0x%lx from %s\n",
 
575
                size, sector, disk->name);
 
576
  
 
577
  status = 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");
 
583
  
 
584
  return GRUB_ERR_NONE;
 
585
}
 
586
 
 
587
static grub_err_t
 
588
grub_efidisk_write (struct grub_disk *disk, unsigned long sector,
 
589
                    unsigned long size, const char *buf)
 
590
{
 
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;
 
596
  
 
597
  d = disk->data;
 
598
  dio = d->disk_io;
 
599
  bio = d->block_io;
 
600
  
 
601
  grub_dprintf ("efidisk", "writing 0x%lx sectors at the sector 0x%lx to %s\n",
 
602
                size, sector, disk->name);
 
603
  
 
604
  status = dio->write (dio, bio->media->media_id,
 
605
                       (grub_efi_uint64_t) sector << GRUB_DISK_SECTOR_BITS,
 
606
                       (grub_efi_uintn_t) size << GRUB_DISK_SECTOR_BITS,
 
607
                       (void *) buf);
 
608
  if (status != GRUB_EFI_SUCCESS)
 
609
    return grub_error (GRUB_ERR_WRITE_ERROR, "efidisk write error");
 
610
  
 
611
  return GRUB_ERR_NONE;
 
612
}
 
613
 
 
614
static 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
 
 
626
void
 
627
grub_efidisk_init (void)
 
628
{
 
629
  enumerate_disks ();
 
630
  grub_disk_dev_register (&grub_efidisk_dev);
 
631
}
 
632
 
 
633
void
 
634
grub_efidisk_fini (void)
 
635
{
 
636
  free_devices (fd_devices);
 
637
  free_devices (hd_devices);
 
638
  free_devices (cd_devices);
 
639
  grub_disk_dev_unregister (&grub_efidisk_dev);
 
640
}
 
641
 
 
642
/* Some utility functions to map GRUB devices with EFI devices.  */
 
643
grub_efi_handle_t
 
644
grub_efidisk_get_device_handle (grub_disk_t disk)
 
645
{
 
646
  struct grub_efidisk_data *d;
 
647
  char type;
 
648
  
 
649
  if (disk->dev->id != GRUB_DISK_DEVICE_EFIDISK_ID)
 
650
    return 0;
 
651
  
 
652
  d = disk->data;
 
653
  type = disk->name[0];
 
654
  
 
655
  switch (type)
 
656
    {
 
657
    case 'f':
 
658
      /* This is the simplest case.  */
 
659
      return d->handle;
 
660
 
 
661
    case 'c':
 
662
      /* FIXME: probably this is not correct.  */
 
663
      return d->handle;
 
664
 
 
665
    case 'h':
 
666
      /* If this is the whole disk, just return its own data.  */
 
667
      if (! disk->partition)
 
668
        return d->handle;
 
669
 
 
670
      /* Otherwise, we must query the corresponding device to the firmware.  */
 
671
      {
 
672
        struct grub_efidisk_data *devices;
 
673
        grub_efi_handle_t handle = 0;
 
674
        auto int find_partition (struct grub_efidisk_data *c);
 
675
 
 
676
        int find_partition (struct grub_efidisk_data *c)
 
677
          {
 
678
            grub_efi_hard_drive_device_path_t hd;
 
679
 
 
680
            grub_memcpy (&hd, c->last_device_path, sizeof (hd));
 
681
            
 
682
            if ((GRUB_EFI_DEVICE_PATH_TYPE (c->last_device_path)
 
683
                 == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE)
 
684
                && (GRUB_EFI_DEVICE_PATH_SUBTYPE (c->last_device_path)
 
685
                    == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE)
 
686
                && (grub_partition_get_start (disk->partition)
 
687
                    == hd.partition_start)
 
688
                && (grub_partition_get_len (disk->partition)
 
689
                    == hd.partition_size))
 
690
              {
 
691
                handle = c->handle;
 
692
                return 1;
 
693
              }
 
694
              
 
695
            return 0;
 
696
          }
 
697
        
 
698
        devices = make_devices ();
 
699
        iterate_child_devices (devices, d, find_partition);
 
700
        free_devices (devices);
 
701
        
 
702
        if (handle != 0)
 
703
          return handle;
 
704
      }
 
705
      break;
 
706
 
 
707
    default:
 
708
      break;
 
709
    }
 
710
  
 
711
  return 0;
 
712
}
 
713
 
 
714
char *
 
715
grub_efidisk_get_device_name (grub_efi_handle_t *handle)
 
716
{
 
717
  grub_efi_device_path_t *dp, *ldp;
 
718
 
 
719
  dp = grub_efi_get_device_path (handle);
 
720
  if (! dp)
 
721
    return 0;
 
722
 
 
723
  ldp = find_last_device_path (dp);
 
724
  if (! ldp)
 
725
    return 0;
 
726
 
 
727
  if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
 
728
      && (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp)
 
729
          == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE))
 
730
    {
 
731
      /* This is a hard disk partition.  */
 
732
      grub_disk_t parent = 0;
 
733
      char *partition_name = 0;
 
734
      char *device_name;
 
735
      grub_efi_device_path_t *dup_dp, *dup_ldp;
 
736
      grub_efi_hard_drive_device_path_t hd;
 
737
      auto int find_parent_disk (const char *name);
 
738
      auto int find_partition (grub_disk_t disk, const grub_partition_t part);
 
739
 
 
740
      /* Find the disk which is the parent of a given hard disk partition.  */
 
741
      int find_parent_disk (const char *name)
 
742
        {
 
743
          grub_disk_t disk;
 
744
 
 
745
          disk = grub_disk_open (name);
 
746
          if (! disk)
 
747
            return 1;
 
748
 
 
749
          if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID)
 
750
            {
 
751
              struct grub_efidisk_data *d;
 
752
              
 
753
              d = disk->data;
 
754
              if (compare_device_paths (d->device_path, dup_dp) == 0)
 
755
                {
 
756
                  parent = disk;
 
757
                  return 1;
 
758
                }
 
759
            }
 
760
 
 
761
          grub_disk_close (disk);
 
762
          return 0;
 
763
        }
 
764
 
 
765
      /* Find the identical partition.  */
 
766
      int find_partition (grub_disk_t disk __attribute__ ((unused)),
 
767
                          const grub_partition_t part)
 
768
        {
 
769
          if (grub_partition_get_start (part) == hd.partition_start
 
770
              && grub_partition_get_len (part) == hd.partition_size)
 
771
            {
 
772
              partition_name = grub_partition_get_name (part);
 
773
              return 1;
 
774
            }
 
775
 
 
776
          return 0;
 
777
        }
 
778
 
 
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;
 
784
 
 
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;
 
790
 
 
791
      grub_efidisk_iterate (find_parent_disk);
 
792
      grub_free (dup_dp);
 
793
 
 
794
      if (! parent)
 
795
        return 0;
 
796
 
 
797
      /* Find a partition which matches the hard drive device path.  */
 
798
      grub_memcpy (&hd, ldp, sizeof (hd));
 
799
      grub_partition_iterate (parent, find_partition);
 
800
      
 
801
      if (! partition_name)
 
802
        {
 
803
          grub_disk_close (parent);
 
804
          return 0;
 
805
        }
 
806
 
 
807
      device_name = grub_malloc (grub_strlen (parent->name) + 1
 
808
                                 + grub_strlen (partition_name) + 1);
 
809
      if (! device_name)
 
810
        {
 
811
          grub_free (partition_name);
 
812
          grub_disk_close (parent);
 
813
          return 0;
 
814
        }
 
815
 
 
816
      grub_sprintf (device_name, "%s,%s", parent->name, partition_name);
 
817
      grub_free (partition_name);
 
818
      grub_disk_close (parent);
 
819
      return device_name;
 
820
    }
 
821
  else
 
822
    {
 
823
      /* This should be an entire disk.  */
 
824
      auto int find_disk (const char *name);
 
825
      char *device_name = 0;
 
826
      
 
827
      int find_disk (const char *name)
 
828
        {
 
829
          grub_disk_t disk;
 
830
          
 
831
          disk = grub_disk_open (name);
 
832
          if (! disk)
 
833
            return 1;
 
834
 
 
835
          if (disk->id == GRUB_DISK_DEVICE_EFIDISK_ID)
 
836
            {
 
837
              struct grub_efidisk_data *d;
 
838
              
 
839
              d = disk->data;
 
840
              if (compare_device_paths (d->device_path, dp) == 0)
 
841
                {
 
842
                  device_name = grub_strdup (disk->name);
 
843
                  grub_disk_close (disk);
 
844
                  return 1;
 
845
                }
 
846
            }
 
847
 
 
848
          grub_disk_close (disk);
 
849
          return 0;
 
850
          
 
851
        }
 
852
      
 
853
      grub_efidisk_iterate (find_disk);
 
854
      return device_name;
 
855
    }
 
856
 
 
857
  return 0;
 
858
}