efiblock.c revision 1.10 1 /* $NetBSD: efiblock.c,v 1.10 2020/11/28 15:24:05 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka (at) netbsd.org>
5 * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #define FSTYPENAMES
31
32 #include <sys/param.h>
33 #include <sys/md5.h>
34 #include <sys/uuid.h>
35
36 #include <fs/cd9660/iso.h>
37
38 #include "efiboot.h"
39 #include "efiblock.h"
40
41 static EFI_HANDLE *efi_block;
42 static UINTN efi_nblock;
43 static struct efi_block_part *efi_block_booted = NULL;
44
45 static TAILQ_HEAD(, efi_block_dev) efi_block_devs = TAILQ_HEAD_INITIALIZER(efi_block_devs);
46
47 static int
48 efi_block_parse(const char *fname, struct efi_block_part **pbpart, char **pfile)
49 {
50 struct efi_block_dev *bdev;
51 struct efi_block_part *bpart;
52 char pathbuf[PATH_MAX], *default_device, *ep = NULL;
53 const char *full_path;
54 intmax_t dev;
55 int part;
56
57 default_device = get_default_device();
58 if (strchr(fname, ':') == NULL) {
59 if (strlen(default_device) > 0) {
60 snprintf(pathbuf, sizeof(pathbuf), "%s:%s", default_device, fname);
61 full_path = pathbuf;
62 *pfile = __UNCONST(fname);
63 } else {
64 return EINVAL;
65 }
66 } else {
67 full_path = fname;
68 *pfile = strchr(fname, ':') + 1;
69 }
70
71 if (strncasecmp(full_path, "hd", 2) != 0)
72 return EINVAL;
73 dev = strtoimax(full_path + 2, &ep, 10);
74 if (dev < 0 || dev >= efi_nblock)
75 return ENXIO;
76 if (ep[0] < 'a' || ep[0] >= 'a' + MAXPARTITIONS || ep[1] != ':')
77 return EINVAL;
78 part = ep[0] - 'a';
79 TAILQ_FOREACH(bdev, &efi_block_devs, entries) {
80 if (bdev->index == dev) {
81 TAILQ_FOREACH(bpart, &bdev->partitions, entries) {
82 if (bpart->index == part) {
83 *pbpart = bpart;
84 return 0;
85 }
86 }
87 }
88 }
89
90 return ENOENT;
91 }
92
93 static void
94 efi_block_generate_hash_mbr(struct efi_block_part *bpart, struct mbr_sector *mbr)
95 {
96 MD5_CTX md5ctx;
97
98 MD5Init(&md5ctx);
99 MD5Update(&md5ctx, (void *)mbr, sizeof(*mbr));
100 MD5Final(bpart->hash, &md5ctx);
101 }
102
103 static void *
104 efi_block_allocate_device_buffer(struct efi_block_dev *bdev, UINTN size,
105 void **buf_start)
106 {
107 void *buf;
108
109 if (bdev->bio->Media->IoAlign <= 1)
110 *buf_start = buf = AllocatePool(size);
111 else {
112 buf = AllocatePool(size + bdev->bio->Media->IoAlign - 1);
113 *buf_start = (buf == NULL) ? NULL :
114 (void *)roundup2((intptr_t)buf, bdev->bio->Media->IoAlign);
115 }
116
117 return buf;
118 }
119
120 static int
121 efi_block_find_partitions_cd9660(struct efi_block_dev *bdev)
122 {
123 struct efi_block_part *bpart;
124 struct iso_primary_descriptor *vd;
125 void *buf, *buf_start;
126 EFI_STATUS status;
127 EFI_LBA lba;
128 UINT32 sz;
129
130 if (bdev->bio->Media->BlockSize != DEV_BSIZE &&
131 bdev->bio->Media->BlockSize != ISO_DEFAULT_BLOCK_SIZE) {
132 return ENXIO;
133 }
134
135 sz = __MAX(sizeof(*vd), bdev->bio->Media->BlockSize);
136 sz = roundup(sz, bdev->bio->Media->BlockSize);
137 if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL) {
138 return ENOMEM;
139 }
140
141 for (lba = 16;; lba++) {
142 status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5,
143 bdev->bio,
144 bdev->media_id,
145 lba * ISO_DEFAULT_BLOCK_SIZE / bdev->bio->Media->BlockSize,
146 sz,
147 buf_start);
148 if (EFI_ERROR(status)) {
149 goto io_error;
150 }
151
152 vd = (struct iso_primary_descriptor *)buf_start;
153 if (memcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) {
154 goto io_error;
155 }
156 if (isonum_711(vd->type) == ISO_VD_END) {
157 goto io_error;
158 }
159 if (isonum_711(vd->type) == ISO_VD_PRIMARY) {
160 break;
161 }
162 }
163
164 if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE) {
165 goto io_error;
166 }
167
168 bpart = alloc(sizeof(*bpart));
169 bpart->index = 0;
170 bpart->bdev = bdev;
171 bpart->type = EFI_BLOCK_PART_CD9660;
172 TAILQ_INSERT_TAIL(&bdev->partitions, bpart, entries);
173
174 FreePool(buf);
175 return 0;
176
177 io_error:
178 FreePool(buf);
179 return EIO;
180 }
181
182 static int
183 efi_block_find_partitions_disklabel(struct efi_block_dev *bdev, struct mbr_sector *mbr, uint32_t start, uint32_t size)
184 {
185 struct efi_block_part *bpart;
186 struct disklabel d;
187 struct partition *p;
188 EFI_STATUS status;
189 EFI_LBA lba;
190 void *buf, *buf_start;
191 UINT32 sz;
192 int n;
193
194 sz = __MAX(sizeof(d), bdev->bio->Media->BlockSize);
195 sz = roundup(sz, bdev->bio->Media->BlockSize);
196 if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
197 return ENOMEM;
198
199 lba = (((EFI_LBA)start + LABELSECTOR) * DEV_BSIZE) / bdev->bio->Media->BlockSize;
200 status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
201 lba, sz, buf_start);
202 if (EFI_ERROR(status) || getdisklabel(buf_start, &d) != NULL) {
203 FreePool(buf);
204 return EIO;
205 }
206 FreePool(buf);
207
208 if (le32toh(d.d_magic) != DISKMAGIC || le32toh(d.d_magic2) != DISKMAGIC)
209 return EINVAL;
210 if (le16toh(d.d_npartitions) > MAXPARTITIONS)
211 return EINVAL;
212
213 for (n = 0; n < le16toh(d.d_npartitions); n++) {
214 p = &d.d_partitions[n];
215 switch (p->p_fstype) {
216 case FS_BSDFFS:
217 case FS_MSDOS:
218 case FS_BSDLFS:
219 break;
220 default:
221 continue;
222 }
223
224 bpart = alloc(sizeof(*bpart));
225 bpart->index = n;
226 bpart->bdev = bdev;
227 bpart->type = EFI_BLOCK_PART_DISKLABEL;
228 bpart->disklabel.secsize = le32toh(d.d_secsize);
229 bpart->disklabel.part = *p;
230 efi_block_generate_hash_mbr(bpart, mbr);
231 TAILQ_INSERT_TAIL(&bdev->partitions, bpart, entries);
232 }
233
234 return 0;
235 }
236
237 static int
238 efi_block_find_partitions_mbr(struct efi_block_dev *bdev)
239 {
240 struct mbr_sector mbr;
241 struct mbr_partition *mbr_part;
242 EFI_STATUS status;
243 void *buf, *buf_start;
244 UINT32 sz;
245 int n;
246
247 sz = __MAX(sizeof(mbr), bdev->bio->Media->BlockSize);
248 sz = roundup(sz, bdev->bio->Media->BlockSize);
249 if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
250 return ENOMEM;
251
252 status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
253 0, sz, buf_start);
254 if (EFI_ERROR(status)) {
255 FreePool(buf);
256 return EIO;
257 }
258 memcpy(&mbr, buf_start, sizeof(mbr));
259 FreePool(buf);
260
261 if (le32toh(mbr.mbr_magic) != MBR_MAGIC)
262 return ENOENT;
263
264 for (n = 0; n < MBR_PART_COUNT; n++) {
265 mbr_part = &mbr.mbr_parts[n];
266 if (le32toh(mbr_part->mbrp_size) == 0)
267 continue;
268 if (mbr_part->mbrp_type == MBR_PTYPE_NETBSD) {
269 efi_block_find_partitions_disklabel(bdev, &mbr, le32toh(mbr_part->mbrp_start), le32toh(mbr_part->mbrp_size));
270 break;
271 }
272 }
273
274 return 0;
275 }
276
277 static const struct {
278 struct uuid guid;
279 uint8_t fstype;
280 } gpt_guid_to_str[] = {
281 { GPT_ENT_TYPE_NETBSD_FFS, FS_BSDFFS },
282 { GPT_ENT_TYPE_NETBSD_LFS, FS_BSDLFS },
283 { GPT_ENT_TYPE_NETBSD_RAIDFRAME, FS_RAID },
284 { GPT_ENT_TYPE_NETBSD_CCD, FS_CCD },
285 { GPT_ENT_TYPE_NETBSD_CGD, FS_CGD },
286 { GPT_ENT_TYPE_MS_BASIC_DATA, FS_MSDOS }, /* or NTFS? ambiguous */
287 { GPT_ENT_TYPE_EFI, FS_MSDOS },
288 };
289
290 static int
291 efi_block_find_partitions_gpt_entry(struct efi_block_dev *bdev, struct gpt_hdr *hdr, struct gpt_ent *ent, UINT32 index)
292 {
293 struct efi_block_part *bpart;
294 uint8_t fstype = FS_UNUSED;
295 struct uuid uuid;
296 int n;
297
298 memcpy(&uuid, ent->ent_type, sizeof(uuid));
299 for (n = 0; n < __arraycount(gpt_guid_to_str); n++)
300 if (memcmp(ent->ent_type, &gpt_guid_to_str[n].guid, sizeof(ent->ent_type)) == 0) {
301 fstype = gpt_guid_to_str[n].fstype;
302 break;
303 }
304 if (fstype == FS_UNUSED)
305 return 0;
306
307 bpart = alloc(sizeof(*bpart));
308 bpart->index = index;
309 bpart->bdev = bdev;
310 bpart->type = EFI_BLOCK_PART_GPT;
311 bpart->gpt.fstype = fstype;
312 bpart->gpt.ent = *ent;
313 memcpy(bpart->hash, ent->ent_guid, sizeof(bpart->hash));
314 TAILQ_INSERT_TAIL(&bdev->partitions, bpart, entries);
315
316 return 0;
317 }
318
319 static int
320 efi_block_find_partitions_gpt(struct efi_block_dev *bdev)
321 {
322 struct gpt_hdr hdr;
323 struct gpt_ent ent;
324 EFI_STATUS status;
325 void *buf, *buf_start;
326 UINT32 sz, entry;
327
328 sz = __MAX(sizeof(hdr), bdev->bio->Media->BlockSize);
329 sz = roundup(sz, bdev->bio->Media->BlockSize);
330 if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
331 return ENOMEM;
332
333 status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
334 GPT_HDR_BLKNO, sz, buf_start);
335 if (EFI_ERROR(status)) {
336 FreePool(buf);
337 return EIO;
338 }
339 memcpy(&hdr, buf_start, sizeof(hdr));
340 FreePool(buf);
341
342 if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0)
343 return ENOENT;
344 if (le32toh(hdr.hdr_entsz) < sizeof(ent))
345 return EINVAL;
346
347 sz = __MAX(le32toh(hdr.hdr_entsz) * le32toh(hdr.hdr_entries), bdev->bio->Media->BlockSize);
348 sz = roundup(sz, bdev->bio->Media->BlockSize);
349 if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
350 return ENOMEM;
351
352 status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
353 le64toh(hdr.hdr_lba_table), sz, buf_start);
354 if (EFI_ERROR(status)) {
355 FreePool(buf);
356 return EIO;
357 }
358
359 for (entry = 0; entry < le32toh(hdr.hdr_entries); entry++) {
360 memcpy(&ent, buf_start + (entry * le32toh(hdr.hdr_entsz)),
361 sizeof(ent));
362 efi_block_find_partitions_gpt_entry(bdev, &hdr, &ent, entry);
363 }
364
365 FreePool(buf);
366
367 return 0;
368 }
369
370 static int
371 efi_block_find_partitions(struct efi_block_dev *bdev)
372 {
373 int error;
374
375 error = efi_block_find_partitions_gpt(bdev);
376 if (error)
377 error = efi_block_find_partitions_mbr(bdev);
378 if (error)
379 error = efi_block_find_partitions_cd9660(bdev);
380
381 return error;
382 }
383
384 void
385 efi_block_probe(void)
386 {
387 struct efi_block_dev *bdev;
388 struct efi_block_part *bpart;
389 EFI_BLOCK_IO *bio;
390 EFI_STATUS status;
391 uint16_t devindex = 0;
392 int depth = -1;
393 int n;
394
395 status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL, &efi_nblock, &efi_block);
396 if (EFI_ERROR(status))
397 return;
398
399 if (efi_bootdp) {
400 depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
401 if (depth == 0)
402 depth = 1;
403 else if (depth == -1)
404 depth = 2;
405 }
406
407 for (n = 0; n < efi_nblock; n++) {
408 status = uefi_call_wrapper(BS->HandleProtocol, 3, efi_block[n], &BlockIoProtocol, (void **)&bio);
409 if (EFI_ERROR(status) || !bio->Media->MediaPresent)
410 continue;
411
412 if (bio->Media->LogicalPartition)
413 continue;
414
415 bdev = alloc(sizeof(*bdev));
416 bdev->index = devindex++;
417 bdev->bio = bio;
418 bdev->media_id = bio->Media->MediaId;
419 bdev->path = DevicePathFromHandle(efi_block[n]);
420 TAILQ_INIT(&bdev->partitions);
421 TAILQ_INSERT_TAIL(&efi_block_devs, bdev, entries);
422
423 efi_block_find_partitions(bdev);
424
425 if (depth > 0 && efi_device_path_ncmp(efi_bootdp, DevicePathFromHandle(efi_block[n]), depth) == 0) {
426 TAILQ_FOREACH(bpart, &bdev->partitions, entries) {
427 uint8_t fstype = FS_UNUSED;
428 switch (bpart->type) {
429 case EFI_BLOCK_PART_DISKLABEL:
430 fstype = bpart->disklabel.part.p_fstype;
431 break;
432 case EFI_BLOCK_PART_GPT:
433 fstype = bpart->gpt.fstype;
434 break;
435 case EFI_BLOCK_PART_CD9660:
436 fstype = FS_ISO9660;
437 break;
438 }
439 if (fstype == FS_BSDFFS || fstype == FS_ISO9660) {
440 char devname[9];
441 snprintf(devname, sizeof(devname), "hd%u%c", bdev->index, bpart->index + 'a');
442 set_default_device(devname);
443 set_default_fstype(fstype);
444 break;
445 }
446 }
447 }
448 }
449 }
450
451 static void
452 print_guid(const uint8_t *guid)
453 {
454 const int index[] = { 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15 };
455 int i;
456
457 for (i = 0; i < 16; i++) {
458 printf("%02x", guid[index[i]]);
459 if (i == 3 || i == 5 || i == 7 || i == 9)
460 printf("-");
461 }
462 }
463
464 void
465 efi_block_show(void)
466 {
467 struct efi_block_dev *bdev;
468 struct efi_block_part *bpart;
469 uint64_t size;
470 CHAR16 *path;
471
472 TAILQ_FOREACH(bdev, &efi_block_devs, entries) {
473 printf("hd%u (", bdev->index);
474
475 /* Size in MB */
476 size = ((bdev->bio->Media->LastBlock + 1) * bdev->bio->Media->BlockSize) / (1024 * 1024);
477 if (size >= 10000)
478 printf("%"PRIu64" GB", size / 1024);
479 else
480 printf("%"PRIu64" MB", size);
481 printf("): ");
482
483 path = DevicePathToStr(bdev->path);
484 Print(L"%s", path);
485 FreePool(path);
486
487 printf("\n");
488
489 TAILQ_FOREACH(bpart, &bdev->partitions, entries) {
490 switch (bpart->type) {
491 case EFI_BLOCK_PART_DISKLABEL:
492 printf(" hd%u%c (", bdev->index, bpart->index + 'a');
493
494 /* Size in MB */
495 size = ((uint64_t)bpart->disklabel.secsize * bpart->disklabel.part.p_size) / (1024 * 1024);
496 if (size >= 10000)
497 printf("%"PRIu64" GB", size / 1024);
498 else
499 printf("%"PRIu64" MB", size);
500 printf("): ");
501
502 printf("%s\n", fstypenames[bpart->disklabel.part.p_fstype]);
503 break;
504 case EFI_BLOCK_PART_GPT:
505 printf(" hd%u%c ", bdev->index, bpart->index + 'a');
506
507 if (bpart->gpt.ent.ent_name[0] == 0x0000) {
508 printf("\"");
509 print_guid(bpart->gpt.ent.ent_guid);
510 printf("\"");
511 } else {
512 Print(L"\"%s\"", bpart->gpt.ent.ent_name);
513 }
514
515 /* Size in MB */
516 size = (le64toh(bpart->gpt.ent.ent_lba_end) - le64toh(bpart->gpt.ent.ent_lba_start)) * bdev->bio->Media->BlockSize;
517 size /= (1024 * 1024);
518 if (size >= 10000)
519 printf(" (%"PRIu64" GB): ", size / 1024);
520 else
521 printf(" (%"PRIu64" MB): ", size);
522
523 printf("%s\n", fstypenames[bpart->gpt.fstype]);
524 break;
525 case EFI_BLOCK_PART_CD9660:
526 printf(" hd%u%c %s\n", bdev->index, bpart->index + 'a', fstypenames[FS_ISO9660]);
527 break;
528 default:
529 break;
530 }
531 }
532 }
533 }
534
535 struct efi_block_part *
536 efi_block_boot_part(void)
537 {
538 return efi_block_booted;
539 }
540
541 int
542 efi_block_open(struct open_file *f, ...)
543 {
544 struct efi_block_part *bpart;
545 const char *fname;
546 char **file;
547 char *path;
548 va_list ap;
549 int rv, n;
550
551 va_start(ap, f);
552 fname = va_arg(ap, const char *);
553 file = va_arg(ap, char **);
554 va_end(ap);
555
556 rv = efi_block_parse(fname, &bpart, &path);
557 if (rv != 0)
558 return rv;
559
560 for (n = 0; n < ndevs; n++)
561 if (strcmp(DEV_NAME(&devsw[n]), "efiblock") == 0) {
562 f->f_dev = &devsw[n];
563 break;
564 }
565 if (n == ndevs)
566 return ENXIO;
567
568 f->f_devdata = bpart;
569
570 *file = path;
571
572 efi_block_booted = bpart;
573
574 return 0;
575 }
576
577 int
578 efi_block_close(struct open_file *f)
579 {
580 return 0;
581 }
582
583 int
584 efi_block_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize)
585 {
586 struct efi_block_part *bpart = devdata;
587 EFI_STATUS status;
588 void *allocated_buf, *aligned_buf;
589
590 if (rw != F_READ)
591 return EROFS;
592
593 switch (bpart->type) {
594 case EFI_BLOCK_PART_DISKLABEL:
595 if (bpart->bdev->bio->Media->BlockSize != bpart->disklabel.secsize) {
596 printf("%s: unsupported block size %d (expected %d)\n", __func__,
597 bpart->bdev->bio->Media->BlockSize, bpart->disklabel.secsize);
598 return EIO;
599 }
600 dblk += bpart->disklabel.part.p_offset;
601 break;
602 case EFI_BLOCK_PART_GPT:
603 if (bpart->bdev->bio->Media->BlockSize != DEV_BSIZE) {
604 printf("%s: unsupported block size %d (expected %d)\n", __func__,
605 bpart->bdev->bio->Media->BlockSize, DEV_BSIZE);
606 return EIO;
607 }
608 dblk += le64toh(bpart->gpt.ent.ent_lba_start);
609 break;
610 case EFI_BLOCK_PART_CD9660:
611 dblk *= ISO_DEFAULT_BLOCK_SIZE / bpart->bdev->bio->Media->BlockSize;
612 break;
613 default:
614 return EINVAL;
615 }
616
617 if ((bpart->bdev->bio->Media->IoAlign <= 1) ||
618 ((intptr_t)buf & (bpart->bdev->bio->Media->IoAlign - 1)) == 0) {
619 allocated_buf = NULL;
620 aligned_buf = buf;
621 } else if ((allocated_buf = efi_block_allocate_device_buffer(bpart->bdev,
622 size, &aligned_buf)) == NULL) {
623 return ENOMEM;
624 }
625
626 status = uefi_call_wrapper(bpart->bdev->bio->ReadBlocks, 5,
627 bpart->bdev->bio, bpart->bdev->media_id, dblk, size, aligned_buf);
628 if (EFI_ERROR(status)) {
629 if (allocated_buf != NULL)
630 FreePool(allocated_buf);
631 return EIO;
632 }
633 if (allocated_buf != NULL) {
634 memcpy(buf, aligned_buf, size);
635 FreePool(allocated_buf);
636 }
637
638 *rsize = size;
639
640 return 0;
641 }
642