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