1 /* $OpenBSD: efidev.c,v 1.5 2019/07/29 22:33:26 yasuoka Exp $ */ 2 3 /* 4 * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> 5 * Copyright (c) 2016 Mark Kettenis 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * 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 #include <sys/param.h> 31 #include <sys/reboot.h> 32 #include <sys/disklabel.h> 33 #include <lib/libz/zlib.h> 34 #include <isofs/cd9660/iso.h> 35 36 #include "libsa.h" 37 38 #include <efi.h> 39 #include "eficall.h" 40 41 extern EFI_BOOT_SERVICES *BS; 42 43 extern int debug; 44 45 #include "disk.h" 46 #include "efidev.h" 47 48 #define EFI_BLKSPERSEC(_ed) ((_ed)->blkio->Media->BlockSize / DEV_BSIZE) 49 #define EFI_SECTOBLK(_ed, _n) ((_n) * EFI_BLKSPERSEC(_ed)) 50 51 static EFI_STATUS 52 efid_io(int, efi_diskinfo_t, u_int, int, void *); 53 static int efid_diskio(int, struct diskinfo *, u_int, int, void *); 54 const char * efi_getdisklabel(efi_diskinfo_t, struct disklabel *); 55 static int efi_getdisklabel_cd9660(efi_diskinfo_t, struct disklabel *); 56 static u_int findopenbsd(efi_diskinfo_t, const char **); 57 static u_int findopenbsd_gpt(efi_diskinfo_t, const char **); 58 static int gpt_chk_mbr(struct dos_partition *, u_int64_t); 59 60 void 61 efid_init(struct diskinfo *dip, void *handle) 62 { 63 EFI_BLOCK_IO *blkio = handle; 64 65 memset(dip, 0, sizeof(struct diskinfo)); 66 dip->ed.blkio = blkio; 67 dip->ed.mediaid = blkio->Media->MediaId; 68 dip->diskio = efid_diskio; 69 dip->strategy = efistrategy; 70 71 if (efi_getdisklabel(&dip->ed, &dip->disklabel) == NULL) 72 dip->flags |= DISKINFO_FLAG_GOODLABEL; 73 } 74 75 static EFI_STATUS 76 efid_io(int rw, efi_diskinfo_t ed, u_int off, int nsect, void *buf) 77 { 78 EFI_STATUS status = EFI_SUCCESS; 79 EFI_PHYSICAL_ADDRESS addr; 80 caddr_t data; 81 82 if (ed->blkio->Media->BlockSize != DEV_BSIZE) 83 return (EFI_UNSUPPORTED); 84 85 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 86 EFI_SIZE_TO_PAGES(nsect * DEV_BSIZE), &addr); 87 if (EFI_ERROR(status)) 88 goto on_eio; 89 data = (caddr_t)(uintptr_t)addr; 90 91 switch (rw) { 92 case F_READ: 93 status = EFI_CALL(ed->blkio->ReadBlocks, 94 ed->blkio, ed->mediaid, off, 95 nsect * DEV_BSIZE, data); 96 if (EFI_ERROR(status)) 97 goto on_eio; 98 memcpy(buf, data, nsect * DEV_BSIZE); 99 break; 100 case F_WRITE: 101 if (ed->blkio->Media->ReadOnly) 102 goto on_eio; 103 memcpy(data, buf, nsect * DEV_BSIZE); 104 status = EFI_CALL(ed->blkio->WriteBlocks, 105 ed->blkio, ed->mediaid, off, 106 nsect * DEV_BSIZE, data); 107 if (EFI_ERROR(status)) 108 goto on_eio; 109 break; 110 } 111 return (EFI_SUCCESS); 112 113 on_eio: 114 BS->FreePages(addr, EFI_SIZE_TO_PAGES(nsect * DEV_BSIZE)); 115 116 return (status); 117 } 118 119 static int 120 efid_diskio(int rw, struct diskinfo *dip, u_int off, int nsect, void *buf) 121 { 122 EFI_STATUS status; 123 124 status = efid_io(rw, &dip->ed, off, nsect, buf); 125 126 return ((EFI_ERROR(status))? -1 : 0); 127 } 128 129 /* 130 * Returns 0 if the MBR with the provided partition array is a GPT protective 131 * MBR, and returns 1 otherwise. A GPT protective MBR would have one and only 132 * one MBR partition, an EFI partition that either covers the whole disk or as 133 * much of it as is possible with a 32bit size field. 134 * 135 * Taken from kern/subr_disk.c. 136 * 137 * NOTE: MS always uses a size of UINT32_MAX for the EFI partition!** 138 */ 139 static int 140 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize) 141 { 142 struct dos_partition *dp2; 143 int efi, found, i; 144 u_int32_t psize; 145 146 found = efi = 0; 147 for (dp2=dp, i=0; i < NDOSPART; i++, dp2++) { 148 if (dp2->dp_typ == DOSPTYP_UNUSED) 149 continue; 150 found++; 151 if (dp2->dp_typ != DOSPTYP_EFI) 152 continue; 153 psize = letoh32(dp2->dp_size); 154 if (psize == (dsize - 1) || 155 psize == UINT32_MAX) { 156 if (letoh32(dp2->dp_start) == 1) 157 efi++; 158 } 159 } 160 if (found == 1 && efi == 1) 161 return (0); 162 163 return (1); 164 } 165 166 /* 167 * Try to find the disk address of the first MBR OpenBSD partition. 168 * 169 * N.B.: must boot from a partition within first 2^32-1 sectors! 170 * 171 * Called only if the MBR on sector 0 is *not* a protective MBR 172 * and *does* have a valid signature. 173 * 174 * We don't check the signatures of EBR's, and they cannot be 175 * protective MBR's so there is no need to check for that. 176 */ 177 static u_int 178 findopenbsd(efi_diskinfo_t ed, const char **err) 179 { 180 EFI_STATUS status; 181 struct dos_mbr mbr; 182 struct dos_partition *dp; 183 u_int mbroff = DOSBBSECTOR; 184 u_int mbr_eoff = DOSBBSECTOR; /* Offset of MBR extended partition. */ 185 int i, maxebr = DOS_MAXEBR, nextebr; 186 187 again: 188 if (!maxebr--) { 189 *err = "too many extended partitions"; 190 return (-1); 191 } 192 193 /* Read MBR */ 194 bzero(&mbr, sizeof(mbr)); 195 status = efid_io(F_READ, ed, mbroff, 1, &mbr); 196 if (EFI_ERROR(status)) { 197 *err = "Disk I/O Error"; 198 return (-1); 199 } 200 201 /* Search for OpenBSD partition */ 202 nextebr = 0; 203 for (i = 0; i < NDOSPART; i++) { 204 dp = &mbr.dmbr_parts[i]; 205 if (!dp->dp_size) 206 continue; 207 #ifdef BIOS_DEBUG 208 if (debug) 209 printf("found partition %u: " 210 "type %u (0x%x) offset %u (0x%x)\n", 211 (int)(dp - mbr.dmbr_parts), 212 dp->dp_typ, dp->dp_typ, 213 dp->dp_start, dp->dp_start); 214 #endif 215 if (dp->dp_typ == DOSPTYP_OPENBSD) { 216 if (dp->dp_start > (dp->dp_start + mbroff)) 217 continue; 218 return (dp->dp_start + mbroff); 219 } 220 221 /* 222 * Record location of next ebr if and only if this is the first 223 * extended partition in this boot record! 224 */ 225 if (!nextebr && (dp->dp_typ == DOSPTYP_EXTEND || 226 dp->dp_typ == DOSPTYP_EXTENDL)) { 227 nextebr = dp->dp_start + mbr_eoff; 228 if (nextebr < dp->dp_start) 229 nextebr = (u_int)-1; 230 if (mbr_eoff == DOSBBSECTOR) 231 mbr_eoff = dp->dp_start; 232 } 233 } 234 235 if (nextebr && nextebr != (u_int)-1) { 236 mbroff = nextebr; 237 goto again; 238 } 239 240 return (-1); 241 } 242 243 /* 244 * Try to find the disk address of the first GPT OpenBSD partition. 245 * 246 * N.B.: must boot from a partition within first 2^32-1 sectors! 247 * 248 * Called only if the MBR on sector 0 *is* a protective MBR 249 * with a valid signature and sector 1 is a valid GPT header. 250 */ 251 static u_int 252 findopenbsd_gpt(efi_diskinfo_t ed, const char **err) 253 { 254 EFI_STATUS status; 255 struct gpt_header gh; 256 int i, part, found; 257 uint64_t lba; 258 uint32_t orig_csum, new_csum; 259 uint32_t ghsize, ghpartsize, ghpartnum, ghpartspersec; 260 uint32_t gpsectors; 261 const char openbsd_uuid_code[] = GPT_UUID_OPENBSD; 262 struct gpt_partition gp; 263 static struct uuid *openbsd_uuid = NULL, openbsd_uuid_space; 264 static u_char buf[4096]; 265 266 /* Prepare OpenBSD UUID */ 267 if (openbsd_uuid == NULL) { 268 /* XXX: should be replaced by uuid_dec_be() */ 269 memcpy(&openbsd_uuid_space, openbsd_uuid_code, 270 sizeof(openbsd_uuid_space)); 271 openbsd_uuid_space.time_low = 272 betoh32(openbsd_uuid_space.time_low); 273 openbsd_uuid_space.time_mid = 274 betoh16(openbsd_uuid_space.time_mid); 275 openbsd_uuid_space.time_hi_and_version = 276 betoh16(openbsd_uuid_space.time_hi_and_version); 277 278 openbsd_uuid = &openbsd_uuid_space; 279 } 280 281 if (EFI_BLKSPERSEC(ed) > 8) { 282 *err = "disk sector > 4096 bytes\n"; 283 return (-1); 284 } 285 286 /* LBA1: GPT Header */ 287 lba = 1; 288 status = efid_io(F_READ, ed, EFI_SECTOBLK(ed, lba), EFI_BLKSPERSEC(ed), 289 buf); 290 if (EFI_ERROR(status)) { 291 *err = "Disk I/O Error"; 292 return (-1); 293 } 294 memcpy(&gh, buf, sizeof(gh)); 295 296 /* Check signature */ 297 if (letoh64(gh.gh_sig) != GPTSIGNATURE) { 298 *err = "bad GPT signature\n"; 299 return (-1); 300 } 301 302 if (letoh32(gh.gh_rev) != GPTREVISION) { 303 *err = "bad GPT revision\n"; 304 return (-1); 305 } 306 307 ghsize = letoh32(gh.gh_size); 308 if (ghsize < GPTMINHDRSIZE || ghsize > sizeof(struct gpt_header)) { 309 *err = "bad GPT header size\n"; 310 return (-1); 311 } 312 313 /* Check checksum */ 314 orig_csum = gh.gh_csum; 315 gh.gh_csum = 0; 316 new_csum = crc32(0, (unsigned char *)&gh, ghsize); 317 gh.gh_csum = orig_csum; 318 if (letoh32(orig_csum) != new_csum) { 319 *err = "bad GPT header checksum\n"; 320 return (-1); 321 } 322 323 lba = letoh64(gh.gh_part_lba); 324 ghpartsize = letoh32(gh.gh_part_size); 325 ghpartspersec = ed->blkio->Media->BlockSize / ghpartsize; 326 ghpartnum = letoh32(gh.gh_part_num); 327 gpsectors = (ghpartnum + ghpartspersec - 1) / ghpartspersec; 328 new_csum = crc32(0L, Z_NULL, 0); 329 found = 0; 330 for (i = 0; i < gpsectors; i++, lba++) { 331 status = efid_io(F_READ, ed, EFI_SECTOBLK(ed, lba), 332 EFI_BLKSPERSEC(ed), buf); 333 if (EFI_ERROR(status)) { 334 *err = "Disk I/O Error"; 335 return (-1); 336 } 337 for (part = 0; part < ghpartspersec; part++) { 338 if (ghpartnum == 0) 339 break; 340 new_csum = crc32(new_csum, buf + part * sizeof(gp), 341 sizeof(gp)); 342 ghpartnum--; 343 if (found) 344 continue; 345 memcpy(&gp, buf + part * sizeof(gp), sizeof(gp)); 346 if (memcmp(&gp.gp_type, openbsd_uuid, 347 sizeof(struct uuid)) == 0) 348 found = 1; 349 } 350 } 351 if (new_csum != letoh32(gh.gh_part_csum)) { 352 *err = "bad GPT entries checksum\n"; 353 return (-1); 354 } 355 if (found) { 356 lba = letoh64(gp.gp_lba_start); 357 /* Bootloaders do not current handle addresses > UINT_MAX! */ 358 if (lba > UINT_MAX || EFI_SECTOBLK(ed, lba) > UINT_MAX) { 359 *err = "OpenBSD Partition LBA > 2**32 - 1"; 360 return (-1); 361 } 362 return (u_int)lba; 363 } 364 365 return (-1); 366 } 367 368 const char * 369 efi_getdisklabel(efi_diskinfo_t ed, struct disklabel *label) 370 { 371 u_int start = 0; 372 uint8_t buf[DEV_BSIZE]; 373 struct dos_partition dosparts[NDOSPART]; 374 EFI_STATUS status; 375 const char *err = NULL; 376 int error; 377 378 /* 379 * Read sector 0. Ensure it has a valid MBR signature. 380 * 381 * If it's a protective MBR then try to find the disklabel via 382 * GPT. If it's not a protective MBR, try to find the disklabel 383 * via MBR. 384 */ 385 memset(buf, 0, sizeof(buf)); 386 status = efid_io(F_READ, ed, DOSBBSECTOR, 1, buf); 387 if (EFI_ERROR(status)) 388 return ("Disk I/O Error"); 389 390 /* Check MBR signature. */ 391 if (buf[510] != 0x55 || buf[511] != 0xaa) { 392 if (efi_getdisklabel_cd9660(ed, label) == 0) 393 return (NULL); 394 return ("invalid MBR signature"); 395 } 396 397 memcpy(dosparts, buf+DOSPARTOFF, sizeof(dosparts)); 398 399 /* check for GPT protective MBR. */ 400 if (gpt_chk_mbr(dosparts, ed->blkio->Media->LastBlock + 1) == 0) { 401 start = findopenbsd_gpt(ed, &err); 402 if (start == (u_int)-1) { 403 if (err != NULL) 404 return (err); 405 return ("no OpenBSD GPT partition"); 406 } 407 } else { 408 start = findopenbsd(ed, &err); 409 if (start == (u_int)-1) { 410 if (err != NULL) 411 return (err); 412 return "no OpenBSD MBR partition\n"; 413 } 414 } 415 416 /* Load BSD disklabel */ 417 #ifdef BIOS_DEBUG 418 if (debug) 419 printf("loading disklabel @ %u\n", start + DOS_LABELSECTOR); 420 #endif 421 /* read disklabel */ 422 error = efid_io(F_READ, ed, EFI_SECTOBLK(ed, start) + DOS_LABELSECTOR, 423 1, buf); 424 425 if (error) 426 return "failed to read disklabel"; 427 428 /* Fill in disklabel */ 429 return (getdisklabel(buf, label)); 430 } 431 432 static int 433 efi_getdisklabel_cd9660(efi_diskinfo_t ed, struct disklabel *label) 434 { 435 int off; 436 uint8_t buf[DEV_BSIZE]; 437 EFI_STATUS status; 438 439 for (off = 0; off < 100; off++) { 440 status = efid_io(F_READ, ed, 441 EFI_BLKSPERSEC(ed) * (16 + off), 1, buf); 442 if (EFI_ERROR(status)) 443 return (-1); 444 if (bcmp(buf + 1, ISO_STANDARD_ID, 5) != 0 || 445 buf[0] == ISO_VD_END) 446 return (-1); 447 if (buf[0] == ISO_VD_PRIMARY) 448 break; 449 } 450 if (off >= 100) 451 return (-1); 452 453 /* Create an imaginary disk label */ 454 label->d_secsize = 2048; 455 label->d_ntracks = 1; 456 label->d_nsectors = 100; 457 label->d_ncylinders = 1; 458 label->d_secpercyl = label->d_ntracks * label->d_nsectors; 459 460 strncpy(label->d_typename, "ATAPI CD-ROM", sizeof(label->d_typename)); 461 label->d_type = DTYPE_ATAPI; 462 463 strncpy(label->d_packname, "fictitious", sizeof(label->d_packname)); 464 DL_SETDSIZE(label, 100); 465 466 label->d_bbsize = 2048; 467 label->d_sbsize = 2048; 468 469 /* 'a' partition covering the "whole" disk */ 470 DL_SETPOFFSET(&label->d_partitions[0], 0); 471 DL_SETPSIZE(&label->d_partitions[0], 100); 472 label->d_partitions[0].p_fstype = FS_UNUSED; 473 474 /* The raw partition is special */ 475 DL_SETPOFFSET(&label->d_partitions[RAW_PART], 0); 476 DL_SETPSIZE(&label->d_partitions[RAW_PART], 100); 477 label->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 478 479 label->d_npartitions = MAXPARTITIONS; 480 481 label->d_magic = DISKMAGIC; 482 label->d_magic2 = DISKMAGIC; 483 label->d_checksum = dkcksum(label); 484 485 return (0); 486 } 487 488 int 489 efiopen(struct open_file *f, ...) 490 { 491 struct diskinfo *dip = NULL; 492 va_list ap; 493 u_int unit, part; 494 int i = 0; 495 496 va_start(ap, f); 497 unit = va_arg(ap, u_int); 498 part = va_arg(ap, u_int); 499 va_end(ap); 500 501 if (part >= MAXPARTITIONS) 502 return (ENXIO); 503 504 TAILQ_FOREACH(dip, &disklist, list) { 505 if (i == unit) 506 break; 507 i++; 508 } 509 510 if (dip == NULL) 511 return (ENXIO); 512 513 if ((dip->flags & DISKINFO_FLAG_GOODLABEL) == 0) 514 return (ENXIO); 515 516 dip->part = part; 517 bootdev_dip = dip; 518 f->f_devdata = dip; 519 520 return 0; 521 } 522 523 int 524 efistrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf, 525 size_t *rsize) 526 { 527 struct diskinfo *dip = (struct diskinfo *)devdata; 528 int error = 0; 529 size_t nsect; 530 531 nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE; 532 blk += DL_SECTOBLK(&dip->disklabel, 533 dip->disklabel.d_partitions[dip->part].p_offset); 534 535 if (blk < 0) 536 error = EINVAL; 537 else 538 error = efid_diskio(rw, dip, blk, nsect, buf); 539 540 if (rsize != NULL) 541 *rsize = nsect * DEV_BSIZE; 542 543 return (error); 544 } 545 546 int 547 eficlose(struct open_file *f) 548 { 549 f->f_devdata = NULL; 550 551 return 0; 552 } 553 554 int 555 efiioctl(struct open_file *f, u_long cmd, void *data) 556 { 557 return 0; 558 } 559