xref: /netbsd/sys/arch/sgimips/sgimips/disksubr.c (revision bf9ec67e)
1 /*	$NetBSD: disksubr.c,v 1.8 2002/03/13 13:12:29 simonb Exp $	*/
2 
3 /*
4  * Copyright (c) 2001 Christopher Sekiya
5  * Copyright (c) 2001 Wayne Knowles
6  * Copyright (c) 2000 Soren S. Jorvang
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *          This product includes software developed for the
20  *          NetBSD Project.  See http://www.netbsd.org/ for
21  *          information about NetBSD.
22  * 4. The name of the author may not be used to endorse or promote products
23  *    derived from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/buf.h>
40 #include <sys/disklabel.h>
41 #include <sys/disk.h>
42 #include <ufs/ufs/dinode.h>
43 #include <ufs/ffs/fs.h>
44 
45 #include <machine/disklabel.h>
46 
47 static int disklabel_bsd_to_sgimips(struct disklabel *lp, struct sgilabel *vh);
48 static char *disklabel_sgimips_to_bsd(struct sgilabel *vh, struct disklabel *lp);
49 
50 int mipsvh_cksum(struct sgilabel *vhp);
51 
52 #define LABELSIZE(lp)	((char *)&lp->d_partitions[lp->d_npartitions] - \
53 			 (char *)lp)
54 
55 
56 /*
57  * Attempt to read a disk label from a device using the indicated
58  * strategy routine. The label must be partly set up before this:
59  * secpercyl, secsize and anything required for a block i/o read
60  * operation in the driver's strategy/start routines must be
61  * filled in before calling us.
62  *
63  * Return buffer for use in signalling errors if requested.
64  *
65  * Returns null on success and an error string on failure.
66  */
67 
68 char *
69 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp)
70 {
71 	struct buf *bp;
72 	struct disklabel *dlp;
73 	struct sgilabel *slp;
74 	int err;
75 
76 	/* Minimal requirements for archetypal disk label. */
77 	if (lp->d_secsize == 0)
78 		lp->d_secsize = DEV_BSIZE;
79 	if (lp->d_secperunit == 0)
80 		lp->d_secperunit = 0x1fffffff;
81 
82 	/* Obtain buffer to probe drive with. */
83 	bp = geteblk((int)lp->d_secsize);
84 
85 	bp->b_dev = dev;
86 	bp->b_blkno = LABELSECTOR;
87 	bp->b_bcount = lp->d_secsize;
88 	bp->b_flags |= B_READ;
89 	bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
90 	(*strat)(bp);
91 	err = biowait(bp);
92 	brelse(bp);
93 
94 	if (err)
95 		return "error reading disklabel";
96 
97 	/* Check for NetBSD label in second sector */
98 	dlp = (struct disklabel *)(bp->b_un.b_addr + LABELOFFSET);
99 	if (dlp->d_magic == DISKMAGIC)
100 		if (!dkcksum(dlp)) {
101 			memcpy(lp, dlp, LABELSIZE(dlp));
102 			return NULL;	/* NetBSD label found */
103 	}
104 
105 	bp = geteblk((int)lp->d_secsize);
106 	bp->b_dev = dev;
107 	bp->b_blkno = 0;
108 	bp->b_bcount = lp->d_secsize;
109 	bp->b_flags |= B_READ;
110 	bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
111 	(*strat)(bp);
112 	err = biowait(bp);
113 	brelse(bp);
114 
115 	if (err)
116 		return "error reading volume header";
117 
118 	/* Check for a SGI label. */
119 	slp = (struct sgilabel *)bp->b_un.b_addr;
120 	if (be32toh(slp->magic) != SGILABEL_MAGIC)
121 		return "no disk label";
122 
123 	return disklabel_sgimips_to_bsd(slp, lp);
124 }
125 
126 int
127 setdisklabel(struct disklabel *olp, struct disklabel *nlp, unsigned long openmask, struct cpu_disklabel *clp)
128 {
129 	register int i;
130 	register struct partition *opp, *npp;
131 
132 	if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
133 	    dkcksum(nlp) != 0)
134 		return (EINVAL);
135 	while ((i = ffs(openmask)) != 0) {
136 		i--;
137 		openmask &= ~(1 << i);
138 		if (nlp->d_npartitions <= i)
139 			return (EBUSY);
140 		opp = &olp->d_partitions[i];
141 		npp = &nlp->d_partitions[i];
142 		if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
143 			return (EBUSY);
144 		/*
145 		 * Copy internally-set partition information
146 		 * if new label doesn't include it.		XXX
147 		 */
148 		if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) {
149 			npp->p_fstype = opp->p_fstype;
150 			npp->p_fsize = opp->p_fsize;
151 			npp->p_frag = opp->p_frag;
152 			npp->p_cpg = opp->p_cpg;
153 		}
154 	}
155 	nlp->d_checksum = 0;
156 	nlp->d_checksum = dkcksum(nlp);
157 	*olp = *nlp;
158 	return (0);
159 }
160 
161 #define dkpart(dev)		(minor(dev) & 07)
162 #define dkminor(unit, part)	(((unit) << 3) | (part))
163 
164 int
165 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp)
166 {
167 	struct buf *bp;
168 	int labelpart;
169 	int error;
170 
171 	labelpart = dkpart(dev);
172 	if (lp->d_partitions[labelpart].p_offset != 0) {
173 		if (lp->d_partitions[0].p_offset != 0)
174 			return (EXDEV);			/* not quite right */
175 		labelpart = 0;
176 	}
177 
178 	/* Read sgimips volume header before merging NetBSD partition info */
179 	bp = geteblk((int)lp->d_secsize);
180 
181 	bp->b_dev = dev;
182 	bp->b_blkno = 0;
183 	bp->b_bcount = lp->d_secsize;
184 	bp->b_flags |= B_READ;
185 	bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
186 	(*strat)(bp);
187 
188 	if((error = biowait(bp)) != 0)
189 		goto ioerror;
190 
191 	if ((error = disklabel_bsd_to_sgimips(lp, (void *)bp->b_data)) != 0)
192 		goto ioerror;
193 
194 	/* Write sgimips label to first sector */
195 	bp->b_flags &= ~(B_READ|B_DONE);
196 	bp->b_flags |= B_WRITE;
197 	(*strat)(bp);
198 	if ((error = biowait(bp)) != 0)
199 		goto ioerror;
200 
201 	/* Write NetBSD disk label to second sector */
202 	memset(bp->b_data, 0, lp->d_secsize);
203 	memcpy(bp->b_data, lp, sizeof(*lp));
204 	bp->b_blkno = LABELSECTOR;
205 	bp->b_bcount = lp->d_secsize;
206 	bp->b_cylinder = bp->b_blkno / lp->d_secpercyl;
207 	bp->b_flags &= ~(B_READ | B_DONE);
208 	bp->b_flags |= B_WRITE;
209 	(*strat)(bp);
210 	error = biowait(bp);
211 
212 ioerror:
213 	brelse(bp);
214 	return error;
215 }
216 
217 
218 /*
219  * Determine the size of the transfer, and make sure it is
220  * within the boundaries of the partition. Adjust transfer
221  * if needed, and signal errors or early completion.
222  */
223 int
224 bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel)
225 {
226 	struct partition *p = lp->d_partitions + DISKPART(bp->b_dev);
227 	int maxsz = p->p_size;
228 	int sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT;
229 
230 	/*
231 	 * Overwriting disk label?
232 	 * The label is always in sector LABELSECTOR.
233 	 */
234 	if (bp->b_blkno + p->p_offset <= LABELSECTOR &&
235 	    (bp->b_flags & B_READ) == 0 && wlabel == 0) {
236 		bp->b_error = EROFS;
237 		goto bad;
238 	}
239 
240 	/*
241 	 * Beyond partition?
242 	 */
243 	if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) {
244 		/* if exactly at end of disk, return an EOF */
245 		if (bp->b_blkno == maxsz) {
246 			bp->b_resid = bp->b_bcount;
247 			return(0);
248 		}
249 		/* or truncate if part of it fits */
250 		sz = maxsz - bp->b_blkno;
251 		if (sz <= 0) {
252 			bp->b_error = EINVAL;
253 			goto bad;
254 		}
255 		bp->b_bcount = sz << DEV_BSHIFT;
256 	}
257 
258 	/* calculate cylinder for disksort to order transfers with */
259 	bp->b_resid = (bp->b_blkno + p->p_offset) / lp->d_secpercyl;
260 	return(1);
261 bad:
262 	bp->b_flags |= B_ERROR;
263 	return(-1);
264 }
265 
266 struct partitionmap {
267 	int	mips_part;	/* sgimips partition number */
268 	int	mips_type;	/* sgimips partition type */
269 	int	bsd_part;	/* BSD partition number */
270 	int	bsd_type;	/* BSD partition type */
271 };
272 
273 struct partitionmap partition_map[] = {
274      /* slice	sgimips type		BSD	BSD Type */
275 	{0,	SGI_PTYPE_BSD,		0,	FS_BSDFFS},
276 	{1,	SGI_PTYPE_RAW,		1,	FS_SWAP},
277 	{2,	SGI_PTYPE_BSD,		10,	FS_BSDFFS},
278 	{3,	SGI_PTYPE_BSD,		3,	FS_BSDFFS},
279 	{4,	SGI_PTYPE_BSD,		4,	FS_BSDFFS},
280 	{5,	SGI_PTYPE_BSD,		5,	FS_BSDFFS},
281 	{6,	SGI_PTYPE_BSD,		6,	FS_BSDFFS},
282 	{7,	SGI_PTYPE_BSD,		7,	FS_BSDFFS},
283 	{8,	SGI_PTYPE_VOLHDR,	8,	FS_OTHER},
284 	{9,	SGI_PTYPE_BSD,		9,	FS_BSDFFS},
285 	{10,	SGI_PTYPE_VOLUME,	2,	FS_OTHER},
286 	{11,	SGI_PTYPE_BSD,		11,	FS_BSDFFS},
287 	{12,	SGI_PTYPE_BSD,		12,	FS_BSDFFS},
288 	{13,	SGI_PTYPE_BSD,		13,	FS_BSDFFS},
289 	{14,	SGI_PTYPE_BSD,		14,	FS_BSDFFS},
290 	{15,	SGI_PTYPE_BSD,		15,	FS_BSDFFS}
291 };
292 
293 #define NPARTMAP	(sizeof(partition_map)/sizeof(struct partitionmap))
294 
295 /*
296  * Convert a sgimips disk label into a NetBSD disk label.
297  *
298  * Returns NULL on success, otherwise an error string
299  */
300 static char *
301 disklabel_sgimips_to_bsd(struct sgilabel *vh, struct disklabel *lp)
302 {
303 	int  i, bp, mp;
304 	struct partition *lpp;
305 	if (mipsvh_cksum(vh))
306 		return ("sgimips disk label corrupted");
307 
308 	lp->d_secsize    = vh->dp.dp_secbytes;
309 	lp->d_nsectors   = vh->dp.dp_secs;
310 	lp->d_ntracks    = vh->dp.dp_trks0;
311 	lp->d_ncylinders = vh->dp.dp_cyls;
312 	lp->d_interleave = vh->dp.dp_interleave;
313 
314 
315 	lp->d_secpercyl  = lp->d_nsectors * lp->d_ntracks;
316 	lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders;
317 
318 	lp->d_bbsize = BBSIZE;
319 	lp->d_sbsize = SBSIZE;
320 	lp->d_npartitions = MAXPARTITIONS;
321 
322 	for (i = 0; i < 16; i++) {
323 		mp = partition_map[i].mips_part;
324 		bp = partition_map[i].bsd_part;
325 
326 		lpp = &lp->d_partitions[bp];
327 		lpp->p_offset = vh->partitions[mp].first;
328 		lpp->p_size = vh->partitions[mp].blocks;
329 		lpp->p_fstype = partition_map[i].bsd_type;
330 		if (lpp->p_fstype == FS_BSDFFS) {
331 			lpp->p_fsize = 1024;
332 			lpp->p_frag = 8;
333 			lpp->p_cpg = 16;
334 		}
335 	}
336 	return NULL;
337 }
338 
339 
340 /*
341  * Convert a NetBSD disk label into a sgimips disk label.
342  *
343  * Returns NULL on success, otherwise an error string
344  */
345 static int
346 disklabel_bsd_to_sgimips(struct disklabel *lp, struct sgilabel *vh)
347 {
348 	int i, bp, mp;
349 	struct partition *lpp;
350 
351 	if (vh->magic != SGILABEL_MAGIC || mipsvh_cksum(vh) != 0) {
352 		memset((void *)vh, 0, sizeof *vh);
353 		vh->magic = SGILABEL_MAGIC;
354 		vh->root = 0;        /* a*/
355 		vh->swap = 1;        /* b*/
356 	}
357 
358 	strcpy(vh->bootfile, "/netbsd");
359 	vh->dp.dp_skew = lp->d_trackskew;
360 	vh->dp.dp_gap1 = 1; /* XXX */
361 	vh->dp.dp_gap2 = 1; /* XXX */
362 	vh->dp.dp_cyls = lp->d_ncylinders;
363 	vh->dp.dp_shd0 = 0;
364 	vh->dp.dp_trks0 = lp->d_ntracks;
365 	vh->dp.dp_secs = lp->d_nsectors;
366 	vh->dp.dp_secbytes = lp->d_secsize;
367 	vh->dp.dp_interleave = lp->d_interleave;
368 	vh->dp.dp_nretries = 22;
369 
370 	for (i = 0; i < 16; i++) {
371 		mp = partition_map[i].mips_part;
372 		bp = partition_map[i].bsd_part;
373 
374 		lpp = &lp->d_partitions[bp];
375 		vh->partitions[mp].first = lpp->p_offset;
376 		vh->partitions[mp].blocks = lpp->p_size;
377 		vh->partitions[mp].type = partition_map[i].mips_type;
378 	}
379 
380 	/*
381 	 * Create a fake partition for bootstrap code (or SASH)
382 	 */
383 	vh->partitions[8].first = 0;
384 	vh->partitions[8].blocks = vh->partitions[vh->root].first +
385 		BBSIZE / vh->dp.dp_secbytes;
386 	vh->partitions[8].type = SGI_PTYPE_VOLHDR;
387 
388 	vh->checksum = 0;
389 	vh->checksum = -mipsvh_cksum(vh);
390 	return 0;
391 }
392 
393 /*
394  * Compute checksum for MIPS disk volume header
395  *
396  * Mips volume header checksum is the 32bit 2's complement sum
397  * of the entire volume header structure
398  */
399 int
400 mipsvh_cksum(struct sgilabel *vhp)
401 {
402 	int i, *ptr;
403 	int cksum = 0;
404 
405 	ptr = (int *)vhp;
406 	i = sizeof(*vhp) / sizeof(*ptr);
407 	while (i--)
408 		cksum += *ptr++;
409 	return cksum;
410 }
411 
412