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