1 /* $NetBSD: disksubr.c,v 1.39 2009/03/16 23:11:09 dsl Exp $ */ 2 3 /* 4 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. 5 * All rights reserved. 6 * 7 * Authors: Keith Bostic, Chris G. Demetriou 8 * 9 * Permission to use, copy, modify and distribute this software and 10 * its documentation is hereby granted, provided that both the copyright 11 * notice and this permission notice appear in all copies of the 12 * software, derivative works or modified versions, and any portions 13 * thereof, and that both notices appear in supporting documentation. 14 * 15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 16 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 17 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 18 * 19 * Carnegie Mellon requests users of this software to return to 20 * 21 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 22 * School of Computer Science 23 * Carnegie Mellon University 24 * Pittsburgh PA 15213-3890 25 * 26 * any improvements or extensions that they make and grant Carnegie the 27 * rights to redistribute these changes. 28 */ 29 30 #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 31 32 __KERNEL_RCSID(0, "$NetBSD: disksubr.c,v 1.39 2009/03/16 23:11:09 dsl Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/buf.h> 37 #include <sys/ioccom.h> 38 #include <sys/device.h> 39 #include <sys/disklabel.h> 40 #include <sys/disk.h> 41 42 #include <machine/cpu.h> 43 #include <machine/autoconf.h> 44 45 extern struct device *bootdv; 46 47 /* 48 * Attempt to read a disk label from a device 49 * using the indicated strategy routine. 50 * The label must be partly set up before this: 51 * secpercyl and anything required in the strategy routine 52 * (e.g., sector size) must be filled in before calling us. 53 * Returns null on success and an error string on failure. 54 */ 55 const char * 56 readdisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp) 57 { 58 struct buf *bp; 59 struct disklabel *dlp; 60 struct dkbad *bdp; 61 const char *msg = NULL; 62 int i; 63 64 /* minimal requirements for archtypal disk label */ 65 if (lp->d_secsize == 0) 66 lp->d_secsize = DEV_BSIZE; 67 if (lp->d_secperunit == 0) 68 lp->d_secperunit = 0x1fffffff; 69 lp->d_npartitions = RAW_PART + 1; 70 if (lp->d_partitions[RAW_PART].p_size == 0) 71 lp->d_partitions[RAW_PART].p_size = lp->d_secperunit; 72 lp->d_partitions[RAW_PART].p_offset = 0; 73 74 /* obtain buffer to probe drive with */ 75 bp = geteblk((int)lp->d_secsize); 76 77 /* next, dig out disk label */ 78 bp->b_dev = dev; 79 bp->b_blkno = LABELSECTOR; 80 bp->b_cylinder = 0; 81 bp->b_bcount = lp->d_secsize; 82 bp->b_flags |= B_READ; 83 (*strat)(bp); 84 85 /* if successful, locate disk label within block and validate */ 86 if (biowait(bp)) { 87 msg = "disk label read error"; 88 goto done; 89 } 90 91 dlp = (struct disklabel *)((char *)bp->b_data + LABELOFFSET); 92 if (dlp->d_magic == DISKMAGIC) { 93 if (dkcksum(dlp)) 94 msg = "NetBSD disk label corrupted"; 95 else 96 *lp = *dlp; 97 } else 98 msg = "no disk label"; 99 if (msg) 100 goto done; 101 102 /* obtain bad sector table if requested and present */ 103 if (clp && (bdp = &clp->bad) != NULL && (lp->d_flags & D_BADSECT)) { 104 struct dkbad *db; 105 106 i = 0; 107 do { 108 /* read a bad sector table */ 109 bp->b_oflags &= ~BO_DONE; 110 bp->b_flags |= B_READ; 111 bp->b_blkno = lp->d_secperunit - lp->d_nsectors + i; 112 if (lp->d_secsize > DEV_BSIZE) 113 bp->b_blkno *= lp->d_secsize / DEV_BSIZE; 114 else 115 bp->b_blkno /= DEV_BSIZE / lp->d_secsize; 116 bp->b_bcount = lp->d_secsize; 117 bp->b_cylinder = lp->d_ncylinders - 1; 118 (*strat)(bp); 119 120 /* if successful, validate, otherwise try another */ 121 if (biowait(bp)) { 122 msg = "bad sector table I/O error"; 123 } else { 124 db = (struct dkbad *)(bp->b_data); 125 #define DKBAD_MAGIC 0x4321 126 if (db->bt_mbz == 0 127 && db->bt_flag == DKBAD_MAGIC) { 128 msg = NULL; 129 *bdp = *db; 130 break; 131 } else 132 msg = "bad sector table corrupted"; 133 } 134 } while (bp->b_error != 0 && (i += 2) < 10 && 135 i < lp->d_nsectors); 136 } 137 138 done: 139 brelse(bp, 0); 140 return (msg); 141 } 142 143 /* 144 * Check new disk label for sensibility before setting it. 145 */ 146 int 147 setdisklabel(struct disklabel *olp, struct disklabel *nlp, u_long openmask, struct cpu_disklabel *clp) 148 { 149 int i; 150 struct partition *opp, *npp; 151 152 /* sanity clause */ 153 if (nlp->d_secpercyl == 0 || nlp->d_secsize == 0 || 154 (nlp->d_secsize % DEV_BSIZE) != 0) 155 return(EINVAL); 156 157 #ifdef notdef 158 /* XXX WHY WAS THIS HERE?! */ 159 /* special case to allow disklabel to be invalidated */ 160 if (nlp->d_magic == 0xffffffff) { 161 *olp = *nlp; 162 return (0); 163 } 164 #endif 165 166 if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC || 167 dkcksum(nlp) != 0) 168 return (EINVAL); 169 170 while ((i = ffs(openmask)) != 0) { 171 i--; 172 openmask &= ~(1 << i); 173 if (nlp->d_npartitions <= i) 174 return (EBUSY); 175 opp = &olp->d_partitions[i]; 176 npp = &nlp->d_partitions[i]; 177 if (npp->p_offset != opp->p_offset || npp->p_size < opp->p_size) 178 return (EBUSY); 179 /* 180 * Copy internally-set partition information 181 * if new label doesn't include it. XXX 182 */ 183 if (npp->p_fstype == FS_UNUSED && opp->p_fstype != FS_UNUSED) { 184 npp->p_fstype = opp->p_fstype; 185 npp->p_fsize = opp->p_fsize; 186 npp->p_frag = opp->p_frag; 187 npp->p_cpg = opp->p_cpg; 188 } 189 } 190 nlp->d_checksum = 0; 191 nlp->d_checksum = dkcksum(nlp); 192 *olp = *nlp; 193 return (0); 194 } 195 196 /* 197 * Write disk label back to device after modification. 198 * This means write out the rigid disk blocks to represent the 199 * label. Hope the user was careful. 200 */ 201 int 202 writedisklabel(dev_t dev, void (*strat)(struct buf *), struct disklabel *lp, struct cpu_disklabel *clp) 203 { 204 struct buf *bp; 205 struct disklabel *dlp; 206 int error = 0; 207 208 bp = geteblk((int)lp->d_secsize); 209 bp->b_dev = dev; 210 bp->b_blkno = LABELSECTOR; 211 bp->b_cylinder = 0; 212 bp->b_bcount = lp->d_secsize; 213 bp->b_flags |= B_READ; /* get current label */ 214 (*strat)(bp); 215 if ((error = biowait(bp)) != 0) 216 goto done; 217 218 dlp = (struct disklabel *)((char *)bp->b_data + LABELOFFSET); 219 *dlp = *lp; /* struct assignment */ 220 221 /* 222 * The Alpha requires that the boot block be checksummed. 223 * The first 63 8-bit quantites are summed into the 64th. 224 */ 225 { 226 int i; 227 u_long *dp, sum; 228 229 dp = (u_long *)bp->b_data; 230 sum = 0; 231 for (i = 0; i < 63; i++) 232 sum += dp[i]; 233 dp[63] = sum; 234 } 235 236 bp->b_flags &= ~B_READ; 237 bp->b_flags |= B_WRITE; 238 bp->b_oflags &= ~BO_DONE; 239 (*strat)(bp); 240 error = biowait(bp); 241 242 done: 243 brelse(bp, 0); 244 return (error); 245 } 246