Home | History | Annotate | Line # | Download | only in lib
biosdisk.c revision 1.27
      1 /*	$NetBSD: biosdisk.c,v 1.27 2008/01/04 20:05:46 dsl Exp $	*/
      2 #define DISK_DEBUG
      3 
      4 /*
      5  * Copyright (c) 1996, 1998
      6  *	Matthias Drochner.  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 WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  *
     28  */
     29 
     30 /*
     31  * raw BIOS disk device for libsa.
     32  * needs lowlevel parts from bios_disk.S and biosdisk_ll.c
     33  * partly from netbsd:sys/arch/i386/boot/disk.c
     34  * no bad144 handling!
     35  *
     36  * A lot of this must match sys/kern/subr_disk_mbr.c
     37  */
     38 
     39 /*
     40  * Ported to boot 386BSD by Julian Elischer (julian (at) tfs.com) Sept 1992
     41  *
     42  * Mach Operating System
     43  * Copyright (c) 1992, 1991 Carnegie Mellon University
     44  * All Rights Reserved.
     45  *
     46  * Permission to use, copy, modify and distribute this software and its
     47  * documentation is hereby granted, provided that both the copyright
     48  * notice and this permission notice appear in all copies of the
     49  * software, derivative works or modified versions, and any portions
     50  * thereof, and that both notices appear in supporting documentation.
     51  *
     52  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
     53  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
     54  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     55  *
     56  * Carnegie Mellon requests users of this software to return to
     57  *
     58  *  Software Distribution Coordinator  or  Software.Distribution (at) CS.CMU.EDU
     59  *  School of Computer Science
     60  *  Carnegie Mellon University
     61  *  Pittsburgh PA 15213-3890
     62  *
     63  * any improvements or extensions that they make and grant Carnegie Mellon
     64  * the rights to redistribute these changes.
     65  */
     66 
     67 #include <sys/types.h>
     68 #include <sys/disklabel.h>
     69 #include <sys/md5.h>
     70 #include <sys/param.h>
     71 
     72 #include <fs/cd9660/iso.h>
     73 
     74 #include <lib/libsa/stand.h>
     75 #include <lib/libsa/saerrno.h>
     76 #include <machine/stdarg.h>
     77 
     78 #include "libi386.h"
     79 #include "biosdisk_ll.h"
     80 #include "biosdisk.h"
     81 #ifdef _STANDALONE
     82 #include "bootinfo.h"
     83 #endif
     84 
     85 #define BUFSIZE	2048	/* must be large enough for a CD sector */
     86 
     87 struct biosdisk {
     88 	struct biosdisk_ll ll;
     89 	int             boff;
     90 	char            buf[BUFSIZE];
     91 };
     92 
     93 #ifdef _STANDALONE
     94 static struct btinfo_bootdisk bi_disk;
     95 static struct btinfo_bootwedge bi_wedge;
     96 #endif
     97 
     98 #define	RF_PROTECTED_SECTORS	64	/* XXX refer to <.../rf_optnames.h> */
     99 
    100 int
    101 biosdisk_strategy(void *devdata, int flag, daddr_t dblk, size_t size,
    102 		  void *buf, size_t *rsize)
    103 {
    104 	struct biosdisk *d;
    105 	int blks, frag;
    106 
    107 	if (flag != F_READ)
    108 		return EROFS;
    109 
    110 	d = (struct biosdisk *) devdata;
    111 
    112 	if (d->ll.type == BIOSDISK_TYPE_CD)
    113 		dblk = dblk * DEV_BSIZE / ISO_DEFAULT_BLOCK_SIZE;
    114 
    115 	dblk += d->boff;
    116 
    117 	blks = size / d->ll.secsize;
    118 	if (blks && readsects(&d->ll, dblk, blks, buf, 0)) {
    119 		if (rsize)
    120 			*rsize = 0;
    121 		return EIO;
    122 	}
    123 
    124 	/* needed for CD */
    125 	frag = size % d->ll.secsize;
    126 	if (frag) {
    127 		if (readsects(&d->ll, dblk + blks, 1, d->buf, 0)) {
    128 			if (rsize)
    129 				*rsize = blks * d->ll.secsize;
    130 			return EIO;
    131 		}
    132 		memcpy(buf + blks * d->ll.secsize, d->buf, frag);
    133 	}
    134 
    135 	if (rsize)
    136 		*rsize = size;
    137 	return 0;
    138 }
    139 
    140 static struct biosdisk *
    141 alloc_biosdisk(int biosdev)
    142 {
    143 	struct biosdisk *d;
    144 
    145 	d = alloc(sizeof(*d));
    146 	if (d == NULL)
    147 		return NULL;
    148 	memset(d, 0, sizeof(*d));
    149 
    150 	d->ll.dev = biosdev;
    151 	if (set_geometry(&d->ll, NULL)) {
    152 #ifdef DISK_DEBUG
    153 		printf("no geometry information\n");
    154 #endif
    155 		dealloc(d, sizeof(*d));
    156 		return NULL;
    157 	}
    158 	return d;
    159 }
    160 
    161 #ifndef NO_DISKLABEL
    162 static int
    163 check_label(struct biosdisk *d, int sector)
    164 {
    165 	struct disklabel *lp;
    166 
    167 	/* find partition in NetBSD disklabel */
    168 	if (readsects(&d->ll, sector + LABELSECTOR, 1, d->buf, 0)) {
    169 #ifdef DISK_DEBUG
    170 		printf("Error reading disklabel\n");
    171 #endif
    172 		return EIO;
    173 	}
    174 	lp = (struct disklabel *) (d->buf + LABELOFFSET);
    175 	if (lp->d_magic != DISKMAGIC || dkcksum(lp)) {
    176 #ifdef DISK_DEBUG
    177 		printf("warning: no disklabel in sector %u\n", sector);
    178 #endif
    179 		return -1;
    180 	}
    181 
    182 	d->boff = sector;
    183 	return 0;
    184 }
    185 
    186 static int
    187 read_label(struct biosdisk *d)
    188 {
    189 	struct disklabel dflt_lbl;
    190 	struct mbr_partition mbr[MBR_PART_COUNT];
    191 	struct partition *p;
    192 	int sector, i;
    193 	int error;
    194 	int typ;
    195 	int ext_base, this_ext, next_ext;
    196 #ifdef COMPAT_386BSD_MBRPART
    197 	int sector_386bsd = -1;
    198 #endif
    199 
    200 	memset(&dflt_lbl, 0, sizeof(dflt_lbl));
    201 	dflt_lbl.d_npartitions = 8;
    202 
    203 	d->boff = 0;
    204 
    205 	if (d->ll.type != BIOSDISK_TYPE_HD)
    206 		/* No label on floppy and CD */
    207 		return -1;
    208 
    209 	/*
    210 	 * find NetBSD Partition in DOS partition table
    211 	 * XXX check magic???
    212 	 */
    213 	ext_base = 0;
    214 	next_ext = 0;
    215 	for (;;) {
    216 		this_ext = ext_base + next_ext;
    217 		next_ext = 0;
    218 		if (readsects(&d->ll, this_ext, 1, d->buf, 0)) {
    219 #ifdef DISK_DEBUG
    220 			printf("error reading MBR sector %d\n", this_ext);
    221 #endif
    222 			return EIO;
    223 		}
    224 		memcpy(&mbr, ((struct mbr_sector *)d->buf)->mbr_parts,
    225 		       sizeof(mbr));
    226 		/* Look for NetBSD partition ID */
    227 		for (i = 0; i < MBR_PART_COUNT; i++) {
    228 			typ = mbr[i].mbrp_type;
    229 			if (typ == 0)
    230 				continue;
    231 			sector = this_ext + mbr[i].mbrp_start;
    232 #ifdef DISK_DEBUG
    233 			printf("ptn type %d in sector %u\n", typ, sector);
    234 #endif
    235 			if (typ == MBR_PTYPE_NETBSD) {
    236 				error = check_label(d, sector);
    237 				if (error >= 0)
    238 					return error;
    239 			}
    240 			if (MBR_IS_EXTENDED(typ)) {
    241 				next_ext = mbr[i].mbrp_start;
    242 				continue;
    243 			}
    244 #ifdef COMPAT_386BSD_MBRPART
    245 			if (this_ext == 0 && typ == MBR_PTYPE_386BSD)
    246 				sector_386bsd = sector;
    247 #endif
    248 			if (this_ext != 0) {
    249 				if (dflt_lbl.d_npartitions >= MAXPARTITIONS)
    250 					continue;
    251 				p = &dflt_lbl.d_partitions[dflt_lbl.d_npartitions++];
    252 			} else
    253 				p = &dflt_lbl.d_partitions[i];
    254 			p->p_offset = sector;
    255 			p->p_size = mbr[i].mbrp_size;
    256 			p->p_fstype = xlat_mbr_fstype(typ);
    257 		}
    258 		if (next_ext == 0)
    259 			break;
    260 		if (ext_base == 0) {
    261 			ext_base = next_ext;
    262 			next_ext = 0;
    263 		}
    264 	}
    265 
    266 	sector = 0;
    267 #ifdef COMPAT_386BSD_MBRPART
    268 	if (sector_386bsd != -1) {
    269 		printf("old BSD partition ID!\n");
    270 		sector = sector_386bsd;
    271 	}
    272 #endif
    273 
    274 	/*
    275 	 * One of two things:
    276 	 * 	1. no MBR
    277 	 *	2. no NetBSD partition in MBR
    278 	 *
    279 	 * We simply default to "start of disk" in this case and
    280 	 * press on.
    281 	 */
    282 	error = check_label(d, sector);
    283 	if (error >= 0)
    284 		return error;
    285 
    286 	/*
    287 	 * Nothing at start of disk, return info from mbr partitions.
    288 	 */
    289 	/* XXX fill it to make checksum match kernel one */
    290 	dflt_lbl.d_checksum = dkcksum(&dflt_lbl);
    291 	memcpy(d->buf, &dflt_lbl, sizeof(dflt_lbl));
    292 	return 0;
    293 }
    294 #endif /* NO_DISKLABEL */
    295 
    296 /* Determine likely partition for possible sector number of dos
    297  * partition.
    298  */
    299 
    300 int
    301 biosdisk_findpartition(int biosdev, u_int sector)
    302 {
    303 #ifdef NO_DISKLABEL
    304 	return 0;
    305 #else
    306 	struct biosdisk *d;
    307 	int partition = 0;
    308 	struct disklabel *lp;
    309 #ifdef DISK_DEBUG
    310 	printf("looking for partition device %x, sector %u\n", biosdev, sector);
    311 #endif
    312 
    313 	/* Look for netbsd partition that is the dos boot one */
    314 	d = alloc_biosdisk(biosdev);
    315 	if (d == NULL)
    316 		return 0;
    317 
    318 	if (read_label(d) == 0) {
    319 		lp = (struct disklabel *)(d->buf + LABELOFFSET);
    320 		for (partition = lp->d_npartitions; --partition;){
    321 			if (lp->d_partitions[partition].p_fstype == FS_UNUSED)
    322 				continue;
    323 			if (lp->d_partitions[partition].p_offset == sector)
    324 				break;
    325 		}
    326 	}
    327 
    328 	dealloc(d, sizeof(*d));
    329 	return partition;
    330 #endif /* NO_DISKLABEL */
    331 }
    332 
    333 int
    334 biosdisk_open(struct open_file *f, ...)
    335 /* struct open_file *f, int biosdev, int partition */
    336 {
    337 	va_list ap;
    338 	struct biosdisk *d;
    339 	int biosdev;
    340 	int partition;
    341 #ifndef NO_DISKLABEL
    342 	struct disklabel *lp;
    343 #endif
    344 	int error = 0;
    345 
    346 	va_start(ap, f);
    347 	biosdev = va_arg(ap, int);
    348 	d = alloc_biosdisk(biosdev);
    349 	if (d == NULL) {
    350 		error = ENXIO;
    351 		goto out;
    352 	}
    353 
    354 	partition = va_arg(ap, int);
    355 #ifdef _STANDALONE
    356 	bi_disk.biosdev = d->ll.dev;
    357 	bi_disk.partition = partition;
    358 	bi_disk.labelsector = -1;
    359 
    360 	bi_wedge.biosdev = d->ll.dev;
    361 	bi_wedge.matchblk = -1;
    362 #endif
    363 
    364 #ifndef NO_DISKLABEL
    365 	if (partition == RAW_PART)
    366 		goto nolabel;
    367 	error = read_label(d);
    368 	if (error == -1) {
    369 		error = 0;
    370 		goto nolabel;
    371 	}
    372 	if (error)
    373 		goto out;
    374 
    375 	lp = (struct disklabel *) (d->buf + LABELOFFSET);
    376 	if (partition >= lp->d_npartitions ||
    377 	    lp->d_partitions[partition].p_fstype == FS_UNUSED) {
    378 #ifdef DISK_DEBUG
    379 		printf("illegal partition\n");
    380 #endif
    381 		error = EPART;
    382 		goto out;
    383 	}
    384 #ifdef _STANDALONE
    385 	bi_disk.labelsector = d->boff + LABELSECTOR;
    386 	bi_disk.label.type = lp->d_type;
    387 	memcpy(bi_disk.label.packname, lp->d_packname, 16);
    388 	bi_disk.label.checksum = lp->d_checksum;
    389 
    390 	bi_wedge.startblk = lp->d_partitions[partition].p_offset;
    391 	bi_wedge.nblks = lp->d_partitions[partition].p_size;
    392 	bi_wedge.matchblk = d->boff + LABELSECTOR;
    393 	bi_wedge.matchnblks = 1;
    394 	{
    395 		MD5_CTX ctx;
    396 
    397 		MD5Init(&ctx);
    398 		MD5Update(&ctx, (void *) d->buf, 512);
    399 		MD5Final(bi_wedge.matchhash, &ctx);
    400 	}
    401 #endif
    402 	d->boff = lp->d_partitions[partition].p_offset;
    403 	if (lp->d_partitions[partition].p_fstype == FS_RAID)
    404 		d->boff += RF_PROTECTED_SECTORS;
    405 nolabel:
    406 #endif /* NO_DISKLABEL */
    407 
    408 #ifdef DISK_DEBUG
    409 	printf("partition @%d\n", d->boff);
    410 #endif
    411 
    412 #ifdef _STANDALONE
    413 	BI_ADD(&bi_disk, BTINFO_BOOTDISK, sizeof(bi_disk));
    414 	BI_ADD(&bi_wedge, BTINFO_BOOTWEDGE, sizeof(bi_wedge));
    415 #endif
    416 
    417 	f->f_devdata = d;
    418 out:
    419         va_end(ap);
    420 	if (error)
    421 		dealloc(d, sizeof(struct biosdisk));
    422 	return error;
    423 }
    424 
    425 #ifndef LIBSA_NO_FS_CLOSE
    426 int
    427 biosdisk_close(struct open_file *f)
    428 {
    429 	struct biosdisk *d = f->f_devdata;
    430 
    431 	/* let the floppy drive go off */
    432 	if (d->ll.type == BIOSDISK_TYPE_FD)
    433 		delay(3000000);	/* 2s is enough on all PCs I found */
    434 
    435 	dealloc(d, sizeof(struct biosdisk));
    436 	f->f_devdata = NULL;
    437 	return 0;
    438 }
    439 #endif
    440 
    441 int
    442 biosdisk_ioctl(struct open_file *f, u_long cmd, void *arg)
    443 {
    444 	return EIO;
    445 }
    446