bad144.c revision 1.1 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