xref: /netbsd-src/sys/arch/atari/atari/disksubr.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: disksubr.c,v 1.34 2007/10/17 19:53:45 garbled 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.34 2007/10/17 19:53:45 garbled 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_flags    &= ~(B_READ|B_DONE);
238 		bp->b_flags    |= B_WRITE;
239 		bp->b_bcount   = BBMINSIZE;
240 		bp->b_blkno    = blk;
241 		bp->b_cylinder = blk / lp->d_secpercyl;
242 		(*strat)(bp);
243 		rv = biowait(bp);
244 	}
245 	brelse(bp, 0);
246 	return(rv);
247 }
248 
249 /*
250  * Read bootblock at block `blkno' and check
251  * if it contains a valid NetBSD disk label.
252  *
253  * Returns:  0 if successful,
254  *          -1 if an I/O error occurred,
255  *          +1 if no valid label was found.
256  */
257 static int
258 bsd_label(dev, strat, label, blkno, offsetp)
259 	dev_t			dev;
260 	void			(*strat)(struct buf *);
261 	struct disklabel	*label;
262 	u_int			blkno,
263 				*offsetp;
264 {
265 	struct buf		*bp;
266 	int			rv;
267 
268 	bp = geteblk(BBMINSIZE);
269 	bp->b_dev      = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
270 	bp->b_flags    |= B_READ;
271 	bp->b_bcount   = BBMINSIZE;
272 	bp->b_blkno    = blkno;
273 	bp->b_cylinder = blkno / label->d_secpercyl;
274 	(*strat)(bp);
275 
276 	rv = -1;
277 	if (!biowait(bp)) {
278 		struct bootblock *bb;
279 		u_int32_t   *p, *end;
280 
281 		rv  = 1;
282 		bb  = (struct bootblock *)bp->b_data;
283 		end = (u_int32_t *)((char *)&bb[1] - sizeof(struct disklabel));
284 		for (p = (u_int32_t *)bb; p < end; ++p) {
285 			struct disklabel *dl = (struct disklabel *)&p[1];
286 			/*
287 			 * Compatibility kludge: the boot block magic number is
288 			 * new in 1.1A, in previous versions the disklabel was
289 			 * stored at the end of the boot block (offset 7168).
290 			 */
291 			if (  (  (p[0] == NBDAMAGIC && blkno == 0)
292 			      || (p[0] == AHDIMAGIC && blkno != 0)
293 #ifdef COMPAT_11
294 			      || (char *)dl - (char *)bb == 7168
295 #endif
296 			      )
297 			   && dl->d_npartitions <= MAXPARTITIONS
298 			   && dl->d_magic2 == DISKMAGIC
299 			   && dl->d_magic  == DISKMAGIC
300 		  	   && dkcksum(dl)  == 0
301 			   )	{
302 				if (offsetp != NULL)
303 					*offsetp = (char *)dl - (char *)bb;
304 				*label = *dl;
305 				rv     = 0;
306 				break;
307 			}
308 		}
309 	}
310 	brelse(bp, 0);
311 	return(rv);
312 }
313 
314 #ifdef DISKLABEL_AHDI
315 /*
316  * Check for consistency between the NetBSD partition table
317  * and the AHDI auxiliary root sectors. There's no good reason
318  * to force such consistency, but issuing a warning may help
319  * an inexperienced sysadmin to prevent corruption of AHDI
320  * partitions.
321  */
322 static void
323 ck_label(dl, cdl)
324 	struct disklabel	*dl;
325 	struct cpu_disklabel	*cdl;
326 {
327 	u_int			*rp, i;
328 
329 	for (i = 0; i < dl->d_npartitions; ++i) {
330 		struct partition *p = &dl->d_partitions[i];
331 		if (i == RAW_PART || p->p_size == 0)
332 			continue;
333 		if ( (p->p_offset >= cdl->cd_bslst
334 		   && p->p_offset <= cdl->cd_bslend)
335 		  || (cdl->cd_bslst >= p->p_offset
336 		   && cdl->cd_bslst <  p->p_offset + p->p_size)) {
337 			uprintf("Warning: NetBSD partition %c includes"
338 				" AHDI bad sector list\n", 'a'+i);
339 		}
340 		for (rp = &cdl->cd_roots[0]; *rp; ++rp) {
341 			if (*rp >= p->p_offset
342 			  && *rp < p->p_offset + p->p_size) {
343 				uprintf("Warning: NetBSD partition %c"
344 				" includes AHDI auxiliary root\n", 'a'+i);
345 			}
346 		}
347 	}
348 }
349 
350 /*
351  * Check volume for the existence of an AHDI label. Fetch
352  * NetBSD label from NBD or RAW partition, or otherwise
353  * create a fake NetBSD label based on the AHDI label.
354  *
355  * Returns:  0 if successful,
356  *          -1 if an I/O error occurred,
357  *          +1 if no valid AHDI label was found.
358  */
359 int
360 ahdi_label(dev, strat, dl, cdl)
361 	dev_t			dev;
362 	void			(*strat)(struct buf *);
363 	struct disklabel	*dl;
364 	struct cpu_disklabel	*cdl;
365 {
366 	struct ahdi_ptbl	apt;
367 	u_int			i;
368 	int			j;
369 
370 	/*
371 	 * The AHDI format requires a specific block size.
372 	 */
373 	if (dl->d_secsize != AHDI_BSIZE)
374 		return(1);
375 
376 	/*
377 	 * Fetch the AHDI partition descriptors.
378 	 */
379 	apt.at_cdl    = cdl;
380 	apt.at_nroots = apt.at_nparts = 0;
381 	i = ahdi_getparts(dev, strat, dl->d_secpercyl,
382 			  AHDI_BBLOCK, AHDI_BBLOCK, &apt);
383 	if (i) {
384 		if (i < dl->d_secperunit)
385 			return(-1);	/* disk read error		*/
386 		else return(1);		/* reading past end of medium	*/
387 	}
388 
389 	/*
390 	 * Perform sanity checks.
391 	 */
392 	if (apt.at_bslst == 0 || apt.at_bslend == 0) {
393 		/*
394 		 * Illegal according to Atari, however some hd-utils
395 		 * use it - notably ICD *sigh*
396 		 * Work around it.....
397 		 */
398 		apt.at_bslst = apt.at_bslend = 0;
399 		uprintf("Warning: Illegal 'bad sector list' format"
400 			"- assuming non exists\n");
401 	}
402 	if (apt.at_hdsize == 0 || apt.at_nparts == 0)	/* unlikely */
403 		return(1);
404 	if (apt.at_nparts > AHDI_MAXPARTS)		/* XXX kludge */
405 		return(-1);
406 	for (i = 0; i < apt.at_nparts; ++i) {
407 		struct ahdi_part *p1 = &apt.at_parts[i];
408 
409 		for (j = 0; j < apt.at_nroots; ++j) {
410 			u_int	aux = apt.at_roots[j];
411 			if (aux >= p1->ap_st && aux <= p1->ap_end)
412 				return(1);
413 		}
414 		for (j = i + 1; j < apt.at_nparts; ++j) {
415 			struct ahdi_part *p2 = &apt.at_parts[j];
416 			if (p1->ap_st >= p2->ap_st && p1->ap_st <= p2->ap_end)
417 				return(1);
418 			if (p2->ap_st >= p1->ap_st && p2->ap_st <= p1->ap_end)
419 				return(1);
420 		}
421 		if (p1->ap_st >= apt.at_bslst && p1->ap_st <= apt.at_bslend)
422 			return(1);
423 		if (apt.at_bslst >= p1->ap_st && apt.at_bslst <= p1->ap_end)
424 			return(1);
425 	}
426 
427 	/*
428 	 * Search for a NetBSD disk label
429 	 */
430 	apt.at_bblock = NO_BOOT_BLOCK;
431 	for (i = 0; i < apt.at_nparts; ++i) {
432 		struct ahdi_part *pd = &apt.at_parts[i];
433 		u_int		 id  = *((u_int32_t *)&pd->ap_flg);
434 		if (id == AHDI_PID_NBD || id == AHDI_PID_RAW) {
435 			u_int	blkno = pd->ap_st;
436 			j = bsd_label(dev, strat, dl, blkno, &apt.at_label);
437 			if (j < 0) {
438 				return(j);		/* I/O error */
439 			}
440 			if (!j) {
441 				apt.at_bblock = blkno;	/* got it */
442 				ck_label(dl, cdl);
443 				return(0);
444 			}
445 			/*
446 			 * Not yet, but if this is the first NBD partition
447 			 * on this volume, we'll mark it anyway as a possible
448 			 * destination for future writedisklabel() calls, just
449 			 * in case there is no valid disk label on any of the
450 			 * other AHDI partitions.
451 			 */
452 			if (id == AHDI_PID_NBD
453 			    && apt.at_bblock == NO_BOOT_BLOCK)
454 				apt.at_bblock = blkno;
455 		}
456 	}
457 
458 	/*
459 	 * No NetBSD disk label on this volume, use the AHDI
460 	 * label to create a fake BSD label. If there is no
461 	 * NBD partition on this volume either, subsequent
462 	 * writedisklabel() calls will fail.
463 	 */
464 	ahdi_to_bsd(dl, &apt);
465 	return(0);
466 }
467 
468 /*
469  * Map the AHDI partition table to the NetBSD table.
470  *
471  * This means:
472  *  Part 0   : Root
473  *  Part 1   : Swap
474  *  Part 2   : Whole disk
475  *  Part 3.. : User partitions
476  *
477  * When more than one root partition is found, only the first one will
478  * be recognized as such. The others are mapped as user partitions.
479  */
480 static void
481 ahdi_to_bsd(dl, apt)
482 	struct disklabel	*dl;
483 	struct ahdi_ptbl	*apt;
484 {
485 	int		i, have_root, user_part;
486 
487 	user_part = RAW_PART;
488 	have_root = (apt->at_bblock != NO_BOOT_BLOCK);
489 
490 	for (i = 0; i < apt->at_nparts; ++i) {
491 		struct ahdi_part *pd = &apt->at_parts[i];
492 		int		 fst, pno = -1;
493 
494 		switch (*((u_int32_t *)&pd->ap_flg)) {
495 			case AHDI_PID_NBD:
496 				/*
497 				 * If this partition has been marked as the
498 				 * first NBD partition, it will be the root
499 				 * partition.
500 				 */
501 				if (pd->ap_st == apt->at_bblock)
502 					pno = 0;
503 				/* FALL THROUGH */
504 			case AHDI_PID_NBR:
505 				/*
506 				 * If there is no NBD partition and this is
507 				 * the first NBR partition, it will be the
508 				 * root partition.
509 				 */
510 				if (!have_root) {
511 					have_root = 1;
512 					pno = 0;
513 				}
514 				/* FALL THROUGH */
515 			case AHDI_PID_NBU:
516 				fst = FS_BSDFFS;
517 				break;
518 			case AHDI_PID_NBS:
519 			case AHDI_PID_SWP:
520 				if (dl->d_partitions[1].p_size == 0)
521 					pno = 1;
522 				fst = FS_SWAP;
523 				break;
524 			case AHDI_PID_BGM:
525 			case AHDI_PID_GEM:
526 				fst = FS_MSDOS;
527 				break;
528 			default:
529 				fst = FS_OTHER;
530 				break;
531 		}
532 		if (pno < 0) {
533 			if((pno = user_part + 1) >= MAXPARTITIONS)
534 				continue;
535 			user_part = pno;
536 		}
537 		dl->d_partitions[pno].p_size   = pd->ap_end - pd->ap_st + 1;
538 		dl->d_partitions[pno].p_offset = pd->ap_st;
539 		dl->d_partitions[pno].p_fstype = fst;
540 	}
541 	dl->d_npartitions = user_part + 1;
542 }
543 
544 /*
545  * Fetch the AHDI partitions and auxiliary roots.
546  *
547  * Returns:  0 if successful,
548  *           otherwise an I/O error occurred, and the
549  *           number of the offending block is returned.
550  */
551 static u_int
552 ahdi_getparts(dev, strat, secpercyl, rsec, esec, apt)
553 	dev_t			dev;
554 	void			(*strat)(struct buf *);
555 	u_int			secpercyl,
556 				rsec, esec;
557 	struct ahdi_ptbl	*apt;
558 {
559 	struct ahdi_part	*part, *end;
560 	struct ahdi_root	*root;
561 	struct buf		*bp;
562 	u_int			rv;
563 
564 	bp = geteblk(AHDI_BSIZE);
565 	bp->b_dev      = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
566 	bp->b_flags    |= B_READ;
567 	bp->b_bcount   = AHDI_BSIZE;
568 	bp->b_blkno    = rsec;
569 	bp->b_cylinder = rsec / secpercyl;
570 	(*strat)(bp);
571 	if (biowait(bp)) {
572 		rv = rsec + (rsec == 0);
573 		goto done;
574 	}
575 	root = (struct ahdi_root *)bp->b_data;
576 
577 	if (rsec == AHDI_BBLOCK)
578 		end = &root->ar_parts[AHDI_MAXRPD];
579 	else end = &root->ar_parts[AHDI_MAXARPD];
580 	for (part = root->ar_parts; part < end; ++part) {
581 		u_int	id = *((u_int32_t *)&part->ap_flg);
582 		if (!(id & 0x01000000))
583 			continue;
584 		if ((id &= 0x00ffffff) == AHDI_PID_XGM) {
585 			u_int	offs = part->ap_st + esec;
586 			if (apt->at_nroots < AHDI_MAXROOTS)
587 				apt->at_roots[apt->at_nroots] = offs;
588 			apt->at_nroots += 1;
589 			rv = ahdi_getparts(dev, strat, secpercyl, offs,
590 				(esec == AHDI_BBLOCK) ? offs : esec, apt);
591 			if (rv)
592 				goto done;
593 			continue;
594 		}
595 		else if (apt->at_nparts < AHDI_MAXPARTS) {
596 			struct ahdi_part *p = &apt->at_parts[apt->at_nparts];
597 			*((u_int32_t *)&p->ap_flg) = id;
598 			p->ap_st  = part->ap_st + rsec;
599 			p->ap_end = p->ap_st + part->ap_size - 1;
600 		}
601 		apt->at_nparts += 1;
602 	}
603 	apt->at_hdsize = root->ar_hdsize;
604 	apt->at_bslst  = root->ar_bslst;
605 	apt->at_bslend = root->ar_bslst + root->ar_bslsize - 1;
606 	rv = 0;
607 done:
608 	brelse(bp, 0);
609 	return(rv);
610 }
611 #endif /* DISKLABEL_AHDI */
612