xref: /openbsd-src/sys/arch/sparc64/sparc64/disksubr.c (revision 6db064f3b5beb8c471ae4f814748d5fb5c802dad)
1 /*	$OpenBSD: disksubr.c,v 1.74 2022/10/11 23:39:08 krw Exp $	*/
2 /*	$NetBSD: disksubr.c,v 1.13 2000/12/17 22:39:18 pk Exp $ */
3 
4 /*
5  * Copyright (c) 1994, 1995 Gordon W. Ross
6  * Copyright (c) 1994 Theo de Raadt
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. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/buf.h>
35 #include <sys/disklabel.h>
36 #include <sys/disk.h>
37 
38 #include <dev/sun/disklabel.h>
39 
40 #include "cd.h"
41 
42 static	int disklabel_sun_to_bsd(dev_t dev, struct sun_disklabel *,
43     struct disklabel *);
44 static	int disklabel_bsd_to_sun(struct disklabel *, struct sun_disklabel *);
45 static __inline u_int sun_extended_sum(struct sun_disklabel *, void *);
46 
47 #if NCD > 0
48 extern void cdstrategy(struct buf *);
49 #endif
50 
51 /*
52  * Attempt to read a disk label from a device
53  * using the indicated strategy routine.
54  * The label must be partly set up before this:
55  * secpercyl, secsize and anything required for a block i/o read
56  * operation in the driver's strategy/start routines
57  * must be filled in before calling us.
58  */
59 int
readdisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,int spoofonly)60 readdisklabel(dev_t dev, void (*strat)(struct buf *),
61     struct disklabel *lp, int spoofonly)
62 {
63 	struct sun_disklabel *slp;
64 	struct buf *bp = NULL;
65 	int error;
66 
67 	if ((error = initdisklabel(lp)))
68 		goto done;
69 	lp->d_flags |= D_VENDOR;
70 
71 	/*
72 	 * On sparc64 we check for a CD label first, because our
73 	 * CD install media contains both sparc & sparc64 labels.
74 	 * We want the sparc64 machine to find the "CD label", not
75 	 * the SunOS label, for loading its kernel.
76 	 */
77 #if NCD > 0
78 	if (strat == cdstrategy) {
79 #if defined(CD9660)
80 		if (iso_disklabelspoof(dev, strat, lp) == 0)
81 			goto done;
82 #endif
83 #if defined(UDF)
84 		if (udf_disklabelspoof(dev, strat, lp) == 0)
85 			goto done;
86 #endif
87 	}
88 #endif /* NCD > 0 */
89 
90 	/* get buffer and initialize it */
91 	bp = geteblk(lp->d_secsize);
92 	bp->b_dev = dev;
93 
94 	if (spoofonly)
95 		goto doslabel;
96 
97 	error = readdisksector(bp, strat, lp, DL_BLKTOSEC(lp, LABELSECTOR));
98 	if (error)
99 		goto done;
100 
101 	slp = (struct sun_disklabel *)bp->b_data;
102 	if (slp->sl_magic == SUN_DKMAGIC) {
103 		error = disklabel_sun_to_bsd(bp->b_dev, slp, lp);
104 		goto done;
105 	}
106 
107 	error = checkdisklabel(bp->b_dev, bp->b_data + LABELOFFSET, lp, 0,
108 	    DL_GETDSIZE(lp));
109 	if (error == 0)
110 		goto done;
111 
112 doslabel:
113 	error = readdoslabel(bp, strat, lp, NULL, spoofonly);
114 	if (error == 0)
115 		goto done;
116 
117 	/* A CD9660/UDF label may be on a non-CD drive, so recheck */
118 #if defined(CD9660)
119 	error = iso_disklabelspoof(dev, strat, lp);
120 	if (error == 0)
121 		goto done;
122 #endif
123 #if defined(UDF)
124 	error = udf_disklabelspoof(dev, strat, lp);
125 	if (error == 0)
126 		goto done;
127 #endif
128 
129 done:
130 	if (bp) {
131 		bp->b_flags |= B_INVAL;
132 		brelse(bp);
133 	}
134 	disk_change = 1;
135 	return (error);
136 }
137 
138 /*
139  * Write disk label back to device after modification.
140  */
141 int
writedisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp)142 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp)
143 {
144 	struct buf *bp = NULL;
145 	int error;
146 
147 	/* get buffer and initialize it */
148 	bp = geteblk(lp->d_secsize);
149 	bp->b_dev = dev;
150 
151 	error = disklabel_bsd_to_sun(lp, (struct sun_disklabel *)bp->b_data);
152 	if (error)
153 		goto done;
154 
155 	/* Write out the updated label. */
156 	bp->b_blkno = LABELSECTOR;
157 	bp->b_bcount = lp->d_secsize;
158 	CLR(bp->b_flags, B_READ | B_WRITE | B_DONE);
159 	SET(bp->b_flags, B_BUSY | B_WRITE | B_RAW);
160 	(*strat)(bp);
161 	error = biowait(bp);
162 
163 done:
164 	if (bp) {
165 		bp->b_flags |= B_INVAL;
166 		brelse(bp);
167 	}
168 	disk_change = 1;
169 	return (error);
170 }
171 
172 /************************************************************************
173  *
174  * The rest of this was taken from arch/sparc/scsi/sun_disklabel.c
175  * and then substantially rewritten by Gordon W. Ross
176  *
177  ************************************************************************/
178 
179 /* What partition types to assume for Sun disklabels: */
180 static u_char
181 sun_fstypes[16] = {
182 	FS_BSDFFS,	/* a */
183 	FS_SWAP,	/* b */
184 	FS_UNUSED,	/* c - whole disk */
185 	FS_BSDFFS,	/* d */
186 	FS_BSDFFS,	/* e */
187 	FS_BSDFFS,	/* f */
188 	FS_BSDFFS,	/* g */
189 	FS_BSDFFS,	/* h */
190 	FS_BSDFFS,	/* i */
191 	FS_BSDFFS,	/* j */
192 	FS_BSDFFS,	/* k */
193 	FS_BSDFFS,	/* l */
194 	FS_BSDFFS,	/* m */
195 	FS_BSDFFS,	/* n */
196 	FS_BSDFFS,	/* o */
197 	FS_BSDFFS	/* p */
198 };
199 
200 /*
201  * Given a struct sun_disklabel, assume it has an extended partition
202  * table and compute the correct value for sl_xpsum.
203  */
204 static __inline u_int
sun_extended_sum(struct sun_disklabel * sl,void * end)205 sun_extended_sum(struct sun_disklabel *sl, void *end)
206 {
207 	u_int sum, *xp, *ep;
208 
209 	xp = (u_int *)&sl->sl_xpmag;
210 	ep = (u_int *)end;
211 
212 	sum = 0;
213 	for (; xp < ep; xp++)
214 		sum += *xp;
215 	return (sum);
216 }
217 
218 /*
219  * Given a SunOS disk label, set lp to a BSD disk label.
220  * The BSD label is cleared out before this is called.
221  */
222 static int
disklabel_sun_to_bsd(dev_t dev,struct sun_disklabel * sl,struct disklabel * lp)223 disklabel_sun_to_bsd(dev_t dev, struct sun_disklabel *sl, struct disklabel *lp)
224 {
225 	struct sun_preamble *preamble = (struct sun_preamble *)sl;
226 	struct sun_partinfo *ppp;
227 	struct sun_dkpart *spp;
228 	struct partition *npp;
229 	u_short cksum = 0, *sp1, *sp2;
230 	int i, secpercyl;
231 
232 	/* Verify the XOR check. */
233 	sp1 = (u_short *)sl;
234 	sp2 = (u_short *)(sl + 1);
235 	while (sp1 < sp2)
236 		cksum ^= *sp1++;
237 	if (cksum != 0)
238 		return (EINVAL);	/* SunOS disk label, bad checksum */
239 
240 	/* Format conversion. */
241 	lp->d_magic = DISKMAGIC;
242 	lp->d_magic2 = DISKMAGIC;
243 	lp->d_flags = D_VENDOR;
244 	memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname));
245 
246 	lp->d_secsize = DEV_BSIZE;
247 	lp->d_nsectors = sl->sl_nsectors;
248 	lp->d_ntracks = sl->sl_ntracks;
249 	lp->d_ncylinders = sl->sl_ncylinders;
250 
251 	secpercyl = sl->sl_nsectors * sl->sl_ntracks;
252 	lp->d_secpercyl = secpercyl;
253 	/* If unset or initialized as full disk, permit refinement */
254 	if (DL_GETDSIZE(lp) == 0 || DL_GETDSIZE(lp) == MAXDISKSIZE)
255 		DL_SETDSIZE(lp, (u_int64_t)secpercyl * sl->sl_ncylinders);
256 	lp->d_version = 1;
257 
258 	memcpy(&lp->d_uid, &sl->sl_uid, sizeof(lp->d_uid));
259 
260 	lp->d_acylinders = sl->sl_acylinders;
261 
262 	lp->d_npartitions = MAXPARTITIONS;
263 
264 	for (i = 0; i < 8; i++) {
265 		spp = &sl->sl_part[i];
266 		npp = &lp->d_partitions[i];
267 		DL_SETPOFFSET(npp, spp->sdkp_cyloffset * secpercyl);
268 		DL_SETPSIZE(npp, spp->sdkp_nsectors);
269 		if (DL_GETPSIZE(npp) == 0) {
270 			npp->p_fstype = FS_UNUSED;
271 		} else {
272 			npp->p_fstype = sun_fstypes[i];
273 			if (npp->p_fstype == FS_BSDFFS) {
274 				/*
275 				 * The sun label does not store the FFS fields,
276 				 * so just set them with default values here.
277 				 */
278 				npp->p_fragblock =
279 				    DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
280 				npp->p_cpg = 16;
281 			}
282 		}
283 	}
284 
285 	/* Clear "extended" partition info, tentatively */
286 	for (i = 0; i < SUNXPART; i++) {
287 		npp = &lp->d_partitions[i+8];
288 		DL_SETPOFFSET(npp, 0);
289 		DL_SETPSIZE(npp, 0);
290 		npp->p_fstype = FS_UNUSED;
291 	}
292 
293 	/* Check to see if there's an "extended" partition table
294 	 * SL_XPMAG partitions had checksums up to just before the
295 	 * (new) sl_types variable, while SL_XPMAGTYP partitions have
296 	 * checksums up to the just before the (new) sl_xxx1 variable.
297 	 * Also, disklabels created prior to the addition of sl_uid will
298 	 * have a checksum to just before the sl_uid variable.
299 	 */
300 	if ((sl->sl_xpmag == SL_XPMAG &&
301 	    sun_extended_sum(sl, &sl->sl_types) == sl->sl_xpsum) ||
302 	    (sl->sl_xpmag == SL_XPMAGTYP &&
303 	    sun_extended_sum(sl, &sl->sl_uid) == sl->sl_xpsum) ||
304 	    (sl->sl_xpmag == SL_XPMAGTYP &&
305 	    sun_extended_sum(sl, &sl->sl_xxx1) == sl->sl_xpsum)) {
306 		/*
307 		 * There is.  Copy over the "extended" partitions.
308 		 * This code parallels the loop for partitions a-h.
309 		 */
310 		for (i = 0; i < SUNXPART; i++) {
311 			spp = &sl->sl_xpart[i];
312 			npp = &lp->d_partitions[i+8];
313 			DL_SETPOFFSET(npp, spp->sdkp_cyloffset * secpercyl);
314 			DL_SETPSIZE(npp, spp->sdkp_nsectors);
315 			if (DL_GETPSIZE(npp) == 0) {
316 				npp->p_fstype = FS_UNUSED;
317 				continue;
318 			}
319 			npp->p_fstype = sun_fstypes[i+8];
320 			if (npp->p_fstype == FS_BSDFFS) {
321 				npp->p_fragblock =
322 				    DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
323 				npp->p_cpg = 16;
324 			}
325 		}
326 		if (sl->sl_xpmag == SL_XPMAGTYP) {
327 			for (i = 0; i < MAXPARTITIONS; i++) {
328 				npp = &lp->d_partitions[i];
329 				npp->p_fstype = sl->sl_types[i];
330 				npp->p_fragblock = sl->sl_fragblock[i];
331 				npp->p_cpg = sl->sl_cpg[i];
332 			}
333 		}
334 	} else if (preamble->sl_nparts <= 8) {
335 		/*
336 		 * A more traditional Sun label.  Recognise certain filesystem
337 		 * types from it, if they are available.
338 		 */
339 		i = preamble->sl_nparts;
340 		if (i == 0)
341 			i = 8;
342 
343 		npp = &lp->d_partitions[i-1];
344 		ppp = &preamble->sl_part[i-1];
345 		for (; i > 0; i--, npp--, ppp--) {
346 			if (npp->p_size == 0)
347 				continue;
348 			if ((ppp->spi_tag == 0) && (ppp->spi_flag == 0))
349 				continue;
350 
351 			switch (ppp->spi_tag) {
352 			case SPTAG_SUNOS_ROOT:
353 			case SPTAG_SUNOS_USR:
354 			case SPTAG_SUNOS_VAR:
355 			case SPTAG_SUNOS_HOME:
356 				npp->p_fstype = FS_BSDFFS;
357 				npp->p_fragblock =
358 				    DISKLABELV1_FFS_FRAGBLOCK(2048, 8);
359 				npp->p_cpg = 16;
360 				break;
361 			case SPTAG_LINUX_EXT2:
362 				npp->p_fstype = FS_EXT2FS;
363 				break;
364 			default:
365 				/* FS_SWAP for _SUNOS_SWAP and _LINUX_SWAP? */
366 				npp->p_fstype = FS_UNUSED;
367 				break;
368 			}
369 		}
370 	}
371 
372 	lp->d_checksum = 0;
373 	lp->d_checksum = dkcksum(lp);
374 	return (checkdisklabel(dev, lp, lp, 0, DL_GETDSIZE(lp)));
375 }
376 
377 /*
378  * Given a BSD disk label, update the Sun disklabel
379  * pointed to by cp with the new info.  Note that the
380  * Sun disklabel may have other info we need to keep.
381  */
382 static int
disklabel_bsd_to_sun(struct disklabel * lp,struct sun_disklabel * sl)383 disklabel_bsd_to_sun(struct disklabel *lp, struct sun_disklabel *sl)
384 {
385 	struct partition *npp;
386 	struct sun_dkpart *spp;
387 	int i, secpercyl;
388 	u_short cksum, *sp1, *sp2;
389 
390 	/* Enforce preconditions */
391 	if (lp->d_secsize != DEV_BSIZE || lp->d_nsectors == 0 ||
392 	    lp->d_ntracks == 0)
393 		return (EINVAL);
394 
395 	/* Format conversion. */
396 	bzero(sl, sizeof(*sl));
397 	memcpy(sl->sl_text, lp->d_packname, sizeof(lp->d_packname));
398 	sl->sl_pcylinders = lp->d_ncylinders + lp->d_acylinders; /* XXX */
399 	sl->sl_ncylinders = lp->d_ncylinders;
400 	sl->sl_acylinders = lp->d_acylinders;
401 	sl->sl_ntracks = lp->d_ntracks;
402 	sl->sl_nsectors = lp->d_nsectors;
403 
404 	memcpy(&sl->sl_uid, &lp->d_uid, sizeof(lp->d_uid));
405 
406 	secpercyl = sl->sl_nsectors * sl->sl_ntracks;
407 	for (i = 0; i < 8; i++) {
408 		spp = &sl->sl_part[i];
409 		npp = &lp->d_partitions[i];
410 		spp->sdkp_cyloffset = 0;
411 		spp->sdkp_nsectors = 0;
412 		if (DL_GETPSIZE(npp)) {
413 			if (DL_GETPOFFSET(npp) % secpercyl)
414 				return (EINVAL);
415 			spp->sdkp_cyloffset = DL_GETPOFFSET(npp) / secpercyl;
416 			spp->sdkp_nsectors = DL_GETPSIZE(npp);
417 		}
418 	}
419 	sl->sl_magic = SUN_DKMAGIC;
420 
421 	for (i = 0; i < SUNXPART; i++) {
422 		spp = &sl->sl_xpart[i];
423 		npp = &lp->d_partitions[i+8];
424 		spp->sdkp_cyloffset = 0;
425 		spp->sdkp_nsectors = 0;
426 		if (DL_GETPSIZE(npp)) {
427 			if (DL_GETPOFFSET(npp) % secpercyl)
428 				return (EINVAL);
429 			spp->sdkp_cyloffset = DL_GETPOFFSET(npp) / secpercyl;
430 			spp->sdkp_nsectors = DL_GETPSIZE(npp);
431 		}
432 	}
433 	for (i = 0; i < MAXPARTITIONS; i++) {
434 		npp = &lp->d_partitions[i];
435 		sl->sl_types[i] = npp->p_fstype;
436 		sl->sl_fragblock[i] = npp->p_fragblock;
437 		sl->sl_cpg[i] = npp->p_cpg;
438 	}
439 	sl->sl_xpmag = SL_XPMAGTYP;
440 	sl->sl_xpsum = sun_extended_sum(sl, &sl->sl_xxx1);
441 
442 	/* Correct the XOR check. */
443 	sp1 = (u_short *)sl;
444 	sp2 = (u_short *)(sl + 1);
445 	sl->sl_cksum = cksum = 0;
446 	while (sp1 < sp2)
447 		cksum ^= *sp1++;
448 	sl->sl_cksum = cksum;
449 
450 	return (0);
451 }
452