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