1 /* $NetBSD: disksubr.c,v 1.45 2022/05/24 06:28:00 andvar Exp $ */
2
3 /*
4 * Copyright (c) 1995 Leo Weppelman.
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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.45 2022/05/24 06:28:00 andvar Exp $");
30
31 #ifndef DISKLABEL_NBDA
32 #define DISKLABEL_NBDA /* required */
33 #endif
34
35 #include "opt_compat_netbsd.h"
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/buf.h>
40 #include <ufs/ufs/dinode.h>
41 #include <ufs/ffs/fs.h>
42 #include <sys/disk.h>
43 #include <sys/disklabel.h>
44 #include <machine/ahdilabel.h>
45
46 /*
47 * BBSIZE in <ufs/ffs/fs.h> must be greater than
48 * or equal to BBMINSIZE in <machine/disklabel.h>
49 */
50 #if BBSIZE < BBMINSIZE
51 #error BBSIZE smaller than BBMINSIZE
52 #endif
53
54 static void ck_label(struct disklabel *, struct cpu_disklabel *);
55 static int bsd_label(dev_t, void (*)(struct buf *),
56 struct disklabel *, u_int, u_int *);
57 static int ahdi_label(dev_t, void (*)(struct buf *),
58 struct disklabel *, struct cpu_disklabel *);
59 static void ahdi_to_bsd(struct disklabel *, struct ahdi_ptbl *);
60 static u_int ahdi_getparts(dev_t, void (*)(struct buf *), u_int,
61 u_int, u_int, struct ahdi_ptbl *);
62
63 /*
64 * Attempt to read a disk label from a device using the
65 * indicated strategy routine. The label must be partly
66 * set up before this:
67 * secpercyl and anything required in the strategy routine
68 * (e.g. sector size) must be filled in before calling us.
69 * Returns NULL on success and an error string on failure.
70 */
71 const char *
readdisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * clp)72 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
73 struct cpu_disklabel *clp)
74 {
75 int e;
76
77 if (clp != NULL)
78 memset(clp, 0, sizeof *clp);
79 else
80 printf("Warning: clp == NULL\n");
81
82 /*
83 * Give some guaranteed validity to the disk label.
84 */
85 if (lp->d_secsize == 0)
86 lp->d_secsize = DEV_BSIZE;
87 if (lp->d_secperunit == 0)
88 lp->d_secperunit = 0x1fffffff;
89 if (lp->d_secpercyl == 0)
90 return "Zero secpercyl";
91
92 /*
93 * Some parts of the kernel (see scsipi/cd.c for an example)
94 * assume that stuff they already had setup in d_partitions
95 * is still there after reading the disklabel. Hence the
96 * 'if 0'
97 */
98 #if 0
99 memset(lp->d_partitions, 0, sizeof lp->d_partitions);
100 #endif
101
102 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
103 lp->d_npartitions = RAW_PART + 1;
104 lp->d_bbsize = BBSIZE;
105 lp->d_sbsize = SBLOCKSIZE;
106
107 #ifdef DISKLABEL_NBDA
108 /* Try the native NetBSD/Atari format first. */
109 e = bsd_label(dev, strat, lp, 0, clp != NULL ? &clp->cd_label : NULL);
110 #endif
111 #if 0
112 /* Other label formats go here. */
113 if (e > 0)
114 e = foo_label(dev, strat, lp, ...);
115 #endif
116 #ifdef DISKLABEL_AHDI
117 /* The unprotected AHDI format comes last. */
118 if (e > 0 && (clp != NULL))
119 e = ahdi_label(dev, strat, lp, clp);
120 #endif
121 if (e < 0)
122 return "I/O error";
123
124 /* Unknown format or uninitialized volume? */
125 if (e > 0)
126 uprintf("Warning: unknown disklabel format"
127 "- assuming empty disk\n");
128
129 /* Calculate new checksum. */
130 lp->d_magic = lp->d_magic2 = DISKMAGIC;
131 lp->d_checksum = 0;
132 lp->d_checksum = dkcksum(lp);
133
134 return NULL;
135 }
136
137 /*
138 * Write disk label back to device after modification.
139 */
140 int
writedisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * clp)141 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
142 struct cpu_disklabel *clp)
143 {
144 struct buf *bp;
145 u_int blk;
146 int rv;
147
148 blk = clp->cd_bblock;
149 if (blk == NO_BOOT_BLOCK)
150 return ENXIO;
151
152 bp = geteblk(BBMINSIZE);
153 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
154 bp->b_flags |= B_READ;
155 bp->b_bcount = BBMINSIZE;
156 bp->b_blkno = blk;
157 bp->b_cylinder = blk / lp->d_secpercyl;
158 (*strat)(bp);
159 rv = biowait(bp);
160 if (rv == 0) {
161 struct bootblock *bb = (struct bootblock *)bp->b_data;
162 /*
163 * Allthough the disk pack label may appear anywhere
164 * in the boot block while reading, it is always
165 * written at a fixed location.
166 */
167 if (clp->cd_label != LABELOFFSET) {
168 clp->cd_label = LABELOFFSET;
169 memset(bb, 0, sizeof(*bb));
170 }
171 bb->bb_magic = (blk == 0) ? NBDAMAGIC : AHDIMAGIC;
172 BBSETLABEL(bb, lp);
173
174 bp->b_oflags &= ~(BO_DONE);
175 bp->b_flags &= ~(B_READ);
176 bp->b_flags |= B_WRITE;
177 bp->b_bcount = BBMINSIZE;
178 bp->b_blkno = blk;
179 bp->b_cylinder = blk / lp->d_secpercyl;
180 (*strat)(bp);
181 rv = biowait(bp);
182 }
183 brelse(bp, 0);
184 return rv;
185 }
186
187 /*
188 * Read bootblock at block `blkno' and check
189 * if it contains a valid NetBSD disk label.
190 *
191 * Returns: 0 if successful,
192 * -1 if an I/O error occurred,
193 * +1 if no valid label was found.
194 */
195 static int
bsd_label(dev_t dev,void (* strat)(struct buf *),struct disklabel * label,u_int blkno,u_int * offsetp)196 bsd_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *label,
197 u_int blkno, u_int *offsetp)
198 {
199 struct buf *bp;
200 int rv;
201
202 bp = geteblk(BBMINSIZE);
203 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
204 bp->b_flags |= B_READ;
205 bp->b_bcount = BBMINSIZE;
206 bp->b_blkno = blkno;
207 bp->b_cylinder = blkno / label->d_secpercyl;
208 (*strat)(bp);
209
210 rv = -1;
211 if (!biowait(bp)) {
212 struct bootblock *bb;
213 uint32_t *p, *end;
214
215 rv = 1;
216 bb = (struct bootblock *)bp->b_data;
217 end = (uint32_t *)((char *)&bb[1] - sizeof(struct disklabel));
218 for (p = (uint32_t *)bb; p < end; ++p) {
219 struct disklabel *dl = (struct disklabel *)&p[1];
220 /*
221 * Compatibility kludge: the boot block magic number is
222 * new in 1.1A, in previous versions the disklabel was
223 * stored at the end of the boot block (offset 7168).
224 */
225 if (((p[0] == NBDAMAGIC && blkno == 0) ||
226 (p[0] == AHDIMAGIC && blkno != 0)
227 #ifdef COMPAT_11
228 || (char *)dl - (char *)bb == 7168
229 #endif
230 ) &&
231 dl->d_npartitions <= MAXPARTITIONS &&
232 dl->d_magic2 == DISKMAGIC &&
233 dl->d_magic == DISKMAGIC &&
234 dkcksum(dl) == 0) {
235 if (offsetp != NULL)
236 *offsetp = (char *)dl - (char *)bb;
237 *label = *dl;
238 rv = 0;
239 break;
240 }
241 }
242 }
243 brelse(bp, 0);
244 return rv;
245 }
246
247 #ifdef DISKLABEL_AHDI
248 /*
249 * Check for consistency between the NetBSD partition table
250 * and the AHDI auxiliary root sectors. There's no good reason
251 * to force such consistency, but issuing a warning may help
252 * an inexperienced sysadmin to prevent corruption of AHDI
253 * partitions.
254 */
255 static void
ck_label(struct disklabel * dl,struct cpu_disklabel * cdl)256 ck_label(struct disklabel *dl, struct cpu_disklabel *cdl)
257 {
258 u_int *rp, i;
259
260 for (i = 0; i < dl->d_npartitions; ++i) {
261 struct partition *p = &dl->d_partitions[i];
262 if (i == RAW_PART || p->p_size == 0)
263 continue;
264 if ((p->p_offset >= cdl->cd_bslst &&
265 p->p_offset <= cdl->cd_bslend) ||
266 (cdl->cd_bslst >= p->p_offset &&
267 cdl->cd_bslst < p->p_offset + p->p_size)) {
268 uprintf("Warning: NetBSD partition %c includes"
269 " AHDI bad sector list\n", 'a'+i);
270 }
271 for (rp = &cdl->cd_roots[0]; *rp; ++rp) {
272 if (*rp >= p->p_offset &&
273 *rp < p->p_offset + p->p_size) {
274 uprintf("Warning: NetBSD partition %c"
275 " includes AHDI auxiliary root\n", 'a' + i);
276 }
277 }
278 }
279 }
280
281 /*
282 * Check volume for the existence of an AHDI label. Fetch
283 * NetBSD label from NBD or RAW partition, or otherwise
284 * create a fake NetBSD label based on the AHDI label.
285 *
286 * Returns: 0 if successful,
287 * -1 if an I/O error occurred,
288 * +1 if no valid AHDI label was found.
289 */
290 int
ahdi_label(dev_t dev,void (* strat)(struct buf *),struct disklabel * dl,struct cpu_disklabel * cdl)291 ahdi_label(dev_t dev, void (*strat)(struct buf *), struct disklabel *dl,
292 struct cpu_disklabel *cdl)
293 {
294 struct ahdi_ptbl apt;
295 u_int i;
296 int j;
297
298 /*
299 * The AHDI format requires a specific block size.
300 */
301 if (dl->d_secsize != AHDI_BSIZE)
302 return 1;
303
304 /*
305 * Fetch the AHDI partition descriptors.
306 */
307 apt.at_cdl = cdl;
308 apt.at_nroots = apt.at_nparts = 0;
309 i = ahdi_getparts(dev, strat, dl->d_secpercyl,
310 AHDI_BBLOCK, AHDI_BBLOCK, &apt);
311 if (i) {
312 if (i < dl->d_secperunit)
313 return -1; /* disk read error */
314 else
315 return 1; /* reading past end of medium */
316 }
317
318 /*
319 * Perform sanity checks.
320 */
321 if (apt.at_bslst == 0 || apt.at_bslend == 0) {
322 /*
323 * Illegal according to Atari, however some hd-utils
324 * use it - notably ICD *sigh*
325 * Work around it.....
326 */
327 apt.at_bslst = apt.at_bslend = 0;
328 uprintf("Warning: Illegal 'bad sector list' format"
329 "- assuming non exists\n");
330 }
331 if (apt.at_hdsize == 0 || apt.at_nparts == 0) /* unlikely */
332 return 1;
333 if (apt.at_nparts > AHDI_MAXPARTS) /* XXX kludge */
334 return -1;
335 for (i = 0; i < apt.at_nparts; ++i) {
336 struct ahdi_part *p1 = &apt.at_parts[i];
337
338 for (j = 0; j < apt.at_nroots; ++j) {
339 u_int aux = apt.at_roots[j];
340 if (aux >= p1->ap_st && aux <= p1->ap_end)
341 return 1;
342 }
343 for (j = i + 1; j < apt.at_nparts; ++j) {
344 struct ahdi_part *p2 = &apt.at_parts[j];
345 if (p1->ap_st >= p2->ap_st && p1->ap_st <= p2->ap_end)
346 return 1;
347 if (p2->ap_st >= p1->ap_st && p2->ap_st <= p1->ap_end)
348 return 1;
349 }
350 if (p1->ap_st >= apt.at_bslst && p1->ap_st <= apt.at_bslend)
351 return 1;
352 if (apt.at_bslst >= p1->ap_st && apt.at_bslst <= p1->ap_end)
353 return 1;
354 }
355
356 /*
357 * Search for a NetBSD disk label
358 */
359 apt.at_bblock = NO_BOOT_BLOCK;
360 for (i = 0; i < apt.at_nparts; ++i) {
361 struct ahdi_part *pd = &apt.at_parts[i];
362 u_int id = *((uint32_t *)&pd->ap_flg);
363 if (id == AHDI_PID_NBD || id == AHDI_PID_RAW) {
364 u_int blkno = pd->ap_st;
365 j = bsd_label(dev, strat, dl, blkno, &apt.at_label);
366 if (j < 0) {
367 return j; /* I/O error */
368 }
369 if (j == 0) {
370 apt.at_bblock = blkno; /* got it */
371 ck_label(dl, cdl);
372 return 0;
373 }
374 /*
375 * Not yet, but if this is the first NBD partition
376 * on this volume, we'll mark it anyway as a possible
377 * destination for future writedisklabel() calls, just
378 * in case there is no valid disk label on any of the
379 * other AHDI partitions.
380 */
381 if (id == AHDI_PID_NBD &&
382 apt.at_bblock == NO_BOOT_BLOCK)
383 apt.at_bblock = blkno;
384 }
385 }
386
387 /*
388 * No NetBSD disk label on this volume, use the AHDI
389 * label to create a fake BSD label. If there is no
390 * NBD partition on this volume either, subsequent
391 * writedisklabel() calls will fail.
392 */
393 ahdi_to_bsd(dl, &apt);
394 return 0;
395 }
396
397 /*
398 * Map the AHDI partition table to the NetBSD table.
399 *
400 * This means:
401 * Part 0 : Root
402 * Part 1 : Swap
403 * Part 2 : Whole disk
404 * Part 3.. : User partitions
405 *
406 * When more than one root partition is found, only the first one will
407 * be recognized as such. The others are mapped as user partitions.
408 */
409 static void
ahdi_to_bsd(struct disklabel * dl,struct ahdi_ptbl * apt)410 ahdi_to_bsd(struct disklabel *dl, struct ahdi_ptbl *apt)
411 {
412 int i, have_root, user_part;
413
414 user_part = RAW_PART;
415 have_root = (apt->at_bblock != NO_BOOT_BLOCK);
416
417 for (i = 0; i < apt->at_nparts; ++i) {
418 struct ahdi_part *pd = &apt->at_parts[i];
419 int fst, pno = -1;
420
421 switch (*((uint32_t *)&pd->ap_flg)) {
422 case AHDI_PID_NBD:
423 /*
424 * If this partition has been marked as the
425 * first NBD partition, it will be the root
426 * partition.
427 */
428 if (pd->ap_st == apt->at_bblock)
429 pno = 0;
430 /* FALL THROUGH */
431 case AHDI_PID_NBR:
432 /*
433 * If there is no NBD partition and this is
434 * the first NBR partition, it will be the
435 * root partition.
436 */
437 if (!have_root) {
438 have_root = 1;
439 pno = 0;
440 }
441 /* FALL THROUGH */
442 case AHDI_PID_NBU:
443 fst = FS_BSDFFS;
444 break;
445 case AHDI_PID_NBS:
446 case AHDI_PID_SWP:
447 if (dl->d_partitions[1].p_size == 0)
448 pno = 1;
449 fst = FS_SWAP;
450 break;
451 case AHDI_PID_BGM:
452 case AHDI_PID_GEM:
453 fst = FS_MSDOS;
454 break;
455 default:
456 fst = FS_OTHER;
457 break;
458 }
459 if (pno < 0) {
460 if ((pno = user_part + 1) >= MAXPARTITIONS)
461 continue;
462 user_part = pno;
463 }
464 dl->d_partitions[pno].p_size = pd->ap_end - pd->ap_st + 1;
465 dl->d_partitions[pno].p_offset = pd->ap_st;
466 dl->d_partitions[pno].p_fstype = fst;
467 }
468 dl->d_npartitions = user_part + 1;
469 }
470
471 /*
472 * Fetch the AHDI partitions and auxiliary roots.
473 *
474 * Returns: 0 if successful,
475 * otherwise an I/O error occurred, and the
476 * number of the offending block is returned.
477 */
478 static u_int
ahdi_getparts(dev_t dev,void (* strat)(struct buf *),u_int secpercyl,u_int rsec,u_int esec,struct ahdi_ptbl * apt)479 ahdi_getparts(dev_t dev, void (*strat)(struct buf *), u_int secpercyl,
480 u_int rsec, u_int esec, struct ahdi_ptbl *apt)
481 {
482 struct ahdi_part *part, *end;
483 struct ahdi_root *root;
484 struct buf *bp;
485 u_int rv;
486
487 bp = geteblk(AHDI_BSIZE);
488 bp->b_dev = MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART);
489 bp->b_flags |= B_READ;
490 bp->b_bcount = AHDI_BSIZE;
491 bp->b_blkno = rsec;
492 bp->b_cylinder = rsec / secpercyl;
493 (*strat)(bp);
494 if (biowait(bp)) {
495 rv = rsec + (rsec == 0);
496 goto done;
497 }
498 root = (struct ahdi_root *)bp->b_data;
499
500 if (rsec == AHDI_BBLOCK)
501 end = &root->ar_parts[AHDI_MAXRPD];
502 else
503 end = &root->ar_parts[AHDI_MAXARPD];
504 for (part = root->ar_parts; part < end; ++part) {
505 u_int id = *((uint32_t *)&part->ap_flg);
506 if (!(id & 0x01000000))
507 continue;
508 if ((id &= 0x00ffffff) == AHDI_PID_XGM) {
509 u_int offs = part->ap_st + esec;
510 if (apt->at_nroots < AHDI_MAXROOTS)
511 apt->at_roots[apt->at_nroots] = offs;
512 apt->at_nroots += 1;
513 rv = ahdi_getparts(dev, strat, secpercyl, offs,
514 (esec == AHDI_BBLOCK) ? offs : esec, apt);
515 if (rv != 0)
516 goto done;
517 continue;
518 }
519 else if (apt->at_nparts < AHDI_MAXPARTS) {
520 struct ahdi_part *p = &apt->at_parts[apt->at_nparts];
521 *((uint32_t *)&p->ap_flg) = id;
522 p->ap_st = part->ap_st + rsec;
523 p->ap_end = p->ap_st + part->ap_size - 1;
524 }
525 apt->at_nparts += 1;
526 }
527 apt->at_hdsize = root->ar_hdsize;
528 apt->at_bslst = root->ar_bslst;
529 apt->at_bslend = root->ar_bslst + root->ar_bslsize - 1;
530 rv = 0;
531 done:
532 brelse(bp, 0);
533 return rv;
534 }
535 #endif /* DISKLABEL_AHDI */
536