Home | History | Annotate | Line # | Download | only in efiboot
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