1 1.20 rin /* $NetBSD: efiblock.c,v 1.20 2023/06/14 00:52:25 rin 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.20 rin 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