disksubr.c revision 1.11 1 /* $NetBSD: disksubr.c,v 1.11 2009/03/14 21:04:23 dsl 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.11 2009/03/14 21:04:23 dsl 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 *
91 readdisklabel(dev, strat, lp, clp)
92 dev_t dev;
93 void (*strat)(struct buf *);
94 struct disklabel *lp;
95 struct cpu_disklabel *clp;
96 {
97 struct buf *bp;
98 struct disklabel *dlp;
99 struct sun_disklabel *slp;
100 int error;
101
102 /* minimal requirements for archtypal disk label */
103 if (lp->d_secperunit == 0)
104 lp->d_secperunit = 0x1fffffff;
105 if (lp->d_npartitions == 0) {
106 lp->d_npartitions = RAW_PART + 1;
107 if (lp->d_partitions[RAW_PART].p_size == 0)
108 lp->d_partitions[RAW_PART].p_size = 0x1fffffff;
109 lp->d_partitions[RAW_PART].p_offset = 0;
110 }
111
112 /* obtain buffer to probe drive with */
113 bp = geteblk((int)lp->d_secsize);
114
115 /* next, dig out disk label */
116 bp->b_dev = dev;
117 bp->b_blkno = LABELSECTOR;
118 bp->b_cylinder = 0;
119 bp->b_bcount = lp->d_secsize;
120 bp->b_flags |= B_READ;
121 (*strat)(bp);
122
123 /* if successful, locate disk label within block and validate */
124 error = biowait(bp);
125 if (error == 0) {
126 /* Save the whole block in case it has info we need. */
127 memcpy(clp->cd_block, bp->b_data, sizeof(clp->cd_block));
128 }
129 brelse(bp, 0);
130 if (error)
131 return ("disk label read error");
132
133 /* Check for a NetBSD disk label at LABELOFFSET */
134 dlp = (struct disklabel *) (clp->cd_block + LABELOFFSET);
135 if (dlp->d_magic == DISKMAGIC) {
136 if (dkcksum(dlp))
137 return ("NetBSD disk label corrupted");
138 *lp = *dlp;
139 return (NULL);
140 }
141
142 /* Check for a Sun disk label (for PROM compatibility). */
143 slp = (struct sun_disklabel *) clp->cd_block;
144 if (slp->sl_magic == SUN_DKMAGIC)
145 return (disklabel_sun_to_bsd(clp->cd_block, lp));
146
147 /*
148 * Check for a NetBSD disk label somewhere in LABELSECTOR
149 * (compat with others big-endian boxes)
150 */
151 for (dlp = (struct disklabel *)clp->cd_block;
152 dlp <= (struct disklabel *)((char *)clp->cd_block +
153 DEV_BSIZE - sizeof(*dlp));
154 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
155 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
156 continue;
157 }
158 if (dlp->d_npartitions > MAXPARTITIONS || dkcksum(dlp) != 0)
159 return("NetBSD disk label corrupted");
160 else {
161 *lp = *dlp;
162 return(NULL);
163 }
164 }
165
166 memset(clp->cd_block, 0, sizeof(clp->cd_block));
167 return ("no disk label");
168 }
169
170 /*
171 * Check new disk label for sensibility
172 * before setting it.
173 */
174 int
175 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, struct cpu_disklabel *clp)
176 {
177 int i;
178 struct partition *opp, *npp;
179
180 /* sanity clause */
181 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 ||
182 (nlp->d_secsize % DEV_BSIZE) != 0)
183 return(EINVAL);
184
185 /* special case to allow disklabel to be invalidated */
186 if (nlp->d_magic == 0xffffffff) {
187 *olp = *nlp;
188 return (0);
189 }
190
191 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
192 dkcksum(nlp) != 0)
193 return (EINVAL);
194
195 while ((i = ffs(openmask)) != 0) {
196 i--;
197 openmask &= ~(1 << i);
198 if (nlp->d_npartitions <= i)
199 return (EBUSY);
200 opp = &olp->d_partitions[i];
201 npp = &nlp->d_partitions[i];
202 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size)
203 return (EBUSY);
204 }
205
206 *olp = *nlp;
207 return (0);
208 }
209
210 /*
211 * Write disk label back to device after modification.
212 * Current label is already in clp->cd_block[]
213 */
214 int
215 writedisklabel(dev, strat, lp, clp)
216 dev_t dev;
217 void (*strat)(struct buf *);
218 struct disklabel *lp;
219 struct cpu_disklabel *clp;
220 {
221 struct buf *bp;
222 int error;
223 struct disklabel *dlp;
224 struct sun_disklabel *slp;
225
226 /*
227 * Embed native label in a piece of wasteland.
228 */
229 if (sizeof(struct disklabel) > sizeof slp->sl_bsdlabel)
230 return EFBIG;
231
232 slp = (struct sun_disklabel *)clp->cd_block;
233 memset(slp->sl_bsdlabel, 0, sizeof(slp->sl_bsdlabel));
234 dlp = (struct disklabel *)slp->sl_bsdlabel;
235 *dlp = *lp;
236
237 /* Build a SunOS compatible label around the native label */
238 error = disklabel_bsd_to_sun(lp, clp->cd_block);
239 if (error)
240 return (error);
241
242 /* Get a buffer and copy the new label into it. */
243 bp = geteblk((int)lp->d_secsize);
244 memcpy(bp->b_data, clp->cd_block, sizeof(clp->cd_block));
245
246 /* Write out the updated label. */
247 bp->b_dev = dev;
248 bp->b_blkno = LABELSECTOR;
249 bp->b_cylinder = 0;
250 bp->b_bcount = lp->d_secsize;
251 bp->b_flags |= B_WRITE;
252 (*strat)(bp);
253 error = biowait(bp);
254 brelse(bp, 0);
255
256 return (error);
257 }
258
259 /************************************************************************
260 *
261 * The rest of this was taken from arch/sparc/scsi/sun_disklabel.c
262 * and then substantially rewritten by Gordon W. Ross
263 *
264 ************************************************************************/
265
266 /* What partition types to assume for Sun disklabels: */
267 static u_char
268 sun_fstypes[8] = {
269 FS_BSDFFS, /* a */
270 FS_SWAP, /* b */
271 FS_OTHER, /* c - whole disk */
272 FS_BSDFFS, /* d */
273 FS_BSDFFS, /* e */
274 FS_BSDFFS, /* f */
275 FS_BSDFFS, /* g */
276 FS_BSDFFS, /* h */
277 };
278
279 /*
280 * Given a SunOS disk label, set lp to a BSD disk label.
281 * Returns NULL on success, else an error string.
282 *
283 * The BSD label is cleared out before this is called.
284 */
285 static const char *
286 disklabel_sun_to_bsd(char *cp, struct disklabel *lp)
287 {
288 struct sun_disklabel *sl;
289 struct partition *npp;
290 struct sun_dkpart *spp;
291 int i, secpercyl;
292 u_short cksum, *sp1, *sp2;
293
294 sl = (struct sun_disklabel *)cp;
295
296 /* Verify the XOR check. */
297 sp1 = (u_short *)sl;
298 sp2 = (u_short *)(sl + 1);
299 cksum = 0;
300 while (sp1 < sp2)
301 cksum ^= *sp1++;
302 if (cksum != 0)
303 return("SunOS disk label, bad checksum");
304
305 /* Format conversion. */
306 lp->d_magic = DISKMAGIC;
307 lp->d_magic2 = DISKMAGIC;
308 memcpy(lp->d_packname, sl->sl_text, sizeof(lp->d_packname));
309
310 lp->d_secsize = 512;
311 lp->d_nsectors = sl->sl_nsectors;
312 lp->d_ntracks = sl->sl_ntracks;
313 lp->d_ncylinders = sl->sl_ncylinders;
314
315 secpercyl = sl->sl_nsectors * sl->sl_ntracks;
316 lp->d_secpercyl = secpercyl;
317 lp->d_secperunit = secpercyl * sl->sl_ncylinders;
318
319 lp->d_sparespercyl = sl->sl_sparespercyl;
320 lp->d_acylinders = sl->sl_acylinders;
321 lp->d_rpm = sl->sl_rpm;
322 lp->d_interleave = sl->sl_interleave;
323
324 lp->d_npartitions = 8;
325 /* These are as defined in <ufs/ffs/fs.h> */
326 lp->d_bbsize = 8192; /* XXX */
327 lp->d_sbsize = 8192; /* XXX */
328
329 for (i = 0; i < 8; i++) {
330 spp = &sl->sl_part[i];
331 npp = &lp->d_partitions[i];
332 npp->p_offset = spp->sdkp_cyloffset * secpercyl;
333 npp->p_size = spp->sdkp_nsectors;
334 if (npp->p_size == 0) {
335 npp->p_fstype = FS_UNUSED;
336 } else {
337 npp->p_fstype = sun_fstypes[i];
338 if (npp->p_fstype == FS_BSDFFS) {
339 /*
340 * The sun label does not store the FFS fields,
341 * so just set them with default values here.
342 */
343 npp->p_fsize = 1024;
344 npp->p_frag = 8;
345 npp->p_cpg = 16;
346 }
347 }
348 }
349
350 lp->d_checksum = 0;
351 lp->d_checksum = dkcksum(lp);
352 return (NULL);
353 }
354
355 /*
356 * Given a BSD disk label, update the Sun disklabel
357 * pointed to by cp with the new info. Note that the
358 * Sun disklabel may have other info we need to keep.
359 * Returns zero or error code.
360 */
361 static int
362 disklabel_bsd_to_sun(struct disklabel *lp, char *cp)
363 {
364 struct sun_disklabel *sl;
365 struct partition *npp;
366 struct sun_dkpart *spp;
367 int i, secpercyl;
368 u_short cksum, *sp1, *sp2;
369
370 if (lp->d_secsize != 512)
371 return (EINVAL);
372
373 sl = (struct sun_disklabel *)cp;
374
375 /*
376 * Format conversion.
377 */
378 memcpy(sl->sl_text, lp->d_packname, sizeof(lp->d_packname));
379 sl->sl_rpm = lp->d_rpm;
380 sl->sl_pcylinders = lp->d_ncylinders + lp->d_acylinders; /* XXX */
381 sl->sl_sparespercyl = lp->d_sparespercyl;
382 sl->sl_interleave = lp->d_interleave;
383 sl->sl_ncylinders = lp->d_ncylinders;
384 sl->sl_acylinders = lp->d_acylinders;
385 sl->sl_ntracks = lp->d_ntracks;
386 sl->sl_nsectors = lp->d_nsectors;
387
388 secpercyl = sl->sl_nsectors * sl->sl_ntracks;
389 for (i = 0; i < 8; i++) {
390 spp = &sl->sl_part[i];
391 npp = &lp->d_partitions[i];
392
393 /*
394 * SunOS partitions must start on a cylinder boundary.
395 * Note this restriction is forced upon NetBSD/sparc
396 * labels too, since we want to keep both labels
397 * synchronised.
398 */
399 if (npp->p_offset % secpercyl)
400 return (EINVAL);
401 spp->sdkp_cyloffset = npp->p_offset / secpercyl;
402 spp->sdkp_nsectors = npp->p_size;
403 }
404 sl->sl_magic = SUN_DKMAGIC;
405
406 /* Compute the XOR check. */
407 sp1 = (u_short *)sl;
408 sp2 = (u_short *)(sl + 1);
409 sl->sl_cksum = cksum = 0;
410 while (sp1 < sp2)
411 cksum ^= *sp1++;
412 sl->sl_cksum = cksum;
413
414 return (0);
415 }
416
417 /*
418 * Search the bad sector table looking for the specified sector.
419 * Return index if found.
420 * Return -1 if not found.
421 */
422 int
423 isbad(struct dkbad *bt, int cyl, int trk, int sec)
424 {
425 int i;
426 long blk, bblk;
427
428 blk = ((long)cyl << 16) + (trk << 8) + sec;
429 for (i = 0; i < 126; i++) {
430 bblk = ((long)bt->bt_bad[i].bt_cyl << 16) +
431 bt->bt_bad[i].bt_trksec;
432 if (blk == bblk)
433 return (i);
434 if (blk < bblk || bblk < 0)
435 break;
436 }
437 return (-1);
438 }
439