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