efiblock.c revision 1.9 1 /* $NetBSD: efiblock.c,v 1.9 2020/10/18 18:05:48 tnn 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 sz = __MAX(sizeof(*vd), bdev->bio->Media->BlockSize);
131 sz = roundup(sz, bdev->bio->Media->BlockSize);
132 if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
133 return ENOMEM;
134
135 for (lba = 16;; lba++) {
136 status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
137 lba, sz, buf_start);
138 if (EFI_ERROR(status))
139 goto io_error;
140
141 vd = (struct iso_primary_descriptor *)buf_start;
142 if (memcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
143 goto io_error;
144 if (isonum_711(vd->type) == ISO_VD_END)
145 goto io_error;
146 if (isonum_711(vd->type) == ISO_VD_PRIMARY)
147 break;
148 }
149
150 if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE)
151 goto io_error;
152
153 bpart = alloc(sizeof(*bpart));
154 bpart->index = 0;
155 bpart->bdev = bdev;
156 bpart->type = EFI_BLOCK_PART_CD9660;
157 TAILQ_INSERT_TAIL(&bdev->partitions, bpart, entries);
158
159 FreePool(buf);
160 return 0;
161
162 io_error:
163 FreePool(buf);
164 return EIO;
165 }
166
167 static int
168 efi_block_find_partitions_disklabel(struct efi_block_dev *bdev, struct mbr_sector *mbr, uint32_t start, uint32_t size)
169 {
170 struct efi_block_part *bpart;
171 struct disklabel d;
172 struct partition *p;
173 EFI_STATUS status;
174 EFI_LBA lba;
175 void *buf, *buf_start;
176 UINT32 sz;
177 int n;
178
179 sz = __MAX(sizeof(d), bdev->bio->Media->BlockSize);
180 sz = roundup(sz, bdev->bio->Media->BlockSize);
181 if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
182 return ENOMEM;
183
184 lba = (((EFI_LBA)start + LABELSECTOR) * DEV_BSIZE) / bdev->bio->Media->BlockSize;
185 status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
186 lba, sz, buf_start);
187 if (EFI_ERROR(status) || getdisklabel(buf_start, &d) != NULL) {
188 FreePool(buf);
189 return EIO;
190 }
191 FreePool(buf);
192
193 if (le32toh(d.d_magic) != DISKMAGIC || le32toh(d.d_magic2) != DISKMAGIC)
194 return EINVAL;
195 if (le16toh(d.d_npartitions) > MAXPARTITIONS)
196 return EINVAL;
197
198 for (n = 0; n < le16toh(d.d_npartitions); n++) {
199 p = &d.d_partitions[n];
200 switch (p->p_fstype) {
201 case FS_BSDFFS:
202 case FS_MSDOS:
203 case FS_BSDLFS:
204 break;
205 default:
206 continue;
207 }
208
209 bpart = alloc(sizeof(*bpart));
210 bpart->index = n;
211 bpart->bdev = bdev;
212 bpart->type = EFI_BLOCK_PART_DISKLABEL;
213 bpart->disklabel.secsize = le32toh(d.d_secsize);
214 bpart->disklabel.part = *p;
215 efi_block_generate_hash_mbr(bpart, mbr);
216 TAILQ_INSERT_TAIL(&bdev->partitions, bpart, entries);
217 }
218
219 return 0;
220 }
221
222 static int
223 efi_block_find_partitions_mbr(struct efi_block_dev *bdev)
224 {
225 struct mbr_sector mbr;
226 struct mbr_partition *mbr_part;
227 EFI_STATUS status;
228 void *buf, *buf_start;
229 UINT32 sz;
230 int n;
231
232 sz = __MAX(sizeof(mbr), bdev->bio->Media->BlockSize);
233 sz = roundup(sz, bdev->bio->Media->BlockSize);
234 if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
235 return ENOMEM;
236
237 status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
238 0, sz, buf_start);
239 if (EFI_ERROR(status)) {
240 FreePool(buf);
241 return EIO;
242 }
243 memcpy(&mbr, buf_start, sizeof(mbr));
244 FreePool(buf);
245
246 if (le32toh(mbr.mbr_magic) != MBR_MAGIC)
247 return ENOENT;
248
249 for (n = 0; n < MBR_PART_COUNT; n++) {
250 mbr_part = &mbr.mbr_parts[n];
251 if (le32toh(mbr_part->mbrp_size) == 0)
252 continue;
253 if (mbr_part->mbrp_type == MBR_PTYPE_NETBSD) {
254 efi_block_find_partitions_disklabel(bdev, &mbr, le32toh(mbr_part->mbrp_start), le32toh(mbr_part->mbrp_size));
255 break;
256 }
257 }
258
259 return 0;
260 }
261
262 static const struct {
263 struct uuid guid;
264 uint8_t fstype;
265 } gpt_guid_to_str[] = {
266 { GPT_ENT_TYPE_NETBSD_FFS, FS_BSDFFS },
267 { GPT_ENT_TYPE_NETBSD_LFS, FS_BSDLFS },
268 { GPT_ENT_TYPE_NETBSD_RAIDFRAME, FS_RAID },
269 { GPT_ENT_TYPE_NETBSD_CCD, FS_CCD },
270 { GPT_ENT_TYPE_NETBSD_CGD, FS_CGD },
271 { GPT_ENT_TYPE_MS_BASIC_DATA, FS_MSDOS }, /* or NTFS? ambiguous */
272 { GPT_ENT_TYPE_EFI, FS_MSDOS },
273 };
274
275 static int
276 efi_block_find_partitions_gpt_entry(struct efi_block_dev *bdev, struct gpt_hdr *hdr, struct gpt_ent *ent, UINT32 index)
277 {
278 struct efi_block_part *bpart;
279 uint8_t fstype = FS_UNUSED;
280 struct uuid uuid;
281 int n;
282
283 memcpy(&uuid, ent->ent_type, sizeof(uuid));
284 for (n = 0; n < __arraycount(gpt_guid_to_str); n++)
285 if (memcmp(ent->ent_type, &gpt_guid_to_str[n].guid, sizeof(ent->ent_type)) == 0) {
286 fstype = gpt_guid_to_str[n].fstype;
287 break;
288 }
289 if (fstype == FS_UNUSED)
290 return 0;
291
292 bpart = alloc(sizeof(*bpart));
293 bpart->index = index;
294 bpart->bdev = bdev;
295 bpart->type = EFI_BLOCK_PART_GPT;
296 bpart->gpt.fstype = fstype;
297 bpart->gpt.ent = *ent;
298 memcpy(bpart->hash, ent->ent_guid, sizeof(bpart->hash));
299 TAILQ_INSERT_TAIL(&bdev->partitions, bpart, entries);
300
301 return 0;
302 }
303
304 static int
305 efi_block_find_partitions_gpt(struct efi_block_dev *bdev)
306 {
307 struct gpt_hdr hdr;
308 struct gpt_ent ent;
309 EFI_STATUS status;
310 void *buf, *buf_start;
311 UINT32 sz, entry;
312
313 sz = __MAX(sizeof(hdr), bdev->bio->Media->BlockSize);
314 sz = roundup(sz, bdev->bio->Media->BlockSize);
315 if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
316 return ENOMEM;
317
318 status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
319 GPT_HDR_BLKNO, sz, buf_start);
320 if (EFI_ERROR(status)) {
321 FreePool(buf);
322 return EIO;
323 }
324 memcpy(&hdr, buf_start, sizeof(hdr));
325 FreePool(buf);
326
327 if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0)
328 return ENOENT;
329 if (le32toh(hdr.hdr_entsz) < sizeof(ent))
330 return EINVAL;
331
332 sz = __MAX(le32toh(hdr.hdr_entsz) * le32toh(hdr.hdr_entries), bdev->bio->Media->BlockSize);
333 sz = roundup(sz, bdev->bio->Media->BlockSize);
334 if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
335 return ENOMEM;
336
337 status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
338 le64toh(hdr.hdr_lba_table), sz, buf_start);
339 if (EFI_ERROR(status)) {
340 FreePool(buf);
341 return EIO;
342 }
343
344 for (entry = 0; entry < le32toh(hdr.hdr_entries); entry++) {
345 memcpy(&ent, buf_start + (entry * le32toh(hdr.hdr_entsz)),
346 sizeof(ent));
347 efi_block_find_partitions_gpt_entry(bdev, &hdr, &ent, entry);
348 }
349
350 FreePool(buf);
351
352 return 0;
353 }
354
355 static int
356 efi_block_find_partitions(struct efi_block_dev *bdev)
357 {
358 int error;
359
360 error = efi_block_find_partitions_gpt(bdev);
361 if (error)
362 error = efi_block_find_partitions_mbr(bdev);
363 if (error)
364 error = efi_block_find_partitions_cd9660(bdev);
365
366 return error;
367 }
368
369 void
370 efi_block_probe(void)
371 {
372 struct efi_block_dev *bdev;
373 struct efi_block_part *bpart;
374 EFI_BLOCK_IO *bio;
375 EFI_STATUS status;
376 uint16_t devindex = 0;
377 int depth = -1;
378 int n;
379
380 status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL, &efi_nblock, &efi_block);
381 if (EFI_ERROR(status))
382 return;
383
384 if (efi_bootdp) {
385 depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
386 if (depth == 0)
387 depth = 1;
388 else if (depth == -1)
389 depth = 2;
390 }
391
392 for (n = 0; n < efi_nblock; n++) {
393 status = uefi_call_wrapper(BS->HandleProtocol, 3, efi_block[n], &BlockIoProtocol, (void **)&bio);
394 if (EFI_ERROR(status) || !bio->Media->MediaPresent)
395 continue;
396
397 if (bio->Media->LogicalPartition)
398 continue;
399
400 bdev = alloc(sizeof(*bdev));
401 bdev->index = devindex++;
402 bdev->bio = bio;
403 bdev->media_id = bio->Media->MediaId;
404 bdev->path = DevicePathFromHandle(efi_block[n]);
405 TAILQ_INIT(&bdev->partitions);
406 TAILQ_INSERT_TAIL(&efi_block_devs, bdev, entries);
407
408 efi_block_find_partitions(bdev);
409
410 if (depth > 0 && efi_device_path_ncmp(efi_bootdp, DevicePathFromHandle(efi_block[n]), depth) == 0) {
411 TAILQ_FOREACH(bpart, &bdev->partitions, entries) {
412 uint8_t fstype = FS_UNUSED;
413 switch (bpart->type) {
414 case EFI_BLOCK_PART_DISKLABEL:
415 fstype = bpart->disklabel.part.p_fstype;
416 break;
417 case EFI_BLOCK_PART_GPT:
418 fstype = bpart->gpt.fstype;
419 break;
420 case EFI_BLOCK_PART_CD9660:
421 fstype = FS_ISO9660;
422 break;
423 }
424 if (fstype == FS_BSDFFS || fstype == FS_ISO9660) {
425 char devname[9];
426 snprintf(devname, sizeof(devname), "hd%u%c", bdev->index, bpart->index + 'a');
427 set_default_device(devname);
428 set_default_fstype(fstype);
429 break;
430 }
431 }
432 }
433 }
434 }
435
436 static void
437 print_guid(const uint8_t *guid)
438 {
439 const int index[] = { 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15 };
440 int i;
441
442 for (i = 0; i < 16; i++) {
443 printf("%02x", guid[index[i]]);
444 if (i == 3 || i == 5 || i == 7 || i == 9)
445 printf("-");
446 }
447 }
448
449 void
450 efi_block_show(void)
451 {
452 struct efi_block_dev *bdev;
453 struct efi_block_part *bpart;
454 uint64_t size;
455 CHAR16 *path;
456
457 TAILQ_FOREACH(bdev, &efi_block_devs, entries) {
458 printf("hd%u (", bdev->index);
459
460 /* Size in MB */
461 size = ((bdev->bio->Media->LastBlock + 1) * bdev->bio->Media->BlockSize) / (1024 * 1024);
462 if (size >= 10000)
463 printf("%"PRIu64" GB", size / 1024);
464 else
465 printf("%"PRIu64" MB", size);
466 printf("): ");
467
468 path = DevicePathToStr(bdev->path);
469 Print(L"%s", path);
470 FreePool(path);
471
472 printf("\n");
473
474 TAILQ_FOREACH(bpart, &bdev->partitions, entries) {
475 switch (bpart->type) {
476 case EFI_BLOCK_PART_DISKLABEL:
477 printf(" hd%u%c (", bdev->index, bpart->index + 'a');
478
479 /* Size in MB */
480 size = ((uint64_t)bpart->disklabel.secsize * bpart->disklabel.part.p_size) / (1024 * 1024);
481 if (size >= 10000)
482 printf("%"PRIu64" GB", size / 1024);
483 else
484 printf("%"PRIu64" MB", size);
485 printf("): ");
486
487 printf("%s\n", fstypenames[bpart->disklabel.part.p_fstype]);
488 break;
489 case EFI_BLOCK_PART_GPT:
490 printf(" hd%u%c ", bdev->index, bpart->index + 'a');
491
492 if (bpart->gpt.ent.ent_name[0] == 0x0000) {
493 printf("\"");
494 print_guid(bpart->gpt.ent.ent_guid);
495 printf("\"");
496 } else {
497 Print(L"\"%s\"", bpart->gpt.ent.ent_name);
498 }
499
500 /* Size in MB */
501 size = (le64toh(bpart->gpt.ent.ent_lba_end) - le64toh(bpart->gpt.ent.ent_lba_start)) * bdev->bio->Media->BlockSize;
502 size /= (1024 * 1024);
503 if (size >= 10000)
504 printf(" (%"PRIu64" GB): ", size / 1024);
505 else
506 printf(" (%"PRIu64" MB): ", size);
507
508 printf("%s\n", fstypenames[bpart->gpt.fstype]);
509 break;
510 case EFI_BLOCK_PART_CD9660:
511 printf(" hd%u%c %s\n", bdev->index, bpart->index + 'a', fstypenames[FS_ISO9660]);
512 break;
513 default:
514 break;
515 }
516 }
517 }
518 }
519
520 struct efi_block_part *
521 efi_block_boot_part(void)
522 {
523 return efi_block_booted;
524 }
525
526 int
527 efi_block_open(struct open_file *f, ...)
528 {
529 struct efi_block_part *bpart;
530 const char *fname;
531 char **file;
532 char *path;
533 va_list ap;
534 int rv, n;
535
536 va_start(ap, f);
537 fname = va_arg(ap, const char *);
538 file = va_arg(ap, char **);
539 va_end(ap);
540
541 rv = efi_block_parse(fname, &bpart, &path);
542 if (rv != 0)
543 return rv;
544
545 for (n = 0; n < ndevs; n++)
546 if (strcmp(DEV_NAME(&devsw[n]), "efiblock") == 0) {
547 f->f_dev = &devsw[n];
548 break;
549 }
550 if (n == ndevs)
551 return ENXIO;
552
553 f->f_devdata = bpart;
554
555 *file = path;
556
557 efi_block_booted = bpart;
558
559 return 0;
560 }
561
562 int
563 efi_block_close(struct open_file *f)
564 {
565 return 0;
566 }
567
568 int
569 efi_block_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize)
570 {
571 struct efi_block_part *bpart = devdata;
572 EFI_STATUS status;
573 void *allocated_buf, *aligned_buf;
574
575 if (rw != F_READ)
576 return EROFS;
577
578 switch (bpart->type) {
579 case EFI_BLOCK_PART_DISKLABEL:
580 if (bpart->bdev->bio->Media->BlockSize != bpart->disklabel.secsize) {
581 printf("%s: unsupported block size %d (expected %d)\n", __func__,
582 bpart->bdev->bio->Media->BlockSize, bpart->disklabel.secsize);
583 return EIO;
584 }
585 dblk += bpart->disklabel.part.p_offset;
586 break;
587 case EFI_BLOCK_PART_GPT:
588 if (bpart->bdev->bio->Media->BlockSize != DEV_BSIZE) {
589 printf("%s: unsupported block size %d (expected %d)\n", __func__,
590 bpart->bdev->bio->Media->BlockSize, DEV_BSIZE);
591 return EIO;
592 }
593 dblk += le64toh(bpart->gpt.ent.ent_lba_start);
594 break;
595 case EFI_BLOCK_PART_CD9660:
596 break;
597 default:
598 return EINVAL;
599 }
600
601 if ((bpart->bdev->bio->Media->IoAlign <= 1) ||
602 ((intptr_t)buf & (bpart->bdev->bio->Media->IoAlign - 1)) == 0) {
603 allocated_buf = NULL;
604 aligned_buf = buf;
605 } else if ((allocated_buf = efi_block_allocate_device_buffer(bpart->bdev,
606 size, &aligned_buf)) == NULL)
607 return ENOMEM;
608
609 status = uefi_call_wrapper(bpart->bdev->bio->ReadBlocks, 5,
610 bpart->bdev->bio, bpart->bdev->media_id, dblk, size, aligned_buf);
611 if (EFI_ERROR(status)) {
612 if (allocated_buf != NULL)
613 FreePool(allocated_buf);
614 return EIO;
615 }
616 if (allocated_buf != NULL) {
617 memcpy(buf, aligned_buf, size);
618 FreePool(allocated_buf);
619 }
620
621 *rsize = size;
622
623 return 0;
624 }
625