xref: /netbsd/sys/arch/mipsco/mipsco/disksubr.c (revision bf9ec67e)
1 /*	$NetBSD: disksubr.c,v 1.10 2002/03/05 09:40:40 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  *	@(#)ufs_disksubr.c	7.16 (Berkeley) 5/4/91
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/buf.h>
41 #include <sys/device.h>
42 #include <sys/disk.h>
43 #include <sys/disklabel.h>
44 #include <sys/syslog.h>
45 #include <ufs/ufs/dinode.h>		/* XXX for fs.h */
46 #include <ufs/ffs/fs.h>			/* XXX for BBSIZE & SBSIZE */
47 
48 #define	b_cylin	b_resid
49 
50 static char *disklabel_mips_to_bsd __P((struct mips_volheader *,
51 					  struct disklabel *));
52 static int disklabel_bsd_to_mips __P((struct disklabel *,
53 					struct mips_volheader *));
54 static int mipsvh_cksum __P((struct mips_volheader *));
55 
56 #define LABELSIZE(lp)	((char *)&lp->d_partitions[lp->d_npartitions] -	\
57 			 (char *)lp)
58 
59 /*
60  * Attempt to read a disk label from a device
61  * using the indicated strategy routine.
62  * The label must be partly set up before this:
63  * secpercyl and anything required in the strategy routine
64  * (e.g., sector size) must be filled in before calling us.
65  * Returns null on success and an error string on failure.
66  */
67 char *
68 readdisklabel(dev, strat, lp, clp)
69 	dev_t dev;
70 	void (*strat) __P((struct buf *bp));
71 	register struct disklabel *lp;
72 	struct cpu_disklabel *clp;
73 {
74 	register struct buf *bp;
75 	struct disklabel *dlp;
76 	struct mips_volheader *mvp;
77 	int i, err;
78 
79 	/* minimum requirements for disk label */
80 	if (lp->d_secperunit == 0)
81 		lp->d_secperunit = 0x1fffffff;
82 	if (lp->d_npartitions == 0) {
83 		lp->d_npartitions = RAW_PART + 1;
84 		if (lp->d_partitions[RAW_PART].p_size == 0)
85 			lp->d_partitions[RAW_PART].p_size = 0x1fffffff;
86 		lp->d_partitions[RAW_PART].p_offset = 0;
87 	}
88 
89 	bp = geteblk((int)lp->d_secsize);
90 
91 	bp->b_dev = dev;
92 	bp->b_blkno = LABELSECTOR;
93 	bp->b_bcount = lp->d_secsize;
94 	bp->b_flags |= B_READ;
95 	bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
96 	(*strat)(bp);
97 	err = biowait(bp);
98 	brelse(bp);
99 
100 	if (err)
101 		return "error reading disklabel";
102 
103 	/* Check for NetBSD label in second sector */
104 	dlp = (struct disklabel *)(bp->b_un.b_addr + LABELOFFSET);
105 	if (dlp->d_magic == DISKMAGIC)
106 		if (!dkcksum(dlp)) {
107 			memcpy(lp, dlp, LABELSIZE(dlp));
108 			return NULL;	/* NetBSD label found */
109 		}
110 
111 	bp = geteblk((int)lp->d_secsize);
112 	bp->b_dev = dev;
113 	bp->b_blkno = MIPS_VHSECTOR;
114 	bp->b_bcount = lp->d_secsize;
115 	bp->b_flags |= B_READ;
116 	bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
117 	(*strat)(bp);
118 	err = biowait(bp);
119 	brelse(bp);
120 
121 	if (err)
122 		return "error reading volume header";
123 
124 	mvp = (struct mips_volheader *)bp->b_un.b_addr;
125 	/* Check for MIPS RISC/os volume header */
126 	if (mvp->vh_magic == MIPS_VHMAGIC)
127 		return disklabel_mips_to_bsd(mvp, lp);
128 
129 	/* Search for NetBSD label in first sector */
130 	for (i=0; i <= lp->d_secsize - sizeof(*dlp); i += sizeof(long)) {
131 		dlp = (struct disklabel *) ((char *)mvp + i);
132 		if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC) {
133 			if (dlp->d_npartitions > MAXPARTITIONS ||
134 			    dkcksum(dlp) != 0)
135 				return "disk label corrupted";
136 			else {
137 				memcpy(lp, dlp, sizeof *lp);
138 				return NULL; /* Found */
139 			}
140 		}
141 	}
142 	return "no disk label";
143 }
144 
145 /*
146  * Check new disk label for sensibility
147  * before setting it.
148  */
149 int
150 setdisklabel(olp, nlp, openmask, clp)
151 	register struct disklabel *olp, *nlp;
152 	u_long openmask;
153 	struct cpu_disklabel *clp;
154 {
155 	register int i;
156 	register struct partition *opp, *npp;
157 
158 	if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
159 	    dkcksum(nlp) != 0)
160 		return (EINVAL);
161 	while ((i = ffs(openmask)) != 0) {
162 		i--;
163 		openmask &= ~(1 << i);
164 		if (nlp->d_npartitions <= i)
165 			return (EBUSY);
166 		opp = &olp->d_partitions[i];
167 		npp = &nlp->d_partitions[i];
168 		if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
169 			return (EBUSY);
170 		/*
171 		 * Copy internally-set partition information
172 		 * if new label doesn't include it.		XXX
173 		 */
174 		if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
175 			npp->p_fstype = opp->p_fstype;
176 			npp->p_fsize = opp->p_fsize;
177 			npp->p_frag = opp->p_frag;
178 			npp->p_cpg = opp->p_cpg;
179 		}
180 	}
181  	nlp->d_checksum = 0;
182  	nlp->d_checksum = dkcksum(nlp);
183 	*olp = *nlp;
184 	return (0);
185 }
186 
187 /* encoding of disk minor numbers, should be elsewhere... */
188 #define dkunit(dev)		(minor(dev) >> 3)
189 #define dkpart(dev)		(minor(dev) & 07)
190 #define dkminor(unit, part)	(((unit) << 3) | (part))
191 
192 /*
193  * Write disk label back to device after modification.
194  */
195 int
196 writedisklabel(dev, strat, lp, clp)
197 	dev_t dev;
198 	void (*strat) __P((struct buf *bp));
199 	register struct disklabel *lp;
200 	struct cpu_disklabel *clp;
201 {
202 	struct buf *bp;
203 	int labelpart;
204 	int error;
205 
206 	labelpart = dkpart(dev);
207 	if (lp->d_partitions[labelpart].p_offset != 0) {
208 		if (lp->d_partitions[0].p_offset != 0)
209 			return (EXDEV);			/* not quite right */
210 		labelpart = 0;
211 	}
212 
213 	/* Read RISC/os volume header before merging NetBSD partition info*/
214 	bp = geteblk((int)lp->d_secsize);
215 
216 	bp->b_dev = dev;
217 	bp->b_blkno = MIPS_VHSECTOR;
218 	bp->b_bcount = lp->d_secsize;
219 	bp->b_flags |= B_READ;
220 	bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
221 	(*strat)(bp);
222 
223 	if((error = biowait(bp)) != 0)
224 		goto ioerror;
225 
226 	if ((error = disklabel_bsd_to_mips(lp, (void *)bp->b_data)) != 0)
227 		goto ioerror;
228 
229 	/* Write MIPS RISC/os label to first sector */
230 	bp->b_flags &= ~(B_READ|B_DONE);
231 	bp->b_flags |= B_WRITE;
232 	(*strat)(bp);
233 	if ((error = biowait(bp)) != 0)
234 		goto ioerror;
235 
236 	/* Write NetBSD disk label to second sector */
237 	memset(bp->b_data, 0, lp->d_secsize);
238 	memcpy(bp->b_data, lp, sizeof(*lp));
239 	bp->b_blkno = LABELSECTOR;
240 	bp->b_bcount = lp->d_secsize;
241 	bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
242 	bp->b_flags &= ~(B_READ | B_DONE);
243 	bp->b_flags |= B_WRITE;
244 	(*strat)(bp);
245 	error = biowait(bp);
246 
247 ioerror:
248 	brelse(bp);
249 	return error;
250 }
251 
252 /*
253  * Determine the size of the transfer, and make sure it is
254  * within the boundaries of the partition. Adjust transfer
255  * if needed, and signal errors or early completion.
256  */
257 int
258 bounds_check_with_label(bp, lp, wlabel)
259 	struct buf *bp;
260 	struct disklabel *lp;
261 	int wlabel;
262 {
263 
264 	struct partition *p = lp->d_partitions + dkpart(bp->b_dev);
265 	int labelsect = lp->d_partitions[0].p_offset;
266 	int maxsz = p->p_size;
267 	int sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
268 
269 	/* overwriting disk label ? */
270 	/* XXX should also protect bootstrap in first 8K */
271 	if (bp->b_blkno + p->p_offset <= LABELSECTOR + labelsect &&
272 	    (bp->b_flags & B_READ) == 0 && wlabel == 0) {
273 		bp->b_error = EROFS;
274 		goto bad;
275 	}
276 
277 	/* beyond partition? */
278 	if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
279 		/* if exactly at end of disk, return an EOF */
280 		if (bp->b_blkno == maxsz) {
281 			bp->b_resid = bp->b_bcount;
282 			return(0);
283 		}
284 		/* or truncate if part of it fits */
285 		sz = maxsz - bp->b_blkno;
286 		if (sz <= 0) {
287 			bp->b_error = EINVAL;
288 			goto bad;
289 		}
290 		bp->b_bcount = sz << DEV_BSHIFT;
291 	}
292 
293 	/* calculate cylinder for disksort to order transfers with */
294 	bp->b_resid = (bp->b_blkno + p->p_offset) / lp->d_secpercyl;
295 	return(1);
296 bad:
297 	bp->b_flags |= B_ERROR;
298 	return(-1);
299 }
300 
301 /*
302  * Convertion table for mapping partition numbers and types between
303  * a MIPS volume header and a BSD partition table.
304  *
305  * Mips volume header compatibility is required in order to boot
306  * NetBSD from the Mips stand alone shell, but due to the differences
307  * in the partition numbers used along with different methods for
308  * determining partition types we must use a table for mapping the
309  * differences.
310  */
311 
312 struct partitionmap {
313 	int	mips_part;		/* Mips partition number */
314 	int	mips_type;	/* Mips partition type */
315 	int	bsd_part;		/* BSD partition number */
316 	int	bsd_type;	/* BSD partition type */
317 };
318 
319 struct partitionmap partition_map[] = {
320      /* Mips       Mips Type	       BSD      BSD Type */
321 	{0,	MIPS_FS_BSD42,		0,	FS_BSDFFS},
322 	{1,	MIPS_FS_BSD42,		1,	FS_SWAP},
323 	{10,	MIPS_FS_VOLUME,	     RAW_PART,	FS_OTHER},
324 	{3,	MIPS_FS_BSD42,		3,	FS_BSDFFS},
325         {4,	MIPS_FS_BSD42,		4,	FS_BSDFFS},
326         {5,	MIPS_FS_BSD42,		5,	FS_BSDFFS},
327         {6,	MIPS_FS_BSD42,		6,	FS_BSDFFS},
328 	{7,	MIPS_FS_BSD42,		7,	FS_BSDFFS}
329 };
330 #define NPARTMAP	(sizeof(partition_map)/sizeof(struct partitionmap))
331 
332 /*
333  * Convert a RISC/os disk label into a NetBSD disk label.
334  *
335  * Returns NULL on success, otherwise an error string
336  */
337 static char *
338 disklabel_mips_to_bsd(vh, lp)
339 	struct mips_volheader *vh;
340 	struct disklabel *lp;
341 {
342 	int  i, bp, mp;
343 	struct partition *lpp;
344 	if (mipsvh_cksum(vh))
345 		return ("MIPS disk label corrupted");
346 
347 	lp->d_secsize    = vh->vh_dp.dp_secbytes;
348 	lp->d_nsectors   = vh->vh_dp.dp_secs;
349 	lp->d_ntracks    = vh->vh_dp.dp_trks0;
350 	lp->d_ncylinders = vh->vh_dp.dp_cyls;
351 	lp->d_interleave = vh->vh_dp.dp_interleave;
352 
353 	lp->d_secpercyl  = lp->d_nsectors * lp->d_ntracks;
354 	lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
355 
356 	lp->d_bbsize = BBSIZE;
357 	lp->d_sbsize = SBSIZE;
358 	lp->d_npartitions = MAXPARTITIONS;
359 
360 	for (i = 0; i < NPARTMAP; i++) {
361 		mp = partition_map[i].mips_part;
362 		bp = partition_map[i].bsd_part;
363 
364 		lpp = &lp->d_partitions[bp];
365 		lpp->p_offset = vh->vh_part[mp].pt_offset;
366 		lpp->p_size = vh->vh_part[mp].pt_size;
367 		lpp->p_fstype = partition_map[i].bsd_type;
368 		if (lpp->p_fstype == FS_BSDFFS) {
369 			lpp->p_fsize = 1024;
370 			lpp->p_frag = 8;
371 			lpp->p_cpg = 16;
372 		}
373 	}
374 #if DIAGNOSTIC
375 	printf("Warning: using MIPS disk label\n");
376 #endif
377 	return NULL;
378 }
379 
380 /*
381  * Convert a NetBSD disk label into a RISC/os disk label.
382  *
383  * Returns NULL on success, otherwise an error string
384  */
385 static int
386 disklabel_bsd_to_mips(lp, vh)
387 	struct disklabel *lp;
388 	struct mips_volheader *vh;
389 {
390 	int  i, bp, mp;
391 	struct partition *lpp;
392 
393 	if (vh->vh_magic != MIPS_VHMAGIC || mipsvh_cksum(vh) != 0) {
394 #if DIAGNOSTIC
395 		printf("Warning: writing MIPS compatible label\n");
396 #endif
397 		memset((void *)vh, 0, sizeof *vh);
398 		vh->vh_magic = MIPS_VHMAGIC;
399 		vh->vh_root = 0;	/* a*/
400 		vh->vh_swap = 1;	/* b*/
401 	}
402 	strcpy(vh->bootfile, "/netbsd");
403 	vh->vh_dp.dp_skew = lp->d_trackskew;
404 	vh->vh_dp.dp_gap1 = 1; /* XXX */
405 	vh->vh_dp.dp_gap2 = 1; /* XXX */
406 	vh->vh_dp.dp_cyls = lp->d_ncylinders;
407 	vh->vh_dp.dp_shd0 = 0;
408 	vh->vh_dp.dp_trks0 = lp->d_ntracks;
409 	vh->vh_dp.dp_secs = lp->d_nsectors;
410 	vh->vh_dp.dp_secbytes = lp->d_secsize;
411 	vh->vh_dp.dp_interleave = lp->d_interleave;
412 	vh->vh_dp.dp_nretries = 22;
413 
414 	for (i = 0; i < NPARTMAP; i++) {
415 		mp = partition_map[i].mips_part;
416 		bp = partition_map[i].bsd_part;
417 
418 		lpp = &lp->d_partitions[bp];
419 		vh->vh_part[mp].pt_offset = lpp->p_offset;
420 		vh->vh_part[mp].pt_size = lpp->p_size;
421 		vh->vh_part[mp].pt_fstype = partition_map[i].mips_type;
422 	}
423 	/*
424 	 * Create a fake partition for bootstrap code (or SASH)
425 	 */
426 	vh->vh_part[8].pt_offset = 0;
427 	vh->vh_part[8].pt_size = vh->vh_part[vh->vh_root].pt_offset +
428 		BBSIZE / vh->vh_dp.dp_secbytes;
429 	vh->vh_part[8].pt_fstype = MIPS_FS_VOLHDR;
430 
431 	vh->vh_cksum = 0;
432 	vh->vh_cksum = -mipsvh_cksum(vh);
433 	return 0;
434 }
435 
436 /*
437  * Compute checksum for MIPS disk volume header
438  *
439  * Mips volume header checksum is the 32bit 2's complement sum
440  * of the entire volume header structure
441  */
442 int
443 mipsvh_cksum(vhp)
444 	struct mips_volheader *vhp;
445 {
446 	int i, *ptr;
447 	int cksum = 0;
448 
449 	ptr = (int *)vhp;
450 	i = sizeof(*vhp) / sizeof(*ptr);
451 	while (i--)
452 		cksum += *ptr++;
453 	return cksum;
454 }
455