1 /* $OpenBSD: dkcsum.c,v 1.32 2014/09/14 14:17:23 jsg Exp $ */
2
3 /*-
4 * Copyright (c) 1997 Niklas Hallqvist. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 /*
28 * A checksumming pseudo device used to get unique labels of each disk
29 * that needs to be matched to BIOS disks.
30 */
31
32 #include <sys/param.h>
33 #include <sys/buf.h>
34 #include <sys/conf.h>
35 #include <sys/device.h>
36 #include <sys/disklabel.h>
37 #include <sys/fcntl.h>
38 #include <sys/reboot.h>
39 #include <sys/stat.h>
40 #include <sys/systm.h>
41
42 #include <machine/biosvar.h>
43
44 #include <lib/libz/zlib.h>
45
46 dev_t dev_rawpart(struct device *); /* XXX */
47
48 extern u_int32_t bios_cksumlen;
49 extern bios_diskinfo_t *bios_diskinfo;
50 extern dev_t bootdev;
51
52 void
dkcsumattach(void)53 dkcsumattach(void)
54 {
55 struct device *dv;
56 struct buf *bp;
57 struct bdevsw *bdsw;
58 dev_t dev, pribootdev, altbootdev;
59 int error, picked;
60 u_int32_t csum;
61 bios_diskinfo_t *bdi, *hit;
62
63 /* do nothing if no diskinfo passed from /boot, or a bad length */
64 if (bios_diskinfo == NULL || bios_cksumlen * DEV_BSIZE > MAXBSIZE)
65 return;
66
67 /* Do nothing if bootdev is a CD drive. */
68 if (B_TYPE(bootdev) == 6)
69 return;
70
71 #ifdef DEBUG
72 printf("dkcsum: bootdev=%#x\n", bootdev);
73 for (bdi = bios_diskinfo; bdi->bios_number != -1; bdi++) {
74 if (bdi->bios_number & 0x80) {
75 printf("dkcsum: BIOS drive %#x bsd_dev=%#x "
76 "checksum=%#x\n", bdi->bios_number, bdi->bsd_dev,
77 bdi->checksum);
78 }
79 }
80 #endif
81 pribootdev = altbootdev = 0;
82
83 /*
84 * XXX What if DEV_BSIZE is changed to something else than the BIOS
85 * blocksize? Today, /boot doesn't cover that case so neither need
86 * I care here.
87 */
88 bp = geteblk(bios_cksumlen * DEV_BSIZE); /* XXX error check? */
89
90 TAILQ_FOREACH(dv, &alldevs, dv_list) {
91 if (dv->dv_class != DV_DISK)
92 continue;
93 bp->b_dev = dev = dev_rawpart(dv);
94 if (dev == NODEV)
95 continue;
96 bdsw = &bdevsw[major(dev)];
97
98 /*
99 * This open operation guarantees a proper initialization
100 * of the device, for future strategy calls.
101 */
102 error = (*bdsw->d_open)(dev, FREAD, S_IFCHR, curproc);
103 if (error) {
104 /* XXX What to do here? */
105 #ifdef DEBUG
106 printf("dkcsum: %s open failed (%d)\n",
107 dv->dv_xname, error);
108 #endif
109 continue;
110 }
111
112 /* Read blocks to cksum. XXX maybe a d_read should be used. */
113 bp->b_blkno = 0;
114 bp->b_bcount = bios_cksumlen * DEV_BSIZE;
115 bp->b_error = 0; /* B_ERROR and b_error may have stale data. */
116 CLR(bp->b_flags, B_READ | B_WRITE | B_DONE | B_ERROR);
117 SET(bp->b_flags, B_BUSY | B_READ | B_RAW);
118 (*bdsw->d_strategy)(bp);
119 if ((error = biowait(bp))) {
120 /* XXX What to do here? */
121 #ifdef DEBUG
122 printf("dkcsum: %s read failed (%d)\n",
123 dv->dv_xname, error);
124 #endif
125 error = (*bdsw->d_close)(dev, 0, S_IFCHR, curproc);
126 #ifdef DEBUG
127 if (error)
128 printf("dkcsum: %s close failed (%d)\n",
129 dv->dv_xname, error);
130 #endif
131 continue;
132 }
133 error = (*bdsw->d_close)(dev, FREAD, S_IFCHR, curproc);
134 if (error) {
135 /* XXX What to do here? */
136 #ifdef DEBUG
137 printf("dkcsum: %s closed failed (%d)\n",
138 dv->dv_xname, error);
139 #endif
140 continue;
141 }
142
143 csum = adler32(0, bp->b_data, bios_cksumlen * DEV_BSIZE);
144 #ifdef DEBUG
145 printf("dkcsum: %s checksum is %#x\n", dv->dv_xname, csum);
146 #endif
147
148 /* Find the BIOS device */
149 hit = 0;
150 for (bdi = bios_diskinfo; bdi->bios_number != -1; bdi++) {
151 /* Skip non-harddrives */
152 if (!(bdi->bios_number & 0x80))
153 continue;
154 if (bdi->checksum != csum)
155 continue;
156 picked = hit || (bdi->flags & BDI_PICKED);
157 if (!picked)
158 hit = bdi;
159 #ifdef DEBUG
160 printf("dkcsum: %s matches BIOS drive %#x%s\n",
161 dv->dv_xname, bdi->bios_number,
162 (picked ? " IGNORED" : ""));
163 #endif
164 }
165
166 /*
167 * If we have no hit, that's OK, we can see a lot more devices
168 * than the BIOS can, so this case is pretty normal.
169 */
170 if (!hit) {
171 #ifdef DEBUG
172 printf("dkcsum: %s has no matching BIOS drive\n",
173 dv->dv_xname);
174 #endif
175 continue;
176 }
177
178 /*
179 * Fixup bootdev if units match. This means that all of
180 * hd*, sd*, wd*, will be interpreted the same. Not 100%
181 * backwards compatible, but sd* and wd* should be phased-
182 * out in the bootblocks.
183 */
184
185 /* B_TYPE dependent hd unit counting bootblocks */
186 if ((B_ADAPTOR(bootdev) == B_ADAPTOR(hit->bsd_dev)) &&
187 (B_CONTROLLER(bootdev) == B_CONTROLLER(hit->bsd_dev)) &&
188 (B_TYPE(bootdev) == B_TYPE(hit->bsd_dev)) &&
189 (B_UNIT(bootdev) == B_UNIT(hit->bsd_dev))) {
190 int type, ctrl, adap, part, unit;
191
192 type = major(bp->b_dev);
193 adap = B_ADAPTOR(bootdev);
194 ctrl = B_CONTROLLER(bootdev);
195 unit = DISKUNIT(bp->b_dev);
196 part = B_PARTITION(bootdev);
197
198 pribootdev = MAKEBOOTDEV(type, ctrl, adap, unit, part);
199 #ifdef DEBUG
200 printf("dkcsum: %s is primary boot disk\n",
201 dv->dv_xname);
202 #endif
203 }
204 /* B_TYPE independent hd unit counting bootblocks */
205 if (B_UNIT(bootdev) == (hit->bios_number & 0x7F)) {
206 int type, ctrl, adap, part, unit;
207
208 type = major(bp->b_dev);
209 adap = B_ADAPTOR(bootdev);
210 ctrl = B_CONTROLLER(bootdev);
211 unit = DISKUNIT(bp->b_dev);
212 part = B_PARTITION(bootdev);
213
214 altbootdev = MAKEBOOTDEV(type, ctrl, adap, unit, part);
215 #ifdef DEBUG
216 printf("dkcsum: %s is alternate boot disk\n",
217 dv->dv_xname);
218 #endif
219 }
220
221 /* This will overwrite /boot's guess, just so you remember */
222 hit->bsd_dev = MAKEBOOTDEV(major(bp->b_dev), 0, 0,
223 DISKUNIT(bp->b_dev), RAW_PART);
224 hit->flags |= BDI_PICKED;
225 }
226 bootdev = pribootdev ? pribootdev : altbootdev ? altbootdev : bootdev;
227
228 bp->b_flags |= B_INVAL;
229 brelse(bp);
230 }
231