1 /* $NetBSD: disksubr.c,v 1.3 2024/02/10 18:43:51 andvar 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 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.3 2024/02/10 18:43:51 andvar Exp $");
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/buf.h>
38 #include <sys/disklabel.h>
39 #include <sys/disk.h>
40 #include <sys/syslog.h>
41
42 #define NO_MBR_SIGNATURE ((struct mbr_partition *) -1)
43
44 static struct mbr_partition *
45 mbr_findslice(struct mbr_partition* dp, struct buf *bp);
46
47 /*
48 * Scan MBR for NetBSD partition. Return NO_MBR_SIGNATURE if no MBR found
49 * Otherwise, copy valid MBR partition-table into dp, and if a NetBSD
50 * partition is found, return a pointer to it; else return NULL.
51 */
52 static
53 struct mbr_partition *
mbr_findslice(struct mbr_partition * dp,struct buf * bp)54 mbr_findslice(struct mbr_partition *dp, struct buf *bp)
55 {
56 struct mbr_partition *ourdp = NULL;
57 uint16_t *mbrmagicp;
58 int i;
59
60 /* Note: Magic number is little-endian. */
61 mbrmagicp = (uint16_t *)((char *)bp->b_data + MBR_MAGIC_OFFSET);
62 if (*mbrmagicp != MBR_MAGIC)
63 return (NO_MBR_SIGNATURE);
64
65 /* XXX how do we check veracity/bounds of this? */
66 memcpy(dp, (char *)bp->b_data + MBR_PART_OFFSET, MBR_PART_COUNT * sizeof(*dp));
67
68 /* look for NetBSD partition */
69 for (i = 0; i < MBR_PART_COUNT; i++) {
70 if (dp[i].mbrp_type == MBR_PTYPE_NETBSD) {
71 ourdp = &dp[i];
72 break;
73 }
74 }
75
76 return (ourdp);
77 }
78
79
80 /*
81 * Attempt to read a disk label from a device
82 * using the indicated strategy routine.
83 * The label must be partly set up before this:
84 * secpercyl, secsize and anything required for a block i/o read
85 * operation in the driver's strategy/start routines
86 * must be filled in before calling us.
87 *
88 * If dos partition table requested, attempt to load it and
89 * find disklabel inside a DOS partition. Also, if bad block
90 * table needed, attempt to extract it as well. Return buffer
91 * for use in signalling errors if requested.
92 *
93 * Returns null on success and an error string on failure.
94 */
95 const char *
readdisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * osdep)96 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
97 {
98 struct mbr_partition *dp;
99 struct partition *pp;
100 struct dkbad *bdp;
101 struct buf *bp;
102 struct disklabel *dlp;
103 const char *msg = NULL;
104 int dospartoff, cyl, i;
105
106 /* minimal requirements for archtypal disk label */
107 if (lp->d_secsize == 0)
108 lp->d_secsize = DEV_BSIZE;
109 if (lp->d_secperunit == 0)
110 lp->d_secperunit = 0x1fffffff;
111 #if 0
112 if (lp->d_ncylinders == 16383) {
113 printf("disklabel: Disk > 8G ... readjusting chs %d/%d/%d to ",
114 lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors);
115 lp->d_ncylinders = lp->d_secperunit / lp->d_ntracks / lp->d_nsectors;
116 printf("%d/%d/%d\n",
117 lp->d_ncylinders, lp->d_ntracks, lp->d_nsectors);
118 }
119 #endif
120 lp->d_npartitions = RAW_PART + 1;
121 for (i = 0; i < RAW_PART; i++) {
122 lp->d_partitions[i].p_size = 0;
123 lp->d_partitions[i].p_offset = 0;
124 }
125 if (lp->d_partitions[i].p_size == 0)
126 lp->d_partitions[i].p_size = 0x1fffffff;
127 lp->d_partitions[i].p_offset = 0;
128
129 /* get a buffer and initialize it */
130 bp = geteblk((int)lp->d_secsize);
131 bp->b_dev = dev;
132
133 /* do dos partitions in the process of getting disklabel? */
134 dospartoff = 0;
135 cyl = LABELSECTOR / lp->d_secpercyl;
136 if (!osdep)
137 goto nombrpart;
138 dp = osdep->mbrparts;
139
140 /* read master boot record */
141 bp->b_blkno = MBR_BBSECTOR;
142 bp->b_bcount = lp->d_secsize;
143 bp->b_cflags = BC_BUSY;
144 bp->b_flags |= B_READ;
145 bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
146 (*strat)(bp);
147
148 /* if successful, wander through dos partition table */
149 if (biowait(bp)) {
150 msg = "dos partition I/O error";
151 goto done;
152 } else {
153 struct mbr_partition *ourdp = NULL;
154
155 ourdp = mbr_findslice(dp, bp);
156 if (ourdp == NO_MBR_SIGNATURE)
157 goto nombrpart;
158
159 for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
160 /* Install in partition e, f, g, or h. */
161 pp = &lp->d_partitions[RAW_PART + 1 + i];
162 pp->p_offset = dp->mbrp_start;
163 pp->p_size = dp->mbrp_size;
164 if (dp->mbrp_type == MBR_PTYPE_LNXEXT2)
165 pp->p_fstype = FS_EX2FS;
166
167 if (dp->mbrp_type == MBR_PTYPE_LNXSWAP)
168 pp->p_fstype = FS_SWAP;
169
170 /* is this ours? */
171 if (dp == ourdp) {
172 /* need sector address for SCSI/IDE,
173 cylinder for ESDI/ST506/RLL */
174 dospartoff = dp->mbrp_start;
175 cyl = MBR_PCYL(dp->mbrp_scyl, dp->mbrp_ssect);
176
177 /* update disklabel with details */
178 lp->d_partitions[2].p_size =
179 dp->mbrp_size;
180 lp->d_partitions[2].p_offset =
181 dp->mbrp_start;
182 }
183 }
184 lp->d_npartitions = RAW_PART + 1 + i;
185 }
186
187 nombrpart:
188 /* next, dig out disk label */
189 bp->b_blkno = dospartoff + LABELSECTOR;
190 bp->b_cylinder = cyl;
191 bp->b_bcount = lp->d_secsize;
192 bp->b_cflags = BC_BUSY;
193 bp->b_flags = B_READ;
194 (*strat)(bp);
195
196 /* if successful, locate disk label within block and validate */
197 if (biowait(bp)) {
198 msg = "disk label I/O error";
199 goto done;
200 }
201 for (dlp = (struct disklabel *)bp->b_data;
202 dlp <= (struct disklabel *)
203 ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp));
204 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
205 if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) {
206 if (msg == NULL)
207 msg = "no disk label";
208 } else if (dlp->d_npartitions > MAXPARTITIONS ||
209 dkcksum(dlp) != 0)
210 msg = "disk label corrupted";
211 else {
212 *lp = *dlp;
213 msg = NULL;
214 break;
215 }
216 }
217
218 if (msg)
219 goto done;
220
221 /* obtain bad sector table if requested and present */
222 if (osdep && (lp->d_flags & D_BADSECT)) {
223 struct dkbad *db;
224
225 bdp = &osdep->bad;
226 i = 0;
227 do {
228 /* read a bad sector table */
229 bp->b_cflags = BC_BUSY;
230 bp->b_flags = B_READ;
231 bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i;
232 if (lp->d_secsize > DEV_BSIZE)
233 bp->b_blkno *= lp->d_secsize / DEV_BSIZE;
234 else
235 bp->b_blkno /= DEV_BSIZE / lp->d_secsize;
236 bp->b_bcount = lp->d_secsize;
237 bp->b_cylinder = lp->d_ncylinders - 1;
238 (*strat)(bp);
239
240 /* if successful, validate, otherwise try another */
241 if (biowait(bp)) {
242 msg = "bad sector table I/O error";
243 } else {
244 db = (struct dkbad *)(bp->b_data);
245 #define DKBAD_MAGIC 0x4321
246 if (db->bt_mbz == 0
247 && db->bt_flag == DKBAD_MAGIC) {
248 msg = NULL;
249 *bdp = *db;
250 break;
251 } else
252 msg = "bad sector table corrupted";
253 }
254 } while (bp->b_error && (i += 2) < 10 &&
255 i < lp->d_nsectors);
256 }
257
258 done:
259 brelse(bp, BC_INVAL);
260 return (msg);
261 }
262
263 /*
264 * Write disk label back to device after modification.
265 */
266 int
writedisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * osdep)267 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *osdep)
268 {
269 struct mbr_partition *dp;
270 struct buf *bp;
271 struct disklabel *dlp;
272 int error, dospartoff, cyl;
273
274 /* get a buffer and initialize it */
275 bp = geteblk((int)lp->d_secsize);
276 bp->b_dev = dev;
277
278 /* do dos partitions in the process of getting disklabel? */
279 dospartoff = 0;
280 cyl = LABELSECTOR / lp->d_secpercyl;
281 if (!osdep)
282 goto nombrpart;
283 dp = osdep->mbrparts;
284
285 /* read master boot record */
286 bp->b_blkno = MBR_BBSECTOR;
287 bp->b_bcount = lp->d_secsize;
288 bp->b_cflags = BC_BUSY;
289 bp->b_flags = B_READ;
290 bp->b_cylinder = MBR_BBSECTOR / lp->d_secpercyl;
291 (*strat)(bp);
292
293 if ((error = biowait(bp)) == 0) {
294 struct mbr_partition *ourdp = NULL;
295
296 ourdp = mbr_findslice(dp, bp);
297 if (ourdp == NO_MBR_SIGNATURE)
298 goto nombrpart;
299
300 if (ourdp) {
301 /* need sector address for SCSI/IDE,
302 cylinder for ESDI/ST506/RLL */
303 dospartoff = ourdp->mbrp_start;
304 cyl = MBR_PCYL(ourdp->mbrp_scyl, ourdp->mbrp_ssect);
305 }
306 }
307
308 nombrpart:
309 /* next, dig out disk label */
310 bp->b_blkno = dospartoff + LABELSECTOR;
311 bp->b_cylinder = cyl;
312 bp->b_bcount = lp->d_secsize;
313 bp->b_cflags = BC_BUSY;
314 bp->b_flags = B_READ;
315 (*strat)(bp);
316
317 /* if successful, locate disk label within block and validate */
318 if ((error = biowait(bp)) != 0)
319 goto done;
320 for (dlp = (struct disklabel *)bp->b_data;
321 dlp <= (struct disklabel *)
322 ((char *)bp->b_data + lp->d_secsize - sizeof(*dlp));
323 dlp = (struct disklabel *)((char *)dlp + sizeof(long))) {
324 if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC &&
325 dkcksum(dlp) == 0) {
326 *dlp = *lp;
327 bp->b_cflags = BC_BUSY;
328 bp->b_flags = B_WRITE;
329 (*strat)(bp);
330 error = biowait(bp);
331 goto done;
332 }
333 }
334 error = ESRCH;
335
336 done:
337 brelse(bp, BC_INVAL);
338 return (error);
339 }
340