xref: /netbsd-src/sys/arch/atari/atari/disksubr.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: disksubr.c,v 1.29 2003/07/15 01:19:43 lukem Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 Leo Weppelman.
5  * 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  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Leo Weppelman.
18  * 4. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.29 2003/07/15 01:19:43 lukem Exp $");
35 
36 #ifndef DISKLABEL_NBDA
37 #define	DISKLABEL_NBDA	/* required */
38 #endif
39 
40 #include "opt_compat_netbsd.h"
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/buf.h>
45 #include <ufs/ufs/dinode.h>
46 #include <ufs/ffs/fs.h>
47 #include <sys/disk.h>
48 #include <sys/disklabel.h>
49 #include <machine/ahdilabel.h>
50 
51 /*
52  * BBSIZE in <ufs/ffs/fs.h> must be greater than
53  * or equal to BBMINSIZE in <machine/disklabel.h>
54  */
55 #if BBSIZE < BBMINSIZE
56 #error BBSIZE smaller than BBMINSIZE
57 #endif
58 
59 static void  ck_label __P((struct disklabel *, struct cpu_disklabel *));
60 static int   bsd_label __P((dev_t, void (*)(struct buf *),
61 			struct disklabel *, u_int, u_int *));
62 static int   ahdi_label __P((dev_t, void (*)(struct buf *),
63 			struct disklabel *, struct cpu_disklabel *));
64 static void  ahdi_to_bsd __P((struct disklabel *, struct ahdi_ptbl *));
65 static u_int ahdi_getparts __P((dev_t, void (*)(struct buf *), u_int,
66 					u_int, u_int, struct ahdi_ptbl *));
67 
68 /*
69  * Determine the size of the transfer, and make sure it is
70  * within the boundaries of the partition. Adjust transfer
71  * if needed, and signal errors or early completion.
72  */
73 int
74 bounds_check_with_label(dk, bp, wlabel)
75 	struct disk		*dk;
76 	struct buf		*bp;
77 	int			wlabel;
78 {
79 	struct disklabel	*lp = dk->dk_label;
80 	struct partition	*pp;
81 	u_int			maxsz, sz;
82 
83 	pp = &lp->d_partitions[DISKPART(bp->b_dev)];
84 	if (bp->b_flags & B_RAW) {
85 		if (bp->b_bcount & (lp->d_secsize - 1)) {
86 			bp->b_error = EINVAL;
87 			bp->b_flags |= B_ERROR;
88 			return(-1);
89 		}
90 		if (lp->d_secsize < DEV_BSIZE)
91 			maxsz = pp->p_size / (DEV_BSIZE / lp->d_secsize);
92 		else maxsz = pp->p_size * (lp->d_secsize / DEV_BSIZE);
93 		sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
94 	} else {
95 		maxsz = pp->p_size;
96 		sz = (bp->b_bcount + lp->d_secsize - 1) / lp->d_secsize;
97 	}
98 
99 	if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
100 		if (bp->b_blkno == maxsz) {
101 			/*
102 			 * trying to get one block beyond return EOF.
103 			 */
104 			bp->b_resid = bp->b_bcount;
105 			return(0);
106 		}
107 		if (bp->b_blkno > maxsz || bp->b_blkno < 0) {
108 			bp->b_error = EINVAL;
109 			bp->b_flags |= B_ERROR;
110 			return(-1);
111 		}
112 		sz = maxsz - bp->b_blkno;
113 
114 		/*
115 		 * adjust count down
116 		 */
117 		if (bp->b_flags & B_RAW)
118 			bp->b_bcount = sz << DEV_BSHIFT;
119 		else bp->b_bcount = sz * lp->d_secsize;
120 	}
121 
122 	/*
123 	 * calc cylinder for disksort to order transfers with
124 	 */
125 	bp->b_cylinder = (bp->b_blkno + pp->p_offset) / lp->d_secpercyl;
126 	return(1);
127 }
128 
129 /*
130  * Attempt to read a disk label from a device using the
131  * indicated strategy routine. The label must be partly
132  * set up before this:
133  * secpercyl and anything required in the strategy routine
134  * (e.g. sector size) must be filled in before calling us.
135  * Returns NULL on success and an error string on failure.
136  */
137 const char *
138 readdisklabel(dev, strat, lp, clp)
139 	dev_t			dev;
140 	void			(*strat)(struct buf *);
141 	struct disklabel	*lp;
142 	struct cpu_disklabel	*clp;
143 {
144 	int			e;
145 
146 	if (clp != NULL)
147 		bzero(clp, sizeof *clp);
148 	else printf("Warning: clp == NULL\n");
149 
150 	/*
151 	 * Give some guaranteed validity to the disk label.
152 	 */
153 	if (lp->d_secsize == 0)
154 		lp->d_secsize = DEV_BSIZE;
155 	if (lp->d_secperunit == 0)
156 		lp->d_secperunit = 0x1fffffff;
157 	if (lp->d_secpercyl == 0)
158 		return("Zero secpercyl");
159 
160 	/*
161 	 * Some parts of the kernel (see scsipi/cd.c for an example)
162 	 * assume that stuff they already had setup in d_partitions
163 	 * is still there after reading the disklabel. Hence the
164 	 * 'if 0'
165 	 */
166 #if 0
167 	bzero(lp->d_partitions, sizeof lp->d_partitions);
168 #endif
169 
170 	lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
171 	lp->d_npartitions                 = RAW_PART + 1;
172 	lp->d_bbsize                      = BBSIZE;
173 	lp->d_sbsize                      = SBLOCKSIZE;
174 
175 #ifdef DISKLABEL_NBDA
176 	/* Try the native NetBSD/Atari format first. */
177 	e = bsd_label(dev, strat, lp, 0, clp != NULL ? &clp->cd_label : NULL);
178 #endif
179 #if 0
180 	/* Other label formats go here. */
181 	if (e > 0)
182 		e = foo_label(dev, strat, lp, ...);
183 #endif
184 #ifdef DISKLABEL_AHDI
185 	/* The unprotected AHDI format comes last. */
186 	if (e > 0 && (clp != NULL))
187 		e = ahdi_label(dev, strat, lp, clp);
188 #endif
189 	if (e < 0)
190 		return("I/O error");
191 
192 	/* Unknown format or uninitialized volume? */
193 	if (e > 0)
194 		uprintf("Warning: unknown disklabel format"
195 			"- assuming empty disk\n");
196 
197 	/* Calulate new checksum. */
198 	lp->d_magic = lp->d_magic2 = DISKMAGIC;
199 	lp->d_checksum = 0;
200 	lp->d_checksum = dkcksum(lp);
201 
202 	return(NULL);
203 }
204 
205 /*
206  * Check new disk label for sensibility before setting it.
207  */
208 int
209 setdisklabel(olp, nlp, openmask, clp)
210 	struct disklabel	*olp, *nlp;
211 	u_long			openmask;
212 	struct cpu_disklabel	*clp;
213 {
214 	/* special case to allow disklabel to be invalidated */
215 	if (nlp->d_magic == 0xffffffff) {
216 		*olp = *nlp;
217 		return(0);
218 	}
219 
220 	/* sanity clause */
221 	if (nlp->d_secpercyl == 0 || nlp->d_npartitions > MAXPARTITIONS
222 	  || nlp->d_secsize  == 0 || (nlp->d_secsize % DEV_BSIZE) != 0
223 	  || nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC
224 	  || dkcksum(nlp) != 0)
225 		return(EINVAL);
226 
227 #ifdef DISKLABEL_AHDI
228 	if (clp && clp->cd_bblock)
229 		ck_label(nlp, clp);
230 #endif
231 	while (openmask) {
232 		struct partition *op, *np;
233 		int i = ffs(openmask) - 1;
234 		openmask &= ~(1 << i);
235 		if (i >= nlp->d_npartitions)
236 			return(EBUSY);
237 		op = &olp->d_partitions[i];
238 		np = &nlp->d_partitions[i];
239 		if (np->p_offset != op->p_offset || np->p_size < op->p_size)
240 			return(EBUSY);
241 		/*
242 		 * Copy internally-set partition information
243 		 * if new label doesn't include it.		XXX
244 		 */
245 		if (np->p_fstype == FS_UNUSED && op->p_fstype != FS_UNUSED) {
246 			np->p_fstype = op->p_fstype;
247 			np->p_fsize  = op->p_fsize;
248 			np->p_frag   = op->p_frag;
249 			np->p_cpg    = op->p_cpg;
250 		}
251 	}
252  	nlp->d_checksum = 0;
253  	nlp->d_checksum = dkcksum(nlp);
254 	*olp = *nlp;
255 	return(0);
256 }
257 
258 /*
259  * Write disk label back to device after modification.
260  */
261 int
262 writedisklabel(dev, strat, lp, clp)
263 	dev_t			dev;
264 	void			(*strat)(struct buf *);
265 	struct disklabel	*lp;
266 	struct cpu_disklabel	*clp;
267 {
268 	struct buf		*bp;
269 	u_int			blk;
270 	int			rv;
271 
272 	blk = clp->cd_bblock;
273 	if (blk == NO_BOOT_BLOCK)
274 		return(ENXIO);
275 
276 	bp = geteblk(BBMINSIZE);
277 	bp->b_dev      = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
278 	bp->b_flags    |= B_READ;
279 	bp->b_bcount   = BBMINSIZE;
280 	bp->b_blkno    = blk;
281 	bp->b_cylinder = blk / lp->d_secpercyl;
282 	(*strat)(bp);
283 	rv = biowait(bp);
284 	if (!rv) {
285 		struct bootblock *bb = (struct bootblock *)bp->b_data;
286 		/*
287 		 * Allthough the disk pack label may appear anywhere
288 		 * in the boot block while reading, it is always
289 		 * written at a fixed location.
290 		 */
291 		if (clp->cd_label != LABELOFFSET) {
292 			clp->cd_label = LABELOFFSET;
293 			bzero(bb, sizeof(*bb));
294 		}
295 		bb->bb_magic = (blk == 0) ? NBDAMAGIC : AHDIMAGIC;
296 		BBSETLABEL(bb, lp);
297 
298 		bp->b_flags    &= ~(B_READ|B_DONE);
299 		bp->b_flags    |= B_WRITE;
300 		bp->b_bcount   = BBMINSIZE;
301 		bp->b_blkno    = blk;
302 		bp->b_cylinder = blk / lp->d_secpercyl;
303 		(*strat)(bp);
304 		rv = biowait(bp);
305 	}
306 	brelse(bp);
307 	return(rv);
308 }
309 
310 /*
311  * Read bootblock at block `blkno' and check
312  * if it contains a valid NetBSD disk label.
313  *
314  * Returns:  0 if successful,
315  *          -1 if an I/O error occurred,
316  *          +1 if no valid label was found.
317  */
318 static int
319 bsd_label(dev, strat, label, blkno, offsetp)
320 	dev_t			dev;
321 	void			(*strat)(struct buf *);
322 	struct disklabel	*label;
323 	u_int			blkno,
324 				*offsetp;
325 {
326 	struct buf		*bp;
327 	int			rv;
328 
329 	bp = geteblk(BBMINSIZE);
330 	bp->b_dev      = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
331 	bp->b_flags    |= B_READ;
332 	bp->b_bcount   = BBMINSIZE;
333 	bp->b_blkno    = blkno;
334 	bp->b_cylinder = blkno / label->d_secpercyl;
335 	(*strat)(bp);
336 
337 	rv = -1;
338 	if (!biowait(bp)) {
339 		struct bootblock *bb;
340 		u_int32_t   *p, *end;
341 
342 		rv  = 1;
343 		bb  = (struct bootblock *)bp->b_data;
344 		end = (u_int32_t *)((char *)&bb[1] - sizeof(struct disklabel));
345 		for (p = (u_int32_t *)bb; p < end; ++p) {
346 			struct disklabel *dl = (struct disklabel *)&p[1];
347 			/*
348 			 * Compatibility kludge: the boot block magic number is
349 			 * new in 1.1A, in previous versions the disklabel was
350 			 * stored at the end of the boot block (offset 7168).
351 			 */
352 			if (  (  (p[0] == NBDAMAGIC && blkno == 0)
353 			      || (p[0] == AHDIMAGIC && blkno != 0)
354 #ifdef COMPAT_11
355 			      || (char *)dl - (char *)bb == 7168
356 #endif
357 			      )
358 			   && dl->d_npartitions <= MAXPARTITIONS
359 			   && dl->d_magic2 == DISKMAGIC
360 			   && dl->d_magic  == DISKMAGIC
361 		  	   && dkcksum(dl)  == 0
362 			   )	{
363 				if (offsetp != NULL)
364 					*offsetp = (char *)dl - (char *)bb;
365 				*label = *dl;
366 				rv     = 0;
367 				break;
368 			}
369 		}
370 	}
371 	brelse(bp);
372 	return(rv);
373 }
374 
375 #ifdef DISKLABEL_AHDI
376 /*
377  * Check for consistency between the NetBSD partition table
378  * and the AHDI auxiliary root sectors. There's no good reason
379  * to force such consistency, but issuing a warning may help
380  * an inexperienced sysadmin to prevent corruption of AHDI
381  * partitions.
382  */
383 static void
384 ck_label(dl, cdl)
385 	struct disklabel	*dl;
386 	struct cpu_disklabel	*cdl;
387 {
388 	u_int			*rp, i;
389 
390 	for (i = 0; i < dl->d_npartitions; ++i) {
391 		struct partition *p = &dl->d_partitions[i];
392 		if (i == RAW_PART || p->p_size == 0)
393 			continue;
394 		if ( (p->p_offset >= cdl->cd_bslst
395 		   && p->p_offset <= cdl->cd_bslend)
396 		  || (cdl->cd_bslst >= p->p_offset
397 		   && cdl->cd_bslst <  p->p_offset + p->p_size)) {
398 			uprintf("Warning: NetBSD partition %c includes"
399 				" AHDI bad sector list\n", 'a'+i);
400 		}
401 		for (rp = &cdl->cd_roots[0]; *rp; ++rp) {
402 			if (*rp >= p->p_offset
403 			  && *rp < p->p_offset + p->p_size) {
404 				uprintf("Warning: NetBSD partition %c"
405 				" includes AHDI auxiliary root\n", 'a'+i);
406 			}
407 		}
408 	}
409 }
410 
411 /*
412  * Check volume for the existance of an AHDI label. Fetch
413  * NetBSD label from NBD or RAW partition, or otherwise
414  * create a fake NetBSD label based on the AHDI label.
415  *
416  * Returns:  0 if successful,
417  *          -1 if an I/O error occurred,
418  *          +1 if no valid AHDI label was found.
419  */
420 int
421 ahdi_label(dev, strat, dl, cdl)
422 	dev_t			dev;
423 	void			(*strat)(struct buf *);
424 	struct disklabel	*dl;
425 	struct cpu_disklabel	*cdl;
426 {
427 	struct ahdi_ptbl	apt;
428 	u_int			i;
429 	int			j;
430 
431 	/*
432 	 * The AHDI format requires a specific block size.
433 	 */
434 	if (dl->d_secsize != AHDI_BSIZE)
435 		return(1);
436 
437 	/*
438 	 * Fetch the AHDI partition descriptors.
439 	 */
440 	apt.at_cdl    = cdl;
441 	apt.at_nroots = apt.at_nparts = 0;
442 	i = ahdi_getparts(dev, strat, dl->d_secpercyl,
443 			  AHDI_BBLOCK, AHDI_BBLOCK, &apt);
444 	if (i) {
445 		if (i < dl->d_secperunit)
446 			return(-1);	/* disk read error		*/
447 		else return(1);		/* reading past end of medium	*/
448 	}
449 
450 	/*
451 	 * Perform sanity checks.
452 	 */
453 	if (apt.at_bslst == 0 || apt.at_bslend == 0) {
454 		/*
455 		 * Illegal according to Atari, however some hd-utils
456 		 * use it - notably ICD *sigh*
457 		 * Work around it.....
458 		 */
459 		apt.at_bslst = apt.at_bslend = 0;
460 		uprintf("Warning: Illegal 'bad sector list' format"
461 			"- assuming non exists\n");
462 	}
463 	if (apt.at_hdsize == 0 || apt.at_nparts == 0)	/* unlikely */
464 		return(1);
465 	if (apt.at_nparts > AHDI_MAXPARTS)		/* XXX kludge */
466 		return(-1);
467 	for (i = 0; i < apt.at_nparts; ++i) {
468 		struct ahdi_part *p1 = &apt.at_parts[i];
469 
470 		for (j = 0; j < apt.at_nroots; ++j) {
471 			u_int	aux = apt.at_roots[j];
472 			if (aux >= p1->ap_st && aux <= p1->ap_end)
473 				return(1);
474 		}
475 		for (j = i + 1; j < apt.at_nparts; ++j) {
476 			struct ahdi_part *p2 = &apt.at_parts[j];
477 			if (p1->ap_st >= p2->ap_st && p1->ap_st <= p2->ap_end)
478 				return(1);
479 			if (p2->ap_st >= p1->ap_st && p2->ap_st <= p1->ap_end)
480 				return(1);
481 		}
482 		if (p1->ap_st >= apt.at_bslst && p1->ap_st <= apt.at_bslend)
483 			return(1);
484 		if (apt.at_bslst >= p1->ap_st && apt.at_bslst <= p1->ap_end)
485 			return(1);
486 	}
487 
488 	/*
489 	 * Search for a NetBSD disk label
490 	 */
491 	apt.at_bblock = NO_BOOT_BLOCK;
492 	for (i = 0; i < apt.at_nparts; ++i) {
493 		struct ahdi_part *pd = &apt.at_parts[i];
494 		u_int		 id  = *((u_int32_t *)&pd->ap_flg);
495 		if (id == AHDI_PID_NBD || id == AHDI_PID_RAW) {
496 			u_int	blkno = pd->ap_st;
497 			j = bsd_label(dev, strat, dl, blkno, &apt.at_label);
498 			if (j < 0) {
499 				return(j);		/* I/O error */
500 			}
501 			if (!j) {
502 				apt.at_bblock = blkno;	/* got it */
503 				ck_label(dl, cdl);
504 				return(0);
505 			}
506 			/*
507 			 * Not yet, but if this is the first NBD partition
508 			 * on this volume, we'll mark it anyway as a possible
509 			 * destination for future writedisklabel() calls, just
510 			 * in case there is no valid disk label on any of the
511 			 * other AHDI partitions.
512 			 */
513 			if (id == AHDI_PID_NBD
514 			    && apt.at_bblock == NO_BOOT_BLOCK)
515 				apt.at_bblock = blkno;
516 		}
517 	}
518 
519 	/*
520 	 * No NetBSD disk label on this volume, use the AHDI
521 	 * label to create a fake BSD label. If there is no
522 	 * NBD partition on this volume either, subsequent
523 	 * writedisklabel() calls will fail.
524 	 */
525 	ahdi_to_bsd(dl, &apt);
526 	return(0);
527 }
528 
529 /*
530  * Map the AHDI partition table to the NetBSD table.
531  *
532  * This means:
533  *  Part 0   : Root
534  *  Part 1   : Swap
535  *  Part 2   : Whole disk
536  *  Part 3.. : User partitions
537  *
538  * When more than one root partition is found, only the first one will
539  * be recognized as such. The others are mapped as user partitions.
540  */
541 static void
542 ahdi_to_bsd(dl, apt)
543 	struct disklabel	*dl;
544 	struct ahdi_ptbl	*apt;
545 {
546 	int		i, have_root, user_part;
547 
548 	user_part = RAW_PART;
549 	have_root = (apt->at_bblock != NO_BOOT_BLOCK);
550 
551 	for (i = 0; i < apt->at_nparts; ++i) {
552 		struct ahdi_part *pd = &apt->at_parts[i];
553 		int		 fst, pno = -1;
554 
555 		switch (*((u_int32_t *)&pd->ap_flg)) {
556 			case AHDI_PID_NBD:
557 				/*
558 				 * If this partition has been marked as the
559 				 * first NBD partition, it will be the root
560 				 * partition.
561 				 */
562 				if (pd->ap_st == apt->at_bblock)
563 					pno = 0;
564 				/* FALL THROUGH */
565 			case AHDI_PID_NBR:
566 				/*
567 				 * If there is no NBD partition and this is
568 				 * the first NBR partition, it will be the
569 				 * root partition.
570 				 */
571 				if (!have_root) {
572 					have_root = 1;
573 					pno = 0;
574 				}
575 				/* FALL THROUGH */
576 			case AHDI_PID_NBU:
577 				fst = FS_BSDFFS;
578 				break;
579 			case AHDI_PID_NBS:
580 			case AHDI_PID_SWP:
581 				if (dl->d_partitions[1].p_size == 0)
582 					pno = 1;
583 				fst = FS_SWAP;
584 				break;
585 			case AHDI_PID_BGM:
586 			case AHDI_PID_GEM:
587 				fst = FS_MSDOS;
588 				break;
589 			default:
590 				fst = FS_OTHER;
591 				break;
592 		}
593 		if (pno < 0) {
594 			if((pno = user_part + 1) >= MAXPARTITIONS)
595 				continue;
596 			user_part = pno;
597 		}
598 		dl->d_partitions[pno].p_size   = pd->ap_end - pd->ap_st + 1;
599 		dl->d_partitions[pno].p_offset = pd->ap_st;
600 		dl->d_partitions[pno].p_fstype = fst;
601 	}
602 	dl->d_npartitions = user_part + 1;
603 }
604 
605 /*
606  * Fetch the AHDI partitions and auxiliary roots.
607  *
608  * Returns:  0 if successful,
609  *           otherwise an I/O error occurred, and the
610  *           number of the offending block is returned.
611  */
612 static u_int
613 ahdi_getparts(dev, strat, secpercyl, rsec, esec, apt)
614 	dev_t			dev;
615 	void			(*strat)(struct buf *);
616 	u_int			secpercyl,
617 				rsec, esec;
618 	struct ahdi_ptbl	*apt;
619 {
620 	struct ahdi_part	*part, *end;
621 	struct ahdi_root	*root;
622 	struct buf		*bp;
623 	u_int			rv;
624 
625 	bp = geteblk(AHDI_BSIZE);
626 	bp->b_dev      = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
627 	bp->b_flags    |= B_READ;
628 	bp->b_bcount   = AHDI_BSIZE;
629 	bp->b_blkno    = rsec;
630 	bp->b_cylinder = rsec / secpercyl;
631 	(*strat)(bp);
632 	if (biowait(bp)) {
633 		rv = rsec + (rsec == 0);
634 		goto done;
635 	}
636 	root = (struct ahdi_root *)bp->b_data;
637 
638 	if (rsec == AHDI_BBLOCK)
639 		end = &root->ar_parts[AHDI_MAXRPD];
640 	else end = &root->ar_parts[AHDI_MAXARPD];
641 	for (part = root->ar_parts; part < end; ++part) {
642 		u_int	id = *((u_int32_t *)&part->ap_flg);
643 		if (!(id & 0x01000000))
644 			continue;
645 		if ((id &= 0x00ffffff) == AHDI_PID_XGM) {
646 			u_int	offs = part->ap_st + esec;
647 			if (apt->at_nroots < AHDI_MAXROOTS)
648 				apt->at_roots[apt->at_nroots] = offs;
649 			apt->at_nroots += 1;
650 			rv = ahdi_getparts(dev, strat, secpercyl, offs,
651 				(esec == AHDI_BBLOCK) ? offs : esec, apt);
652 			if (rv)
653 				goto done;
654 			continue;
655 		}
656 		else if (apt->at_nparts < AHDI_MAXPARTS) {
657 			struct ahdi_part *p = &apt->at_parts[apt->at_nparts];
658 			*((u_int32_t *)&p->ap_flg) = id;
659 			p->ap_st  = part->ap_st + rsec;
660 			p->ap_end = p->ap_st + part->ap_size - 1;
661 		}
662 		apt->at_nparts += 1;
663 	}
664 	apt->at_hdsize = root->ar_hdsize;
665 	apt->at_bslst  = root->ar_bslst;
666 	apt->at_bslend = root->ar_bslst + root->ar_bslsize - 1;
667 	rv = 0;
668 done:
669 	brelse(bp);
670 	return(rv);
671 }
672 #endif /* DISKLABEL_AHDI */
673