Home | History | Annotate | Line # | Download | only in atari
      1 /*	$NetBSD: disksubr.c,v 1.45 2022/05/24 06:28:00 andvar 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  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.45 2022/05/24 06:28:00 andvar Exp $");
     30 
     31 #ifndef DISKLABEL_NBDA
     32 #define	DISKLABEL_NBDA	/* required */
     33 #endif
     34 
     35 #include "opt_compat_netbsd.h"
     36 
     37 #include <sys/param.h>
     38 #include <sys/systm.h>
     39 #include <sys/buf.h>
     40 #include <ufs/ufs/dinode.h>
     41 #include <ufs/ffs/fs.h>
     42 #include <sys/disk.h>
     43 #include <sys/disklabel.h>
     44 #include <machine/ahdilabel.h>
     45 
     46 /*
     47  * BBSIZE in <ufs/ffs/fs.h> must be greater than
     48  * or equal to BBMINSIZE in <machine/disklabel.h>
     49  */
     50 #if BBSIZE < BBMINSIZE
     51 #error BBSIZE smaller than BBMINSIZE
     52 #endif
     53 
     54 static void  ck_label(struct disklabel *, struct cpu_disklabel *);
     55 static int   bsd_label(dev_t, void (*)(struct buf *),
     56 			struct disklabel *, u_int, u_int *);
     57 static int   ahdi_label(dev_t, void (*)(struct buf *),
     58 			struct disklabel *, struct cpu_disklabel *);
     59 static void  ahdi_to_bsd(struct disklabel *, struct ahdi_ptbl *);
     60 static u_int ahdi_getparts(dev_t, void (*)(struct buf *), u_int,
     61 					u_int, u_int, struct ahdi_ptbl *);
     62 
     63 /*
     64  * Attempt to read a disk label from a device using the
     65  * indicated strategy routine. The label must be partly
     66  * set up before this:
     67  * secpercyl and anything required in the strategy routine
     68  * (e.g. sector size) must be filled in before calling us.
     69  * Returns NULL on success and an error string on failure.
     70  */
     71 const char *
     72 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
     73     struct cpu_disklabel *clp)
     74 {
     75 	int e;
     76 
     77 	if (clp != NULL)
     78 		memset(clp, 0, sizeof *clp);
     79 	else
     80 		printf("Warning: clp == NULL\n");
     81 
     82 	/*
     83 	 * Give some guaranteed validity to the disk label.
     84 	 */
     85 	if (lp->d_secsize == 0)
     86 		lp->d_secsize = DEV_BSIZE;
     87 	if (lp->d_secperunit == 0)
     88 		lp->d_secperunit = 0x1fffffff;
     89 	if (lp->d_secpercyl == 0)
     90 		return "Zero secpercyl";
     91 
     92 	/*
     93 	 * Some parts of the kernel (see scsipi/cd.c for an example)
     94 	 * assume that stuff they already had setup in d_partitions
     95 	 * is still there after reading the disklabel. Hence the
     96 	 * 'if 0'
     97 	 */
     98 #if 0
     99 	memset(lp->d_partitions, 0, sizeof lp->d_partitions);
    100 #endif
    101 
    102 	lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
    103 	lp->d_npartitions                 = RAW_PART + 1;
    104 	lp->d_bbsize                      = BBSIZE;
    105 	lp->d_sbsize                      = SBLOCKSIZE;
    106 
    107 #ifdef DISKLABEL_NBDA
    108 	/* Try the native NetBSD/Atari format first. */
    109 	e = bsd_label(dev, strat, lp, 0, clp != NULL ? &clp->cd_label : NULL);
    110 #endif
    111 #if 0
    112 	/* Other label formats go here. */
    113 	if (e > 0)
    114 		e = foo_label(dev, strat, lp, ...);
    115 #endif
    116 #ifdef DISKLABEL_AHDI
    117 	/* The unprotected AHDI format comes last. */
    118 	if (e > 0 && (clp != NULL))
    119 		e = ahdi_label(dev, strat, lp, clp);
    120 #endif
    121 	if (e < 0)
    122 		return "I/O error";
    123 
    124 	/* Unknown format or uninitialized volume? */
    125 	if (e > 0)
    126 		uprintf("Warning: unknown disklabel format"
    127 		    "- assuming empty disk\n");
    128 
    129 	/* Calculate new checksum. */
    130 	lp->d_magic = lp->d_magic2 = DISKMAGIC;
    131 	lp->d_checksum = 0;
    132 	lp->d_checksum = dkcksum(lp);
    133 
    134 	return NULL;
    135 }
    136 
    137 /*
    138  * Write disk label back to device after modification.
    139  */
    140 int
    141 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
    142     struct cpu_disklabel *clp)
    143 {
    144 	struct buf *bp;
    145 	u_int blk;
    146 	int rv;
    147 
    148 	blk = clp->cd_bblock;
    149 	if (blk == NO_BOOT_BLOCK)
    150 		return ENXIO;
    151 
    152 	bp = geteblk(BBMINSIZE);
    153 	bp->b_dev      = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
    154 	bp->b_flags    |= B_READ;
    155 	bp->b_bcount   = BBMINSIZE;
    156 	bp->b_blkno    = blk;
    157 	bp->b_cylinder = blk / lp->d_secpercyl;
    158 	(*strat)(bp);
    159 	rv = biowait(bp);
    160 	if (rv == 0) {
    161 		struct bootblock *bb = (struct bootblock *)bp->b_data;
    162 		/*
    163 		 * Allthough the disk pack label may appear anywhere
    164 		 * in the boot block while reading, it is always
    165 		 * written at a fixed location.
    166 		 */
    167 		if (clp->cd_label != LABELOFFSET) {
    168 			clp->cd_label = LABELOFFSET;
    169 			memset(bb, 0, sizeof(*bb));
    170 		}
    171 		bb->bb_magic = (blk == 0) ? NBDAMAGIC : AHDIMAGIC;
    172 		BBSETLABEL(bb, lp);
    173 
    174 		bp->b_oflags   &= ~(BO_DONE);
    175 		bp->b_flags    &= ~(B_READ);
    176 		bp->b_flags    |= B_WRITE;
    177 		bp->b_bcount   = BBMINSIZE;
    178 		bp->b_blkno    = blk;
    179 		bp->b_cylinder = blk / lp->d_secpercyl;
    180 		(*strat)(bp);
    181 		rv = biowait(bp);
    182 	}
    183 	brelse(bp, 0);
    184 	return rv;
    185 }
    186 
    187 /*
    188  * Read bootblock at block `blkno' and check
    189  * if it contains a valid NetBSD disk label.
    190  *
    191  * Returns:  0 if successful,
    192  *          -1 if an I/O error occurred,
    193  *          +1 if no valid label was found.
    194  */
    195 static int
    196 bsd_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *label,
    197     u_int blkno, u_int *offsetp)
    198 {
    199 	struct buf *bp;
    200 	int rv;
    201 
    202 	bp = geteblk(BBMINSIZE);
    203 	bp->b_dev      = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
    204 	bp->b_flags    |= B_READ;
    205 	bp->b_bcount   = BBMINSIZE;
    206 	bp->b_blkno    = blkno;
    207 	bp->b_cylinder = blkno / label->d_secpercyl;
    208 	(*strat)(bp);
    209 
    210 	rv = -1;
    211 	if (!biowait(bp)) {
    212 		struct bootblock *bb;
    213 		uint32_t *p, *end;
    214 
    215 		rv  = 1;
    216 		bb  = (struct bootblock *)bp->b_data;
    217 		end = (uint32_t *)((char *)&bb[1] - sizeof(struct disklabel));
    218 		for (p = (uint32_t *)bb; p < end; ++p) {
    219 			struct disklabel *dl = (struct disklabel *)&p[1];
    220 			/*
    221 			 * Compatibility kludge: the boot block magic number is
    222 			 * new in 1.1A, in previous versions the disklabel was
    223 			 * stored at the end of the boot block (offset 7168).
    224 			 */
    225 			if (((p[0] == NBDAMAGIC && blkno == 0) ||
    226 			     (p[0] == AHDIMAGIC && blkno != 0)
    227 #ifdef COMPAT_11
    228 			      || (char *)dl - (char *)bb == 7168
    229 #endif
    230 			      ) &&
    231 			    dl->d_npartitions <= MAXPARTITIONS &&
    232 			    dl->d_magic2 == DISKMAGIC &&
    233 			    dl->d_magic  == DISKMAGIC &&
    234 			    dkcksum(dl)  == 0)	{
    235 				if (offsetp != NULL)
    236 					*offsetp = (char *)dl - (char *)bb;
    237 				*label = *dl;
    238 				rv     = 0;
    239 				break;
    240 			}
    241 		}
    242 	}
    243 	brelse(bp, 0);
    244 	return rv;
    245 }
    246 
    247 #ifdef DISKLABEL_AHDI
    248 /*
    249  * Check for consistency between the NetBSD partition table
    250  * and the AHDI auxiliary root sectors. There's no good reason
    251  * to force such consistency, but issuing a warning may help
    252  * an inexperienced sysadmin to prevent corruption of AHDI
    253  * partitions.
    254  */
    255 static void
    256 ck_label(struct disklabel *dl, struct cpu_disklabel *cdl)
    257 {
    258 	u_int *rp, i;
    259 
    260 	for (i = 0; i < dl->d_npartitions; ++i) {
    261 		struct partition *p = &dl->d_partitions[i];
    262 		if (i == RAW_PART || p->p_size == 0)
    263 			continue;
    264 		if ((p->p_offset >= cdl->cd_bslst &&
    265 		     p->p_offset <= cdl->cd_bslend) ||
    266 		    (cdl->cd_bslst >= p->p_offset &&
    267 		     cdl->cd_bslst <  p->p_offset + p->p_size)) {
    268 			uprintf("Warning: NetBSD partition %c includes"
    269 			    " AHDI bad sector list\n", 'a'+i);
    270 		}
    271 		for (rp = &cdl->cd_roots[0]; *rp; ++rp) {
    272 			if (*rp >= p->p_offset &&
    273 			    *rp < p->p_offset + p->p_size) {
    274 				uprintf("Warning: NetBSD partition %c"
    275 				    " includes AHDI auxiliary root\n", 'a' + i);
    276 			}
    277 		}
    278 	}
    279 }
    280 
    281 /*
    282  * Check volume for the existence of an AHDI label. Fetch
    283  * NetBSD label from NBD or RAW partition, or otherwise
    284  * create a fake NetBSD label based on the AHDI label.
    285  *
    286  * Returns:  0 if successful,
    287  *          -1 if an I/O error occurred,
    288  *          +1 if no valid AHDI label was found.
    289  */
    290 int
    291 ahdi_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *dl,
    292     struct cpu_disklabel *cdl)
    293 {
    294 	struct ahdi_ptbl apt;
    295 	u_int i;
    296 	int j;
    297 
    298 	/*
    299 	 * The AHDI format requires a specific block size.
    300 	 */
    301 	if (dl->d_secsize != AHDI_BSIZE)
    302 		return 1;
    303 
    304 	/*
    305 	 * Fetch the AHDI partition descriptors.
    306 	 */
    307 	apt.at_cdl    = cdl;
    308 	apt.at_nroots = apt.at_nparts = 0;
    309 	i = ahdi_getparts(dev, strat, dl->d_secpercyl,
    310 	    AHDI_BBLOCK, AHDI_BBLOCK, &apt);
    311 	if (i) {
    312 		if (i < dl->d_secperunit)
    313 			return -1;	/* disk read error		*/
    314 		else
    315 			return 1;	/* reading past end of medium	*/
    316 	}
    317 
    318 	/*
    319 	 * Perform sanity checks.
    320 	 */
    321 	if (apt.at_bslst == 0 || apt.at_bslend == 0) {
    322 		/*
    323 		 * Illegal according to Atari, however some hd-utils
    324 		 * use it - notably ICD *sigh*
    325 		 * Work around it.....
    326 		 */
    327 		apt.at_bslst = apt.at_bslend = 0;
    328 		uprintf("Warning: Illegal 'bad sector list' format"
    329 		    "- assuming non exists\n");
    330 	}
    331 	if (apt.at_hdsize == 0 || apt.at_nparts == 0)	/* unlikely */
    332 		return 1;
    333 	if (apt.at_nparts > AHDI_MAXPARTS)		/* XXX kludge */
    334 		return -1;
    335 	for (i = 0; i < apt.at_nparts; ++i) {
    336 		struct ahdi_part *p1 = &apt.at_parts[i];
    337 
    338 		for (j = 0; j < apt.at_nroots; ++j) {
    339 			u_int aux = apt.at_roots[j];
    340 			if (aux >= p1->ap_st && aux <= p1->ap_end)
    341 				return 1;
    342 		}
    343 		for (j = i + 1; j < apt.at_nparts; ++j) {
    344 			struct ahdi_part *p2 = &apt.at_parts[j];
    345 			if (p1->ap_st >= p2->ap_st && p1->ap_st <= p2->ap_end)
    346 				return 1;
    347 			if (p2->ap_st >= p1->ap_st && p2->ap_st <= p1->ap_end)
    348 				return 1;
    349 		}
    350 		if (p1->ap_st >= apt.at_bslst && p1->ap_st <= apt.at_bslend)
    351 			return 1;
    352 		if (apt.at_bslst >= p1->ap_st && apt.at_bslst <= p1->ap_end)
    353 			return 1;
    354 	}
    355 
    356 	/*
    357 	 * Search for a NetBSD disk label
    358 	 */
    359 	apt.at_bblock = NO_BOOT_BLOCK;
    360 	for (i = 0; i < apt.at_nparts; ++i) {
    361 		struct ahdi_part *pd = &apt.at_parts[i];
    362 		u_int id  = *((uint32_t *)&pd->ap_flg);
    363 		if (id == AHDI_PID_NBD || id == AHDI_PID_RAW) {
    364 			u_int blkno = pd->ap_st;
    365 			j = bsd_label(dev, strat, dl, blkno, &apt.at_label);
    366 			if (j < 0) {
    367 				return j;		/* I/O error */
    368 			}
    369 			if (j == 0) {
    370 				apt.at_bblock = blkno;	/* got it */
    371 				ck_label(dl, cdl);
    372 				return 0;
    373 			}
    374 			/*
    375 			 * Not yet, but if this is the first NBD partition
    376 			 * on this volume, we'll mark it anyway as a possible
    377 			 * destination for future writedisklabel() calls, just
    378 			 * in case there is no valid disk label on any of the
    379 			 * other AHDI partitions.
    380 			 */
    381 			if (id == AHDI_PID_NBD &&
    382 			    apt.at_bblock == NO_BOOT_BLOCK)
    383 				apt.at_bblock = blkno;
    384 		}
    385 	}
    386 
    387 	/*
    388 	 * No NetBSD disk label on this volume, use the AHDI
    389 	 * label to create a fake BSD label. If there is no
    390 	 * NBD partition on this volume either, subsequent
    391 	 * writedisklabel() calls will fail.
    392 	 */
    393 	ahdi_to_bsd(dl, &apt);
    394 	return 0;
    395 }
    396 
    397 /*
    398  * Map the AHDI partition table to the NetBSD table.
    399  *
    400  * This means:
    401  *  Part 0   : Root
    402  *  Part 1   : Swap
    403  *  Part 2   : Whole disk
    404  *  Part 3.. : User partitions
    405  *
    406  * When more than one root partition is found, only the first one will
    407  * be recognized as such. The others are mapped as user partitions.
    408  */
    409 static void
    410 ahdi_to_bsd(struct disklabel *dl, struct ahdi_ptbl *apt)
    411 {
    412 	int i, have_root, user_part;
    413 
    414 	user_part = RAW_PART;
    415 	have_root = (apt->at_bblock != NO_BOOT_BLOCK);
    416 
    417 	for (i = 0; i < apt->at_nparts; ++i) {
    418 		struct ahdi_part *pd = &apt->at_parts[i];
    419 		int fst, pno = -1;
    420 
    421 		switch (*((uint32_t *)&pd->ap_flg)) {
    422 			case AHDI_PID_NBD:
    423 				/*
    424 				 * If this partition has been marked as the
    425 				 * first NBD partition, it will be the root
    426 				 * partition.
    427 				 */
    428 				if (pd->ap_st == apt->at_bblock)
    429 					pno = 0;
    430 				/* FALL THROUGH */
    431 			case AHDI_PID_NBR:
    432 				/*
    433 				 * If there is no NBD partition and this is
    434 				 * the first NBR partition, it will be the
    435 				 * root partition.
    436 				 */
    437 				if (!have_root) {
    438 					have_root = 1;
    439 					pno = 0;
    440 				}
    441 				/* FALL THROUGH */
    442 			case AHDI_PID_NBU:
    443 				fst = FS_BSDFFS;
    444 				break;
    445 			case AHDI_PID_NBS:
    446 			case AHDI_PID_SWP:
    447 				if (dl->d_partitions[1].p_size == 0)
    448 					pno = 1;
    449 				fst = FS_SWAP;
    450 				break;
    451 			case AHDI_PID_BGM:
    452 			case AHDI_PID_GEM:
    453 				fst = FS_MSDOS;
    454 				break;
    455 			default:
    456 				fst = FS_OTHER;
    457 				break;
    458 		}
    459 		if (pno < 0) {
    460 			if ((pno = user_part + 1) >= MAXPARTITIONS)
    461 				continue;
    462 			user_part = pno;
    463 		}
    464 		dl->d_partitions[pno].p_size   = pd->ap_end - pd->ap_st + 1;
    465 		dl->d_partitions[pno].p_offset = pd->ap_st;
    466 		dl->d_partitions[pno].p_fstype = fst;
    467 	}
    468 	dl->d_npartitions = user_part + 1;
    469 }
    470 
    471 /*
    472  * Fetch the AHDI partitions and auxiliary roots.
    473  *
    474  * Returns:  0 if successful,
    475  *           otherwise an I/O error occurred, and the
    476  *           number of the offending block is returned.
    477  */
    478 static u_int
    479 ahdi_getparts(dev_t dev, void (*strat)(struct buf *), u_int secpercyl,
    480     u_int rsec, u_int esec, struct ahdi_ptbl *apt)
    481 {
    482 	struct ahdi_part *part, *end;
    483 	struct ahdi_root *root;
    484 	struct buf *bp;
    485 	u_int rv;
    486 
    487 	bp = geteblk(AHDI_BSIZE);
    488 	bp->b_dev      = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
    489 	bp->b_flags    |= B_READ;
    490 	bp->b_bcount   = AHDI_BSIZE;
    491 	bp->b_blkno    = rsec;
    492 	bp->b_cylinder = rsec / secpercyl;
    493 	(*strat)(bp);
    494 	if (biowait(bp)) {
    495 		rv = rsec + (rsec == 0);
    496 		goto done;
    497 	}
    498 	root = (struct ahdi_root *)bp->b_data;
    499 
    500 	if (rsec == AHDI_BBLOCK)
    501 		end = &root->ar_parts[AHDI_MAXRPD];
    502 	else
    503 		end = &root->ar_parts[AHDI_MAXARPD];
    504 	for (part = root->ar_parts; part < end; ++part) {
    505 		u_int id = *((uint32_t *)&part->ap_flg);
    506 		if (!(id & 0x01000000))
    507 			continue;
    508 		if ((id &= 0x00ffffff) == AHDI_PID_XGM) {
    509 			u_int offs = part->ap_st + esec;
    510 			if (apt->at_nroots < AHDI_MAXROOTS)
    511 				apt->at_roots[apt->at_nroots] = offs;
    512 			apt->at_nroots += 1;
    513 			rv = ahdi_getparts(dev, strat, secpercyl, offs,
    514 				(esec == AHDI_BBLOCK) ? offs : esec, apt);
    515 			if (rv != 0)
    516 				goto done;
    517 			continue;
    518 		}
    519 		else if (apt->at_nparts < AHDI_MAXPARTS) {
    520 			struct ahdi_part *p = &apt->at_parts[apt->at_nparts];
    521 			*((uint32_t *)&p->ap_flg) = id;
    522 			p->ap_st  = part->ap_st + rsec;
    523 			p->ap_end = p->ap_st + part->ap_size - 1;
    524 		}
    525 		apt->at_nparts += 1;
    526 	}
    527 	apt->at_hdsize = root->ar_hdsize;
    528 	apt->at_bslst  = root->ar_bslst;
    529 	apt->at_bslend = root->ar_bslst + root->ar_bslsize - 1;
    530 	rv = 0;
    531 done:
    532 	brelse(bp, 0);
    533 	return rv;
    534 }
    535 #endif /* DISKLABEL_AHDI */
    536