1 /* $OpenBSD: dkcsum.c,v 1.21 2006/05/11 13:21:11 mickey 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/proc.h>
39 #include <sys/reboot.h>
40 #include <sys/stat.h>
41 #include <sys/systm.h>
42
43 #include <machine/biosvar.h>
44
45 #include <lib/libz/zlib.h>
46
47 dev_t dev_rawpart(struct device *); /* XXX */
48
49 extern u_int32_t bios_cksumlen;
50 extern bios_diskinfo_t *bios_diskinfo;
51 extern dev_t bootdev;
52
53 void
54 dkcsumattach(void)
55 {
56 struct device *dv;
57 struct buf *bp;
58 struct bdevsw *bdsw;
59 dev_t dev, pribootdev, altbootdev;
60 int error, picked;
61 u_int32_t csum;
62 bios_diskinfo_t *bdi, *hit;
63
64 /* do nothing if no diskinfo passed from /boot, or a bad length */
65 if (bios_diskinfo == NULL || bios_cksumlen * DEV_BSIZE > MAXBSIZE)
66 return;
67
68 #ifdef DEBUG
69 printf("dkcsum: bootdev=%#x\n", bootdev);
70 for (bdi = bios_diskinfo; bdi->bios_number != -1; bdi++)
71 if (bdi->bios_number & 0x80)
72 printf("dkcsum: BIOS drive %#x checksum is %#x\n",
73 bdi->bios_number, bdi->checksum);
74 #endif
75 pribootdev = altbootdev = 0;
76
77 /*
78 * XXX What if DEV_BSIZE is changed to something else than the BIOS
79 * blocksize? Today, /boot doesn't cover that case so neither need
80 * I care here.
81 */
82 bp = geteblk(bios_cksumlen * DEV_BSIZE); /* XXX error check? */
83
84 TAILQ_FOREACH(dv, &alldevs, dv_list) {
85 if (dv->dv_class != DV_DISK)
86 continue;
87 bp->b_dev = dev = dev_rawpart(dv);
88 if (dev == NODEV)
89 continue;
90 bdsw = &bdevsw[major(dev)];
91
92 /*
93 * This open operation guarantees a proper initialization
94 * of the device, for future strategy calls.
95 */
96 error = (*bdsw->d_open)(dev, FREAD, S_IFCHR, curproc);
97 if (error) {
98 /* XXX What to do here? */
99 #ifdef DEBUG
100 printf("dkcsum: %s open failed (%d)\n",
101 dv->dv_xname, error);
102 #endif
103 continue;
104 }
105
106 /* Read blocks to cksum. XXX maybe a d_read should be used. */
107 bp->b_blkno = 0;
108 bp->b_bcount = bios_cksumlen * DEV_BSIZE;
109 bp->b_flags = B_BUSY | B_READ;
110 bp->b_cylinder = 0;
111 (*bdsw->d_strategy)(bp);
112 if ((error = biowait(bp))) {
113 /* XXX What to do here? */
114 #ifdef DEBUG
115 printf("dkcsum: %s read failed (%d)\n",
116 dv->dv_xname, error);
117 #endif
118 error = (*bdsw->d_close)(dev, 0, S_IFCHR, curproc);
119 #ifdef DEBUG
120 if (error)
121 printf("dkcsum: %s close failed (%d)\n",
122 dv->dv_xname, error);
123 #endif
124 continue;
125 }
126 error = (*bdsw->d_close)(dev, FREAD, S_IFCHR, curproc);
127 if (error) {
128 /* XXX What to do here? */
129 #ifdef DEBUG
130 printf("dkcsum: %s closed failed (%d)\n",
131 dv->dv_xname, error);
132 #endif
133 continue;
134 }
135
136 csum = adler32(0, bp->b_data, bios_cksumlen * DEV_BSIZE);
137 #ifdef DEBUG
138 printf("dkcsum: %s checksum is %#x\n", dv->dv_xname, csum);
139 #endif
140
141 /* Find the BIOS device */
142 hit = 0;
143 for (bdi = bios_diskinfo; bdi->bios_number != -1; bdi++) {
144 /* Skip non-harddrives */
145 if (!(bdi->bios_number & 0x80))
146 continue;
147 if (bdi->checksum != csum)
148 continue;
149 picked = hit || (bdi->flags & BDI_PICKED);
150 if (!picked)
151 hit = bdi;
152 printf("dkcsum: %s matches BIOS drive %#x%s\n",
153 dv->dv_xname, bdi->bios_number,
154 (picked ? " IGNORED" : ""));
155 }
156
157 /*
158 * If we have no hit, that's OK, we can see a lot more devices
159 * than the BIOS can, so this case is pretty normal.
160 */
161 if (!hit) {
162 #ifdef DEBUG
163 printf("dkcsum: %s has no matching BIOS drive\n",
164 dv->dv_xname);
165 #endif
166 continue;
167 }
168
169 /*
170 * Fixup bootdev if units match. This means that all of
171 * hd*, sd*, wd*, will be interpreted the same. Not 100%
172 * backwards compatible, but sd* and wd* should be phased-
173 * out in the bootblocks.
174 */
175
176 /* B_TYPE dependent hd unit counting bootblocks */
177 if ((B_TYPE(bootdev) == B_TYPE(hit->bsd_dev)) &&
178 (B_UNIT(bootdev) == B_UNIT(hit->bsd_dev))) {
179 int type, ctrl, adap, part, unit;
180
181 type = major(bp->b_dev);
182 adap = B_ADAPTOR(bootdev);
183 ctrl = B_CONTROLLER(bootdev);
184 unit = DISKUNIT(bp->b_dev);
185 part = B_PARTITION(bootdev);
186
187 pribootdev = MAKEBOOTDEV(type, ctrl, adap, unit, part);
188 #ifdef DEBUG
189 printf("dkcsum: %s is primary boot disk\n",
190 dv->dv_xname);
191 #endif
192 }
193 /* B_TYPE independent hd unit counting bootblocks */
194 if (B_UNIT(bootdev) == (hit->bios_number & 0x7F)) {
195 int type, ctrl, adap, part, unit;
196
197 type = major(bp->b_dev);
198 adap = B_ADAPTOR(bootdev);
199 ctrl = B_CONTROLLER(bootdev);
200 unit = DISKUNIT(bp->b_dev);
201 part = B_PARTITION(bootdev);
202
203 altbootdev = MAKEBOOTDEV(type, ctrl, adap, unit, part);
204 #ifdef DEBUG
205 printf("dkcsum: %s is alternate boot disk\n",
206 dv->dv_xname);
207 #endif
208 }
209
210 /* This will overwrite /boot's guess, just so you remember */
211 hit->bsd_dev = MAKEBOOTDEV(major(bp->b_dev), 0, 0,
212 DISKUNIT(bp->b_dev), RAW_PART);
213 hit->flags |= BDI_PICKED;
214 }
215 bootdev = pribootdev ? pribootdev : altbootdev ? altbootdev : bootdev;
216
217 bp->b_flags |= B_INVAL;
218 brelse(bp);
219 }