1 /* $NetBSD: disksubr.c,v 1.23 2008/01/02 11:48:28 ad 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/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.23 2008/01/02 11:48:28 ad Exp $"); 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/buf.h> 43 #include <sys/disklabel.h> 44 #include <sys/disk.h> 45 #include <ufs/ufs/dinode.h> 46 #include <ufs/ffs/fs.h> 47 48 #include <machine/disklabel.h> 49 50 static int disklabel_bsd_to_sgimips(struct disklabel *lp, 51 struct sgi_boot_block *vh); 52 static const char *disklabel_sgimips_to_bsd(struct sgi_boot_block *vh, 53 struct disklabel *lp); 54 55 int mipsvh_cksum(struct sgi_boot_block *vhp); 56 57 #define LABELSIZE(lp) ((char *)&lp->d_partitions[lp->d_npartitions] - \ 58 (char *)lp) 59 60 61 /* 62 * Attempt to read a disk label from a device using the indicated 63 * strategy routine. The label must be partly set up before this: 64 * secpercyl, secsize and anything required for a block i/o read 65 * operation in the driver's strategy/start routines must be 66 * filled in before calling us. 67 * 68 * Return buffer for use in signalling errors if requested. 69 * 70 * Returns null on success and an error string on failure. 71 */ 72 73 const char * 74 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp) 75 { 76 struct buf *bp; 77 struct disklabel *dlp; 78 struct sgi_boot_block *slp; 79 int err; 80 81 /* Minimal requirements for archetypal disk label. */ 82 if (lp->d_secsize == 0) 83 lp->d_secsize = DEV_BSIZE; 84 if (lp->d_secperunit == 0) 85 lp->d_secperunit = 0x1fffffff; 86 87 /* Obtain buffer to probe drive with. */ 88 bp = geteblk((int)lp->d_secsize); 89 90 bp->b_dev = dev; 91 bp->b_blkno = LABELSECTOR; 92 bp->b_bcount = lp->d_secsize; 93 bp->b_flags |= B_READ; 94 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 95 (*strat)(bp); 96 err = biowait(bp); 97 brelse(bp, 0); 98 99 if (err) 100 return "error reading disklabel"; 101 102 /* Check for NetBSD label in second sector */ 103 dlp = (struct disklabel *)((char *)bp->b_data + LABELOFFSET); 104 if (dlp->d_magic == DISKMAGIC) 105 if (!dkcksum(dlp)) { 106 memcpy(lp, dlp, LABELSIZE(dlp)); 107 return NULL; /* NetBSD label found */ 108 } 109 110 bp = geteblk((int)lp->d_secsize); 111 bp->b_dev = dev; 112 bp->b_blkno = 0; 113 bp->b_bcount = lp->d_secsize; 114 bp->b_flags |= B_READ; 115 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 116 (*strat)(bp); 117 err = biowait(bp); 118 brelse(bp, 0); 119 120 if (err) 121 return "error reading volume header"; 122 123 /* Check for a SGI label. */ 124 slp = (struct sgi_boot_block *)bp->b_data; 125 if (be32toh(slp->magic) != SGI_BOOT_BLOCK_MAGIC) 126 return "no disk label"; 127 128 return disklabel_sgimips_to_bsd(slp, lp); 129 } 130 131 int 132 setdisklabel(struct disklabel *olp, struct disklabel *nlp, unsigned long openmask, struct cpu_disklabel *clp) 133 { 134 register int i; 135 register struct partition *opp, *npp; 136 137 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 138 dkcksum(nlp) != 0) 139 return (EINVAL); 140 while ((i = ffs(openmask)) != 0) { 141 i--; 142 openmask &= ~(1 << i); 143 if (nlp->d_npartitions <= i) 144 return (EBUSY); 145 opp = &olp->d_partitions[i]; 146 npp = &nlp->d_partitions[i]; 147 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 148 return (EBUSY); 149 /* 150 * Copy internally-set partition information 151 * if new label doesn't include it. XXX 152 */ 153 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 154 npp->p_fstype = opp->p_fstype; 155 npp->p_fsize = opp->p_fsize; 156 npp->p_frag = opp->p_frag; 157 npp->p_cpg = opp->p_cpg; 158 } 159 } 160 nlp->d_checksum = 0; 161 nlp->d_checksum = dkcksum(nlp); 162 *olp = *nlp; 163 return (0); 164 } 165 166 #define dkpart(dev) (minor(dev) & 07) 167 #define dkminor(unit, part) (((unit) << 3) | (part)) 168 169 int 170 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp) 171 { 172 struct buf *bp; 173 int labelpart; 174 int error; 175 176 labelpart = dkpart(dev); 177 if (lp->d_partitions[labelpart].p_offset != 0) { 178 if (lp->d_partitions[0].p_offset != 0) 179 return (EXDEV); /* not quite right */ 180 labelpart = 0; 181 } 182 183 /* Read sgimips volume header before merging NetBSD partition info */ 184 bp = geteblk((int)lp->d_secsize); 185 186 bp->b_dev = dev; 187 bp->b_blkno = 0; 188 bp->b_bcount = lp->d_secsize; 189 bp->b_flags |= B_READ; 190 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 191 (*strat)(bp); 192 193 if((error = biowait(bp)) != 0) 194 goto ioerror; 195 196 if ((error = disklabel_bsd_to_sgimips(lp, (void *)bp->b_data)) != 0) 197 goto ioerror; 198 199 /* Write sgimips label to first sector */ 200 bp->b_oflags &= ~(BO_DONE); 201 bp->b_flags &= ~(B_READ); 202 bp->b_flags |= B_WRITE; 203 (*strat)(bp); 204 if ((error = biowait(bp)) != 0) 205 goto ioerror; 206 207 /* Write NetBSD disk label to second sector */ 208 memset(bp->b_data, 0, lp->d_secsize); 209 memcpy(bp->b_data, lp, sizeof(*lp)); 210 bp->b_blkno = LABELSECTOR; 211 bp->b_bcount = lp->d_secsize; 212 bp->b_cylinder = bp->b_blkno / lp->d_secpercyl; 213 bp->b_oflags &= ~(BO_DONE); 214 bp->b_flags &= ~(B_READ); 215 bp->b_flags |= B_WRITE; 216 (*strat)(bp); 217 error = biowait(bp); 218 219 ioerror: 220 brelse(bp, 0); 221 return error; 222 } 223 224 struct partitionmap { 225 int mips_part; /* sgimips partition number */ 226 int mips_type; /* sgimips partition type */ 227 int bsd_part; /* BSD partition number */ 228 int bsd_type; /* BSD partition type */ 229 }; 230 231 struct partitionmap partition_map[] = { 232 /* slice sgimips type BSD BSD Type */ 233 {0, SGI_PTYPE_BSD, 0, FS_BSDFFS}, 234 {1, SGI_PTYPE_RAW, 1, FS_SWAP}, 235 {2, SGI_PTYPE_BSD, 10, FS_BSDFFS}, 236 {3, SGI_PTYPE_BSD, 3, FS_BSDFFS}, 237 {4, SGI_PTYPE_BSD, 4, FS_BSDFFS}, 238 {5, SGI_PTYPE_BSD, 5, FS_BSDFFS}, 239 {6, SGI_PTYPE_BSD, 6, FS_BSDFFS}, 240 {7, SGI_PTYPE_BSD, 7, FS_BSDFFS}, 241 {8, SGI_PTYPE_VOLHDR, 8, FS_OTHER}, 242 {9, SGI_PTYPE_BSD, 9, FS_BSDFFS}, 243 {10, SGI_PTYPE_VOLUME, 2, FS_OTHER}, 244 {11, SGI_PTYPE_BSD, 11, FS_BSDFFS}, 245 {12, SGI_PTYPE_BSD, 12, FS_BSDFFS}, 246 {13, SGI_PTYPE_BSD, 13, FS_BSDFFS}, 247 {14, SGI_PTYPE_BSD, 14, FS_BSDFFS}, 248 {15, SGI_PTYPE_BSD, 15, FS_BSDFFS} 249 }; 250 251 #define NPARTMAP (sizeof(partition_map)/sizeof(struct partitionmap)) 252 253 /* 254 * Convert a sgimips disk label into a NetBSD disk label. 255 * 256 * Returns NULL on success, otherwise an error string 257 */ 258 static const char * 259 disklabel_sgimips_to_bsd(struct sgi_boot_block *vh, struct disklabel *lp) 260 { 261 int i, bp, mp; 262 struct partition *lpp; 263 if (mipsvh_cksum(vh)) 264 return ("sgimips disk label corrupted"); 265 266 #if 0 /* ARCS ignores dp_secbytes and it may be wrong; use default instead */ 267 lp->d_secsize = vh->dp.dp_secbytes; 268 #endif 269 lp->d_nsectors = vh->dp.dp_secs; 270 lp->d_ntracks = vh->dp.dp_trks0; 271 lp->d_ncylinders = vh->dp.dp_cyls; 272 lp->d_interleave = vh->dp.dp_interleave; 273 274 275 lp->d_secpercyl = lp->d_nsectors * lp->d_ntracks; 276 lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; 277 278 lp->d_bbsize = BBSIZE; 279 lp->d_sbsize = SBLOCKSIZE; 280 lp->d_npartitions = MAXPARTITIONS; 281 282 for (i = 0; i < 16; i++) { 283 mp = partition_map[i].mips_part; 284 bp = partition_map[i].bsd_part; 285 286 lpp = &lp->d_partitions[bp]; 287 lpp->p_offset = vh->partitions[mp].first; 288 /* XXX ARCS ignores dp_secbytes on calculating offsets */ 289 if (lp->d_secsize > DEV_BSIZE) 290 lpp->p_offset /= lp->d_secsize / DEV_BSIZE; 291 lpp->p_size = vh->partitions[mp].blocks; 292 lpp->p_fstype = partition_map[i].bsd_type; 293 if (lpp->p_fstype == FS_BSDFFS) { 294 lpp->p_fsize = 1024; 295 lpp->p_frag = 8; 296 lpp->p_cpg = 16; 297 } 298 } 299 return NULL; 300 } 301 302 303 /* 304 * Convert a NetBSD disk label into a sgimips disk label. 305 * 306 * Returns NULL on success, otherwise an error string 307 */ 308 static int 309 disklabel_bsd_to_sgimips(struct disklabel *lp, struct sgi_boot_block *vh) 310 { 311 int i, bp, mp; 312 struct partition *lpp; 313 314 if (vh->magic != SGI_BOOT_BLOCK_MAGIC || mipsvh_cksum(vh) != 0) { 315 memset((void *)vh, 0, sizeof *vh); 316 vh->magic = SGI_BOOT_BLOCK_MAGIC; 317 vh->root = 0; /* a*/ 318 vh->swap = 1; /* b*/ 319 } 320 321 strcpy(vh->bootfile, "/netbsd"); 322 vh->dp.dp_skew = lp->d_trackskew; 323 vh->dp.dp_gap1 = 1; /* XXX */ 324 vh->dp.dp_gap2 = 1; /* XXX */ 325 vh->dp.dp_cyls = lp->d_ncylinders; 326 vh->dp.dp_shd0 = 0; 327 vh->dp.dp_trks0 = lp->d_ntracks; 328 vh->dp.dp_secs = lp->d_nsectors; 329 #if 0 /* ARCS ignores dp_secbytes; leave it default */ 330 vh->dp.dp_secbytes = lp->d_secsize; 331 #else 332 vh->dp.dp_secbytes = SGI_BOOT_BLOCK_BLOCKSIZE; 333 #endif 334 vh->dp.dp_interleave = lp->d_interleave; 335 vh->dp.dp_nretries = 22; 336 337 for (i = 0; i < 16; i++) { 338 mp = partition_map[i].mips_part; 339 bp = partition_map[i].bsd_part; 340 341 lpp = &lp->d_partitions[bp]; 342 vh->partitions[mp].first = lpp->p_offset; 343 /* XXX ARCS ignores dp_secbytes on calculating offsets */ 344 if (lp->d_secsize > SGI_BOOT_BLOCK_BLOCKSIZE) 345 vh->partitions[mp].first *= 346 lp->d_secsize / SGI_BOOT_BLOCK_BLOCKSIZE; 347 vh->partitions[mp].blocks = lpp->p_size; 348 vh->partitions[mp].type = partition_map[i].mips_type; 349 } 350 351 /* 352 * Create a fake partition for bootstrap code (or SASH) 353 */ 354 vh->partitions[8].first = 0; 355 vh->partitions[8].blocks = vh->partitions[vh->root].first + 356 BBSIZE / vh->dp.dp_secbytes; 357 vh->partitions[8].type = SGI_PTYPE_VOLHDR; 358 359 vh->checksum = 0; 360 vh->checksum = -mipsvh_cksum(vh); 361 return 0; 362 } 363 364 /* 365 * Compute checksum for MIPS disk volume header 366 * 367 * Mips volume header checksum is the 32bit 2's complement sum 368 * of the entire volume header structure 369 */ 370 int 371 mipsvh_cksum(struct sgi_boot_block *vhp) 372 { 373 int i, *ptr; 374 int cksum = 0; 375 376 ptr = (int *)vhp; 377 i = sizeof(*vhp) / sizeof(*ptr); 378 while (i--) 379 cksum += *ptr++; 380 return cksum; 381 } 382 383