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