1 /* $NetBSD: disksubr.c,v 1.8 2002/03/13 13:12:29 simonb Exp $ */ 2 3 /* 4 * Copyright (c) 2001 Christopher Sekiya 5 * Copyright (c) 2001 Wayne Knowles 6 * Copyright (c) 2000 Soren S. Jorvang 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the 20 * NetBSD Project. See http://www.netbsd.org/ for 21 * information about NetBSD. 22 * 4. The name of the author may not be used to endorse or promote products 23 * derived from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/buf.h> 40 #include <sys/disklabel.h> 41 #include <sys/disk.h> 42 #include <ufs/ufs/dinode.h> 43 #include <ufs/ffs/fs.h> 44 45 #include <machine/disklabel.h> 46 47 static int disklabel_bsd_to_sgimips(struct disklabel *lp, struct sgilabel *vh); 48 static char *disklabel_sgimips_to_bsd(struct sgilabel *vh, struct disklabel *lp); 49 50 int mipsvh_cksum(struct sgilabel *vhp); 51 52 #define LABELSIZE(lp) ((char *)&lp->d_partitions[lp->d_npartitions] - \ 53 (char *)lp) 54 55 56 /* 57 * Attempt to read a disk label from a device using the indicated 58 * strategy routine. The label must be partly set up before this: 59 * secpercyl, secsize and anything required for a block i/o read 60 * operation in the driver's strategy/start routines must be 61 * filled in before calling us. 62 * 63 * Return buffer for use in signalling errors if requested. 64 * 65 * Returns null on success and an error string on failure. 66 */ 67 68 char * 69 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp) 70 { 71 struct buf *bp; 72 struct disklabel *dlp; 73 struct sgilabel *slp; 74 int err; 75 76 /* Minimal requirements for archetypal disk label. */ 77 if (lp->d_secsize == 0) 78 lp->d_secsize = DEV_BSIZE; 79 if (lp->d_secperunit == 0) 80 lp->d_secperunit = 0x1fffffff; 81 82 /* Obtain buffer to probe drive with. */ 83 bp = geteblk((int)lp->d_secsize); 84 85 bp->b_dev = dev; 86 bp->b_blkno = LABELSECTOR; 87 bp->b_bcount = lp->d_secsize; 88 bp->b_flags |= B_READ; 89 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 90 (*strat)(bp); 91 err = biowait(bp); 92 brelse(bp); 93 94 if (err) 95 return "error reading disklabel"; 96 97 /* Check for NetBSD label in second sector */ 98 dlp = (struct disklabel *)(bp->b_un.b_addr + LABELOFFSET); 99 if (dlp->d_magic == DISKMAGIC) 100 if (!dkcksum(dlp)) { 101 memcpy(lp, dlp, LABELSIZE(dlp)); 102 return NULL; /* NetBSD label found */ 103 } 104 105 bp = geteblk((int)lp->d_secsize); 106 bp->b_dev = dev; 107 bp->b_blkno = 0; 108 bp->b_bcount = lp->d_secsize; 109 bp->b_flags |= B_READ; 110 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 111 (*strat)(bp); 112 err = biowait(bp); 113 brelse(bp); 114 115 if (err) 116 return "error reading volume header"; 117 118 /* Check for a SGI label. */ 119 slp = (struct sgilabel *)bp->b_un.b_addr; 120 if (be32toh(slp->magic) != SGILABEL_MAGIC) 121 return "no disk label"; 122 123 return disklabel_sgimips_to_bsd(slp, lp); 124 } 125 126 int 127 setdisklabel(struct disklabel *olp, struct disklabel *nlp, unsigned long openmask, struct cpu_disklabel *clp) 128 { 129 register int i; 130 register struct partition *opp, *npp; 131 132 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 133 dkcksum(nlp) != 0) 134 return (EINVAL); 135 while ((i = ffs(openmask)) != 0) { 136 i--; 137 openmask &= ~(1 << i); 138 if (nlp->d_npartitions <= i) 139 return (EBUSY); 140 opp = &olp->d_partitions[i]; 141 npp = &nlp->d_partitions[i]; 142 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 143 return (EBUSY); 144 /* 145 * Copy internally-set partition information 146 * if new label doesn't include it. XXX 147 */ 148 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 149 npp->p_fstype = opp->p_fstype; 150 npp->p_fsize = opp->p_fsize; 151 npp->p_frag = opp->p_frag; 152 npp->p_cpg = opp->p_cpg; 153 } 154 } 155 nlp->d_checksum = 0; 156 nlp->d_checksum = dkcksum(nlp); 157 *olp = *nlp; 158 return (0); 159 } 160 161 #define dkpart(dev) (minor(dev) & 07) 162 #define dkminor(unit, part) (((unit) << 3) | (part)) 163 164 int 165 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp) 166 { 167 struct buf *bp; 168 int labelpart; 169 int error; 170 171 labelpart = dkpart(dev); 172 if (lp->d_partitions[labelpart].p_offset != 0) { 173 if (lp->d_partitions[0].p_offset != 0) 174 return (EXDEV); /* not quite right */ 175 labelpart = 0; 176 } 177 178 /* Read sgimips volume header before merging NetBSD partition info */ 179 bp = geteblk((int)lp->d_secsize); 180 181 bp->b_dev = dev; 182 bp->b_blkno = 0; 183 bp->b_bcount = lp->d_secsize; 184 bp->b_flags |= B_READ; 185 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 186 (*strat)(bp); 187 188 if((error = biowait(bp)) != 0) 189 goto ioerror; 190 191 if ((error = disklabel_bsd_to_sgimips(lp, (void *)bp->b_data)) != 0) 192 goto ioerror; 193 194 /* Write sgimips label to first sector */ 195 bp->b_flags &= ~(B_READ|B_DONE); 196 bp->b_flags |= B_WRITE; 197 (*strat)(bp); 198 if ((error = biowait(bp)) != 0) 199 goto ioerror; 200 201 /* Write NetBSD disk label to second sector */ 202 memset(bp->b_data, 0, lp->d_secsize); 203 memcpy(bp->b_data, lp, sizeof(*lp)); 204 bp->b_blkno = LABELSECTOR; 205 bp->b_bcount = lp->d_secsize; 206 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 207 bp->b_flags &= ~(B_READ | B_DONE); 208 bp->b_flags |= B_WRITE; 209 (*strat)(bp); 210 error = biowait(bp); 211 212 ioerror: 213 brelse(bp); 214 return error; 215 } 216 217 218 /* 219 * Determine the size of the transfer, and make sure it is 220 * within the boundaries of the partition. Adjust transfer 221 * if needed, and signal errors or early completion. 222 */ 223 int 224 bounds_check_with_label(struct buf *bp, struct disklabel *lp, int wlabel) 225 { 226 struct partition *p = lp->d_partitions + DISKPART(bp->b_dev); 227 int maxsz = p->p_size; 228 int sz = (bp->b_bcount + DEV_BSIZE - 1) >> DEV_BSHIFT; 229 230 /* 231 * Overwriting disk label? 232 * The label is always in sector LABELSECTOR. 233 */ 234 if (bp->b_blkno + p->p_offset <= LABELSECTOR && 235 (bp->b_flags & B_READ) == 0 && wlabel == 0) { 236 bp->b_error = EROFS; 237 goto bad; 238 } 239 240 /* 241 * Beyond partition? 242 */ 243 if (bp->b_blkno < 0 || bp->b_blkno + sz > maxsz) { 244 /* if exactly at end of disk, return an EOF */ 245 if (bp->b_blkno == maxsz) { 246 bp->b_resid = bp->b_bcount; 247 return(0); 248 } 249 /* or truncate if part of it fits */ 250 sz = maxsz - bp->b_blkno; 251 if (sz <= 0) { 252 bp->b_error = EINVAL; 253 goto bad; 254 } 255 bp->b_bcount = sz << DEV_BSHIFT; 256 } 257 258 /* calculate cylinder for disksort to order transfers with */ 259 bp->b_resid = (bp->b_blkno + p->p_offset) / lp->d_secpercyl; 260 return(1); 261 bad: 262 bp->b_flags |= B_ERROR; 263 return(-1); 264 } 265 266 struct partitionmap { 267 int mips_part; /* sgimips partition number */ 268 int mips_type; /* sgimips partition type */ 269 int bsd_part; /* BSD partition number */ 270 int bsd_type; /* BSD partition type */ 271 }; 272 273 struct partitionmap partition_map[] = { 274 /* slice sgimips type BSD BSD Type */ 275 {0, SGI_PTYPE_BSD, 0, FS_BSDFFS}, 276 {1, SGI_PTYPE_RAW, 1, FS_SWAP}, 277 {2, SGI_PTYPE_BSD, 10, FS_BSDFFS}, 278 {3, SGI_PTYPE_BSD, 3, FS_BSDFFS}, 279 {4, SGI_PTYPE_BSD, 4, FS_BSDFFS}, 280 {5, SGI_PTYPE_BSD, 5, FS_BSDFFS}, 281 {6, SGI_PTYPE_BSD, 6, FS_BSDFFS}, 282 {7, SGI_PTYPE_BSD, 7, FS_BSDFFS}, 283 {8, SGI_PTYPE_VOLHDR, 8, FS_OTHER}, 284 {9, SGI_PTYPE_BSD, 9, FS_BSDFFS}, 285 {10, SGI_PTYPE_VOLUME, 2, FS_OTHER}, 286 {11, SGI_PTYPE_BSD, 11, FS_BSDFFS}, 287 {12, SGI_PTYPE_BSD, 12, FS_BSDFFS}, 288 {13, SGI_PTYPE_BSD, 13, FS_BSDFFS}, 289 {14, SGI_PTYPE_BSD, 14, FS_BSDFFS}, 290 {15, SGI_PTYPE_BSD, 15, FS_BSDFFS} 291 }; 292 293 #define NPARTMAP (sizeof(partition_map)/sizeof(struct partitionmap)) 294 295 /* 296 * Convert a sgimips disk label into a NetBSD disk label. 297 * 298 * Returns NULL on success, otherwise an error string 299 */ 300 static char * 301 disklabel_sgimips_to_bsd(struct sgilabel *vh, struct disklabel *lp) 302 { 303 int i, bp, mp; 304 struct partition *lpp; 305 if (mipsvh_cksum(vh)) 306 return ("sgimips disk label corrupted"); 307 308 lp->d_secsize = vh->dp.dp_secbytes; 309 lp->d_nsectors = vh->dp.dp_secs; 310 lp->d_ntracks = vh->dp.dp_trks0; 311 lp->d_ncylinders = vh->dp.dp_cyls; 312 lp->d_interleave = vh->dp.dp_interleave; 313 314 315 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; 316 lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; 317 318 lp->d_bbsize = BBSIZE; 319 lp->d_sbsize = SBSIZE; 320 lp->d_npartitions = MAXPARTITIONS; 321 322 for (i = 0; i < 16; i++) { 323 mp = partition_map[i].mips_part; 324 bp = partition_map[i].bsd_part; 325 326 lpp = &lp->d_partitions[bp]; 327 lpp->p_offset = vh->partitions[mp].first; 328 lpp->p_size = vh->partitions[mp].blocks; 329 lpp->p_fstype = partition_map[i].bsd_type; 330 if (lpp->p_fstype == FS_BSDFFS) { 331 lpp->p_fsize = 1024; 332 lpp->p_frag = 8; 333 lpp->p_cpg = 16; 334 } 335 } 336 return NULL; 337 } 338 339 340 /* 341 * Convert a NetBSD disk label into a sgimips disk label. 342 * 343 * Returns NULL on success, otherwise an error string 344 */ 345 static int 346 disklabel_bsd_to_sgimips(struct disklabel *lp, struct sgilabel *vh) 347 { 348 int i, bp, mp; 349 struct partition *lpp; 350 351 if (vh->magic != SGILABEL_MAGIC || mipsvh_cksum(vh) != 0) { 352 memset((void *)vh, 0, sizeof *vh); 353 vh->magic = SGILABEL_MAGIC; 354 vh->root = 0; /* a*/ 355 vh->swap = 1; /* b*/ 356 } 357 358 strcpy(vh->bootfile, "/netbsd"); 359 vh->dp.dp_skew = lp->d_trackskew; 360 vh->dp.dp_gap1 = 1; /* XXX */ 361 vh->dp.dp_gap2 = 1; /* XXX */ 362 vh->dp.dp_cyls = lp->d_ncylinders; 363 vh->dp.dp_shd0 = 0; 364 vh->dp.dp_trks0 = lp->d_ntracks; 365 vh->dp.dp_secs = lp->d_nsectors; 366 vh->dp.dp_secbytes = lp->d_secsize; 367 vh->dp.dp_interleave = lp->d_interleave; 368 vh->dp.dp_nretries = 22; 369 370 for (i = 0; i < 16; i++) { 371 mp = partition_map[i].mips_part; 372 bp = partition_map[i].bsd_part; 373 374 lpp = &lp->d_partitions[bp]; 375 vh->partitions[mp].first = lpp->p_offset; 376 vh->partitions[mp].blocks = lpp->p_size; 377 vh->partitions[mp].type = partition_map[i].mips_type; 378 } 379 380 /* 381 * Create a fake partition for bootstrap code (or SASH) 382 */ 383 vh->partitions[8].first = 0; 384 vh->partitions[8].blocks = vh->partitions[vh->root].first + 385 BBSIZE / vh->dp.dp_secbytes; 386 vh->partitions[8].type = SGI_PTYPE_VOLHDR; 387 388 vh->checksum = 0; 389 vh->checksum = -mipsvh_cksum(vh); 390 return 0; 391 } 392 393 /* 394 * Compute checksum for MIPS disk volume header 395 * 396 * Mips volume header checksum is the 32bit 2's complement sum 397 * of the entire volume header structure 398 */ 399 int 400 mipsvh_cksum(struct sgilabel *vhp) 401 { 402 int i, *ptr; 403 int cksum = 0; 404 405 ptr = (int *)vhp; 406 i = sizeof(*vhp) / sizeof(*ptr); 407 while (i--) 408 cksum += *ptr++; 409 return cksum; 410 } 411 412