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