Home | History | Annotate | Line # | Download | only in lib
biosdisk.c revision 1.46
      1 /*	$NetBSD: biosdisk.c,v 1.46 2017/01/24 11:09:14 nonaka Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1996, 1998
      5  *	Matthias Drochner.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  *
     27  */
     28 
     29 /*
     30  * raw BIOS disk device for libsa.
     31  * needs lowlevel parts from bios_disk.S and biosdisk_ll.c
     32  * partly from netbsd:sys/arch/i386/boot/disk.c
     33  * no bad144 handling!
     34  *
     35  * A lot of this must match sys/kern/subr_disk_mbr.c
     36  */
     37 
     38 /*
     39  * Ported to boot 386BSD by Julian Elischer (julian (at) tfs.com) Sept 1992
     40  *
     41  * Mach Operating System
     42  * Copyright (c) 1992, 1991 Carnegie Mellon University
     43  * All Rights Reserved.
     44  *
     45  * Permission to use, copy, modify and distribute this software and its
     46  * documentation is hereby granted, provided that both the copyright
     47  * notice and this permission notice appear in all copies of the
     48  * software, derivative works or modified versions, and any portions
     49  * thereof, and that both notices appear in supporting documentation.
     50  *
     51  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
     52  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
     53  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     54  *
     55  * Carnegie Mellon requests users of this software to return to
     56  *
     57  *  Software Distribution Coordinator  or  Software.Distribution (at) CS.CMU.EDU
     58  *  School of Computer Science
     59  *  Carnegie Mellon University
     60  *  Pittsburgh PA 15213-3890
     61  *
     62  * any improvements or extensions that they make and grant Carnegie Mellon
     63  * the rights to redistribute these changes.
     64  */
     65 
     66 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
     67 #define FSTYPENAMES
     68 #endif
     69 
     70 #include <lib/libkern/libkern.h>
     71 #include <lib/libsa/stand.h>
     72 
     73 #include <sys/types.h>
     74 #include <sys/md5.h>
     75 #include <sys/param.h>
     76 #include <sys/disklabel.h>
     77 #include <sys/disklabel_gpt.h>
     78 #include <sys/uuid.h>
     79 
     80 #include <fs/cd9660/iso.h>
     81 
     82 #include <lib/libsa/saerrno.h>
     83 #include <machine/cpu.h>
     84 
     85 #include "libi386.h"
     86 #include "biosdisk_ll.h"
     87 #include "biosdisk.h"
     88 #ifdef _STANDALONE
     89 #include "bootinfo.h"
     90 #endif
     91 
     92 #define BUFSIZE	2048	/* must be large enough for a CD sector */
     93 
     94 #define BIOSDISKNPART 26
     95 
     96 struct biosdisk {
     97 	struct biosdisk_ll ll;
     98 	daddr_t         boff;
     99 	char            buf[BUFSIZE];
    100 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
    101 	struct {
    102 		daddr_t offset;
    103 		daddr_t size;
    104 		int     fstype;
    105 #ifdef EFIBOOT
    106 		const struct gpt_part {
    107 			const struct uuid *guid;
    108 			const char *name;
    109 		} *guid;
    110 		uint64_t attr;
    111 #endif
    112 	} part[BIOSDISKNPART];
    113 #endif
    114 };
    115 
    116 #ifndef NO_GPT
    117 const struct uuid GET_nbsd_raid = GPT_ENT_TYPE_NETBSD_RAIDFRAME;
    118 const struct uuid GET_nbsd_ffs = GPT_ENT_TYPE_NETBSD_FFS;
    119 const struct uuid GET_nbsd_lfs = GPT_ENT_TYPE_NETBSD_LFS;
    120 const struct uuid GET_nbsd_swap = GPT_ENT_TYPE_NETBSD_SWAP;
    121 const struct uuid GET_nbsd_ccd = GPT_ENT_TYPE_NETBSD_CCD;
    122 const struct uuid GET_nbsd_cgd = GPT_ENT_TYPE_NETBSD_CGD;
    123 #ifdef EFIBOOT
    124 const struct uuid GET_efi = GPT_ENT_TYPE_EFI;
    125 const struct uuid GET_mbr = GPT_ENT_TYPE_MBR;
    126 const struct uuid GET_fbsd = GPT_ENT_TYPE_FREEBSD;
    127 const struct uuid GET_fbsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
    128 const struct uuid GET_fbsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
    129 const struct uuid GET_fbsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
    130 const struct uuid GET_fbsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
    131 const struct uuid GET_ms_rsvd = GPT_ENT_TYPE_MS_RESERVED;
    132 const struct uuid GET_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
    133 const struct uuid GET_ms_ldm_metadata = GPT_ENT_TYPE_MS_LDM_METADATA;
    134 const struct uuid GET_ms_ldm_data = GPT_ENT_TYPE_MS_LDM_DATA;
    135 const struct uuid GET_linux_data = GPT_ENT_TYPE_LINUX_DATA;
    136 const struct uuid GET_linux_raid = GPT_ENT_TYPE_LINUX_RAID;
    137 const struct uuid GET_linux_swap = GPT_ENT_TYPE_LINUX_SWAP;
    138 const struct uuid GET_linux_lvm = GPT_ENT_TYPE_LINUX_LVM;
    139 const struct uuid GET_apple_hfs = GPT_ENT_TYPE_APPLE_HFS;
    140 const struct uuid GET_apple_ufs = GPT_ENT_TYPE_APPLE_UFS;
    141 const struct uuid GET_bios = GPT_ENT_TYPE_BIOS;
    142 
    143 const struct gpt_part gpt_parts[] = {
    144 	{ &GET_nbsd_raid,	"NetBSD RAID" },
    145 	{ &GET_nbsd_ffs,	"NetBSD FFS" },
    146 	{ &GET_nbsd_lfs,	"NetBSD LFS" },
    147 	{ &GET_nbsd_swap,	"NetBSD Swap" },
    148 	{ &GET_nbsd_ccd,	"NetBSD ccd" },
    149 	{ &GET_nbsd_cgd,	"NetBSD cgd" },
    150 	{ &GET_efi,		"EFI System" },
    151 	{ &GET_mbr,		"MBR" },
    152 	{ &GET_fbsd,		"FreeBSD" },
    153 	{ &GET_fbsd_swap,	"FreeBSD Swap" },
    154 	{ &GET_fbsd_ufs,	"FreeBSD UFS" },
    155 	{ &GET_fbsd_vinum,	"FreeBSD Vinum" },
    156 	{ &GET_fbsd_zfs,	"FreeBSD ZFS" },
    157 	{ &GET_ms_rsvd,		"Microsoft Reserved" },
    158 	{ &GET_ms_basic_data,	"Microsoft Basic data" },
    159 	{ &GET_ms_ldm_metadata,	"Microsoft LDM metadata" },
    160 	{ &GET_ms_ldm_data,	"Microsoft LDM data" },
    161 	{ &GET_linux_data,	"Linux data" },
    162 	{ &GET_linux_raid,	"Linux RAID" },
    163 	{ &GET_linux_swap,	"Linux Swap" },
    164 	{ &GET_linux_lvm,	"Linux LVM" },
    165 	{ &GET_apple_hfs,	"Apple HFS" },
    166 	{ &GET_apple_ufs,	"Apple UFS" },
    167 	{ &GET_bios,		"BIOS Boot (GRUB)" },
    168 };
    169 #endif
    170 #endif /* NO_GPT */
    171 
    172 #ifdef _STANDALONE
    173 static struct btinfo_bootdisk bi_disk;
    174 static struct btinfo_bootwedge bi_wedge;
    175 #endif
    176 
    177 #define MBR_PARTS(buf) ((char *)(buf) + offsetof(struct mbr_sector, mbr_parts))
    178 
    179 #define	RF_PROTECTED_SECTORS	64	/* XXX refer to <.../rf_optnames.h> */
    180 
    181 int
    182 biosdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
    183 		  void *buf, size_t *rsize)
    184 {
    185 	struct biosdisk *d;
    186 	int blks, frag;
    187 
    188 	if (flag != F_READ)
    189 		return EROFS;
    190 
    191 	d = (struct biosdisk *) devdata;
    192 
    193 	if (d->ll.type == BIOSDISK_TYPE_CD)
    194 		dblk = dblk * DEV_BSIZE / ISO_DEFAULT_BLOCK_SIZE;
    195 
    196 	dblk += d->boff;
    197 
    198 	blks = size / d->ll.secsize;
    199 	if (blks && readsects(&d->ll, dblk, blks, buf, 0)) {
    200 		if (rsize)
    201 			*rsize = 0;
    202 		return EIO;
    203 	}
    204 
    205 	/* needed for CD */
    206 	frag = size % d->ll.secsize;
    207 	if (frag) {
    208 		if (readsects(&d->ll, dblk + blks, 1, d->buf, 0)) {
    209 			if (rsize)
    210 				*rsize = blks * d->ll.secsize;
    211 			return EIO;
    212 		}
    213 		memcpy(buf + blks * d->ll.secsize, d->buf, frag);
    214 	}
    215 
    216 	if (rsize)
    217 		*rsize = size;
    218 	return 0;
    219 }
    220 
    221 static struct biosdisk *
    222 alloc_biosdisk(int biosdev)
    223 {
    224 	struct biosdisk *d;
    225 
    226 	d = alloc(sizeof(*d));
    227 	if (d == NULL)
    228 		return NULL;
    229 	memset(d, 0, sizeof(*d));
    230 
    231 	d->ll.dev = biosdev;
    232 	if (set_geometry(&d->ll, NULL)) {
    233 #ifdef DISK_DEBUG
    234 		printf("no geometry information\n");
    235 #endif
    236 		dealloc(d, sizeof(*d));
    237 		return NULL;
    238 	}
    239 	return d;
    240 }
    241 
    242 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
    243 static void
    244 md5(void *hash, const void *data, size_t len)
    245 {
    246 	MD5_CTX ctx;
    247 
    248 	MD5Init(&ctx);
    249 	MD5Update(&ctx, data, len);
    250 	MD5Final(hash, &ctx);
    251 
    252 	return;
    253 }
    254 #endif
    255 
    256 #ifndef NO_GPT
    257 static bool
    258 guid_is_nil(const struct uuid *u)
    259 {
    260 	static const struct uuid nil = { .time_low = 0 };
    261 	return (memcmp(u, &nil, sizeof(*u)) == 0 ? true : false);
    262 }
    263 
    264 static bool
    265 guid_is_equal(const struct uuid *a, const struct uuid *b)
    266 {
    267 	return (memcmp(a, b, sizeof(*a)) == 0 ? true : false);
    268 }
    269 
    270 static int
    271 check_gpt(struct biosdisk *d, daddr_t sector)
    272 {
    273 	struct gpt_hdr gpth;
    274 	const struct gpt_ent *ep;
    275 	const struct uuid *u;
    276 	daddr_t entblk;
    277 	size_t size;
    278 	uint32_t crc;
    279 	int sectors;
    280 	int entries;
    281 	int entry;
    282 	int i, j;
    283 
    284 	/* read in gpt_hdr sector */
    285 	if (readsects(&d->ll, sector, 1, d->buf, 1)) {
    286 #ifdef DISK_DEBUG
    287 		printf("Error reading GPT header at %"PRId64"\n", sector);
    288 #endif
    289 		return EIO;
    290 	}
    291 
    292 	memcpy(&gpth, d->buf, sizeof(gpth));
    293 
    294 	if (memcmp(GPT_HDR_SIG, gpth.hdr_sig, sizeof(gpth.hdr_sig)))
    295 		return -1;
    296 
    297 	crc = gpth.hdr_crc_self;
    298 	gpth.hdr_crc_self = 0;
    299 	gpth.hdr_crc_self = crc32(0, (const void *)&gpth, GPT_HDR_SIZE);
    300 	if (gpth.hdr_crc_self != crc) {
    301 		return -1;
    302 	}
    303 
    304 	if (gpth.hdr_lba_self != sector)
    305 		return -1;
    306 
    307 #ifdef _STANDALONE
    308 	bi_wedge.matchblk = sector;
    309 	bi_wedge.matchnblks = 1;
    310 
    311 	md5(bi_wedge.matchhash, d->buf, d->ll.secsize);
    312 #endif
    313 
    314 	sectors = sizeof(d->buf)/d->ll.secsize; /* sectors per buffer */
    315 	entries = sizeof(d->buf)/gpth.hdr_entsz; /* entries per buffer */
    316 	entblk = gpth.hdr_lba_table;
    317 	crc = crc32(0, NULL, 0);
    318 
    319 	j = 0;
    320 	ep = (const struct gpt_ent *)d->buf;
    321 
    322 	for (entry = 0; entry < gpth.hdr_entries; entry += entries) {
    323 		size = MIN(sizeof(d->buf),
    324 		    (gpth.hdr_entries - entry) * gpth.hdr_entsz);
    325 		entries = size / gpth.hdr_entsz;
    326 		sectors = roundup(size, d->ll.secsize) / d->ll.secsize;
    327 		if (readsects(&d->ll, entblk, sectors, d->buf, 1))
    328 			return -1;
    329 		entblk += sectors;
    330 		crc = crc32(crc, (const void *)d->buf, size);
    331 
    332 		for (i = 0; j < BIOSDISKNPART && i < entries; i++) {
    333 			u = (const struct uuid *)ep[i].ent_type;
    334 			if (!guid_is_nil(u)) {
    335 				d->part[j].offset = ep[i].ent_lba_start;
    336 				d->part[j].size = ep[i].ent_lba_end -
    337 				    ep[i].ent_lba_start + 1;
    338 				if (guid_is_equal(u, &GET_nbsd_ffs))
    339 					d->part[j].fstype = FS_BSDFFS;
    340 				else if (guid_is_equal(u, &GET_nbsd_lfs))
    341 					d->part[j].fstype = FS_BSDLFS;
    342 				else if (guid_is_equal(u, &GET_nbsd_raid))
    343 					d->part[j].fstype = FS_RAID;
    344 				else if (guid_is_equal(u, &GET_nbsd_swap))
    345 					d->part[j].fstype = FS_SWAP;
    346 				else if (guid_is_equal(u, &GET_nbsd_ccd))
    347 					d->part[j].fstype = FS_CCD;
    348 				else if (guid_is_equal(u, &GET_nbsd_cgd))
    349 					d->part[j].fstype = FS_CGD;
    350 				else
    351 					d->part[j].fstype = FS_OTHER;
    352 #ifdef EFIBOOT
    353 				for (int k = 0;
    354 				     k < __arraycount(gpt_parts);
    355 				     k++) {
    356 					if (guid_is_equal(u, gpt_parts[k].guid))
    357 						d->part[j].guid = &gpt_parts[k];
    358 				}
    359 				d->part[j].attr = ep[i].ent_attr;
    360 #endif
    361 				j++;
    362 			}
    363 		}
    364 
    365 	}
    366 
    367 	if (crc != gpth.hdr_crc_table) {
    368 #ifdef DISK_DEBUG
    369 		printf("GPT table CRC invalid\n");
    370 #endif
    371 		return -1;
    372 	}
    373 
    374 	return 0;
    375 }
    376 
    377 static int
    378 read_gpt(struct biosdisk *d)
    379 {
    380 	struct biosdisk_extinfo ed;
    381 	daddr_t gptsector[2];
    382 	int i, error;
    383 
    384 	if (d->ll.type != BIOSDISK_TYPE_HD)
    385 		/* No GPT on floppy and CD */
    386 		return -1;
    387 
    388 	gptsector[0] = GPT_HDR_BLKNO;
    389 	if (set_geometry(&d->ll, &ed) == 0 && d->ll.flags & BIOSDISK_INT13EXT) {
    390 		gptsector[1] = ed.totsec - 1;
    391 		/* Sanity check values returned from BIOS */
    392 		if (ed.sbytes >= 512 && (ed.sbytes & (ed.sbytes - 1)) == 0)
    393 			d->ll.secsize = ed.sbytes;
    394 	} else {
    395 #ifdef DISK_DEBUG
    396 		printf("Unable to determine extended disk geometry - "
    397 			"using CHS\n");
    398 #endif
    399 		/* at least try some other reasonable values then */
    400 		gptsector[1] = d->ll.chs_sectors - 1;
    401 	}
    402 
    403 	for (i = 0; i < __arraycount(gptsector); i++) {
    404 		error = check_gpt(d, gptsector[i]);
    405 		if (error == 0)
    406 			break;
    407 	}
    408 
    409 	if (i >= __arraycount(gptsector)) {
    410 		memset(d->part, 0, sizeof(d->part));
    411 		return -1;
    412 	}
    413 
    414 #ifndef USE_SECONDARY_GPT
    415 	if (i > 0) {
    416 #ifdef DISK_DEBUG
    417 		printf("ignoring valid secondary GPT\n");
    418 #endif
    419 		return -1;
    420 	}
    421 #endif
    422 
    423 #ifdef DISK_DEBUG
    424 	printf("using %s GPT\n", (i == 0) ? "primary" : "secondary");
    425 #endif
    426 	return 0;
    427 }
    428 #endif	/* !NO_GPT */
    429 
    430 #ifndef NO_DISKLABEL
    431 static void
    432 ingest_label(struct biosdisk *d, struct disklabel *lp)
    433 {
    434 	int part;
    435 
    436 	memset(d->part, 0, sizeof(d->part));
    437 
    438 	for (part = 0; part < lp->d_npartitions; part++) {
    439 		if (lp->d_partitions[part].p_size == 0)
    440 			continue;
    441 		if (lp->d_partitions[part].p_fstype == FS_UNUSED)
    442 			continue;
    443 		d->part[part].fstype = lp->d_partitions[part].p_fstype;
    444 		d->part[part].offset = lp->d_partitions[part].p_offset;
    445 		d->part[part].size = lp->d_partitions[part].p_size;
    446 	}
    447 }
    448 
    449 static int
    450 check_label(struct biosdisk *d, daddr_t sector)
    451 {
    452 	struct disklabel *lp;
    453 
    454 	/* find partition in NetBSD disklabel */
    455 	if (readsects(&d->ll, sector + LABELSECTOR, 1, d->buf, 0)) {
    456 #ifdef DISK_DEBUG
    457 		printf("Error reading disklabel\n");
    458 #endif
    459 		return EIO;
    460 	}
    461 	lp = (struct disklabel *) (d->buf + LABELOFFSET);
    462 	if (lp->d_magic != DISKMAGIC || dkcksum(lp)) {
    463 #ifdef DISK_DEBUG
    464 		printf("warning: no disklabel in sector %"PRId64"\n", sector);
    465 #endif
    466 		return -1;
    467 	}
    468 
    469 	ingest_label(d, lp);
    470 
    471 #ifdef _STANDALONE
    472 	bi_disk.labelsector = sector + LABELSECTOR;
    473 	bi_disk.label.type = lp->d_type;
    474 	memcpy(bi_disk.label.packname, lp->d_packname, 16);
    475 	bi_disk.label.checksum = lp->d_checksum;
    476 
    477 	bi_wedge.matchblk = sector + LABELSECTOR;
    478 	bi_wedge.matchnblks = 1;
    479 
    480 	md5(bi_wedge.matchhash, d->buf, d->ll.secsize);
    481 #endif
    482 
    483 	return 0;
    484 }
    485 
    486 static int
    487 read_minix_subp(struct biosdisk *d, struct disklabel* dflt_lbl,
    488 			int this_ext, daddr_t sector)
    489 {
    490 	struct mbr_partition mbr[MBR_PART_COUNT];
    491 	int i;
    492 	int typ;
    493 	struct partition *p;
    494 
    495 	if (readsects(&d->ll, sector, 1, d->buf, 0)) {
    496 #ifdef DISK_DEBUG
    497 		printf("Error reading MFS sector %"PRId64"\n", sector);
    498 #endif
    499 		return EIO;
    500 	}
    501 	if ((uint8_t)d->buf[510] != 0x55 || (uint8_t)d->buf[511] != 0xAA) {
    502 		return -1;
    503 	}
    504 	memcpy(&mbr, MBR_PARTS(d->buf), sizeof(mbr));
    505 	for (i = 0; i < MBR_PART_COUNT; i++) {
    506 		typ = mbr[i].mbrp_type;
    507 		if (typ == 0)
    508 			continue;
    509 		sector = this_ext + mbr[i].mbrp_start;
    510 		if (dflt_lbl->d_npartitions >= MAXPARTITIONS)
    511 			continue;
    512 		p = &dflt_lbl->d_partitions[dflt_lbl->d_npartitions++];
    513 		p->p_offset = sector;
    514 		p->p_size = mbr[i].mbrp_size;
    515 		p->p_fstype = xlat_mbr_fstype(typ);
    516 	}
    517 	return 0;
    518 }
    519 
    520 static int
    521 read_label(struct biosdisk *d)
    522 {
    523 	struct disklabel dflt_lbl;
    524 	struct mbr_partition mbr[MBR_PART_COUNT];
    525 	struct partition *p;
    526 	uint32_t sector;
    527 	int i;
    528 	int error;
    529 	int typ;
    530 	uint32_t ext_base, this_ext, next_ext;
    531 #ifdef COMPAT_386BSD_MBRPART
    532 	int sector_386bsd = -1;
    533 #endif
    534 
    535 	memset(&dflt_lbl, 0, sizeof(dflt_lbl));
    536 	dflt_lbl.d_npartitions = 8;
    537 
    538 	d->boff = 0;
    539 
    540 	if (d->ll.type != BIOSDISK_TYPE_HD)
    541 		/* No label on floppy and CD */
    542 		return -1;
    543 
    544 	/*
    545 	 * find NetBSD Partition in DOS partition table
    546 	 * XXX check magic???
    547 	 */
    548 	ext_base = 0;
    549 	next_ext = 0;
    550 	for (;;) {
    551 		this_ext = ext_base + next_ext;
    552 		next_ext = 0;
    553 		if (readsects(&d->ll, this_ext, 1, d->buf, 0)) {
    554 #ifdef DISK_DEBUG
    555 			printf("error reading MBR sector %u\n", this_ext);
    556 #endif
    557 			return EIO;
    558 		}
    559 		memcpy(&mbr, MBR_PARTS(d->buf), sizeof(mbr));
    560 		/* Look for NetBSD partition ID */
    561 		for (i = 0; i < MBR_PART_COUNT; i++) {
    562 			typ = mbr[i].mbrp_type;
    563 			if (typ == 0)
    564 				continue;
    565 			sector = this_ext + mbr[i].mbrp_start;
    566 #ifdef DISK_DEBUG
    567 			printf("ptn type %d in sector %u\n", typ, sector);
    568 #endif
    569                         if (typ == MBR_PTYPE_MINIX_14B) {
    570 				if (!read_minix_subp(d, &dflt_lbl,
    571 						   this_ext, sector)) {
    572 					/* Don't add "container" partition */
    573 					continue;
    574 				}
    575 			}
    576 			if (typ == MBR_PTYPE_NETBSD) {
    577 				error = check_label(d, sector);
    578 				if (error >= 0)
    579 					return error;
    580 			}
    581 			if (MBR_IS_EXTENDED(typ)) {
    582 				next_ext = mbr[i].mbrp_start;
    583 				continue;
    584 			}
    585 #ifdef COMPAT_386BSD_MBRPART
    586 			if (this_ext == 0 && typ == MBR_PTYPE_386BSD)
    587 				sector_386bsd = sector;
    588 #endif
    589 			if (this_ext != 0) {
    590 				if (dflt_lbl.d_npartitions >= MAXPARTITIONS)
    591 					continue;
    592 				p = &dflt_lbl.d_partitions[dflt_lbl.d_npartitions++];
    593 			} else
    594 				p = &dflt_lbl.d_partitions[i];
    595 			p->p_offset = sector;
    596 			p->p_size = mbr[i].mbrp_size;
    597 			p->p_fstype = xlat_mbr_fstype(typ);
    598 		}
    599 		if (next_ext == 0)
    600 			break;
    601 		if (ext_base == 0) {
    602 			ext_base = next_ext;
    603 			next_ext = 0;
    604 		}
    605 	}
    606 
    607 	sector = 0;
    608 #ifdef COMPAT_386BSD_MBRPART
    609 	if (sector_386bsd != -1) {
    610 		printf("old BSD partition ID!\n");
    611 		sector = sector_386bsd;
    612 	}
    613 #endif
    614 
    615 	/*
    616 	 * One of two things:
    617 	 * 	1. no MBR
    618 	 *	2. no NetBSD partition in MBR
    619 	 *
    620 	 * We simply default to "start of disk" in this case and
    621 	 * press on.
    622 	 */
    623 	error = check_label(d, sector);
    624 	if (error >= 0)
    625 		return error;
    626 
    627 	/*
    628 	 * Nothing at start of disk, return info from mbr partitions.
    629 	 */
    630 	/* XXX fill it to make checksum match kernel one */
    631 	dflt_lbl.d_checksum = dkcksum(&dflt_lbl);
    632 	ingest_label(d, &dflt_lbl);
    633 	return 0;
    634 }
    635 #endif /* NO_DISKLABEL */
    636 
    637 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
    638 static int
    639 read_partitions(struct biosdisk *d)
    640 {
    641 	int error;
    642 
    643 	error = -1;
    644 
    645 #ifndef NO_GPT
    646 	error = read_gpt(d);
    647 	if (error == 0)
    648 		return 0;
    649 
    650 #endif
    651 #ifndef NO_DISKLABEL
    652 	error = read_label(d);
    653 
    654 #endif
    655 	return error;
    656 }
    657 #endif
    658 
    659 void
    660 biosdisk_probe(void)
    661 {
    662 	struct biosdisk d;
    663 	struct biosdisk_extinfo ed;
    664 	uint64_t size;
    665 	int first;
    666 	int i;
    667 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
    668 	int part;
    669 #endif
    670 
    671 	for (i = 0; i < MAX_BIOSDISKS + 2; i++) {
    672 		first = 1;
    673 		memset(&d, 0, sizeof(d));
    674 		memset(&ed, 0, sizeof(ed));
    675 		if (i >= MAX_BIOSDISKS)
    676 			d.ll.dev = 0x00 + i - MAX_BIOSDISKS;	/* fd */
    677 		else
    678 			d.ll.dev = 0x80 + i;			/* hd/cd */
    679 		if (set_geometry(&d.ll, &ed))
    680 			continue;
    681 		printf("disk ");
    682 		switch (d.ll.type) {
    683 		case BIOSDISK_TYPE_CD:
    684 			printf("cd0\n  cd0a\n");
    685 			break;
    686 		case BIOSDISK_TYPE_FD:
    687 			printf("fd%d\n", d.ll.dev & 0x7f);
    688 			printf("  fd%da\n", d.ll.dev & 0x7f);
    689 			break;
    690 		case BIOSDISK_TYPE_HD:
    691 			printf("hd%d", d.ll.dev & 0x7f);
    692 			if (d.ll.flags & BIOSDISK_INT13EXT) {
    693 				printf(" size ");
    694 				size = ed.totsec * ed.sbytes;
    695 				if (size >= (10ULL * 1024 * 1024 * 1024))
    696 					printf("%"PRIu64" GB",
    697 					    size / (1024 * 1024 * 1024));
    698 				else
    699 					printf("%"PRIu64" MB",
    700 					    size / (1024 * 1024));
    701 			}
    702 			printf("\n");
    703 			break;
    704 		}
    705 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
    706 		if (d.ll.type != BIOSDISK_TYPE_HD)
    707 			continue;
    708 
    709 		if (read_partitions(&d) != 0)
    710 			continue;
    711 
    712 		for (part = 0; part < BIOSDISKNPART; part++) {
    713 			if (d.part[part].size == 0)
    714 				continue;
    715 			if (d.part[part].fstype == FS_UNUSED)
    716 				continue;
    717 			if (first) {
    718 				printf(" ");
    719 				first = 0;
    720 			}
    721 			printf(" hd%d%c(", d.ll.dev & 0x7f, part + 'a');
    722 #ifdef EFIBOOT
    723 			if (d.part[part].guid != NULL)
    724 				printf("%s", d.part[part].guid->name);
    725 			else
    726 #endif
    727 			if (d.part[part].fstype < FSMAXTYPES)
    728 				printf("%s",
    729 				  fstypenames[d.part[part].fstype]);
    730 			else
    731 				printf("%d", d.part[part].fstype);
    732 			printf(")");
    733 		}
    734 #endif
    735 		if (first == 0)
    736 			printf("\n");
    737 	}
    738 }
    739 
    740 /* Determine likely partition for possible sector number of dos
    741  * partition.
    742  */
    743 
    744 int
    745 biosdisk_findpartition(int biosdev, daddr_t sector)
    746 {
    747 #if defined(NO_DISKLABEL) && defined(NO_GPT)
    748 	return 0;
    749 #else
    750 	struct biosdisk *d;
    751 	int partition = 0;
    752 #ifdef EFIBOOT
    753 	int candidate = 0;
    754 #endif
    755 
    756 #ifdef DISK_DEBUG
    757 	printf("looking for partition device %x, sector %"PRId64"\n", biosdev, sector);
    758 #endif
    759 
    760 	/* Look for netbsd partition that is the dos boot one */
    761 	d = alloc_biosdisk(biosdev);
    762 	if (d == NULL)
    763 		return 0;
    764 
    765 	if (read_partitions(d) == 0) {
    766 		for (partition = (BIOSDISKNPART-1); --partition;) {
    767 			if (d->part[partition].fstype == FS_UNUSED)
    768 				continue;
    769 #ifdef EFIBOOT
    770 			if (d->part[partition].attr & GPT_ENT_ATTR_BOOTME) {
    771 				switch (d->part[partition].fstype) {
    772 				case FS_BSDFFS:
    773 				case FS_BSDLFS:
    774 				case FS_RAID:
    775 				case FS_CCD:
    776 				case FS_CGD:
    777 					break;
    778 
    779 				default:
    780 					candidate = partition;
    781 					continue;
    782 				}
    783 				break;
    784 			}
    785 #else
    786 			if (d->part[partition].offset == sector)
    787 				break;
    788 #endif
    789 		}
    790 #ifdef EFIBOOT
    791 		if (partition == 0 && candidate != 0)
    792 			partition = candidate;
    793 #endif
    794 	}
    795 
    796 	dealloc(d, sizeof(*d));
    797 	return partition;
    798 #endif /* NO_DISKLABEL && NO_GPT */
    799 }
    800 
    801 #ifdef _STANDALONE
    802 static void
    803 add_biosdisk_bootinfo(void)
    804 {
    805 	if (bootinfo == NULL) {
    806 		return;
    807 	}
    808 	BI_ADD(&bi_disk, BTINFO_BOOTDISK, sizeof(bi_disk));
    809 	BI_ADD(&bi_wedge, BTINFO_BOOTWEDGE, sizeof(bi_wedge));
    810 	return;
    811 }
    812 #endif
    813 
    814 int
    815 biosdisk_open(struct open_file *f, ...)
    816 /* struct open_file *f, int biosdev, int partition */
    817 {
    818 	va_list ap;
    819 	struct biosdisk *d;
    820 	int biosdev;
    821 	int partition;
    822 	int error = 0;
    823 
    824 	va_start(ap, f);
    825 	biosdev = va_arg(ap, int);
    826 	d = alloc_biosdisk(biosdev);
    827 	if (d == NULL) {
    828 		error = ENXIO;
    829 		goto out;
    830 	}
    831 
    832 	partition = va_arg(ap, int);
    833 #ifdef _STANDALONE
    834 	bi_disk.biosdev = d->ll.dev;
    835 	bi_disk.partition = partition;
    836 	bi_disk.labelsector = -1;
    837 
    838 	bi_wedge.biosdev = d->ll.dev;
    839 	bi_wedge.matchblk = -1;
    840 #endif
    841 
    842 #if !defined(NO_DISKLABEL) || !defined(NO_GPT)
    843 	error = read_partitions(d);
    844 	if (error == -1) {
    845 		error = 0;
    846 		goto nolabel;
    847 	}
    848 	if (error)
    849 		goto out;
    850 
    851 	if (partition >= BIOSDISKNPART ||
    852 	    d->part[partition].fstype == FS_UNUSED) {
    853 #ifdef DISK_DEBUG
    854 		printf("illegal partition\n");
    855 #endif
    856 		error = EPART;
    857 		goto out;
    858 	}
    859 
    860 	d->boff = d->part[partition].offset;
    861 
    862 	if (d->part[partition].fstype == FS_RAID)
    863 		d->boff += RF_PROTECTED_SECTORS;
    864 
    865 #ifdef _STANDALONE
    866 	bi_wedge.startblk = d->part[partition].offset;
    867 	bi_wedge.nblks = d->part[partition].size;
    868 #endif
    869 
    870 nolabel:
    871 #endif
    872 #ifdef DISK_DEBUG
    873 	printf("partition @%"PRId64"\n", d->boff);
    874 #endif
    875 
    876 #ifdef _STANDALONE
    877 	add_biosdisk_bootinfo();
    878 #endif
    879 
    880 	f->f_devdata = d;
    881 out:
    882         va_end(ap);
    883 	if (error)
    884 		dealloc(d, sizeof(*d));
    885 	return error;
    886 }
    887 
    888 #ifndef LIBSA_NO_FS_CLOSE
    889 int
    890 biosdisk_close(struct open_file *f)
    891 {
    892 	struct biosdisk *d = f->f_devdata;
    893 
    894 	/* let the floppy drive go off */
    895 	if (d->ll.type == BIOSDISK_TYPE_FD)
    896 		wait_sec(3);	/* 2s is enough on all PCs I found */
    897 
    898 	dealloc(d, sizeof(*d));
    899 	f->f_devdata = NULL;
    900 	return 0;
    901 }
    902 #endif
    903 
    904 int
    905 biosdisk_ioctl(struct open_file *f, u_long cmd, void *arg)
    906 {
    907 	return EIO;
    908 }
    909