1*9703a26eSgutteridge /* $NetBSD: subr_disk_mbr.c,v 1.58 2023/04/03 18:03:24 gutteridge Exp $ */
2d38f6f31Sdsl
3d38f6f31Sdsl /*
4d38f6f31Sdsl * Copyright (c) 1982, 1986, 1988 Regents of the University of California.
5d38f6f31Sdsl * All rights reserved.
6d38f6f31Sdsl *
7d38f6f31Sdsl * Redistribution and use in source and binary forms, with or without
8d38f6f31Sdsl * modification, are permitted provided that the following conditions
9d38f6f31Sdsl * are met:
10d38f6f31Sdsl * 1. Redistributions of source code must retain the above copyright
11d38f6f31Sdsl * notice, this list of conditions and the following disclaimer.
12d38f6f31Sdsl * 2. Redistributions in binary form must reproduce the above copyright
13d38f6f31Sdsl * notice, this list of conditions and the following disclaimer in the
14d38f6f31Sdsl * documentation and/or other materials provided with the distribution.
15aad01611Sagc * 3. Neither the name of the University nor the names of its contributors
16d38f6f31Sdsl * may be used to endorse or promote products derived from this software
17d38f6f31Sdsl * without specific prior written permission.
18d38f6f31Sdsl *
19d38f6f31Sdsl * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20d38f6f31Sdsl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21d38f6f31Sdsl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22d38f6f31Sdsl * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23d38f6f31Sdsl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24d38f6f31Sdsl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25d38f6f31Sdsl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26d38f6f31Sdsl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27d38f6f31Sdsl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28d38f6f31Sdsl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29d38f6f31Sdsl * SUCH DAMAGE.
30d38f6f31Sdsl *
31d38f6f31Sdsl * @(#)ufs_disksubr.c 7.16 (Berkeley) 5/4/91
32d38f6f31Sdsl */
33d38f6f31Sdsl
34d38f6f31Sdsl /*
35d38f6f31Sdsl * Code to find a NetBSD label on a disk that contains an i386 style MBR.
36d38f6f31Sdsl * The first NetBSD label found in the 2nd sector of a NetBSD partition
3740bac108Sdsl * is used.
3840bac108Sdsl * If we don't find a label searching the MBR, we look at the start of the
3940bac108Sdsl * disk, if that fails then a label is faked up from the MBR.
40d38f6f31Sdsl *
41339f5c57Sreinoud * If there isn't a disklabel or anything in the MBR then the disc is searched
42339f5c57Sreinoud * for ecma-167/iso9660/udf style partition indicators.
43339f5c57Sreinoud * Useful for media or files that contain single filesystems (etc).
44d38f6f31Sdsl *
45d38f6f31Sdsl * This code will read host endian netbsd labels from little endian MBR.
46d38f6f31Sdsl *
47d38f6f31Sdsl * Based on the i386 disksubr.c
48d38f6f31Sdsl *
4940bac108Sdsl * Since the mbr only has 32bit fields for sector addresses, we do the same.
5040bac108Sdsl *
51d38f6f31Sdsl * XXX There are potential problems writing labels to disks where there
52d38f6f31Sdsl * is only space for 8 netbsd partitions but this code has been compiled
53d38f6f31Sdsl * with MAXPARTITIONS=16.
54d38f6f31Sdsl */
55d38f6f31Sdsl
56d38f6f31Sdsl #include <sys/cdefs.h>
57*9703a26eSgutteridge __KERNEL_RCSID(0, "$NetBSD: subr_disk_mbr.c,v 1.58 2023/04/03 18:03:24 gutteridge Exp $");
584a1c505cSmrg
594a1c505cSmrg #ifdef _KERNEL_OPT
604a1c505cSmrg #include "opt_mbr.h"
614a1c505cSmrg #include "opt_disklabel.h"
624a1c505cSmrg #endif /* _KERNEL_OPT */
63d38f6f31Sdsl
64d38f6f31Sdsl #include <sys/param.h>
65d38f6f31Sdsl #include <sys/systm.h>
66d38f6f31Sdsl #include <sys/buf.h>
67db12d3f8Sdyoung #include <sys/bootblock.h>
68d38f6f31Sdsl #include <sys/disklabel.h>
69d38f6f31Sdsl #include <sys/disk.h>
70d38f6f31Sdsl #include <sys/syslog.h>
71339f5c57Sreinoud #include <sys/vnode.h>
72339f5c57Sreinoud #include <sys/fcntl.h>
73339f5c57Sreinoud #include <sys/conf.h>
74339f5c57Sreinoud #include <sys/cdio.h>
7509dbb89bSpooka #include <sys/dkbad.h>
76339f5c57Sreinoud #include <fs/udf/ecma167-udf.h>
77339f5c57Sreinoud
78339f5c57Sreinoud #include <sys/kauth.h>
79d38f6f31Sdsl
80a89c49eeSdsl typedef struct mbr_partition mbr_partition_t;
81a89c49eeSdsl
82db0d6983Sdsl /*
838257134aSpooka * We allocate a buffer 3 sectors large, and look in all....
84db0d6983Sdsl * That means we find labels written by other ports with different offsets.
85db0d6983Sdsl * LABELSECTOR and LABELOFFSET are only used if the disk doesn't have a label.
86db0d6983Sdsl */
878257134aSpooka #define SCANBLOCKS 3
888257134aSpooka #define DISKLABEL_SIZE 404
898257134aSpooka #if LABELSECTOR*DEV_BSIZE + LABELOFFSET > SCANBLOCKS*DEV_BSIZE - DISKLABEL_SIZE
90b2cb120dSpooka #if _MACHINE != ews4800mips /* XXX: fail silently, ews4800mips LABELSECTOR */
91db0d6983Sdsl #error Invalid LABELSECTOR or LABELOFFSET
92db0d6983Sdsl #endif
9325362a67Stsutsui #endif
94db0d6983Sdsl
95d38f6f31Sdsl #define MBR_LABELSECTOR 1
96d38f6f31Sdsl
97d38f6f31Sdsl #define SCAN_CONTINUE 0
98d38f6f31Sdsl #define SCAN_FOUND 1
99d38f6f31Sdsl #define SCAN_ERROR 2
100d38f6f31Sdsl
101d38f6f31Sdsl typedef struct mbr_args {
102d38f6f31Sdsl struct disklabel *lp;
103d38f6f31Sdsl void (*strat)(struct buf *);
104d38f6f31Sdsl struct buf *bp;
105d38f6f31Sdsl const char *msg;
106d38f6f31Sdsl int error;
10740bac108Sdsl int written; /* number of times we wrote label */
108db0d6983Sdsl int found_mbr; /* set if disk has a valid mbr */
10940bac108Sdsl uint label_sector; /* where we found the label */
110db0d6983Sdsl int action;
1111ee8f4a4Sdyoung uint32_t secperunit;
112d38f6f31Sdsl #define READ_LABEL 1
11340bac108Sdsl #define UPDATE_LABEL 2
11440bac108Sdsl #define WRITE_LABEL 3
115db0d6983Sdsl } mbr_args_t;
116db0d6983Sdsl
117db0d6983Sdsl static int validate_label(mbr_args_t *, uint);
118a89c49eeSdsl static int look_netbsd_part(mbr_args_t *, mbr_partition_t *, int, uint);
119a89c49eeSdsl static int write_netbsd_label(mbr_args_t *, mbr_partition_t *, int, uint);
120d38f6f31Sdsl
121d38f6f31Sdsl static int
read_sector(mbr_args_t * a,uint sector,int count)122db0d6983Sdsl read_sector(mbr_args_t *a, uint sector, int count)
123d38f6f31Sdsl {
124d38f6f31Sdsl int error;
125d38f6f31Sdsl
1261ee8f4a4Sdyoung error = disk_read_sectors(a->strat, a->lp, a->bp, sector, count);
127d38f6f31Sdsl if (error != 0)
128d38f6f31Sdsl a->error = error;
129d38f6f31Sdsl return error;
130d38f6f31Sdsl }
131d38f6f31Sdsl
132d38f6f31Sdsl /*
133d38f6f31Sdsl * Scan MBR for partitions, call 'action' routine for each.
134d38f6f31Sdsl */
135d38f6f31Sdsl
136d38f6f31Sdsl static int
scan_mbr(mbr_args_t * a,int (* actn)(mbr_args_t *,mbr_partition_t *,int,uint))137a89c49eeSdsl scan_mbr(mbr_args_t *a, int (*actn)(mbr_args_t *, mbr_partition_t *, int, uint))
138d38f6f31Sdsl {
139a89c49eeSdsl mbr_partition_t ptns[MBR_PART_COUNT];
140a89c49eeSdsl mbr_partition_t *dp;
1411c33b4e6Slukem struct mbr_sector *mbr;
142d38f6f31Sdsl uint ext_base, this_ext, next_ext;
143d38f6f31Sdsl int rval;
144d38f6f31Sdsl int i;
1451214d130Schristos int j;
146d38f6f31Sdsl #ifdef COMPAT_386BSD_MBRPART
147d38f6f31Sdsl int dp_386bsd = -1;
1481214d130Schristos int ap_386bsd = -1;
149d38f6f31Sdsl #endif
150d38f6f31Sdsl
151d38f6f31Sdsl ext_base = 0;
152d38f6f31Sdsl this_ext = 0;
153d38f6f31Sdsl for (;;) {
154db0d6983Sdsl if (read_sector(a, this_ext, 1)) {
155d38f6f31Sdsl a->msg = "dos partition I/O error";
156d38f6f31Sdsl return SCAN_ERROR;
157d38f6f31Sdsl }
158d38f6f31Sdsl
159d38f6f31Sdsl /* Note: Magic number is little-endian. */
160d38f6f31Sdsl mbr = (void *)a->bp->b_data;
1611c33b4e6Slukem if (mbr->mbr_magic != htole16(MBR_MAGIC))
16240bac108Sdsl return SCAN_CONTINUE;
163d38f6f31Sdsl
1649708c4d5Smatt /*
1659708c4d5Smatt * If this is a protective MBR, bail now.
1669708c4d5Smatt */
1679708c4d5Smatt if (mbr->mbr_parts[0].mbrp_type == MBR_PTYPE_PMBR
1689708c4d5Smatt && mbr->mbr_parts[1].mbrp_type == MBR_PTYPE_UNUSED
1699708c4d5Smatt && mbr->mbr_parts[2].mbrp_type == MBR_PTYPE_UNUSED
1709708c4d5Smatt && mbr->mbr_parts[3].mbrp_type == MBR_PTYPE_UNUSED)
1719708c4d5Smatt return SCAN_CONTINUE;
1729708c4d5Smatt
173d38f6f31Sdsl /* Copy data out of buffer so action can use bp */
174d38f6f31Sdsl memcpy(ptns, &mbr->mbr_parts, sizeof ptns);
175d38f6f31Sdsl
1764ead7c35Schristos /* Look for drivers and skip them */
177db0d6983Sdsl if (ext_base == 0 && ptns[0].mbrp_type == MBR_PTYPE_DM6_DDO) {
178d9585f8dSjmmv /* We've found a DM6 DDO partition type (used by
179d9585f8dSjmmv * the Ontrack Disk Manager drivers).
180d9585f8dSjmmv *
181d9585f8dSjmmv * Ensure that there are no other partitions in the
182d9585f8dSjmmv * MBR and jump to the real partition table (stored
183d9585f8dSjmmv * in the first sector of the second track). */
1844f3d5a9cSthorpej bool ok = true;
1854ead7c35Schristos
1864ead7c35Schristos for (i = 1; i < MBR_PART_COUNT; i++)
1874ead7c35Schristos if (ptns[i].mbrp_type != MBR_PTYPE_UNUSED)
1884f3d5a9cSthorpej ok = false;
1894ead7c35Schristos
1904ead7c35Schristos if (ok) {
191ddeaed40Schristos this_ext = le32toh(a->lp->d_secpercyl /
192ddeaed40Schristos a->lp->d_ntracks);
1934ead7c35Schristos continue;
1944ead7c35Schristos }
1954ead7c35Schristos }
1964ead7c35Schristos
197d38f6f31Sdsl /* look for NetBSD partition */
198d38f6f31Sdsl next_ext = 0;
199d38f6f31Sdsl dp = ptns;
2001214d130Schristos j = 0;
2011c33b4e6Slukem for (i = 0; i < MBR_PART_COUNT; i++, dp++) {
2021214d130Schristos if (dp->mbrp_type == MBR_PTYPE_UNUSED)
203d38f6f31Sdsl continue;
204db0d6983Sdsl /* Check end of partition is inside disk limits */
205db0d6983Sdsl if ((uint64_t)ext_base + le32toh(dp->mbrp_start) +
206db0d6983Sdsl le32toh(dp->mbrp_size) > a->lp->d_secperunit) {
207db0d6983Sdsl /* This mbr doesn't look good.... */
208db0d6983Sdsl a->msg = "mbr partition exceeds disk size";
209db0d6983Sdsl /* ...but don't report this as an error (yet) */
210db0d6983Sdsl return SCAN_CONTINUE;
211db0d6983Sdsl }
212db0d6983Sdsl a->found_mbr = 1;
2131c33b4e6Slukem if (MBR_IS_EXTENDED(dp->mbrp_type)) {
214d38f6f31Sdsl next_ext = le32toh(dp->mbrp_start);
215d38f6f31Sdsl continue;
216d38f6f31Sdsl }
217d38f6f31Sdsl #ifdef COMPAT_386BSD_MBRPART
2181c33b4e6Slukem if (dp->mbrp_type == MBR_PTYPE_386BSD) {
219d38f6f31Sdsl /*
220d38f6f31Sdsl * If more than one matches, take last,
221d38f6f31Sdsl * as NetBSD install tool does.
222d38f6f31Sdsl */
2231214d130Schristos if (this_ext == 0) {
224d38f6f31Sdsl dp_386bsd = i;
2251214d130Schristos ap_386bsd = j;
2261214d130Schristos }
227d38f6f31Sdsl continue;
228d38f6f31Sdsl }
229d38f6f31Sdsl #endif
2301214d130Schristos rval = (*actn)(a, dp, j, this_ext);
231d38f6f31Sdsl if (rval != SCAN_CONTINUE)
232d38f6f31Sdsl return rval;
2331214d130Schristos j++;
234d38f6f31Sdsl }
235d38f6f31Sdsl if (next_ext == 0)
236d38f6f31Sdsl break;
237d38f6f31Sdsl if (ext_base == 0) {
238d38f6f31Sdsl ext_base = next_ext;
239d38f6f31Sdsl next_ext = 0;
240d38f6f31Sdsl }
241d38f6f31Sdsl next_ext += ext_base;
242d38f6f31Sdsl if (next_ext <= this_ext)
243d38f6f31Sdsl break;
244d38f6f31Sdsl this_ext = next_ext;
245d38f6f31Sdsl }
246d38f6f31Sdsl #ifdef COMPAT_386BSD_MBRPART
247d38f6f31Sdsl if (this_ext == 0 && dp_386bsd != -1)
2481214d130Schristos return (*actn)(a, &ptns[dp_386bsd], ap_386bsd, 0);
249d38f6f31Sdsl #endif
250d38f6f31Sdsl return SCAN_CONTINUE;
251d38f6f31Sdsl }
252d38f6f31Sdsl
253339f5c57Sreinoud
254339f5c57Sreinoud static void
scan_iso_vrs_session(mbr_args_t * a,uint32_t first_sector,int * is_iso9660,int * is_udf)255339f5c57Sreinoud scan_iso_vrs_session(mbr_args_t *a, uint32_t first_sector,
256339f5c57Sreinoud int *is_iso9660, int *is_udf)
257339f5c57Sreinoud {
258339f5c57Sreinoud struct vrs_desc *vrsd;
259339f5c57Sreinoud uint64_t vrs;
260339f5c57Sreinoud int sector_size;
2610da18cc3Sreinoud int blks, inc;
262339f5c57Sreinoud
263339f5c57Sreinoud sector_size = a->lp->d_secsize;
264339f5c57Sreinoud blks = sector_size / DEV_BSIZE;
2650da18cc3Sreinoud inc = MAX(1, 2048 / sector_size);
266339f5c57Sreinoud
267339f5c57Sreinoud /* by definition */
268339f5c57Sreinoud vrs = ((32*1024 + sector_size - 1) / sector_size)
269339f5c57Sreinoud + first_sector;
270339f5c57Sreinoud
271339f5c57Sreinoud /* read first vrs sector */
272a0d3b4c9Sreinoud if (read_sector(a, vrs * blks, 1))
273339f5c57Sreinoud return;
274339f5c57Sreinoud
275339f5c57Sreinoud /* skip all CD001 records */
276339f5c57Sreinoud vrsd = a->bp->b_data;
2770da18cc3Sreinoud /* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */
278339f5c57Sreinoud while (memcmp(vrsd->identifier, "CD001", 5) == 0) {
279339f5c57Sreinoud /* for sure */
280339f5c57Sreinoud *is_iso9660 = first_sector;
281339f5c57Sreinoud
2820da18cc3Sreinoud vrs += inc;
283a0d3b4c9Sreinoud if (read_sector(a, vrs * blks, 1))
284339f5c57Sreinoud return;
285339f5c57Sreinoud }
286339f5c57Sreinoud
287339f5c57Sreinoud /* search for BEA01 */
288339f5c57Sreinoud vrsd = a->bp->b_data;
289339f5c57Sreinoud /* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */
290339f5c57Sreinoud if (memcmp(vrsd->identifier, "BEA01", 5))
291339f5c57Sreinoud return;
292339f5c57Sreinoud
293339f5c57Sreinoud /* read successor */
2940da18cc3Sreinoud vrs += inc;
295a0d3b4c9Sreinoud if (read_sector(a, vrs * blks, 1))
296339f5c57Sreinoud return;
297339f5c57Sreinoud
298339f5c57Sreinoud /* check for NSR[23] */
299339f5c57Sreinoud vrsd = a->bp->b_data;
3000da18cc3Sreinoud /* printf("vrsd->identifier = `%s`\n", vrsd->identifier); */
301339f5c57Sreinoud if (memcmp(vrsd->identifier, "NSR0", 4))
302339f5c57Sreinoud return;
303339f5c57Sreinoud
304339f5c57Sreinoud *is_udf = first_sector;
305339f5c57Sreinoud }
306339f5c57Sreinoud
307339f5c57Sreinoud
308339f5c57Sreinoud /*
309339f5c57Sreinoud * Scan for ISO Volume Recognition Sequences
310339f5c57Sreinoud */
311339f5c57Sreinoud
312339f5c57Sreinoud static int
scan_iso_vrs(mbr_args_t * a)313339f5c57Sreinoud scan_iso_vrs(mbr_args_t *a)
314339f5c57Sreinoud {
315339f5c57Sreinoud struct mmc_discinfo di;
316339f5c57Sreinoud struct mmc_trackinfo ti;
317339f5c57Sreinoud dev_t dev;
318339f5c57Sreinoud uint64_t sector;
319339f5c57Sreinoud int is_iso9660, is_udf;
320339f5c57Sreinoud int tracknr, sessionnr;
321339f5c57Sreinoud int new_session, error;
322339f5c57Sreinoud
323339f5c57Sreinoud is_iso9660 = is_udf = -1;
324339f5c57Sreinoud
325339f5c57Sreinoud /* parse all sessions of disc if we're on a SCSI MMC device */
326339f5c57Sreinoud if (a->lp->d_flags & D_SCSI_MMC) {
327339f5c57Sreinoud /* get disc info */
328339f5c57Sreinoud dev = a->bp->b_dev;
329339f5c57Sreinoud error = bdev_ioctl(dev, MMCGETDISCINFO, &di, FKIOCTL, curlwp);
330339f5c57Sreinoud if (error)
331339f5c57Sreinoud return SCAN_CONTINUE;
332339f5c57Sreinoud
333*9703a26eSgutteridge /* go through all (data) tracks */
334339f5c57Sreinoud sessionnr = -1;
335339f5c57Sreinoud for (tracknr = di.first_track;
336339f5c57Sreinoud tracknr <= di.first_track_last_session; tracknr++)
337339f5c57Sreinoud {
338339f5c57Sreinoud ti.tracknr = tracknr;
339339f5c57Sreinoud error = bdev_ioctl(dev, MMCGETTRACKINFO, &ti,
340339f5c57Sreinoud FKIOCTL, curlwp);
341339f5c57Sreinoud if (error)
342339f5c57Sreinoud return SCAN_CONTINUE;
343339f5c57Sreinoud new_session = (ti.sessionnr != sessionnr);
344339f5c57Sreinoud sessionnr = ti.sessionnr;
345339f5c57Sreinoud if (new_session) {
346339f5c57Sreinoud if (ti.flags & MMC_TRACKINFO_BLANK)
347339f5c57Sreinoud continue;
348339f5c57Sreinoud if (!(ti.flags & MMC_TRACKINFO_DATA))
349339f5c57Sreinoud continue;
350339f5c57Sreinoud sector = ti.track_start;
351339f5c57Sreinoud scan_iso_vrs_session(a, sector,
352339f5c57Sreinoud &is_iso9660, &is_udf);
353339f5c57Sreinoud }
354339f5c57Sreinoud }
355339f5c57Sreinoud } else {
356339f5c57Sreinoud /* try start of disc */
357339f5c57Sreinoud sector = 0;
358339f5c57Sreinoud scan_iso_vrs_session(a, sector, &is_iso9660, &is_udf);
359339f5c57Sreinoud }
360339f5c57Sreinoud
361339f5c57Sreinoud if ((is_iso9660 < 0) && (is_udf < 0))
362339f5c57Sreinoud return SCAN_CONTINUE;
363339f5c57Sreinoud
36469f4b23bSreinoud strncpy(a->lp->d_typename, "iso partition", 16);
36569f4b23bSreinoud
366106bf8d8Smlelstv /* adjust session information for iso9660 partition */
367339f5c57Sreinoud if (is_iso9660 >= 0) {
368339f5c57Sreinoud /* set 'a' partition to iso9660 */
369339f5c57Sreinoud a->lp->d_partitions[0].p_offset = 0;
370339f5c57Sreinoud a->lp->d_partitions[0].p_size = a->lp->d_secperunit;
371339f5c57Sreinoud a->lp->d_partitions[0].p_cdsession = is_iso9660;
372339f5c57Sreinoud a->lp->d_partitions[0].p_fstype = FS_ISO9660;
373339f5c57Sreinoud }
374339f5c57Sreinoud
375339f5c57Sreinoud /* UDF doesn't care about the cd session specified here */
376339f5c57Sreinoud
377339f5c57Sreinoud return SCAN_FOUND;
378339f5c57Sreinoud }
379339f5c57Sreinoud
380339f5c57Sreinoud
381d38f6f31Sdsl /*
382d38f6f31Sdsl * Attempt to read a disk label from a device
383d38f6f31Sdsl * using the indicated strategy routine.
384d38f6f31Sdsl * The label must be partly set up before this:
385d38f6f31Sdsl * secpercyl, secsize and anything required for a block i/o read
386d38f6f31Sdsl * operation in the driver's strategy/start routines
387d38f6f31Sdsl * must be filled in before calling us.
388d38f6f31Sdsl *
389d38f6f31Sdsl * If dos partition table requested, attempt to load it and
390d38f6f31Sdsl * find disklabel inside a DOS partition. Also, if bad block
391d38f6f31Sdsl * table needed, attempt to extract it as well. Return buffer
392d38f6f31Sdsl * for use in signalling errors if requested.
393d38f6f31Sdsl *
394d38f6f31Sdsl * Returns null on success and an error string on failure.
395d38f6f31Sdsl */
396d38f6f31Sdsl const char *
readdisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * osdep)397a89c49eeSdsl readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
398a89c49eeSdsl struct cpu_disklabel *osdep)
399d38f6f31Sdsl {
400d38f6f31Sdsl int rval;
401d38f6f31Sdsl int i;
402d38f6f31Sdsl mbr_args_t a;
403d38f6f31Sdsl
40440bac108Sdsl memset(&a, 0, sizeof a);
405d38f6f31Sdsl a.lp = lp;
406d38f6f31Sdsl a.strat = strat;
407db0d6983Sdsl a.action = READ_LABEL;
408d38f6f31Sdsl
409d38f6f31Sdsl /* minimal requirements for architypal disk label */
410d38f6f31Sdsl if (lp->d_secsize == 0)
411d38f6f31Sdsl lp->d_secsize = DEV_BSIZE;
412d38f6f31Sdsl if (lp->d_secperunit == 0)
413d38f6f31Sdsl lp->d_secperunit = 0x1fffffff;
4141ee8f4a4Sdyoung a.secperunit = lp->d_secperunit;
415d38f6f31Sdsl lp->d_npartitions = RAW_PART + 1;
416d38f6f31Sdsl for (i = 0; i < RAW_PART; i++) {
417d38f6f31Sdsl lp->d_partitions[i].p_size = 0;
418d38f6f31Sdsl lp->d_partitions[i].p_offset = 0;
419d38f6f31Sdsl }
420d38f6f31Sdsl if (lp->d_partitions[RAW_PART].p_size == 0)
421db0d6983Sdsl lp->d_partitions[RAW_PART].p_size = lp->d_secperunit;
422d38f6f31Sdsl lp->d_partitions[RAW_PART].p_offset = 0;
423d38f6f31Sdsl
424d38f6f31Sdsl /*
425d38f6f31Sdsl * Set partition 'a' to be the whole disk.
426d38f6f31Sdsl * Cleared if we find an mbr or a netbsd label.
427d38f6f31Sdsl */
428d38f6f31Sdsl lp->d_partitions[0].p_size = lp->d_partitions[RAW_PART].p_size;
429d38f6f31Sdsl lp->d_partitions[0].p_fstype = FS_BSDFFS;
430d38f6f31Sdsl
431339f5c57Sreinoud /*
432a0d3b4c9Sreinoud * Get a buffer big enough to read a disklabel in and initialize it
4338257134aSpooka * make it three sectors long for the validate_label(); see comment at
434a0d3b4c9Sreinoud * start of file.
435339f5c57Sreinoud */
4368257134aSpooka a.bp = geteblk(SCANBLOCKS * (int)lp->d_secsize);
437d38f6f31Sdsl a.bp->b_dev = dev;
438d38f6f31Sdsl
439d38f6f31Sdsl if (osdep)
440d38f6f31Sdsl /*
441d38f6f31Sdsl * Scan mbr searching for netbsd partition and saving
442d38f6f31Sdsl * bios partition information to use if the netbsd one
443d38f6f31Sdsl * is absent.
444d38f6f31Sdsl */
445d38f6f31Sdsl rval = scan_mbr(&a, look_netbsd_part);
446d38f6f31Sdsl else
447d38f6f31Sdsl rval = SCAN_CONTINUE;
448d38f6f31Sdsl
44940bac108Sdsl if (rval == SCAN_CONTINUE) {
450d38f6f31Sdsl /* Look at start of disk */
451db0d6983Sdsl rval = validate_label(&a, 0);
452d38f6f31Sdsl }
453d38f6f31Sdsl
454339f5c57Sreinoud if (rval == SCAN_CONTINUE) {
455339f5c57Sreinoud rval = scan_iso_vrs(&a);
456339f5c57Sreinoud }
45740bac108Sdsl #if 0
45840bac108Sdsl /*
45940bac108Sdsl * Save sector where we found the label for the 'don't overwrite
46040bac108Sdsl * the label' check in bounds_check_with_label.
46140bac108Sdsl */
46240bac108Sdsl if (rval == SCAN_FOUND)
46340bac108Sdsl xxx->label_sector = a.label_sector;
46440bac108Sdsl #endif
46540bac108Sdsl
466d38f6f31Sdsl /* Obtain bad sector table if requested and present */
46709dbb89bSpooka #ifdef __HAVE_DISKLABEL_DKBAD
468d38f6f31Sdsl if (rval == SCAN_FOUND && osdep && (lp->d_flags & D_BADSECT)) {
46909dbb89bSpooka struct dkbad *bdp, *db;
470d38f6f31Sdsl int blkno;
471d38f6f31Sdsl
472d38f6f31Sdsl bdp = &osdep->bad;
473d38f6f31Sdsl i = 0;
474d38f6f31Sdsl rval = SCAN_ERROR;
475d38f6f31Sdsl do {
476d38f6f31Sdsl /* read a bad sector table */
477d38f6f31Sdsl blkno = lp->d_secperunit - lp->d_nsectors + i;
478d38f6f31Sdsl if (lp->d_secsize > DEV_BSIZE)
479d38f6f31Sdsl blkno *= lp->d_secsize / DEV_BSIZE;
480d38f6f31Sdsl else
481d38f6f31Sdsl blkno /= DEV_BSIZE / lp->d_secsize;
482d38f6f31Sdsl /* if successful, validate, otherwise try another */
483db0d6983Sdsl if (read_sector(&a, blkno, 1)) {
484d38f6f31Sdsl a.msg = "bad sector table I/O error";
485d38f6f31Sdsl continue;
486d38f6f31Sdsl }
487d38f6f31Sdsl db = (struct dkbad *)(a.bp->b_data);
488d38f6f31Sdsl #define DKBAD_MAGIC 0x4321
489d38f6f31Sdsl if (db->bt_mbz != 0 || db->bt_flag != DKBAD_MAGIC) {
490d38f6f31Sdsl a.msg = "bad sector table corrupted";
491d38f6f31Sdsl continue;
492d38f6f31Sdsl }
493d38f6f31Sdsl rval = SCAN_FOUND;
494d38f6f31Sdsl *bdp = *db;
495d38f6f31Sdsl break;
49666fefd11Sad } while (a.bp->b_error && (i += 2) < 10 &&
497d38f6f31Sdsl i < lp->d_nsectors);
498d38f6f31Sdsl }
49909dbb89bSpooka #endif /* __HAVE_DISKLABEL_DKBAD */
500d38f6f31Sdsl
5019f56dfa5Sad brelse(a.bp, 0);
502db0d6983Sdsl if (rval == SCAN_ERROR || rval == SCAN_CONTINUE)
503cb6200f8Scube return a.msg;
504d38f6f31Sdsl return NULL;
505d38f6f31Sdsl }
506d38f6f31Sdsl
507d38f6f31Sdsl static int
look_netbsd_part(mbr_args_t * a,mbr_partition_t * dp,int slot,uint ext_base)508a89c49eeSdsl look_netbsd_part(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base)
509d38f6f31Sdsl {
510d38f6f31Sdsl struct partition *pp;
511d38f6f31Sdsl int ptn_base = ext_base + le32toh(dp->mbrp_start);
512d38f6f31Sdsl int rval;
513d38f6f31Sdsl
514d38f6f31Sdsl if (
515d38f6f31Sdsl #ifdef COMPAT_386BSD_MBRPART
5161c33b4e6Slukem dp->mbrp_type == MBR_PTYPE_386BSD ||
517d38f6f31Sdsl #endif
5181c33b4e6Slukem dp->mbrp_type == MBR_PTYPE_NETBSD) {
519db0d6983Sdsl rval = validate_label(a, ptn_base);
520d38f6f31Sdsl
5215f7cb96aSdsl #if RAW_PART == 3
522d38f6f31Sdsl /* Put actual location where we found the label into ptn 2 */
5235f7cb96aSdsl if (rval == SCAN_FOUND || a->lp->d_partitions[2].p_size == 0) {
524d38f6f31Sdsl a->lp->d_partitions[2].p_size = le32toh(dp->mbrp_size);
525d38f6f31Sdsl a->lp->d_partitions[2].p_offset = ptn_base;
526d38f6f31Sdsl }
5275f7cb96aSdsl #endif
528d38f6f31Sdsl
529d38f6f31Sdsl /* If we got a netbsd label look no further */
530d38f6f31Sdsl if (rval == SCAN_FOUND)
531d38f6f31Sdsl return rval;
532d38f6f31Sdsl }
533d38f6f31Sdsl
534d38f6f31Sdsl /* Install main partitions into e..h and extended into i+ */
535d38f6f31Sdsl if (ext_base == 0)
536d38f6f31Sdsl slot += 4;
537d38f6f31Sdsl else {
5381c33b4e6Slukem slot = 4 + MBR_PART_COUNT;
539d38f6f31Sdsl pp = &a->lp->d_partitions[slot];
540d38f6f31Sdsl for (; slot < MAXPARTITIONS; pp++, slot++) {
541d38f6f31Sdsl /* This gets called twice - avoid duplicates */
542d38f6f31Sdsl if (pp->p_offset == ptn_base &&
543d38f6f31Sdsl pp->p_size == le32toh(dp->mbrp_size))
544d38f6f31Sdsl break;
545d38f6f31Sdsl if (pp->p_size == 0)
546d38f6f31Sdsl break;
547d38f6f31Sdsl }
548d38f6f31Sdsl }
549d38f6f31Sdsl
550d38f6f31Sdsl if (slot < MAXPARTITIONS) {
551d38f6f31Sdsl /* Stop 'a' being the entire disk */
552d38f6f31Sdsl a->lp->d_partitions[0].p_size = 0;
553d38f6f31Sdsl a->lp->d_partitions[0].p_fstype = 0;
554d38f6f31Sdsl
555d38f6f31Sdsl /* save partition info */
556d38f6f31Sdsl pp = &a->lp->d_partitions[slot];
557d38f6f31Sdsl pp->p_offset = ptn_base;
558d38f6f31Sdsl pp->p_size = le32toh(dp->mbrp_size);
5591c33b4e6Slukem pp->p_fstype = xlat_mbr_fstype(dp->mbrp_type);
560d38f6f31Sdsl
561d38f6f31Sdsl if (slot >= a->lp->d_npartitions)
562d38f6f31Sdsl a->lp->d_npartitions = slot + 1;
563d38f6f31Sdsl }
564d38f6f31Sdsl
565d38f6f31Sdsl return SCAN_CONTINUE;
566d38f6f31Sdsl }
567d38f6f31Sdsl
56827e7f6e2Skamil __noubsan
56978497bf9Schristos static bool
check_label_magic(const struct disklabel * dlp,uint32_t diskmagic)57078497bf9Schristos check_label_magic(const struct disklabel *dlp, uint32_t diskmagic)
57178497bf9Schristos {
5722783cd3dSkamil return memcmp(&dlp->d_magic, &diskmagic, sizeof(diskmagic)) == 0 &&
5732783cd3dSkamil memcmp(&dlp->d_magic2, &diskmagic, sizeof(diskmagic)) == 0;
57478497bf9Schristos }
575d38f6f31Sdsl
576a0bccedeSrin #ifdef DISKLABEL_EI
577a0bccedeSrin /*
578a0bccedeSrin * - For read, convert a label to the native byte order.
579a0bccedeSrin * - For update or write, if a label already exists, keep its byte order.
580a0bccedeSrin * Otherwise, write a new label in the native byte order.
581a0bccedeSrin */
582a0bccedeSrin #endif
583d38f6f31Sdsl static int
validate_label(mbr_args_t * a,uint label_sector)584db0d6983Sdsl validate_label(mbr_args_t *a, uint label_sector)
585d38f6f31Sdsl {
58638522d36Skamil struct disklabel *dlp;
587db0d6983Sdsl char *dlp_lim, *dlp_byte;
588d38f6f31Sdsl int error;
589a0bccedeSrin #ifdef DISKLABEL_EI
590a0bccedeSrin int swapped = 0;
591a0bccedeSrin uint16_t npartitions;
592a0bccedeSrin #endif
593d38f6f31Sdsl
594d38f6f31Sdsl /* Next, dig out disk label */
5958257134aSpooka if (read_sector(a, label_sector, SCANBLOCKS)) {
596d38f6f31Sdsl a->msg = "disk label read failed";
597d38f6f31Sdsl return SCAN_ERROR;
598d38f6f31Sdsl }
599d38f6f31Sdsl
600d38f6f31Sdsl /* Locate disk label within block and validate */
601d38f6f31Sdsl /*
602d38f6f31Sdsl * XXX (dsl) This search may be a waste of time, a lot of other i386
603d38f6f31Sdsl * code assumes the label is at offset LABELOFFSET (=0) in the sector.
604d38f6f31Sdsl *
60540bac108Sdsl * If we want to support disks from other netbsd ports, then the
606d38f6f31Sdsl * code should also allow for a shorter label nearer the end of
607d38f6f31Sdsl * the disk sector, and (IIRC) labels within 8k of the disk start.
608d38f6f31Sdsl */
60940bac108Sdsl dlp = (void *)a->bp->b_data;
61053524e44Schristos dlp_lim = (char *)a->bp->b_data + a->bp->b_bcount - sizeof *dlp;
611789917a9Schristos for (;; dlp = (void *)((char *)dlp + sizeof(uint32_t))) {
612db0d6983Sdsl if ((char *)dlp > dlp_lim) {
613db0d6983Sdsl if (a->action != WRITE_LABEL)
61440bac108Sdsl return SCAN_CONTINUE;
615fcddf71fSwiz /* Write at arch. dependent default location */
61653524e44Schristos dlp_byte = (char *)a->bp->b_data + LABELOFFSET;
617db0d6983Sdsl if (label_sector)
618db0d6983Sdsl dlp_byte += MBR_LABELSECTOR * a->lp->d_secsize;
619db0d6983Sdsl else
620db0d6983Sdsl dlp_byte += LABELSECTOR * a->lp->d_secsize;
621db0d6983Sdsl dlp = (void *)dlp_byte;
622db0d6983Sdsl break;
623db0d6983Sdsl }
62438522d36Skamil if (!check_label_magic(dlp, DISKMAGIC))
625a0bccedeSrin #ifdef DISKLABEL_EI
626a0bccedeSrin {
62738522d36Skamil if (!check_label_magic(dlp, bswap32(DISKMAGIC)))
628d38f6f31Sdsl continue;
629a0bccedeSrin
630a0bccedeSrin /*
631a0bccedeSrin * The label is in the other byte order. We need to
632a0bccedeSrin * checksum before swapping the byte order.
633a0bccedeSrin */
63438522d36Skamil npartitions = bswap16(dlp->d_npartitions);
635a0bccedeSrin if (npartitions > MAXPARTITIONS ||
63638522d36Skamil dkcksum_sized(dlp, npartitions) != 0)
637a0bccedeSrin goto corrupted;
638a0bccedeSrin
639a0bccedeSrin swapped = 1;
640a0bccedeSrin }
641a0bccedeSrin #else
642a0bccedeSrin continue;
643a0bccedeSrin #endif
64438522d36Skamil else if (dlp->d_npartitions > MAXPARTITIONS ||
64538522d36Skamil dkcksum(dlp) != 0) {
646a0bccedeSrin #ifdef DISKLABEL_EI
647a0bccedeSrin corrupted:
648a0bccedeSrin #endif
649d38f6f31Sdsl a->msg = "disk label corrupted";
650d38f6f31Sdsl continue;
651d38f6f31Sdsl }
65240bac108Sdsl break;
65340bac108Sdsl }
65440bac108Sdsl
655db0d6983Sdsl switch (a->action) {
656d38f6f31Sdsl case READ_LABEL:
657a0bccedeSrin #ifdef DISKLABEL_EI
658a0bccedeSrin if (swapped)
6594a1c505cSmrg disklabel_swap(a->lp, dlp);
660a0bccedeSrin else
66138522d36Skamil *a->lp = *dlp;
662a0bccedeSrin #else
66338522d36Skamil *a->lp = *dlp;
664a0bccedeSrin #endif
6651ee8f4a4Sdyoung if ((a->msg = convertdisklabel(a->lp, a->strat, a->bp,
6661ee8f4a4Sdyoung a->secperunit)) != NULL)
6671ee8f4a4Sdyoung return SCAN_ERROR;
66840bac108Sdsl a->label_sector = label_sector;
66940bac108Sdsl return SCAN_FOUND;
67040bac108Sdsl case UPDATE_LABEL:
671d38f6f31Sdsl case WRITE_LABEL:
672a0bccedeSrin #ifdef DISKLABEL_EI
673a0bccedeSrin /* DO NOT swap a->lp itself for later references. */
674a0bccedeSrin if (swapped)
6754a1c505cSmrg disklabel_swap(dlp, a->lp);
676a0bccedeSrin else
67738522d36Skamil *dlp = *a->lp;
678a0bccedeSrin #else
67938522d36Skamil *dlp = *a->lp;
680a0bccedeSrin #endif
6814a780c9aSad a->bp->b_oflags &= ~BO_DONE;
6824a780c9aSad a->bp->b_flags &= ~B_READ;
683d38f6f31Sdsl a->bp->b_flags |= B_WRITE;
684d38f6f31Sdsl (*a->strat)(a->bp);
685d38f6f31Sdsl error = biowait(a->bp);
686d38f6f31Sdsl if (error != 0) {
687d38f6f31Sdsl a->error = error;
688d38f6f31Sdsl a->msg = "disk label write failed";
689d38f6f31Sdsl return SCAN_ERROR;
690d38f6f31Sdsl }
69140bac108Sdsl a->written++;
69240bac108Sdsl /* Write label to all mbr partitions */
693d38f6f31Sdsl return SCAN_CONTINUE;
69440bac108Sdsl default:
69540bac108Sdsl return SCAN_ERROR;
69640bac108Sdsl }
697d38f6f31Sdsl }
698d38f6f31Sdsl
699d38f6f31Sdsl /*
700d38f6f31Sdsl * Write disk label back to device after modification.
701d38f6f31Sdsl */
702d38f6f31Sdsl int
writedisklabel(dev_t dev,void (* strat)(struct buf *),struct disklabel * lp,struct cpu_disklabel * osdep)703a89c49eeSdsl writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp,
704a89c49eeSdsl struct cpu_disklabel *osdep)
705d38f6f31Sdsl {
706d38f6f31Sdsl mbr_args_t a;
707d38f6f31Sdsl
70840bac108Sdsl memset(&a, 0, sizeof a);
709d38f6f31Sdsl a.lp = lp;
710d38f6f31Sdsl a.strat = strat;
711d38f6f31Sdsl
712d38f6f31Sdsl /* get a buffer and initialize it */
7138257134aSpooka a.bp = geteblk(SCANBLOCKS * (int)lp->d_secsize);
714d38f6f31Sdsl a.bp->b_dev = dev;
715d38f6f31Sdsl
716db0d6983Sdsl /* osdep => we expect an mbr with label in netbsd ptn */
717db0d6983Sdsl a.action = osdep != NULL ? WRITE_LABEL : UPDATE_LABEL;
718db0d6983Sdsl
719db0d6983Sdsl /* Write/update the label to every netbsd mbr partition */
72040bac108Sdsl scan_mbr(&a, write_netbsd_label);
721d38f6f31Sdsl
722db0d6983Sdsl /* Old write the label at the start of the volume on disks that
723db0d6983Sdsl * don't have a valid mbr (always update an existing one) */
724db0d6983Sdsl a.action = a.found_mbr ? UPDATE_LABEL : WRITE_LABEL;
725db0d6983Sdsl validate_label(&a, 0);
72640bac108Sdsl
72740bac108Sdsl if (a.written == 0 && a.error == 0)
728d38f6f31Sdsl a.error = ESRCH;
729d38f6f31Sdsl
7309f56dfa5Sad brelse(a.bp, 0);
731d38f6f31Sdsl return a.error;
732d38f6f31Sdsl }
733d38f6f31Sdsl
734d38f6f31Sdsl static int
write_netbsd_label(mbr_args_t * a,mbr_partition_t * dp,int slot,uint ext_base)7351a7bc55dSyamt write_netbsd_label(mbr_args_t *a, mbr_partition_t *dp, int slot, uint ext_base)
736d38f6f31Sdsl {
737d38f6f31Sdsl int ptn_base = ext_base + le32toh(dp->mbrp_start);
738d38f6f31Sdsl
7391c33b4e6Slukem if (dp->mbrp_type != MBR_PTYPE_NETBSD)
740d38f6f31Sdsl return SCAN_CONTINUE;
741d38f6f31Sdsl
742db0d6983Sdsl return validate_label(a, ptn_base);
743d38f6f31Sdsl }
744