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