xref: /netbsd/sys/arch/sbmips/sbmips/disksubr.c (revision bf9ec67e)
1 /* $NetBSD: disksubr.c,v 1.2 2002/03/17 06:28:57 simonb 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 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/buf.h>
39 #include <sys/disklabel.h>
40 #include <sys/disk.h>
41 #include <sys/syslog.h>
42 
43 #define	NO_MBR_SIGNATURE ((struct mbr_partition *) -1)
44 
45 static struct mbr_partition *
46 mbr_findslice(struct mbr_partition* dp, struct buf *bp);
47 
48 /*
49  * Scan MBR for NetBSD partittion.  Return NO_MBR_SIGNATURE if no MBR found
50  * Otherwise, copy valid MBR partition-table into dp, and if a NetBSD
51  * partition is found, return a pointer to it; else return  NULL.
52  */
53 static
54 struct mbr_partition *
55 mbr_findslice(dp, bp)
56 	struct mbr_partition *dp;
57 	struct buf *bp;
58 {
59 	struct mbr_partition *ourdp = NULL;
60 	uint16_t *mbrmagicp;
61 	int i;
62 
63 	/* Note: Magic number is little-endian. */
64 	mbrmagicp = (uint16_t *)(bp->b_data + MBR_MAGICOFF);
65 	if (*mbrmagicp != MBR_MAGIC)
66 		return (NO_MBR_SIGNATURE);
67 
68 	/* XXX how do we check veracity/bounds of this? */
69 	memcpy(dp, bp->b_data + MBR_PARTOFF, NMBRPART * sizeof(*dp));
70 
71 	/* look for NetBSD partition */
72 	for (i = 0; i < NMBRPART; i++) {
73 		if (dp[i].mbrp_typ == MBR_PTYPE_NETBSD) {
74 			ourdp = &dp[i];
75 			break;
76 		}
77 	}
78 
79 	return (ourdp);
80 }
81 
82 
83 /*
84  * Attempt to read a disk label from a device
85  * using the indicated stategy routine.
86  * The label must be partly set up before this:
87  * secpercyl, secsize and anything required for a block i/o read
88  * operation in the driver's strategy/start routines
89  * must be filled in before calling us.
90  *
91  * If dos partition table requested, attempt to load it and
92  * find disklabel inside a DOS partition. Also, if bad block
93  * table needed, attempt to extract it as well. Return buffer
94  * for use in signalling errors if requested.
95  *
96  * Returns null on success and an error string on failure.
97  */
98 char *
99 readdisklabel(dev, strat, lp, osdep)
100 	dev_t dev;
101 	void (*strat)(struct buf *);
102 	struct disklabel *lp;
103 	struct cpu_disklabel *osdep;
104 {
105 	struct mbr_partition *dp;
106 	struct partition *pp;
107 	struct dkbad *bdp;
108 	struct buf *bp;
109 	struct disklabel *dlp;
110 	char *msg = NULL;
111 	int dospartoff, cyl, i;
112 
113 	/* minimal requirements for archtypal disk label */
114 	if (lp->d_secsize == 0)
115 		lp->d_secsize = DEV_BSIZE;
116 	if (lp->d_secperunit == 0)
117 		lp->d_secperunit = 0x1fffffff;
118 #if 0
119 	if (lp->d_ncylinders == 16383) {
120 		printf("disklabel: Disk > 8G ... readjusting chs %d/%d/%d to ",
121 			lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors);
122 		lp->d_ncylinders = lp->d_secperunit /  lp->d_ntracks / lp->d_nsectors;
123 		printf("%d/%d/%d\n",
124 			lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors);
125 	}
126 #endif
127 	lp->d_npartitions = RAW_PART + 1;
128 	for (i = 0; i < RAW_PART; i++) {
129 		lp->d_partitions[i].p_size = 0;
130 		lp->d_partitions[i].p_offset = 0;
131 	}
132 	if (lp->d_partitions[i].p_size == 0)
133 		lp->d_partitions[i].p_size = 0x1fffffff;
134 	lp->d_partitions[i].p_offset = 0;
135 
136 	/* get a buffer and initialize it */
137 	bp = geteblk((int)lp->d_secsize);
138 	bp->b_dev = dev;
139 
140 	/* do dos partitions in the process of getting disklabel? */
141 	dospartoff = 0;
142 	cyl = LABELSECTOR / lp->d_secpercyl;
143 	if (!osdep)
144 		goto nombrpart;
145 	dp = osdep->dosparts;
146 
147 	/* read master boot record */
148 	bp->b_blkno = MBR_BBSECTOR;
149 	bp->b_bcount = lp->d_secsize;
150 	bp->b_flags = B_BUSY | B_READ;
151 	bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
152 	(*strat)(bp);
153 
154 	/* if successful, wander through dos partition table */
155 	if (biowait(bp)) {
156 		msg = "dos partition I/O error";
157 		goto done;
158 	} else {
159 		struct mbr_partition *ourdp = NULL;
160 
161 		ourdp = mbr_findslice(dp, bp);
162 		if (ourdp ==  NO_MBR_SIGNATURE)
163 			goto nombrpart;
164 
165 		for (i = 0; i < NMBRPART; i++, dp++) {
166 			/* Install in partition e, f, g, or h. */
167 			pp = &lp->d_partitions[RAW_PART + 1 + i];
168 			pp->p_offset = dp->mbrp_start;
169 			pp->p_size = dp->mbrp_size;
170 			if (dp->mbrp_typ == MBR_PTYPE_LNXEXT2)
171 				pp->p_fstype = FS_EX2FS;
172 
173 			if (dp->mbrp_typ == MBR_PTYPE_LNXSWAP)
174 				pp->p_fstype = FS_SWAP;
175 
176 			/* is this ours? */
177 			if (dp == ourdp) {
178 				/* need sector address for SCSI/IDE,
179 				 cylinder for ESDI/ST506/RLL */
180 				dospartoff = dp->mbrp_start;
181 				cyl = MBR_PCYL(dp->mbrp_scyl, dp->mbrp_ssect);
182 
183 				/* update disklabel with details */
184 				lp->d_partitions[2].p_size =
185 				    dp->mbrp_size;
186 				lp->d_partitions[2].p_offset =
187 				    dp->mbrp_start;
188 			}
189 		}
190 		lp->d_npartitions = RAW_PART + 1 + i;
191 	}
192 
193 nombrpart:
194 	/* next, dig out disk label */
195 	bp->b_blkno = dospartoff + LABELSECTOR;
196 	bp->b_cylinder = cyl;
197 	bp->b_bcount = lp->d_secsize;
198 	bp->b_flags = B_BUSY | B_READ;
199 	(*strat)(bp);
200 
201 	/* if successful, locate disk label within block and validate */
202 	if (biowait(bp)) {
203 		msg = "disk label I/O error";
204 		goto done;
205 	}
206 	for (dlp = (struct disklabel *)bp->b_data;
207 	    dlp <= (struct disklabel *)
208 		(bp->b_data + lp->d_secsize - sizeof(*dlp));
209 	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
210 		if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
211 			if (msg == NULL)
212 				msg = "no disk label";
213 		} else if (dlp->d_npartitions > MAXPARTITIONS ||
214 			   dkcksum(dlp) != 0)
215 			msg = "disk label corrupted";
216 		else {
217 			*lp = *dlp;
218 			msg = NULL;
219 			break;
220 		}
221 	}
222 
223 	if (msg)
224 		goto done;
225 
226 	/* obtain bad sector table if requested and present */
227 	if (osdep && (lp->d_flags & D_BADSECT)) {
228 		struct dkbad *db;
229 
230 		bdp = &osdep->bad;
231 		i = 0;
232 		do {
233 			/* read a bad sector table */
234 			bp->b_flags = B_BUSY | B_READ;
235 			bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i;
236 			if (lp->d_secsize > DEV_BSIZE)
237 				bp->b_blkno *= lp->d_secsize / DEV_BSIZE;
238 			else
239 				bp->b_blkno /= DEV_BSIZE / lp->d_secsize;
240 			bp->b_bcount = lp->d_secsize;
241 			bp->b_cylinder = lp->d_ncylinders - 1;
242 			(*strat)(bp);
243 
244 			/* if successful, validate, otherwise try another */
245 			if (biowait(bp)) {
246 				msg = "bad sector table I/O error";
247 			} else {
248 				db = (struct dkbad *)(bp->b_data);
249 #define	DKBAD_MAGIC 0x4321
250 				if (db->bt_mbz == 0
251 					&& db->bt_flag == DKBAD_MAGIC) {
252 					msg = NULL;
253 					*bdp = *db;
254 					break;
255 				} else
256 					msg = "bad sector table corrupted";
257 			}
258 		} while ((bp->b_flags & B_ERROR) && (i += 2) < 10 &&
259 			i < lp->d_nsectors);
260 	}
261 
262 done:
263 	bp->b_flags |= B_INVAL;
264 	brelse(bp);
265 	return (msg);
266 }
267 
268 /*
269  * Check new disk label for sensibility
270  * before setting it.
271  */
272 int
273 setdisklabel(olp, nlp, openmask, osdep)
274 	struct disklabel *olp, *nlp;
275 	u_long openmask;
276 	struct cpu_disklabel *osdep;
277 {
278 	int i;
279 	struct partition *opp, *npp;
280 
281 	/* sanity clause */
282 	if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0
283 		|| (nlp->d_secsize % DEV_BSIZE) != 0)
284 			return(EINVAL);
285 
286 	/* special case to allow disklabel to be invalidated */
287 	if (nlp->d_magic == 0xffffffff) {
288 		*olp = *nlp;
289 		return (0);
290 	}
291 
292 	if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
293 	    dkcksum(nlp) != 0)
294 		return (EINVAL);
295 
296 	/* XXX missing check if other dos partitions will be overwritten */
297 
298 	while (openmask != 0) {
299 		i = ffs(openmask) - 1;
300 		openmask &= ~(1 << i);
301 		if (nlp->d_npartitions <= i)
302 			return (EBUSY);
303 		opp = &olp->d_partitions[i];
304 		npp = &nlp->d_partitions[i];
305 		if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
306 			return (EBUSY);
307 		/*
308 		 * Copy internally-set partition information
309 		 * if new label doesn't include it.		XXX
310 		 */
311 		if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
312 			npp->p_fstype = opp->p_fstype;
313 			npp->p_fsize = opp->p_fsize;
314 			npp->p_frag = opp->p_frag;
315 			npp->p_cpg = opp->p_cpg;
316 		}
317 	}
318  	nlp->d_checksum = 0;
319  	nlp->d_checksum = dkcksum(nlp);
320 	*olp = *nlp;
321 	return (0);
322 }
323 
324 
325 /*
326  * Write disk label back to device after modification.
327  */
328 int
329 writedisklabel(dev, strat, lp, osdep)
330 	dev_t dev;
331 	void (*strat)(struct buf *);
332 	struct disklabel *lp;
333 	struct cpu_disklabel *osdep;
334 {
335 	struct mbr_partition *dp;
336 	struct buf *bp;
337 	struct disklabel *dlp;
338 	int error, dospartoff, cyl;
339 
340 	/* get a buffer and initialize it */
341 	bp = geteblk((int)lp->d_secsize);
342 	bp->b_dev = dev;
343 
344 	/* do dos partitions in the process of getting disklabel? */
345 	dospartoff = 0;
346 	cyl = LABELSECTOR / lp->d_secpercyl;
347 	if (!osdep)
348 		goto nombrpart;
349 	dp = osdep->dosparts;
350 
351 	/* read master boot record */
352 	bp->b_blkno = MBR_BBSECTOR;
353 	bp->b_bcount = lp->d_secsize;
354 	bp->b_flags = B_BUSY | B_READ;
355 	bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
356 	(*strat)(bp);
357 
358 	if ((error = biowait(bp)) == 0) {
359 		struct mbr_partition *ourdp = NULL;
360 
361 		ourdp = mbr_findslice(dp, bp);
362 		if (ourdp ==  NO_MBR_SIGNATURE)
363 			goto nombrpart;
364 
365 		if (ourdp) {
366 			/* need sector address for SCSI/IDE,
367 			 cylinder for ESDI/ST506/RLL */
368 			dospartoff = ourdp->mbrp_start;
369 			cyl = MBR_PCYL(ourdp->mbrp_scyl, ourdp->mbrp_ssect);
370 		}
371 	}
372 
373 nombrpart:
374 	/* next, dig out disk label */
375 	bp->b_blkno = dospartoff + LABELSECTOR;
376 	bp->b_cylinder = cyl;
377 	bp->b_bcount = lp->d_secsize;
378 	bp->b_flags = B_BUSY | B_READ;
379 	(*strat)(bp);
380 
381 	/* if successful, locate disk label within block and validate */
382 	if ((error = biowait(bp)) != 0)
383 		goto done;
384 	for (dlp = (struct disklabel *)bp->b_data;
385 	    dlp <= (struct disklabel *)
386 		(bp->b_data + lp->d_secsize - sizeof(*dlp));
387 	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
388 		if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
389 		    dkcksum(dlp) == 0) {
390 			*dlp = *lp;
391 			bp->b_flags = B_BUSY | B_WRITE;
392 			(*strat)(bp);
393 			error = biowait(bp);
394 			goto done;
395 		}
396 	}
397 	error = ESRCH;
398 
399 done:
400 	bp->b_flags |= B_INVAL;
401 	brelse(bp);
402 	return (error);
403 }
404 
405 /*
406  * Determine the size of the transfer, and make sure it is
407  * within the boundaries of the partition. Adjust transfer
408  * if needed, and signal errors or early completion.
409  */
410 int
411 bounds_check_with_label(bp, lp, wlabel)
412 	struct buf *bp;
413 	struct disklabel *lp;
414 	int wlabel;
415 {
416 	struct partition *p = lp->d_partitions + DISKPART(bp->b_dev);
417 	int labelsector = lp->d_partitions[2].p_offset + LABELSECTOR;
418 	int sz;
419 
420 	sz = howmany(bp->b_bcount, lp->d_secsize);
421 
422 	if (bp->b_blkno + sz > p->p_size) {
423 		sz = p->p_size - bp->b_blkno;
424 		if (sz == 0) {
425 			/* If exactly at end of disk, return EOF. */
426 			bp->b_resid = bp->b_bcount;
427 			goto done;
428 		}
429 		if (sz < 0) {
430 			/* If past end of disk, return EINVAL. */
431 			bp->b_error = EINVAL;
432 			goto bad;
433 		}
434 		/* Otherwise, truncate request. */
435 		bp->b_bcount = sz << DEV_BSHIFT;
436 	}
437 
438 	/* Overwriting disk label? */
439 	if (bp->b_blkno + p->p_offset <= labelsector &&
440 #if LABELSECTOR != 0
441 	    bp->b_blkno + p->p_offset + sz > labelsector &&
442 #endif
443 	    (bp->b_flags & B_READ) == 0 && !wlabel) {
444 		bp->b_error = EROFS;
445 		goto bad;
446 	}
447 
448 	/* calculate cylinder for disksort to order transfers with */
449 	bp->b_cylinder = (bp->b_blkno + p->p_offset) /
450 	    (lp->d_secsize / DEV_BSIZE) / lp->d_secpercyl;
451 	return (1);
452 
453 bad:
454 	bp->b_flags |= B_ERROR;
455 done:
456 	return (0);
457 }
458