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