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