xref: /netbsd/sys/arch/sh3/sh3/disksubr.c (revision bf9ec67e)
1 /*	$NetBSD: disksubr.c,v 1.8 2002/04/28 17:10:38 uch Exp $	*/
2 
3 /*
4  * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
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 the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *	@(#)ufs_disksubr.c	7.16 (Berkeley) 5/4/91
36  */
37 
38 #include "opt_mbr.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/buf.h>
43 #include <sys/disklabel.h>
44 #include <sys/syslog.h>
45 
46 #include <machine/bswap.h>
47 
48 int fat_types[] = { MBR_PTYPE_FAT12, MBR_PTYPE_FAT16S,
49 		    MBR_PTYPE_FAT16B, MBR_PTYPE_FAT32,
50 		    MBR_PTYPE_FAT32L, MBR_PTYPE_FAT16L,
51 		    -1 };
52 
53 #define	NO_MBR_SIGNATURE ((struct mbr_partition *) -1)
54 
55 void change_endian_disk_label(struct disklabel *);
56 u_int sh3_dkcksum(struct disklabel *);
57 static struct mbr_partition *mbr_findslice(struct mbr_partition *,
58     struct buf *);
59 
60 void
61 change_endian_disk_label(struct disklabel *lp)
62 {
63 	int i;
64 	u_int16_t d;
65 	/* u_int8_t t; */
66 #define	SW16(X) lp->X = bswap16(lp->X)
67 #define	SW32(X) lp->X = bswap32(lp->X)
68 
69 	SW32(d_magic);
70 	SW16(d_type);
71 	SW16(d_subtype);
72 
73 	for (i = 0; i < sizeof(lp->d_typename); i += 2) {
74 		d = *(u_int16_t *)&lp->d_typename[i];
75 		*(u_int16_t *)&lp->d_typename[i] = bswap16(d);
76 	}
77 
78 	for (i = 0; i < sizeof(lp->d_un); i += 2) {
79 		d = *(u_int16_t *)&lp->d_un.un_d_packname[i];
80 		*(u_int16_t *)&lp->d_un.un_d_packname[i] = bswap16(d);
81 	}
82 
83 	SW32(d_secsize);
84 	SW32(d_nsectors);
85 	SW32(d_ntracks);
86 	SW32(d_ncylinders);
87 	SW32(d_secpercyl);
88 	SW32(d_secperunit);
89 
90 	SW16(d_sparespertrack);
91 	SW16(d_sparespercyl);
92 
93 	SW32(d_acylinders);
94 
95 	SW16(d_rpm);
96 	SW16(d_interleave);
97 	SW16(d_trackskew);		/* sector 0 skew, per track */
98 	SW16(d_cylskew);		/* sector 0 skew, per cylinder */
99 	SW32(d_headswitch);		/* head switch time, usec */
100 	SW32(d_trkseek);		/* track-to-track seek, usec */
101 	SW32(d_flags);			/* generic flags */
102 
103 	for (i = 0; i < NDDATA; i++)
104 		SW32(d_drivedata[i]);	/* drive-type specific information */
105 
106 	for (i = 0; i < NSPARE; i++)
107 		SW32 (d_spare[i]);	/* reserved for future use */
108 
109 	SW32(d_magic2);			/* the magic number (again) */
110 	SW16(d_checksum);		/* xor of data incl. partitions */
111 
112 	/* filesystem and partition information: */
113 	SW16(d_npartitions);	/* number of partitions in following */
114 	SW32(d_bbsize);		/* size of boot area at sn0, bytes */
115 	SW32(d_sbsize);		/* max size of fs superblock, bytes */
116 
117 	for (i = 0; i < MAXPARTITIONS; i++) {
118 		SW32(d_partitions[i].p_size);
119 		SW32(d_partitions[i].p_offset);
120 		SW32(d_partitions[i].p_fsize);
121 #if 0
122 		t = lp->d_partitions[i].p_fstype;
123 		lp->d_partitions[i].p_fstype =
124 		    lp->d_partitions[i].p_frag;
125 		lp->d_partitions[i].p_frag = t;
126 #endif
127 		SW16(d_partitions[i].__partition_u1.cpg);
128 #if 0
129 		printf("size,offset,fsize,fstype,frag=[%x,%x,%x,%x,%x]\n",
130 		    lp->d_partitions[i].p_size,
131 		    lp->d_partitions[i].p_offset,
132 		    lp->d_partitions[i].p_fsize,
133 		    lp->d_partitions[i].p_fstype,
134 		    lp->d_partitions[i].p_frag);
135 #endif
136 	}
137 };
138 
139 
140 /*
141  * Scan MBR for  NetBSD partittion.  Return NO_MBR_SIGNATURE if no MBR found
142  * Otherwise, copy valid MBR partition-table into dp, and if a NetBSD
143  * partition is found, return a pointer to it; else return  NULL.
144  */
145 static struct mbr_partition *
146 mbr_findslice(struct mbr_partition *dp, struct buf *bp)
147 {
148 	struct mbr_partition *ourdp = NULL;
149 	u_int16_t *mbrmagicp;
150 	int i;
151 
152 	/* Note: Magic number is little-endian. */
153 	mbrmagicp = (u_int16_t *)(bp->b_data + MBR_MAGICOFF);
154 	if (*mbrmagicp != MBR_MAGIC)
155 		return (NO_MBR_SIGNATURE);
156 
157 	/* XXX how do we check veracity/bounds of this? */
158 	memcpy(dp, bp->b_data + MBR_PARTOFF, NMBRPART * sizeof(*dp));
159 
160 	/* look for NetBSD partition */
161 	for (i = 0; i < NMBRPART; i++) {
162 		if (dp[i].mbrp_typ == MBR_PTYPE_NETBSD) {
163 			ourdp = &dp[i];
164 			break;
165 		}
166 	}
167 
168 	return (ourdp);
169 }
170 
171 
172 /*
173  * Attempt to read a disk label from a device
174  * using the indicated strategy routine.
175  * The label must be partly set up before this:
176  * secpercyl, secsize and anything required for a block i/o read
177  * operation in the driver's strategy/start routines
178  * must be filled in before calling us.
179  *
180  * If dos partition table requested, attempt to load it and
181  * find disklabel inside a DOS partition. Also, if bad block
182  * table needed, attempt to extract it as well. Return buffer
183  * for use in signalling errors if requested.
184  *
185  * Returns null on success and an error string on failure.
186  */
187 char *
188 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
189     struct cpu_disklabel *osdep)
190 {
191 	struct mbr_partition *dp;
192 	struct partition *pp;
193 	struct dkbad *bdp;
194 	struct buf *bp;
195 	struct disklabel *dlp;
196 	char *msg = NULL;
197 	int dospartoff, cyl, i, *ip;
198 	static struct disklabel dls;
199 
200 	/* minimal requirements for archtypal disk label */
201 	if (lp->d_secsize == 0)
202 		lp->d_secsize = DEV_BSIZE;
203 	if (lp->d_secperunit == 0)
204 		lp->d_secperunit = 0x1fffffff;
205 #if 0
206 	if (lp->d_ncylinders == 16383) {
207 		printf("disklabel: Disk > 8G ... readjusting chs %d/%d/%d to ",
208 		    lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors);
209 		lp->d_ncylinders = lp->d_secperunit /  lp->d_ntracks / lp->d_nsectors;
210 		printf("%d/%d/%d\n",
211 		    lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors);
212 	}
213 #endif
214 	lp->d_npartitions = RAW_PART + 1;
215 	for (i = 0; i < RAW_PART; i++) {
216 		lp->d_partitions[i].p_size = 0;
217 		lp->d_partitions[i].p_offset = 0;
218 	}
219 	if (lp->d_partitions[i].p_size == 0)
220 		lp->d_partitions[i].p_size = 0x1fffffff;
221 	lp->d_partitions[i].p_offset = 0;
222 
223 	/* get a buffer and initialize it */
224 	bp = geteblk((int)lp->d_secsize);
225 	bp->b_dev = dev;
226 
227 	/* do dos partitions in the process of getting disklabel? */
228 	dospartoff = 0;
229 	cyl = LABELSECTOR / lp->d_secpercyl;
230 	if (!osdep)
231 		goto nombrpart;
232 	dp = osdep->dosparts;
233 
234 	/* read master boot record */
235 	bp->b_blkno = MBR_BBSECTOR;
236 	bp->b_bcount = lp->d_secsize;
237 	bp->b_flags |= B_READ;
238 	bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
239 	(*strat)(bp);
240 
241 	/* if successful, wander through dos partition table */
242 	if (biowait(bp)) {
243 		msg = "dos partition I/O error";
244 		goto done;
245 	} else {
246 		struct mbr_partition *ourdp = NULL;
247 
248 		ourdp = mbr_findslice(dp, bp);
249 		if (ourdp ==  NO_MBR_SIGNATURE)
250 			goto nombrpart;
251 
252 		for (i = 0; i < NMBRPART; i++, dp++) {
253 			/* Install in partition e, f, g, or h. */
254 			pp = &lp->d_partitions[RAW_PART + 1 + i];
255 			pp->p_offset = dp->mbrp_start;
256 			pp->p_size = dp->mbrp_size;
257 			for (ip = fat_types; *ip != -1; ip++) {
258 				if (dp->mbrp_typ == *ip)
259 					pp->p_fstype = FS_MSDOS;
260 			}
261 			if (dp->mbrp_typ == MBR_PTYPE_LNXEXT2)
262 				pp->p_fstype = FS_EX2FS;
263 
264 			/* is this ours? */
265 			if (dp == ourdp) {
266 				/* need sector address for SCSI/IDE,
267 				   cylinder for ESDI/ST506/RLL */
268 				dospartoff = dp->mbrp_start;
269 				cyl = MBR_PCYL(dp->mbrp_scyl, dp->mbrp_ssect);
270 
271 				/* update disklabel with details */
272 				lp->d_partitions[2].p_size =
273 				    dp->mbrp_size;
274 				lp->d_partitions[2].p_offset =
275 				    dp->mbrp_start;
276 #if 0
277 				if (lp->d_ntracks != dp->mbrp_ehd + 1 ||
278 				    lp->d_nsectors != MBR_PSECT(dp->mbrp_esect)) {
279 					printf("disklabel: BIOS sees chs %d/%d/%d as ",
280 					    lp->d_ncylinders, lp->d_ntracks,
281 					    lp->d_nsectors);
282 					lp->d_ntracks = dp->mbrp_ehd + 1;
283 					lp->d_nsectors = MBR_PSECT(dp->mbrp_esect);
284 					lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
285 					lp->d_ncylinders = lp->d_secperunit / lp->d_secpercyl;
286 					if (! lp->d_ncylinders)
287 						lp->d_ncylinders = 1;
288 					printf("%d/%d/%d\n",
289 					    lp->d_ncylinders, lp->d_ntracks,
290 					    lp->d_nsectors);
291 				}
292 #endif
293 			}
294 		}
295 		lp->d_npartitions = RAW_PART + 1 + i;
296 	}
297 
298  nombrpart:
299 	/* next, dig out disk label */
300 	bp->b_blkno = dospartoff + LABELSECTOR;
301 	bp->b_cylinder = cyl;
302 	bp->b_bcount = lp->d_secsize;
303 	bp->b_flags &= ~(B_DONE);
304 	bp->b_flags |= B_READ;
305 	(*strat)(bp);
306 
307 	/* if successful, locate disk label within block and validate */
308 	if (biowait(bp)) {
309 		msg = "disk label I/O error";
310 		goto done;
311 	}
312 	for (dlp = (struct disklabel *)bp->b_data;
313 	    dlp <= (struct disklabel *)(bp->b_data + lp->d_secsize - sizeof(*dlp));
314 	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
315 		dls = *dlp;
316 
317 		change_endian_disk_label(&dls);
318 		if (dls.d_magic != DISKMAGIC || dls.d_magic2 != DISKMAGIC) {
319 			if (msg == NULL)
320 				msg = "no disk label";
321 		} else if (dls.d_npartitions > MAXPARTITIONS ||
322 		    sh3_dkcksum(&dls) != 0)
323 			msg = "disk label corrupted";
324 		else {
325 			*lp = dls;
326 			msg = NULL;
327 			break;
328 		}
329 	}
330 
331 	if (msg)
332 		goto done;
333 
334 	/* obtain bad sector table if requested and present */
335 	if (osdep && (lp->d_flags & D_BADSECT)) {
336 		struct dkbad *db;
337 
338 		bdp = &osdep->bad;
339 		i = 0;
340 		do {
341 			/* read a bad sector table */
342 			bp->b_flags &= ~(B_DONE);
343 			bp->b_flags |= B_READ;
344 			bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i;
345 			if (lp->d_secsize > DEV_BSIZE)
346 				bp->b_blkno *= lp->d_secsize / DEV_BSIZE;
347 			else
348 				bp->b_blkno /= DEV_BSIZE / lp->d_secsize;
349 			bp->b_bcount = lp->d_secsize;
350 			bp->b_cylinder = lp->d_ncylinders - 1;
351 			(*strat)(bp);
352 
353 			/* if successful, validate, otherwise try another */
354 			if (biowait(bp)) {
355 				msg = "bad sector table I/O error";
356 			} else {
357 				db = (struct dkbad *)(bp->b_data);
358 #define	DKBAD_MAGIC 0x4321
359 				if (db->bt_mbz == 0
360 				    && db->bt_flag == DKBAD_MAGIC) {
361 					msg = NULL;
362 					*bdp = *db;
363 					break;
364 				} else
365 					msg = "bad sector table corrupted";
366 			}
367 		} while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
368 		    i < lp->d_nsectors);
369 	}
370 
371  done:
372 	brelse(bp);
373 	return (msg);
374 }
375 
376 u_int
377 sh3_dkcksum(struct disklabel *lp)
378 {
379 	struct disklabel tdl;
380 	u_short *start, *end;
381 	int offset;
382 	u_short sum = 0;
383 	u_short w;
384 
385 	tdl = *lp;
386 
387 	change_endian_disk_label(&tdl);
388 	start = (u_short *)lp;
389 	end = (u_short *)&lp->d_partitions[lp->d_npartitions];
390 	offset = end - start;
391 
392 	start = (u_short *)&tdl;
393 	end = start + offset;
394 	while (start < end) {
395 		w = *start++;
396 		sum ^= bswap16(w);
397 	}
398 
399 	return (sum);
400 }
401 
402 /*
403  * Check new disk label for sensibility
404  * before setting it.
405  */
406 int
407 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask,
408     struct cpu_disklabel *osdep)
409 {
410 	int i;
411 	struct partition *opp, *npp;
412 
413 	/* sanity clause */
414 	if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0
415 	    || (nlp->d_secsize % DEV_BSIZE) != 0)
416 		return(EINVAL);
417 
418 	/* special case to allow disklabel to be invalidated */
419 	if (nlp->d_magic == 0xffffffff) {
420 		*olp = *nlp;
421 		return (0);
422 	}
423 
424 	if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
425 	    sh3_dkcksum(nlp) != 0)
426 		return (EINVAL);
427 
428 	/* XXX missing check if other dos partitions will be overwritten */
429 
430 	while (openmask != 0) {
431 		i = ffs(openmask) - 1;
432 		openmask &= ~(1 << i);
433 		if (nlp->d_npartitions <= i)
434 			return (EBUSY);
435 		opp = &olp->d_partitions[i];
436 		npp = &nlp->d_partitions[i];
437 		if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
438 			return (EBUSY);
439 		/*
440 		 * Copy internally-set partition information
441 		 * if new label doesn't include it.		XXX
442 		 */
443 		if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
444 			npp->p_fstype = opp->p_fstype;
445 			npp->p_fsize = opp->p_fsize;
446 			npp->p_frag = opp->p_frag;
447 			npp->p_cpg = opp->p_cpg;
448 		}
449 	}
450 	nlp->d_checksum = 0;
451 	nlp->d_checksum = sh3_dkcksum(nlp);
452 	*olp = *nlp;
453 	return (0);
454 }
455 
456 
457 /*
458  * Write disk label back to device after modification.
459  */
460 int
461 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
462     struct cpu_disklabel *osdep)
463 {
464 	struct mbr_partition *dp;
465 	struct buf *bp;
466 	struct disklabel *dlp;
467 	int error, dospartoff, cyl;
468 	static struct disklabel dls;
469 
470 	/* get a buffer and initialize it */
471 	bp = geteblk((int)lp->d_secsize);
472 	bp->b_dev = dev;
473 
474 	/* do dos partitions in the process of getting disklabel? */
475 	dospartoff = 0;
476 	cyl = LABELSECTOR / lp->d_secpercyl;
477 	if (!osdep)
478 		goto nombrpart;
479 	dp = osdep->dosparts;
480 
481 	/* read master boot record */
482 	bp->b_blkno = MBR_BBSECTOR;
483 	bp->b_bcount = lp->d_secsize;
484 	bp->b_flags |= B_READ;
485 	bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
486 	(*strat)(bp);
487 
488 	if ((error = biowait(bp)) == 0) {
489 		struct mbr_partition *ourdp = NULL;
490 
491 		ourdp = mbr_findslice(dp, bp);
492 		if (ourdp ==  NO_MBR_SIGNATURE)
493 			goto nombrpart;
494 
495 		if (ourdp) {
496 			/* need sector address for SCSI/IDE,
497 			   cylinder for ESDI/ST506/RLL */
498 			dospartoff = ourdp->mbrp_start;
499 			cyl = MBR_PCYL(ourdp->mbrp_scyl, ourdp->mbrp_ssect);
500 		}
501 	}
502 
503  nombrpart:
504 #ifdef maybe
505 	/* disklabel in appropriate location? */
506 	if (lp->d_partitions[2].p_offset != 0
507 	    && lp->d_partitions[2].p_offset != dospartoff) {
508 		error = EXDEV;
509 		goto done;
510 	}
511 #endif
512 
513 	/* next, dig out disk label */
514 	bp->b_blkno = dospartoff + LABELSECTOR;
515 	bp->b_cylinder = cyl;
516 	bp->b_bcount = lp->d_secsize;
517 	bp->b_flags &= ~(B_DONE);
518 	bp->b_flags |= B_READ;
519 	(*strat)(bp);
520 
521 	/* if successful, locate disk label within block and validate */
522 	if ((error = biowait(bp)) != 0)
523 		goto done;
524 	for (dlp = (struct disklabel *)bp->b_data;
525 	    dlp <= (struct disklabel *)(bp->b_data + lp->d_secsize - sizeof(*dlp));
526 	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
527 		dls = *dlp;
528 
529 		change_endian_disk_label(&dls);
530 		if (dls.d_magic == DISKMAGIC && dls.d_magic2 == DISKMAGIC &&
531 		    sh3_dkcksum(&dls) == 0) {
532 			dls = *lp;
533 			change_endian_disk_label(&dls);
534 			*dlp = dls;
535 			bp->b_flags &= ~(B_READ|B_DONE);
536 			bp->b_flags |= B_WRITE;
537 			(*strat)(bp);
538 			error = biowait(bp);
539 			goto done;
540 		}
541 	}
542 	error = ESRCH;
543 
544  done:
545 	brelse(bp);
546 	return (error);
547 }
548 
549 /*
550  * Determine the size of the transfer, and make sure it is
551  * within the boundaries of the partition. Adjust transfer
552  * if needed, and signal errors or early completion.
553  */
554 int
555 bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel)
556 {
557 	struct partition *p = lp->d_partitions + DISKPART(bp->b_dev);
558 	int labelsector = lp->d_partitions[2].p_offset + LABELSECTOR;
559 	int sz;
560 
561 	sz = howmany(bp->b_bcount, lp->d_secsize);
562 
563 	if (bp->b_blkno + sz > p->p_size) {
564 		sz = p->p_size - bp->b_blkno;
565 		if (sz == 0) {
566 			/* If exactly at end of disk, return EOF. */
567 			bp->b_resid = bp->b_bcount;
568 			goto done;
569 		}
570 		if (sz < 0) {
571 			/* If past end of disk, return EINVAL. */
572 			bp->b_error = EINVAL;
573 			goto bad;
574 		}
575 		/* Otherwise, truncate request. */
576 		bp->b_bcount = sz << DEV_BSHIFT;
577 	}
578 
579 	/* Overwriting disk label? */
580 	if (bp->b_blkno + p->p_offset <= labelsector &&
581 #if LABELSECTOR != 0
582 	    bp->b_blkno + p->p_offset + sz > labelsector &&
583 #endif
584 	    (bp->b_flags & B_READ) == 0 && !wlabel) {
585 		bp->b_error = EROFS;
586 		goto bad;
587 	}
588 
589 	/* calculate cylinder for disksort to order transfers with */
590 	bp->b_cylinder = (bp->b_blkno + p->p_offset) /
591 	    (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
592 	return (1);
593 
594  bad:
595 	bp->b_flags |= B_ERROR;
596  done:
597 	return (0);
598 }
599