xref: /netbsd-src/sys/dev/sun/disksubr.c (revision c20fcc6f26443cf1dd0d06fd92a9eb36b3c409cd)
1 /*	$NetBSD: disksubr.c,v 1.17 2020/05/03 06:31:02 jdc 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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 /*
32  * Copyright (c) 1994, 1995 Gordon W. Ross
33  * Copyright (c) 1994 Theo de Raadt
34  * All rights reserved.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
46  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
47  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
48  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
49  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
50  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
51  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
52  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
53  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
54  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55  */
56 
57 #include <sys/cdefs.h>
58 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.17 2020/05/03 06:31:02 jdc Exp $");
59 
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/buf.h>
63 #include <sys/ioccom.h>
64 #include <sys/device.h>
65 #include <sys/disklabel.h>
66 #include <sys/disk.h>
67 #include <sys/dkbad.h>
68 
69 #include <dev/sun/disklabel.h>
70 
71 #if LABELSECTOR != 0
72 #error  "Default value of LABELSECTOR no longer zero?"
73 #endif
74 
75 static	const char *disklabel_sun_to_bsd(char *, struct disklabel *);
76 static	int disklabel_bsd_to_sun(struct disklabel *, char *);
77 
78 /*
79  * Attempt to read a disk label from a device
80  * using the indicated strategy routine.
81  * The label must be partly set up before this:
82  * secpercyl, secsize and anything required for a block i/o read
83  * operation in the driver's strategy/start routines
84  * must be filled in before calling us.
85  *
86  * Return buffer for use in signalling errors if requested.
87  *
88  * Returns null on success and an error string on failure.
89  */
90 const char *
readdisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * clp)91 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp)
92 {
93 	struct buf *bp;
94 	struct disklabel *dlp;
95 	struct sun_disklabel *slp;
96 	int error;
97 
98 	/* minimal requirements for archtypal disk label */
99 	if (lp->d_secperunit == 0)
100 		lp->d_secperunit = 0x1fffffff;
101 	if (lp->d_npartitions == 0) {
102 		lp->d_npartitions = RAW_PART + 1;
103 		if (lp->d_partitions[RAW_PART].p_size == 0)
104 			lp->d_partitions[RAW_PART].p_size = 0x1fffffff;
105 		lp->d_partitions[RAW_PART].p_offset = 0;
106 	}
107 	if (lp->d_secsize == 0)
108 		return ("sector size 0");
109 
110 	/* obtain buffer to probe drive with */
111 	bp = geteblk((int)lp->d_secsize);
112 
113 	/* next, dig out disk label */
114 	bp->b_dev = dev;
115 	bp->b_blkno = LABELSECTOR;
116 	bp->b_cylinder = 0;
117 	bp->b_bcount = lp->d_secsize;
118 	bp->b_flags |= B_READ;
119 	(*strat)(bp);
120 
121 	/* if successful, locate disk label within block and validate */
122 	error = biowait(bp);
123 	if (error == 0) {
124 		/* Save the whole block in case it has info we need. */
125 		memcpy(clp->cd_block, bp->b_data, sizeof(clp->cd_block));
126 	}
127 	brelse(bp, 0);
128 	if (error)
129 		return ("disk label read error");
130 
131 	/* Check for a NetBSD disk label at LABELOFFSET */
132 	dlp = (struct disklabel *) (clp->cd_block + LABELOFFSET);
133 	if (dlp->d_magic == DISKMAGIC) {
134 		if (dkcksum(dlp))
135 			return ("NetBSD disk label corrupted");
136 		*lp = *dlp;
137 		return (NULL);
138 	}
139 
140 	/* Check for a Sun disk label (for PROM compatibility). */
141 	slp = (struct sun_disklabel *) clp->cd_block;
142 	if (slp->sl_magic == SUN_DKMAGIC)
143 		return (disklabel_sun_to_bsd(clp->cd_block, lp));
144 
145 	/*
146 	 * Check for a NetBSD disk label somewhere in LABELSECTOR
147 	 * (compat with others big-endian boxes)
148 	 */
149 	for (dlp = (struct disklabel *)clp->cd_block;
150 	    dlp <= (struct disklabel *)((char *)clp->cd_block +
151 	    DEV_BSIZE - sizeof(*dlp));
152 	    dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
153 		if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
154 			continue;
155 		}
156 		if (dlp->d_npartitions > MAXPARTITIONS || dkcksum(dlp) != 0)
157 			return("NetBSD disk label corrupted");
158 		else {
159 			*lp = *dlp;
160 			return(NULL);
161 		}
162 	}
163 
164 	memset(clp->cd_block, 0, sizeof(clp->cd_block));
165 	return NULL;
166 }
167 
168 /*
169  * Write disk label back to device after modification.
170  * Current label is already in clp->cd_block[]
171  */
172 int
writedisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * clp)173 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp)
174 {
175 	struct buf *bp;
176 	int error;
177 	struct disklabel *dlp;
178 	struct sun_disklabel *slp;
179 
180 	/*
181 	 * Embed native label in a piece of wasteland.
182 	 */
183 	if (sizeof(struct disklabel) > sizeof slp->sl_bsdlabel)
184 		return EFBIG;
185 
186 	slp = (struct sun_disklabel *)clp->cd_block;
187 	memset(slp->sl_bsdlabel, 0, sizeof(slp->sl_bsdlabel));
188 	dlp = (struct disklabel *)slp->sl_bsdlabel;
189 	*dlp = *lp;
190 
191 	/* Build a SunOS compatible label around the native label */
192 	error = disklabel_bsd_to_sun(lp, clp->cd_block);
193 	if (error)
194 		return (error);
195 
196 	/* Get a buffer and copy the new label into it. */
197 	bp = geteblk((int)lp->d_secsize);
198 	memcpy(bp->b_data, clp->cd_block, sizeof(clp->cd_block));
199 
200 	/* Write out the updated label. */
201 	bp->b_dev = dev;
202 	bp->b_blkno = LABELSECTOR;
203 	bp->b_cylinder = 0;
204 	bp->b_bcount = lp->d_secsize;
205 	bp->b_flags |= B_WRITE;
206 	(*strat)(bp);
207 	error = biowait(bp);
208 	brelse(bp, 0);
209 
210 	return (error);
211 }
212 
213 /************************************************************************
214  *
215  * The rest of this was taken from arch/sparc/scsi/sun_disklabel.c
216  * and then substantially rewritten by Gordon W. Ross
217  *
218  ************************************************************************/
219 
220 /* What partition types to assume for Sun disklabels: */
221 static u_char
222 sun_fstypes[8] = {
223 	FS_BSDFFS,	/* a */
224 	FS_SWAP,	/* b */
225 	FS_OTHER,	/* c - whole disk */
226 	FS_BSDFFS,	/* d */
227 	FS_BSDFFS,	/* e */
228 	FS_BSDFFS,	/* f */
229 	FS_BSDFFS,	/* g */
230 	FS_BSDFFS,	/* h */
231 };
232 
233 /*
234  * Given a SunOS disk label, set lp to a BSD disk label.
235  * Returns NULL on success, else an error string.
236  *
237  * The BSD label is cleared out before this is called.
238  */
239 static const char *
disklabel_sun_to_bsd(char * cp,struct disklabel * lp)240 disklabel_sun_to_bsd(char *cp, struct disklabel *lp)
241 {
242 	struct sun_disklabel *sl;
243 	struct partition *npp;
244 	struct sun_dkpart *spp;
245 	int i, secpercyl;
246 	unsigned int secpblck;
247 	u_short cksum, *sp1, *sp2;
248 
249 	sl = (struct sun_disklabel *)cp;
250 
251 	/* Verify the XOR check. */
252 	sp1 = (u_short *)sl;
253 	sp2 = (u_short *)(sl + 1);
254 	cksum = 0;
255 	while (sp1 < sp2)
256 		cksum ^= *sp1++;
257 	if (cksum != 0)
258 		return("SunOS disk label, bad checksum");
259 
260 	/* Format conversion. */
261 	lp->d_magic = DISKMAGIC;
262 	lp->d_magic2 = DISKMAGIC;
263 	memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname));
264 
265 	secpblck = lp->d_secsize / 512;
266 	if (secpblck == 0) secpblck = 1; /* can't happen */
267 	lp->d_secsize = 512;
268 	lp->d_nsectors   = sl->sl_nsectors;
269 	lp->d_ntracks    = sl->sl_ntracks;
270 	lp->d_ncylinders = sl->sl_ncylinders;
271 
272 	secpercyl = sl->sl_nsectors * sl->sl_ntracks;
273 	lp->d_secpercyl  = secpercyl;
274 	lp->d_secperunit = secpercyl * sl->sl_ncylinders;
275 
276 	lp->d_sparespercyl = sl->sl_sparespercyl;
277 	lp->d_acylinders   = sl->sl_acylinders;
278 	lp->d_rpm          = sl->sl_rpm;
279 	lp->d_interleave   = sl->sl_interleave;
280 
281 	lp->d_npartitions = 8;
282 	/* These are as defined in <ufs/ffs/fs.h> */
283 	lp->d_bbsize = 8192;	/* XXX */
284 	lp->d_sbsize = 8192;	/* XXX */
285 
286 	for (i = 0; i < 8; i++) {
287 		spp = &sl->sl_part[i];
288 		npp = &lp->d_partitions[i];
289 
290 		if (npp->p_fstype == FS_ISO9660
291 		    && spp->sdkp_cyloffset * secpercyl == npp->p_offset*secpblck
292 		    && spp->sdkp_nsectors <= npp->p_size*secpblck
293 		    && npp->p_size > 0 && spp->sdkp_nsectors > 0) {
294 			/*
295 			 * This happens for example on sunlabel'd hybrid
296 			 * (ffs + ISO9660) CDs, like our install CDs.
297 			 * The cd driver has initialized a valid ISO9660
298 			 * partition (including session parameters), so
299 			 * we better not overwrite it.
300 			 */
301 			npp->p_offset *= secpblck;
302 			npp->p_size = spp->sdkp_nsectors;
303 			npp->p_cdsession *= secpblck;
304 			continue;
305 		}
306 		npp->p_offset = spp->sdkp_cyloffset * secpercyl;
307 		npp->p_size = spp->sdkp_nsectors;
308 		if (npp->p_size == 0) {
309 			npp->p_fstype = FS_UNUSED;
310 		} else {
311 			npp->p_fstype = sun_fstypes[i];
312 			if (npp->p_fstype == FS_BSDFFS) {
313 				/*
314 				 * The sun label does not store the FFS fields,
315 				 * so just set them with default values here.
316 				 */
317 				npp->p_fsize = 1024;
318 				npp->p_frag = 8;
319 				npp->p_cpg = 16;
320 			}
321 		}
322 	}
323 
324 	lp->d_checksum = 0;
325 	lp->d_checksum = dkcksum(lp);
326 	return (NULL);
327 }
328 
329 /*
330  * Given a BSD disk label, update the Sun disklabel
331  * pointed to by cp with the new info.  Note that the
332  * Sun disklabel may have other info we need to keep.
333  * Returns zero or error code.
334  */
335 static int
disklabel_bsd_to_sun(struct disklabel * lp,char * cp)336 disklabel_bsd_to_sun(struct disklabel *lp, char *cp)
337 {
338 	struct sun_disklabel *sl;
339 	struct partition *npp;
340 	struct sun_dkpart *spp;
341 	int i, secpercyl;
342 	u_short cksum, *sp1, *sp2;
343 
344 	if (lp->d_secsize != 512)
345 		return (EINVAL);
346 
347 	sl = (struct sun_disklabel *)cp;
348 
349 	/*
350 	 * Format conversion.
351 	 */
352 	memcpy(sl->sl_text, lp->d_packname, sizeof(lp->d_packname));
353 	sl->sl_rpm = lp->d_rpm;
354 	sl->sl_pcylinders   = lp->d_ncylinders + lp->d_acylinders; /* XXX */
355 	sl->sl_sparespercyl = lp->d_sparespercyl;
356 	sl->sl_interleave   = lp->d_interleave;
357 	sl->sl_ncylinders   = lp->d_ncylinders;
358 	sl->sl_acylinders   = lp->d_acylinders;
359 	sl->sl_ntracks      = lp->d_ntracks;
360 	sl->sl_nsectors     = lp->d_nsectors;
361 
362 	secpercyl = sl->sl_nsectors * sl->sl_ntracks;
363 	for (i = 0; i < 8; i++) {
364 		spp = &sl->sl_part[i];
365 		npp = &lp->d_partitions[i];
366 
367 		/*
368 		 * SunOS partitions must start on a cylinder boundary.
369 		 * Note this restriction is forced upon NetBSD/sparc
370 		 * labels too, since we want to keep both labels
371 		 * synchronised.
372 		 */
373 		if (npp->p_offset % secpercyl)
374 			return (EINVAL);
375 		spp->sdkp_cyloffset = npp->p_offset / secpercyl;
376 		spp->sdkp_nsectors = npp->p_size;
377 	}
378 	sl->sl_magic = SUN_DKMAGIC;
379 
380 	/* Compute the XOR check. */
381 	sp1 = (u_short *)sl;
382 	sp2 = (u_short *)(sl + 1);
383 	sl->sl_cksum = cksum = 0;
384 	while (sp1 < sp2)
385 		cksum ^= *sp1++;
386 	sl->sl_cksum = cksum;
387 
388 	return (0);
389 }
390 
391 /*
392  * Search the bad sector table looking for the specified sector.
393  * Return index if found.
394  * Return -1 if not found.
395  */
396 int
isbad(struct dkbad * bt,int cyl,int trk,int sec)397 isbad(struct dkbad *bt, int cyl, int trk, int sec)
398 {
399 	int i;
400 	long blk, bblk;
401 
402 	blk = ((long)cyl << 16) + (trk << 8) + sec;
403 	for (i = 0; i < 126; i++) {
404 		bblk = ((long)bt->bt_bad[i].bt_cyl << 16) +
405 			bt->bt_bad[i].bt_trksec;
406 		if (blk == bblk)
407 			return (i);
408 		if (blk < bblk || bblk < 0)
409 			break;
410 	}
411 	return (-1);
412 }
413