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