1 /**	$MirOS: src/sys/arch/i386/stand/libsa/diskprobe.c,v 1.20 2012/01/14 21:44:26 tg Exp $ */
2 /*	$OpenBSD: diskprobe.c,v 1.29 2007/06/18 22:11:20 krw Exp $	*/
3 
4 /*
5  * Copyright (c) 1997 Tobias Weingartner
6  * Copyright (c) 2002, 2003, 2009, 2012
7  *	Thorsten "mirabilos" Glaser <tg@mirbsd.org>
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  */
32 
33 /* We don't want the disk type names from disklabel.h */
34 #undef DKTYPENAMES
35 
36 #include <sys/param.h>
37 #include <sys/queue.h>
38 #include <sys/reboot.h>
39 #include <sys/disklabel.h>
40 #include <stand/boot/bootarg.h>
41 #include <machine/biosvar.h>
42 #include <zlib.h>
43 #include "disk.h"
44 #include "biosdev.h"
45 #include "libsa.h"
46 
47 #define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE	/* Max # of blks to cksum */
48 
49 /* Local Prototypes */
50 static int disksum(int);
51 static void hardprobe_one(int);
52 
53 /* List of disk devices we found/probed */
54 struct disklist_lh disklist;
55 
56 /* Pointer to boot device */
57 struct diskinfo *bootdev_dip = NULL;
58 
59 /* Pointer to original boot device */
60 struct diskinfo *start_dip = NULL;
61 
62 extern int debug;
63 
64 /* Probe for all BIOS floppies */
65 static void
floppyprobe(void)66 floppyprobe(void)
67 {
68 	struct diskinfo *dip;
69 	int i;
70 
71 	/* Floppies */
72 	for (i = 0; i < 4; i++) {
73 		dip = alloc(sizeof(struct diskinfo));
74 		bzero(dip, sizeof(*dip));
75 
76 		if (bios_getdiskinfo(i, &dip->bios_info)) {
77 #ifdef BIOS_DEBUG
78 			if (debug)
79 				printf(" <!fd%u>", i);
80 #endif
81 			free(dip, 0);
82 			break;
83 		}
84 
85 		if (!start_dip && i386_biosdev == i)
86 			start_dip = dip;
87 
88 		dip->name[0] = 'f';
89 		dip->name[1] = 'd';
90 		dip->name[2] = '0' + i;
91 		dip->name[3] = '\0';
92 		printf(" %s", dip->name);
93 
94 		/* Fill out best we can - (fd?) */
95 		dip->bios_info.bsd_dev = MAKEBOOTDEV(2, 0, 0, i, RAW_PART);
96 
97 		/*
98 		 * Delay reading the disklabel until we're sure we want
99 		 * to boot from the floppy. Doing this avoids a delay
100 		 * (sometimes very long) when trying to read the label
101 		 * and the drive is unplugged.
102 		 */
103 		dip->bios_info.flags |= BDI_BADLABEL;
104 
105 		/* Add to queue of disks */
106 		TAILQ_INSERT_TAIL(&disklist, dip, list);
107 	}
108 }
109 
110 /* Probe for all BIOS hard disks */
111 static void
hardprobe(void)112 hardprobe(void)
113 {
114 	int i;
115 
116 	if (i386_biosdev & 0x80)
117 		hardprobe_one(i386_biosdev);
118 	for (i = 0x80; i < 0x88; i++)
119 		if (i != i386_biosdev)
120 			hardprobe_one(i);
121 }
122 
123 static void
hardprobe_one(int i)124 hardprobe_one(int i)
125 {
126 	static unsigned int cddv = 0, ide = 0, scsi = 0;
127 	struct diskinfo *dip;
128 	u_int bsdunit, type;
129 
130 	dip = alloc(sizeof(struct diskinfo));
131 	bzero(dip, sizeof(*dip));
132 
133 	if (bios_getdiskinfo(i, &dip->bios_info)) {
134 #ifdef BIOS_DEBUG
135 		if (debug)
136 			printf(" <!hd%u>", i&0x7f);
137 #endif
138 		free(dip, 0);
139 		return;
140 	}
141 
142 	if (!start_dip && i386_biosdev == i)
143 		start_dip = dip;
144 
145 	dip->name[0] = 'h';
146 	dip->name[1] = 'd';
147 	dip->name[2] = '0' + (i & 0x7F);
148 	dip->name[3] = '\0';
149 	if (dip->bios_info.flags & BDI_EL_TORITO) {
150 		dip->name[0] = 'c';
151 		dip->name[2] = '0' + cddv++;
152 	}
153 	printf(" %s%s", dip->name, dip->bios_info.flags & BDI_LBA ? "+" : "");
154 
155 	/* Try to find the label, to figure out device type */
156 	if ((bios_getdisklabel(&dip->bios_info, &dip->disklabel)) ) {
157 		if (dip->bios_info.flags & BDI_EL_TORITO) {
158 			bsdunit = dip->name[2] - '0';
159 			type = 6;	/* CD-ROM */
160 		} else {
161 			printf("*");
162 			bsdunit = ide++;
163 			type = 0;	/* XXX let it be IDE */
164 		}
165 	} else {
166 		/* Best guess */
167 		switch (dip->disklabel.d_type) {
168 		case DTYPE_SCSI:
169 			type = 4;
170 			bsdunit = scsi++;
171 			dip->bios_info.flags |= BDI_GOODLABEL;
172 			break;
173 
174 		case DTYPE_ESDI:
175 		case DTYPE_ST506:
176 			type = 0;
177 			bsdunit = ide++;
178 			dip->bios_info.flags |= BDI_GOODLABEL;
179 			break;
180 
181 		case DTYPE_ATAPI:
182 			bsdunit = dip->name[2] - '0';
183 			type = 6;	/* CD-ROM */
184 			dip->bios_info.flags |= BDI_GOODLABEL;
185 			break;
186 
187 		default:
188 			dip->bios_info.flags |= BDI_BADLABEL;
189 			type = 0;	/* XXX Suggest IDE */
190 			bsdunit = ide++;
191 		}
192 	}
193 
194 	dip->bios_info.checksum = 0; /* just in case */
195 	/* Fill out best we can */
196 	dip->bios_info.bsd_dev =
197 	    MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART);
198 
199 	/* Add to queue of disks */
200 	TAILQ_INSERT_TAIL(&disklist, dip, list);
201 }
202 
203 
204 /* Probe for all BIOS supported disks */
205 u_int32_t bios_cksumlen;
206 void
diskprobe(void)207 diskprobe(void)
208 {
209 	struct diskinfo *dip;
210 	int i;
211 
212 	/* These get passed to kernel */
213 	bios_diskinfo_t *bios_diskinfo;
214 
215 	/* Do probes */
216 	floppyprobe();
217 #ifdef BIOS_DEBUG
218 	if (debug)
219 		printf(";");
220 #endif
221 	hardprobe();
222 
223 	if (!start_dip)
224 		start_dip = TAILQ_FIRST(&disklist);
225 
226 	/* Checksumming of hard disks */
227 	for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
228 		;
229 	bios_cksumlen = i;
230 
231 	/* Get space for passing bios_diskinfo stuff to kernel */
232 	for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
233 	    dip = TAILQ_NEXT(dip, list))
234 		i++;
235 	bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t));
236 
237 	/* Copy out the bios_diskinfo stuff */
238 	for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
239 	    dip = TAILQ_NEXT(dip, list))
240 		bios_diskinfo[i++] = dip->bios_info;
241 
242 	bios_diskinfo[i++].bios_number = -1;
243 	/* Register for kernel use */
244 	addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen);
245 	addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t),
246 	    bios_diskinfo);
247 }
248 
249 void
dump_diskinfo(void)250 dump_diskinfo(void)
251 {
252 	struct diskinfo *dip;
253 
254 #ifndef SMALL_BOOT
255 	printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
256 #endif
257 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
258 		bios_diskinfo_t *bdi = &dip->bios_info;
259 
260 		if (bdi->flags & BDI_NOTADISK) {
261 			printf("%s\tnone\n", dip->name);
262 			continue;
263 		}
264 		printf("%s\t0x%X\t%s\t%d\t%d\t%d\t0x%X\t0x%X\n",
265 		    dip->name, bdi->bios_number,
266 		    (bdi->flags & BDI_BADLABEL) ? "*none*" : "label",
267 		    bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
268 		    bdi->flags, bdi->checksum);
269 	}
270 }
271 
272 /*
273  * Checksum one more block on all harddrives
274  *
275  * Use the adler32() function from libz,
276  * as it is quick, small, and available.
277  */
278 int
disksum(int blk)279 disksum(int blk)
280 {
281 	struct diskinfo *dip, *dip2;
282 	int st, reprobe = 0;
283 	char *buf;
284 
285 	buf = alloc(DEV_BSIZE);
286 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
287 		bios_diskinfo_t *bdi = &dip->bios_info;
288 
289 		/* Skip this disk if it is not a HD or has had an I/O error */
290 		if (!(bdi->bios_number & 0x80) ||
291 		    bdi->flags & (BDI_INVALID | BDI_NOTADISK))
292 			continue;
293 
294 		/* Adler32 checksum */
295 		st = biosd_io(F_READ, bdi, blk, 1, buf);
296 		if (st) {
297 			bdi->flags |= BDI_INVALID;
298 			continue;
299 		}
300 		bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
301 
302 		for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
303 				dip2 = TAILQ_NEXT(dip2, list)) {
304 			bios_diskinfo_t *bd = &dip2->bios_info;
305 			if ((bd->bios_number & 0x80) &&
306 			    !(bd->flags & (BDI_INVALID | BDI_NOTADISK)) &&
307 			    bdi->checksum == bd->checksum)
308 				reprobe = 1;
309 		}
310 	}
311 	free(buf, DEV_BSIZE);
312 
313 	return reprobe;
314 }
315