1 /* $NetBSD: bad144.c,v 1.1 2026/07/03 21:27:40 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2026 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Copyright (c) 1982, 1986, 1990, 1993 31 * The Regents of the University of California. All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 3. Neither the name of the University nor the names of its contributors 42 * may be used to endorse or promote products derived from this software 43 * without specific prior written permission. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 * 57 * @(#)dkbad.c 8.2 (Berkeley) 1/12/94 58 */ 59 60 #include <sys/cdefs.h> 61 __KERNEL_RCSID(0, "$NetBSD: bad144.c,v 1.1 2026/07/03 21:27:40 thorpej Exp $"); 62 63 #include <sys/param.h> 64 #include <sys/buf.h> 65 #include <sys/dkbad.h> 66 #include <sys/errno.h> 67 #include <sys/kmem.h> 68 #include <sys/systm.h> 69 70 /* 71 * General description of DEC standard 144, from bad144(8): 72 * 73 * The format of the information is specified by DEC standard 144, as 74 * follows. The bad sector information is located in the first 5 even 75 * numbered sectors of the last track of the disk pack. There are five 76 * identical copies of the information, described by the dkbad structure. 77 * 78 * Replacement sectors are allocated starting with the first sector before 79 * the bad sector information and working backwards towards the beginning of 80 * the disk. A maximum of 126 bad sectors are supported. The position of 81 * the bad sector in the bad sector table determines the replacement sector 82 * to which it corresponds. The bad sectors must be listed in ascending 83 * order. 84 * 85 * (Additional commentary: this is not always true; some disk drivers may 86 * use alternate cylinders for bad sector replacements. This is why we 87 * do not perform the actual remapping here.) 88 * 89 * The bad sector information and replacement sectors are conventionally 90 * only accessible through the ``c'' file system partition of the disk. If 91 * that partition is used for a file system, the user is responsible for 92 * making sure that it does not overlap the bad sector information or any 93 * replacement sectors. Thus, one track plus 126 sectors must be reserved 94 * to allow use of all of the possible bad sector replacements. 95 * 96 * NOTE ABOUT BYTE ORDER: You would think that because this on-disk format 97 * originated at DEC that the fields would be nominally stored in little- 98 * endian byte order, and in historical BSD usage (including 386BSD back 99 * in the day), that held true. HOWEVER, bad144 is also used by the Xylogics 100 * disk drivers, on Sun3 and SPARC systems which are big-endian and those 101 * drivers historically did NOT byte-swap the entries in the table (this also 102 * seems to be consistent with how SunOS versions that supported SMD drives 103 * also treated bad144). So, we have ended up with a mixed bag, and I have 104 * no interest in disturbing that particular sleeping dog. 105 */ 106 107 struct bad144_context { 108 /* Provided by bad144 client at setup time. */ 109 uint32_t bc_secsize; /* drive sector size */ 110 uint32_t bc_ncyl; /* drive data cylinders */ 111 uint32_t bc_ntrkcyl; /* drive tracks (heads) per cylinder */ 112 uint32_t bc_nsectrk; /* drive sectors per track */ 113 114 struct dkbad bc_bt; /* the actual bad sector table */ 115 daddr_t bc_bb[NBT_BAD+1];/* LBA-ized version */ 116 }; 117 118 /* 119 * bad144_init -- 120 * Initialize bad144 sector forwarding context. Returns a context 121 * to be used for bad144 remapping, or NULL if bad144 can't be 122 * used on this drive due to geometry parameters being out of range. 123 */ 124 struct bad144_context * 125 bad144_init(uint32_t secsize, uint32_t ncyl, uint32_t ntrkcyl, uint32_t nsectrk) 126 { 127 int i; 128 129 KASSERT(secsize != 0); 130 KASSERT(ncyl != 0); 131 KASSERT(ntrkcyl != 0); 132 KASSERT(nsectrk != 0); 133 134 if (ncyl > BAD144_MAXCYLNO || 135 ntrkcyl > BAD144_MAXTRKNO || 136 nsectrk > BAD144_MAXSECNO) { 137 /* Can't do bad144 on this drive. */ 138 return NULL; 139 } 140 141 struct bad144_context *ctx = kmem_zalloc(sizeof(*ctx), KM_SLEEP); 142 143 ctx->bc_secsize = secsize; 144 ctx->bc_ncyl = ncyl; 145 ctx->bc_ntrkcyl = ntrkcyl; 146 ctx->bc_nsectrk = nsectrk; 147 148 for (i = 0; i < NBT_BAD; i++) { 149 ctx->bc_bt.bt_bad[i].bt_cyl = 150 ctx->bc_bt.bt_bad[i].bt_trksec = 0xffff; 151 ctx->bc_bb[i] = (daddr_t)-1; 152 } 153 /* There is an extra slot to terminate the list. */ 154 ctx->bc_bb[NBT_BAD] = (daddr_t)-1; 155 156 return ctx; 157 } 158 159 /* 160 * bad144_fini -- 161 * Dispose of a bad144 sector forwarding context. 162 */ 163 void 164 bad144_fini(struct bad144_context * const ctx) 165 { 166 if (ctx != NULL) { 167 kmem_free(ctx, sizeof(*ctx)); 168 } 169 } 170 171 /* 172 * This empty slot check comes from Chuck Cranor's Xylogics SMD 173 * drivers. It differs from the usual -1 check for cylinder, 174 * but presumably he did it this way because bad144 tables like 175 * this are found in the wild ?? (N.B. old SunOS versions seem 176 * to initialize bt_cyl to 0xffff but don't bother with bt_trksec?) 177 */ 178 #define EMPTY_SLOT(b) \ 179 ((bt->bt_bad[i].bt_cyl == 0xffff || \ 180 bt->bt_bad[i].bt_cyl == 0) && \ 181 bt->bt_bad[i].bt_trksec == 0xffff) 182 183 /* 184 * bad144_set -- 185 * Set the bad144 sector fowarding table from the provided 186 * buffer. 187 */ 188 int 189 bad144_set(struct bad144_context * const ctx, const struct dkbad * const bt) 190 { 191 int i; 192 daddr_t key, last_key; 193 194 if (ctx == NULL) { 195 return ENOTSUP; 196 } 197 198 KASSERT(bt != NULL); 199 200 for (last_key = 0, i = 0; i < NBT_BAD; i++) { 201 /* 202 * All bad sector records are packed into the beginning 203 * of the table. Once we see an empty slot, there should 204 * be no more records. Verified below. 205 */ 206 if (EMPTY_SLOT(&bt->bt_bad[i])) { 207 break; 208 } 209 /* 210 * Verify the cylinder / head / sector values in the 211 * table do not exceed the drive's geometry. 212 */ 213 if (bt->bt_bad[i].bt_cyl >= ctx->bc_ncyl || 214 (bt->bt_bad[i].bt_trksec >> 8) >= ctx->bc_ntrkcyl || 215 (bt->bt_bad[i].bt_trksec & 0xff) >= ctx->bc_nsectrk) { 216 goto bad_table; 217 } 218 /* 219 * Verify the table lists bad sectors in ascending order 220 * and that are no duplicates. 221 */ 222 key = ((daddr_t)bt->bt_bad[i].bt_cyl << 16) + 223 bt->bt_bad[i].bt_trksec; 224 if (key <= last_key) { 225 goto bad_table; 226 } 227 last_key = key; 228 } 229 230 /* 231 * Once we see an empty slot, we must only see empty 232 * slots. 233 */ 234 for (; i < NBT_BAD; i++) { 235 if (!EMPTY_SLOT(&bt->bt_bad[i])) { 236 goto bad_table; 237 } 238 } 239 240 memcpy(&ctx->bc_bt, bt, sizeof(ctx->bc_bt)); 241 for (i = 0; i < NBT_BAD; i++) { 242 if (EMPTY_SLOT(&bt->bt_bad[i])) { 243 ctx->bc_bb[i] = (daddr_t)-1; 244 continue; 245 } 246 ctx->bc_bb[i] = 247 (bt->bt_bad[i].bt_cyl * (ctx->bc_ntrkcyl * ctx->bc_nsectrk)) 248 + ((bt->bt_bad[i].bt_trksec >> 8) * ctx->bc_nsectrk) 249 + (bt->bt_bad[i].bt_trksec & 0xff); 250 } 251 252 return 0; 253 254 bad_table: 255 return EINVAL; 256 } 257 258 #define DKBAD_MAGIC 0x4321 259 260 /* 261 * bad144_load -- 262 * Find a bad sector table and load it into the context. 263 */ 264 int 265 bad144_load(struct bad144_context * const ctx, dev_t dev, 266 void (*strat)(struct buf *)) 267 { 268 struct buf *bp; 269 int error, i = 0; 270 271 if (ctx == NULL) { 272 return ESRCH; 273 } 274 275 uint32_t nseccyl = ctx->bc_ntrkcyl * ctx->bc_nsectrk; 276 uint32_t nsecunit = ctx->bc_ncyl * nseccyl; 277 278 bp = geteblk(ctx->bc_secsize); 279 bp->b_dev = dev; 280 281 do { 282 bp->b_oflags &= ~BO_DONE; 283 bp->b_flags |= B_READ; 284 bp->b_blkno = nsecunit - ctx->bc_nsectrk + i; 285 if (ctx->bc_secsize > DEV_BSIZE) { 286 bp->b_blkno *= ctx->bc_secsize / DEV_BSIZE; 287 } else { 288 bp->b_blkno /= DEV_BSIZE / ctx->bc_secsize; 289 } 290 bp->b_bcount = ctx->bc_secsize; 291 bp->b_cylinder = ctx->bc_ncyl - 1; 292 (*strat)(bp); 293 294 if ((error = biowait(bp)) == 0) { 295 struct dkbad *db = (struct dkbad *)bp->b_data; 296 if (db->bt_mbz == 0 && 297 db->bt_flag == DKBAD_MAGIC) { 298 error = bad144_set(ctx, db); 299 break; 300 } else { 301 error = ESRCH; 302 } 303 } 304 } while (error != 0 && (i += 2) < 10 && i < ctx->bc_nsectrk); 305 306 brelse(bp, BC_INVAL); 307 return error; 308 } 309 310 /* 311 * bad144_isbad_chs -- 312 * Search the bad sector table looking for the specified sector. 313 * Return the index if found, -1 if not found. 314 * 315 * This one does CHS addressing and is essentially the traditional 316 * isbad() routine. 317 */ 318 int 319 bad144_isbad_chs(const struct bad144_context * const ctx, 320 uint32_t cyl, uint32_t trk, uint32_t sec) 321 { 322 const struct dkbad * const bt = &ctx->bc_bt; 323 int i; 324 daddr_t key, tkey; 325 326 if (ctx == NULL) { 327 return -1; 328 } 329 330 key = ((daddr_t)cyl << 16) + (trk << 8) + sec; 331 for (i = 0; i < NBT_BAD; i++) { 332 tkey = ((daddr_t)bt->bt_bad[i].bt_cyl << 16) + 333 bt->bt_bad[i].bt_trksec; 334 if (key == tkey) { 335 return i; 336 } 337 if (EMPTY_SLOT(i) || key < tkey) { 338 break; 339 } 340 } 341 return -1; 342 } 343 344 /* 345 * bad144_isbad_lba -- 346 * Search the bad sector table looking for the specified sector. 347 * Return the index if found, -1 if not found. 348 * 349 * This one does LBA addressing, and checks a block range, 350 * also returning the distance to the bad sector. 351 */ 352 int 353 bad144_isbad_lba(const struct bad144_context * const ctx, daddr_t blk, 354 int nblks, int *distancep) 355 { 356 daddr_t blkdiff; 357 int i; 358 359 KASSERT(blk >= 0); 360 KASSERT(nblks > 0); 361 KASSERT(distancep != NULL); 362 363 if (ctx == NULL) { 364 return -1; 365 } 366 367 for (i = 0; (blkdiff = ctx->bc_bb[i]) != (daddr_t)-1; i++) { 368 blkdiff -= blk; 369 if (blkdiff < 0) { 370 continue; 371 } 372 if (blkdiff < nblks) { 373 /* 374 * There is a bad block within this transfer. 375 * If the distance is 0, then it's the first 376 * block in the transfer (i.e. "blk"). Otherwise, 377 * the distance indicates how many blocks can 378 * be transferred before the remap must be done. 379 */ 380 KASSERT(blkdiff < INT_MAX); 381 *distancep = (int)blkdiff; 382 return i; 383 } 384 } 385 return -1; 386 } 387