xref: /netbsd/sys/arch/ofppc/ofppc/disksubr.c (revision 6550d01e)
1 /*	$NetBSD: disksubr.c,v 1.22 2010/12/31 21:50:28 phx Exp $	*/
2 
3 /*-
4  * Copyright (c) 2010 Frank Wille.
5  * All rights reserved.
6  *
7  * Written by Frank Wille for The NetBSD Project.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  * Copyright (c) 1994 Christian E. Hopps
33  *
34  * Redistribution and use in source and binary forms, with or without
35  * modification, are permitted provided that the following conditions
36  * are met:
37  * 1. Redistributions of source code must retain the above copyright
38  *    notice, this list of conditions and the following disclaimer.
39  * 2. Redistributions in binary form must reproduce the above copyright
40  *    notice, this list of conditions and the following disclaimer in the
41  *    documentation and/or other materials provided with the distribution.
42  * 3. All advertising materials mentioning features or use of this software
43  *    must display the following acknowledgement:
44  *	This product includes software developed by the University of
45  *	California, Berkeley and its contributors.
46  * 4. Neither the name of the University nor the names of its contributors
47  *    may be used to endorse or promote products derived from this software
48  *    without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60  * SUCH DAMAGE.
61  *
62  *	@(#)ufs_disksubr.c	7.16 (Berkeley) 5/4/91
63  */
64 
65 #include <sys/cdefs.h>
66 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.22 2010/12/31 21:50:28 phx Exp $");
67 
68 #include "opt_disksubr.h"
69 
70 #include <sys/buf.h>
71 #include <sys/disklabel.h>
72 #include <sys/bswap.h>
73 
74 /*
75  * In /usr/src/sys/dev/scsipi/sd.c, routine sdstart() adjusts the
76  * block numbers, it changes from DEV_BSIZE units to physical units:
77  * blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE);
78  * As long as media with sector sizes of 512 bytes are used, this
79  * doesn't matter (divide by 1), but for successful usage of media with
80  * greater sector sizes (e.g. 640MB MO-media with 2048 bytes/sector)
81  * we must multiply block numbers with (lp->d_secsize / DEV_BSIZE)
82  * to keep "unchanged" physical block numbers.
83  */
84 #define SD_C_ADJUSTS_NR
85 
86 #define baddr(bp) (void *)((bp)->b_data)
87 
88 static const char *read_dos_label(dev_t, void (*)(struct buf *),
89     struct disklabel *, struct cpu_disklabel *);
90 static int getFreeLabelEntry(struct disklabel *);
91 static int read_netbsd_label(dev_t, void (*)(struct buf *), struct disklabel *,
92     struct cpu_disklabel *);
93 #ifdef RDB_PART
94 static const char *read_rdb_label(dev_t, void (*)(struct buf *),
95     struct disklabel *, struct cpu_disklabel *);
96 static u_long rdbchksum(void *);
97 static struct adostype getadostype(u_long);
98 #endif
99 
100 /*
101  * Read MBR partition table.
102  *
103  * XXX -
104  * Since FFS is endian sensitive, we pay no effort in attempting to
105  * dig up *BSD/i386 disk labels that may be present on the disk.
106  * Hence anything but DOS partitions is treated as unknown FS type, but
107  * this should suffice to mount_msdos Zip and other removable media.
108  */
109 static const char *
110 read_dos_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
111     struct cpu_disklabel *osdep)
112 {
113 	struct buf *bp;
114 	struct mbr_partition *bsdp, *dp;
115 	const char *msg = NULL;
116 	int i, slot, maxslot = 0;
117 	u_int32_t bsdpartoff;
118 
119 	/* get a buffer and initialize it */
120 	bp = geteblk((int)lp->d_secsize);
121 	bp->b_dev = dev;
122 
123 	/* read master boot record */
124 	bp->b_blkno = MBR_BBSECTOR;
125 	bp->b_bcount = lp->d_secsize;
126 	bp->b_flags |= B_READ;
127 	bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
128 	(*strat)(bp);
129 
130 	bsdpartoff = 0;
131 
132 	/* if successful, wander through dos partition table */
133 	if (biowait(bp)) {
134 		msg = "dos partition I/O error";
135 		goto done;
136 	}
137 	/* XXX */
138 	dp = (struct mbr_partition *)((char *)bp->b_data + MBR_PART_OFFSET);
139 	bsdp = NULL;
140 	for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
141 		switch (dp->mbrp_type) {
142 		case MBR_PTYPE_NETBSD:
143 			bsdp = dp;
144 			break;
145 		case MBR_PTYPE_OPENBSD:
146 		case MBR_PTYPE_386BSD:
147 			if (!bsdp)
148 				bsdp = dp;
149 			break;
150 		}
151 	}
152 	if (!bsdp) {
153 		/* generate fake disklabel */
154 		dp = (struct mbr_partition *)((char *)bp->b_data +
155 		    MBR_PART_OFFSET);
156 		for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
157 			if (!dp->mbrp_type)
158 				continue;
159 			slot = getFreeLabelEntry(lp);
160 			if (slot < 0)
161 				break;
162 			if (slot > maxslot)
163 				maxslot = slot;
164 
165 			lp->d_partitions[slot].p_offset =
166 			    bswap32(dp->mbrp_start);
167 			lp->d_partitions[slot].p_size = bswap32(dp->mbrp_size);
168 
169 			switch (dp->mbrp_type) {
170 			case MBR_PTYPE_FAT12:
171 			case MBR_PTYPE_FAT16S:
172 			case MBR_PTYPE_FAT16B:
173 			case MBR_PTYPE_FAT32:
174 			case MBR_PTYPE_FAT32L:
175 			case MBR_PTYPE_FAT16L:
176 				lp->d_partitions[slot].p_fstype = FS_MSDOS;
177 				break;
178 			default:
179 				lp->d_partitions[slot].p_fstype = FS_OTHER;
180 				break;
181 			}
182 		}
183 		msg = "no NetBSD disk label";
184 	} else {
185 		/* NetBSD partition on MBR */
186 		bsdpartoff = bswap32(bsdp->mbrp_start);
187 
188 		lp->d_partitions[2].p_size = bswap32(bsdp->mbrp_size);
189 		lp->d_partitions[2].p_offset = bswap32(bsdp->mbrp_start);
190 		if (2 > maxslot)
191 			maxslot = 2;
192 		/* read in disklabel, blkno + 1 for DOS disklabel offset */
193 		osdep->cd_labelsector = bsdpartoff + MBR_LABELSECTOR;
194 		osdep->cd_labeloffset = MBR_LABELOFFSET;
195 		if (read_netbsd_label(dev, strat, lp, osdep))
196 			goto done;
197 		msg = "no NetBSD disk label";
198 	}
199 
200 	lp->d_npartitions = ((maxslot >= RAW_PART) ? maxslot : RAW_PART) + 1;
201 
202 done:
203 	brelse(bp, 0);
204 	return msg;
205 }
206 
207 /*
208  * Find an entry in the disk label that is unused and return it
209  * or -1 if no entry
210  */
211 static int
212 getFreeLabelEntry(struct disklabel *lp)
213 {
214 	int i;
215 
216 	for (i = 0; i < MAXPARTITIONS; i++) {
217 		if ((i != RAW_PART)
218 		    && (lp->d_partitions[i].p_fstype == FS_UNUSED))
219 			return i;
220 	}
221 	return -1;
222 }
223 
224 #ifdef RDB_PART
225 /*
226  * Read an Amiga RDB partition table.
227  */
228 static const char *
229 read_rdb_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
230     struct cpu_disklabel *osdep)
231 {
232 	struct adostype adt;
233 	struct buf *bp;
234 	struct disklabel *dlp;
235 	struct partition *pp = NULL;
236 	struct partblock *pbp;
237 	struct rdblock *rbp;
238 	const char *msg;
239 	char *bcpls, *s, bcpli;
240 	int cindex, i, nopname;
241 	u_long nextb;
242 
243 	osdep->rdblock = RDBNULL;
244 	lp->d_npartitions = RAW_PART + 1;
245 
246 	if (lp->d_partitions[RAW_PART].p_size == 0)
247 		lp->d_partitions[RAW_PART].p_size = 0x1fffffff;
248 	lp->d_partitions[RAW_PART].p_offset = 0;
249 	/* if no 'a' partition, default it to copy of 'c' as BSDFFS */
250 	if (lp->d_partitions[0].p_size == 0) {
251 		lp->d_partitions[0].p_size = lp->d_partitions[RAW_PART].p_size;
252 		lp->d_partitions[0].p_offset = 0;
253 		lp->d_partitions[0].p_fstype = FS_BSDFFS;
254 		lp->d_partitions[0].p_fsize = 1024;
255 		lp->d_partitions[0].p_frag = 8;
256 		lp->d_partitions[0].p_cpg = 0;
257 	}
258 
259 	/* obtain buffer to probe drive with */
260 	bp = geteblk((int)lp->d_secsize);
261 
262 	/*
263 	 * request no partition relocation by driver on I/O operations
264 	 */
265 #ifdef _KERNEL
266 	bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
267 #else
268 	bp->b_dev = dev;
269 #endif
270 	msg = NULL;
271 
272 	/*
273 	 * find the RDB block
274 	 */
275 	for (nextb = 0; nextb < RDB_MAXBLOCKS; nextb++) {
276 		bp->b_blkno = nextb;
277 		bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
278 		bp->b_bcount = lp->d_secsize;
279 		bp->b_oflags &= ~(BO_DONE);
280 		bp->b_flags |= B_READ;
281 #ifdef SD_C_ADJUSTS_NR
282 		bp->b_blkno *= (lp->d_secsize / DEV_BSIZE);
283 #endif
284 		strat(bp);
285 
286 		if (biowait(bp)) {
287 			msg = "rdb scan I/O error";
288 			goto done;
289 		}
290 		rbp = baddr(bp);
291 		if (rbp->id == RDBLOCK_ID) {
292 			if (rdbchksum(rbp) == 0)
293 				break;
294 			else
295 				msg = "rdb bad checksum";
296 		}
297 		/* Check for native NetBSD label? */
298 		dlp = (struct disklabel *)((char*)bp->b_data + LABELOFFSET);
299 		if (dlp->d_magic == DISKMAGIC) {
300 			if (dkcksum(dlp))
301 				msg = "NetBSD disk label corrupted";
302 			else {
303 				/* remember block and continue searching? */
304 				*lp = *dlp;
305 				brelse(bp, 0);
306 				return msg;
307 			}
308 		}
309 	}
310 	if (nextb == RDB_MAXBLOCKS) {
311 		if (msg == NULL)
312 			msg = "no disk label";
313 		goto done;
314 	} else if (msg) {
315 		/*
316 		 * maybe we found an invalid one before a valid.
317 		 * clear err.
318 		 */
319 		msg = NULL;
320 	}
321 	osdep->rdblock = nextb;
322 
323 	/* RDB present, clear disklabel partition table before doing PART blks */
324 	for (i = 0; i < MAXPARTITIONS; i++) {
325 		osdep->pbindex[i] = -1;
326 		osdep->pblist[i] = RDBNULL;
327 		if (i == RAW_PART)
328 			continue;
329 		lp->d_partitions[i].p_size = 0;
330 		lp->d_partitions[i].p_offset = 0;
331 	}
332 
333 	lp->d_secsize = rbp->nbytes;
334 	lp->d_nsectors = rbp->nsectors;
335 	lp->d_ntracks = rbp->nheads;
336 	/*
337 	 * should be rdb->ncylinders however this is a bogus value
338 	 * sometimes it seems
339 	 */
340 	if (rbp->highcyl == 0)
341 		lp->d_ncylinders = rbp->ncylinders;
342 	else
343 		lp->d_ncylinders = rbp->highcyl + 1;
344 	/*
345 	 * I also don't trust rdb->secpercyl
346 	 */
347 	lp->d_secpercyl = min(rbp->secpercyl, lp->d_nsectors * lp->d_ntracks);
348 	if (lp->d_secpercyl == 0)
349 		lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks;
350 #ifdef DIAGNOSTIC
351 	if (lp->d_ncylinders != rbp->ncylinders)
352 		printf("warning found rdb->ncylinders(%ld) != "
353 		    "rdb->highcyl(%ld) + 1\n", rbp->ncylinders,
354 		    rbp->highcyl);
355 	if (lp->d_nsectors * lp->d_ntracks != rbp->secpercyl)
356 		printf("warning found rdb->secpercyl(%ld) != "
357 		    "rdb->nsectors(%ld) * rdb->nheads(%ld)\n", rbp->secpercyl,
358 		    rbp->nsectors, rbp->nheads);
359 #endif
360 	lp->d_sparespercyl =
361 	    max(rbp->secpercyl, lp->d_nsectors * lp->d_ntracks)
362 	    - lp->d_secpercyl;
363 	if (lp->d_sparespercyl == 0)
364 		lp->d_sparespertrack = 0;
365 	else {
366 		lp->d_sparespertrack = lp->d_sparespercyl / lp->d_ntracks;
367 #ifdef DIAGNOSTIC
368 		if (lp->d_sparespercyl % lp->d_ntracks)
369 			printf("warning lp->d_sparespercyl(%d) not multiple "
370 			    "of lp->d_ntracks(%d)\n", lp->d_sparespercyl,
371 			    lp->d_ntracks);
372 #endif
373 	}
374 
375 	lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
376 	lp->d_acylinders = rbp->ncylinders - (rbp->highcyl - rbp->lowcyl + 1);
377 	lp->d_rpm = 3600; 		/* good guess I suppose. */
378 	lp->d_interleave = rbp->interleave;
379 	lp->d_headswitch = lp->d_flags = lp->d_trackskew = lp->d_cylskew = 0;
380 	lp->d_trkseek = /* rbp->steprate */ 0;
381 
382 	/*
383 	 * raw partition gets the entire disk
384 	 */
385 	lp->d_partitions[RAW_PART].p_size = rbp->ncylinders * lp->d_secpercyl;
386 
387 	/*
388 	 * scan for partition blocks
389 	 */
390 	nopname = 1;
391 	cindex = 0;
392 	for (nextb = rbp->partbhead; nextb != RDBNULL; nextb = pbp->next) {
393 		bp->b_blkno = nextb;
394 		bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
395 		bp->b_bcount = lp->d_secsize;
396 		bp->b_oflags &= ~(BO_DONE);
397 		bp->b_flags |= B_READ;
398 #ifdef SD_C_ADJUSTS_NR
399 		bp->b_blkno *= (lp->d_secsize / DEV_BSIZE);
400 #endif
401 		strat(bp);
402 
403 		if (biowait(bp)) {
404 			msg = "partition scan I/O error";
405 			goto done;
406 		}
407 		pbp = baddr(bp);
408 
409 		if (pbp->id != PARTBLOCK_ID) {
410 			msg = "partition block with bad id";
411 			goto done;
412 		}
413 		if (rdbchksum(pbp)) {
414 			msg = "partition block bad checksum";
415 			goto done;
416 		}
417 
418 		if (pbp->e.tabsize < 11) {
419 			/*
420 			 * not enough info, too funky for us.
421 			 * I don't want to skip I want it fixed.
422 			 */
423 			msg = "bad partition info (environ < 11)";
424 			goto done;
425 			continue;
426 		}
427 
428 		/*
429 		 * XXXX should be ">" however some vendors don't know
430 		 * what a table size is so, we hack for them.
431 		 * the other checks can fail for all I care but this
432 		 * is a very common value. *sigh*.
433 		 */
434 		if (pbp->e.tabsize >= 16)
435 			adt = getadostype(pbp->e.dostype);
436 		else {
437 			adt.archtype = ADT_UNKNOWN;
438 			adt.fstype = FS_UNUSED;
439 		}
440 
441 		switch (adt.archtype) {
442 		case ADT_NETBSDROOT:
443 			pp = &lp->d_partitions[0];
444 			if (pp->p_size) {
445 				printf("WARN: more than one root, ignoring\n");
446 				osdep->rdblock = RDBNULL;	/* invlidate cpulab */
447 				continue;
448 			}
449 			break;
450 		case ADT_NETBSDSWAP:
451 			pp = &lp->d_partitions[1];
452 			if (pp->p_size) {
453 				printf("WARN: more than one swap, ignoring\n");
454 				osdep->rdblock = RDBNULL;	/* invlidate cpulab */
455 				continue;
456 			}
457 			break;
458 		case ADT_NETBSDUSER:
459 		case ADT_AMIGADOS:
460 		case ADT_AMIX:
461 		case ADT_EXT2:
462 		case ADT_RAID:
463 		case ADT_UNKNOWN:
464 			pp = &lp->d_partitions[lp->d_npartitions];
465 			break;
466 		}
467 		if (lp->d_npartitions <= (pp - lp->d_partitions))
468 			lp->d_npartitions = (pp - lp->d_partitions) + 1;
469 
470 #ifdef DIAGNOSTIC
471 		if (lp->d_secpercyl * lp->d_secsize !=
472 		    (pbp->e.secpertrk * pbp->e.numheads * pbp->e.sizeblock<<2)) {
473 			if (pbp->partname[0] < sizeof(pbp->partname))
474 				pbp->partname[pbp->partname[0] + 1] = 0;
475 			else
476 				pbp->partname[sizeof(pbp->partname) - 1] = 0;
477 			printf("Partition '%s' geometry %ld/%ld differs",
478 			    pbp->partname + 1, pbp->e.numheads,
479 			    pbp->e.secpertrk);
480 			printf(" from RDB %d/%d=%d\n", lp->d_ntracks,
481 			    lp->d_nsectors, lp->d_secpercyl);
482 		}
483 #endif
484 		/*
485 		 * insert sort in increasing offset order
486 		 */
487 		while ((pp - lp->d_partitions) > RAW_PART + 1) {
488 			daddr_t boff;
489 
490 			boff = pbp->e.lowcyl * pbp->e.secpertrk
491 			    * pbp->e.numheads;
492 			if (boff > (pp - 1)->p_offset)
493 				break;
494 			*pp = *(pp - 1);	/* struct copy */
495 			pp--;
496 		}
497 		i = (pp - lp->d_partitions);
498 		if (nopname || i == 1) {
499 			/*
500 			 * either we have no packname yet or we found
501 			 * the swap partition. copy BCPL string into packname
502 			 * [the reason we use the swap partition: the user
503 			 *  can supply a decent packname without worry
504 			 *  of having to access an odly named partition
505 			 *  under AmigaDos]
506 			 */
507 			s = lp->d_packname;
508 			bcpls = &pbp->partname[1];
509 			bcpli = pbp->partname[0];
510 			if (sizeof(lp->d_packname) <= bcpli)
511 				bcpli = sizeof(lp->d_packname) - 1;
512 			while (bcpli--)
513 				*s++ = *bcpls++;
514 			*s = 0;
515 			nopname = 0;
516 		}
517 
518 		pp->p_size = (pbp->e.highcyl - pbp->e.lowcyl + 1)
519 		    * pbp->e.secpertrk * pbp->e.numheads
520 		    * ((pbp->e.sizeblock << 2) / lp->d_secsize);
521 		pp->p_offset = pbp->e.lowcyl * pbp->e.secpertrk
522 		    * pbp->e.numheads
523 		    * ((pbp->e.sizeblock << 2) / lp->d_secsize);
524 		pp->p_fstype = adt.fstype;
525 		if (adt.archtype == ADT_AMIGADOS) {
526 			/*
527 			 * Save reserved blocks at begin in cpg and
528 			 *  adjust size by reserved blocks at end
529 			 */
530 			int bsize, secperblk, minbsize, prefac;
531 
532 			minbsize = max(512, lp->d_secsize);
533 
534 			bsize	  = pbp->e.sizeblock << 2;
535 			secperblk = pbp->e.secperblk;
536 			prefac	  = pbp->e.prefac;
537 
538 			while (bsize > minbsize) {
539 				bsize >>= 1;
540 				secperblk <<= 1;
541 				prefac <<= 1;
542 			}
543 
544 			if (bsize == minbsize) {
545 				pp->p_fsize = bsize;
546 				pp->p_frag = secperblk;
547 				pp->p_cpg = pbp->e.resvblocks;
548 				pp->p_size -= prefac;
549 			} else {
550 				adt.archtype = ADT_UNKNOWN;
551 				adt.fstype = FS_UNUSED;
552 			}
553 		} else if (pbp->e.tabsize > 22 && ISFSARCH_NETBSD(adt)) {
554 			pp->p_fsize = pbp->e.fsize;
555 			pp->p_frag = pbp->e.frag;
556 			pp->p_cpg = pbp->e.cpg;
557 		} else {
558 			pp->p_fsize = 1024;
559 			pp->p_frag = 8;
560 			pp->p_cpg = 0;
561 		}
562 
563 		/*
564 		 * store this partitions block number
565 		 */
566 		osdep->pblist[osdep->pbindex[i] = cindex++] = nextb;
567 	}
568 	/*
569 	 * calulate new checksum.
570 	 */
571 	lp->d_magic = lp->d_magic2 = DISKMAGIC;
572 	lp->d_checksum = 0;
573 	lp->d_checksum = dkcksum(lp);
574 	if (osdep->rdblock != RDBNULL)
575 		osdep->valid = 1;
576 done:
577 	if (osdep->valid == 0)
578 		osdep->rdblock = RDBNULL;
579 	brelse(bp, 0);
580 	return msg;
581 }
582 
583 static u_long
584 rdbchksum(void *bdata)
585 {
586 	u_long *blp, cnt, val;
587 
588 	blp = bdata;
589 	cnt = blp[1];
590 	val = 0;
591 
592 	while (cnt--)
593 		val += *blp++;
594 	return val;
595 }
596 
597 static struct adostype
598 getadostype(u_long dostype)
599 {
600 	struct adostype adt;
601 	u_long b1, t3;
602 
603 	t3 = dostype & 0xffffff00;
604 	b1 = dostype & 0x000000ff;
605 
606 	adt.fstype = b1;
607 
608 	switch (t3) {
609 	case DOST_NBR:
610 		adt.archtype = ADT_NETBSDROOT;
611 		return adt;
612 	case DOST_NBS:
613 		adt.archtype = ADT_NETBSDSWAP;
614 		return adt;
615 	case DOST_NBU:
616 		adt.archtype = ADT_NETBSDUSER;
617 		return adt;
618 	case DOST_MUFS:
619 		/* check for 'muFS'? */
620 		adt.archtype = ADT_AMIGADOS;
621 		adt.fstype = FS_ADOS;
622 		return adt;
623 	case DOST_DOS:
624 		adt.archtype = ADT_AMIGADOS;
625                 if (b1 > 5)
626 			adt.fstype = FS_UNUSED;
627 		else
628 			adt.fstype = FS_ADOS;
629 		return adt;
630 	case DOST_AMIX:
631 		adt.archtype = ADT_AMIX;
632 		if (b1 == 2)
633 			adt.fstype = FS_BSDFFS;
634 		else
635 			adt.fstype = FS_UNUSED;
636 		return adt;
637 	case DOST_XXXBSD:
638 #ifdef DIAGNOSTIC
639 		printf("found dostype: 0x%lx which is deprecated", dostype);
640 #endif
641 		if (b1 == 'S') {
642 			dostype = DOST_NBS;
643 			dostype |= FS_SWAP;
644 		} else {
645 			if (b1 == 'R')
646 				dostype = DOST_NBR;
647 			else
648 				dostype = DOST_NBU;
649 			dostype |= FS_BSDFFS;
650 		}
651 #ifdef DIAGNOSTIC
652 		printf(" using: 0x%lx instead\n", dostype);
653 #endif
654 		return(getadostype(dostype));
655 	case DOST_EXT2:
656 		adt.archtype = ADT_EXT2;
657 		adt.fstype = FS_EX2FS;
658 		return adt;
659 	case DOST_RAID:
660 		adt.archtype = ADT_RAID;
661 		adt.fstype = FS_RAID;
662 		return adt;
663 	default:
664 #ifdef DIAGNOSTIC
665 		printf("warning unknown dostype: 0x%lx marking unused\n",
666 		    dostype);
667 #endif
668 		adt.archtype = ADT_UNKNOWN;
669 		adt.fstype = FS_UNUSED;
670 		return adt;
671 	}
672 }
673 #endif /* RDB_PART */
674 
675 /*
676  * Get raw NetBSD disk label
677  */
678 static int
679 read_netbsd_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
680     struct cpu_disklabel *osdep)
681 {
682 	struct buf *bp;
683 	struct disklabel *dlp;
684 
685 	/* get a buffer and initialize it */
686 	bp = geteblk((int)lp->d_secsize);
687 	bp->b_dev = dev;
688 
689 	/* Now get the label block */
690 	bp->b_blkno = osdep->cd_labelsector;
691 	bp->b_bcount = lp->d_secsize;
692 	bp->b_flags |= B_READ;
693 	bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
694 	(*strat)(bp);
695 
696 	if (biowait(bp))
697 		goto done;
698 
699 	for (dlp = (struct disklabel *)((char *)bp->b_data +
700 		 osdep->cd_labeloffset);
701 	     dlp <= (struct disklabel *)((char *)bp->b_data + lp->d_secsize -
702 	         sizeof (*dlp));
703 	     dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
704 		if (dlp->d_magic == DISKMAGIC
705 		    && dlp->d_magic2 == DISKMAGIC
706 		    && dlp->d_npartitions <= MAXPARTITIONS
707 		    && dkcksum(dlp) == 0) {
708 			*lp = *dlp;
709 			osdep->cd_labeloffset = (char *)dlp -
710 			    (char *)bp->b_data;
711 			brelse(bp, 0);
712 			return 1;
713 		}
714 	}
715 done:
716 	brelse(bp, 0);
717 	return 0;
718 }
719 
720 /*
721  * Attempt to read a disk label from a device using the indicated strategy
722  * routine.  The label must be partly set up before this: secpercyl and
723  * anything required in the strategy routine (e.g., sector size) must be
724  * filled in before calling us.  Returns null on success and an error
725  * string on failure.
726  */
727 const char *
728 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
729     struct cpu_disklabel *osdep)
730 {
731 	struct buf *bp;
732 	const char *msg = NULL;
733 
734 	if (lp->d_secperunit == 0)
735 		lp->d_secperunit = 0x1fffffff;
736 
737 	if (lp->d_secpercyl == 0) {
738 		return msg = "Zero secpercyl";
739 	}
740 	bp = geteblk((int)lp->d_secsize);
741 
742 	bp->b_dev = dev;
743 	bp->b_blkno = 0;
744 	bp->b_resid = 0;
745 	bp->b_bcount = lp->d_secsize;
746 	bp->b_flags |= B_READ;
747 	bp->b_cylinder = 1 / lp->d_secpercyl;
748 	(*strat)(bp);
749 
750 	/* no valid RDB found */
751 	osdep->rdblock = RDBNULL;
752 
753 	/* XXX cd_start is abused as a flag for fictious disklabel */
754 	osdep->cd_start = -1;
755 
756 	if (biowait(bp)) {
757 		msg = "I/O error reading block zero";
758 		goto done;
759 	}
760 
761 	osdep->cd_labelsector = LABELSECTOR;
762 	osdep->cd_labeloffset = LABELOFFSET;
763 
764 	if (read_netbsd_label(dev, strat, lp, osdep))
765 		osdep->cd_start = 0;
766 	else {
767 		if (bswap16(*(u_int16_t *)((char *)bp->b_data +
768 		    MBR_MAGIC_OFFSET)) == MBR_MAGIC) {
769 			/* read_dos_label figures out labelsector/offset */
770 			msg = read_dos_label(dev, strat, lp, osdep);
771 			if (!msg)
772 				osdep->cd_start = 0;
773 		} else {
774 #ifdef RDB_PART
775 			/* scan for RDB partitions */
776 			msg = read_rdb_label(dev, strat, lp, osdep);
777 #else
778 			msg = "no disk label";
779 #endif
780 			osdep->cd_start = 0;	/* XXX for now */
781 		}
782 	}
783 
784 done:
785 	brelse(bp, 0);
786 	return msg;
787 }
788 
789 /*
790  * Check new disk label for sensibility before setting it.
791  */
792 int
793 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask,
794     struct cpu_disklabel *osdep)
795 {
796 	int i;
797 	struct partition *opp, *npp;
798 
799 	/* sanity clause */
800 	if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0
801 	    || (nlp->d_secsize % DEV_BSIZE) != 0)
802 		return EINVAL;
803 
804 	/* special case to allow disklabel to be invalidated */
805 	if (nlp->d_magic == 0xffffffff) {
806 		*olp = *nlp;
807 		return 0;
808 	}
809 
810 	if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC
811 	    || dkcksum(nlp) != 0)
812 		return EINVAL;
813 
814 	while ((i = ffs(openmask)) != 0) {
815 		i--;
816 		openmask &= ~(1 << i);
817 		if (nlp->d_npartitions <= i)
818 			return EBUSY;
819 		opp = &olp->d_partitions[i];
820 		npp = &nlp->d_partitions[i];
821 		if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
822 			return EBUSY;
823 		/*
824 		 * Copy internally-set partition information
825 		 * if new label doesn't include it.		XXX
826 		 */
827 		if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
828 			npp->p_fstype = opp->p_fstype;
829 			npp->p_fsize = opp->p_fsize;
830 			npp->p_frag = opp->p_frag;
831 			npp->p_cpg = opp->p_cpg;
832 		}
833 	}
834  	nlp->d_checksum = 0;
835  	nlp->d_checksum = dkcksum(nlp);
836 
837 	*olp = *nlp;
838 	return 0;
839 }
840 
841 /*
842  * Write disk label back to device after modification.
843  */
844 int
845 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
846     struct cpu_disklabel *osdep)
847 {
848 	struct buf *bp;
849 	int error;
850 	struct disklabel label;
851 
852 	/* If RDB was present, we don't support writing them yet. */
853 	if (osdep->rdblock != RDBNULL)
854 		return EINVAL;
855 
856 	/*
857 	 * Try to re-read a disklabel, in case he changed the MBR.
858 	 */
859 	label = *lp;
860 	readdisklabel(dev, strat, &label, osdep);
861 	if (osdep->cd_start < 0)
862 		return EINVAL;
863 
864 	/* get a buffer and initialize it */
865 	bp = geteblk(lp->d_secsize);
866 	bp->b_dev = dev;
867 
868 	bp->b_blkno = osdep->cd_start + osdep->cd_labelsector;
869 	bp->b_cylinder = bp->b_blkno / (lp->d_secsize / DEV_BSIZE) /
870 	    lp->d_secpercyl;
871 	bp->b_bcount = lp->d_secsize;
872 
873 	bp->b_flags |= B_READ;
874 	(*strat)(bp);
875 	error = biowait(bp);
876 	if (error != 0)
877 		goto done;
878 
879 	bp->b_flags &= ~B_READ;
880 	bp->b_flags |= B_WRITE;
881 	bp->b_oflags &= ~BO_DONE;
882 
883 	memcpy((char *)bp->b_data + osdep->cd_labeloffset, (void *)lp,
884 	    sizeof *lp);
885 
886 	(*strat)(bp);
887 	error = biowait(bp);
888 
889 done:
890 	brelse(bp, 0);
891 
892 	return error;
893 }
894