xref: /openbsd/sys/arch/i386/stand/libsa/diskprobe.c (revision 404b540a)
1 /*	$OpenBSD: diskprobe.c,v 1.31 2009/05/21 23:45:48 krw Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Tobias Weingartner
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 /* We want the disk type names from disklabel.h */
31 #undef DKTYPENAMES
32 
33 #include <sys/param.h>
34 #include <sys/queue.h>
35 #include <sys/reboot.h>
36 #include <sys/disklabel.h>
37 #include <stand/boot/bootarg.h>
38 #include <machine/biosvar.h>
39 #include <lib/libz/zlib.h>
40 #include "disk.h"
41 #include "biosdev.h"
42 #include "libsa.h"
43 
44 #define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE	/* Max # of blks to cksum */
45 
46 /* Local Prototypes */
47 static int disksum(int);
48 
49 /* List of disk devices we found/probed */
50 struct disklist_lh disklist;
51 
52 /* Pointer to boot device */
53 struct diskinfo *bootdev_dip;
54 
55 extern int debug;
56 extern int bios_bootdev;
57 extern int bios_cddev;
58 
59 /* Probe for all BIOS floppies */
60 static void
61 floppyprobe(void)
62 {
63 	struct diskinfo *dip;
64 	int i;
65 
66 	/* Floppies */
67 	for (i = 0; i < 4; i++) {
68 		dip = alloc(sizeof(struct diskinfo));
69 		bzero(dip, sizeof(*dip));
70 
71 		if (bios_getdiskinfo(i, &dip->bios_info)) {
72 #ifdef BIOS_DEBUG
73 			if (debug)
74 				printf(" <!fd%u>", i);
75 #endif
76 			free(dip, 0);
77 			break;
78 		}
79 
80 		printf(" fd%u", i);
81 
82 		/* Fill out best we can - (fd?) */
83 		dip->bios_info.bsd_dev = MAKEBOOTDEV(2, 0, 0, i, RAW_PART);
84 
85 		/*
86 		 * Delay reading the disklabel until we're sure we want
87 		 * to boot from the floppy. Doing this avoids a delay
88 		 * (sometimes very long) when trying to read the label
89 		 * and the drive is unplugged.
90 		 */
91 		dip->bios_info.flags |= BDI_BADLABEL;
92 
93 		/* Add to queue of disks */
94 		TAILQ_INSERT_TAIL(&disklist, dip, list);
95 	}
96 }
97 
98 
99 /* Probe for all BIOS hard disks */
100 static void
101 hardprobe(void)
102 {
103 	struct diskinfo *dip;
104 	int i;
105 	u_int bsdunit, type;
106 	u_int scsi = 0, ide = 0;
107 	const char *dc = (const char *)((0x40 << 4) + 0x75);
108 
109 	/* Hard disks */
110 	for (i = 0x80; i < (0x80 + *dc); i++) {
111 		dip = alloc(sizeof(struct diskinfo));
112 		bzero(dip, sizeof(*dip));
113 
114 		if (bios_getdiskinfo(i, &dip->bios_info)) {
115 #ifdef BIOS_DEBUG
116 			if (debug)
117 				printf(" <!hd%u>", i&0x7f);
118 #endif
119 			free(dip, 0);
120 			break;
121 		}
122 
123 		printf(" hd%u%s", i&0x7f, (dip->bios_info.bios_edd > 0?"+":""));
124 
125 		/* Try to find the label, to figure out device type */
126 		if ((bios_getdisklabel(&dip->bios_info, &dip->disklabel)) ) {
127 			printf("*");
128 			bsdunit = ide++;
129 			type = 0;	/* XXX let it be IDE */
130 		} else {
131 			/* Best guess */
132 			switch (dip->disklabel.d_type) {
133 			case DTYPE_SCSI:
134 				type = 4;
135 				bsdunit = scsi++;
136 				dip->bios_info.flags |= BDI_GOODLABEL;
137 				break;
138 
139 			case DTYPE_ESDI:
140 			case DTYPE_ST506:
141 				type = 0;
142 				bsdunit = ide++;
143 				dip->bios_info.flags |= BDI_GOODLABEL;
144 				break;
145 
146 			default:
147 				dip->bios_info.flags |= BDI_BADLABEL;
148 				type = 0;	/* XXX Suggest IDE */
149 				bsdunit = ide++;
150 			}
151 		}
152 
153 		dip->bios_info.checksum = 0; /* just in case */
154 		/* Fill out best we can */
155 		dip->bios_info.bsd_dev =
156 		    MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART);
157 
158 		/* Add to queue of disks */
159 		TAILQ_INSERT_TAIL(&disklist, dip, list);
160 	}
161 }
162 
163 
164 /* Probe for all BIOS supported disks */
165 u_int32_t bios_cksumlen;
166 void
167 diskprobe(void)
168 {
169 	struct diskinfo *dip;
170 	int i;
171 
172 	/* These get passed to kernel */
173 	bios_diskinfo_t *bios_diskinfo;
174 
175 	/* Init stuff */
176 	TAILQ_INIT(&disklist);
177 
178 	/* Do probes */
179 	floppyprobe();
180 #ifdef BIOS_DEBUG
181 	if (debug)
182 		printf(";");
183 #endif
184 	hardprobe();
185 
186 	/* Checksumming of hard disks */
187 	for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
188 		;
189 	bios_cksumlen = i;
190 
191 	/* Get space for passing bios_diskinfo stuff to kernel */
192 	for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
193 	    dip = TAILQ_NEXT(dip, list))
194 		i++;
195 	bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t));
196 
197 	/* Copy out the bios_diskinfo stuff */
198 	for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
199 	    dip = TAILQ_NEXT(dip, list))
200 		bios_diskinfo[i++] = dip->bios_info;
201 
202 	bios_diskinfo[i++].bios_number = -1;
203 	/* Register for kernel use */
204 	addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen);
205 	addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t),
206 	    bios_diskinfo);
207 }
208 
209 
210 void
211 cdprobe(void)
212 {
213 	struct diskinfo *dip;
214 	int cddev = bios_cddev & 0xff;
215 
216 	/* Another BIOS boot device... */
217 
218 	if (bios_cddev == -1)			/* Not been set, so don't use */
219 		return;
220 
221 	dip = alloc(sizeof(struct diskinfo));
222 	bzero(dip, sizeof(*dip));
223 
224 #if 0
225 	if (bios_getdiskinfo(cddev, &dip->bios_info)) {
226 		printf(" <!cd0>");	/* XXX */
227 		free(dip, 0);
228 		return;
229 	}
230 #endif
231 
232 	printf(" cd0");
233 
234 	dip->bios_info.bios_number = cddev;
235 	dip->bios_info.bios_edd = 1;		/* Use the LBA calls */
236 	dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO;
237 	dip->bios_info.checksum = 0;		 /* just in case */
238 	dip->bios_info.bsd_dev =
239 	    MAKEBOOTDEV(6, 0, 0, 0, RAW_PART);
240 
241 	/* Create an imaginary disk label */
242 	dip->disklabel.d_secsize = 2048;
243 	dip->disklabel.d_ntracks = 1;
244 	dip->disklabel.d_nsectors = 100;
245 	dip->disklabel.d_ncylinders = 1;
246 	dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks *
247 	    dip->disklabel.d_nsectors;
248 	if (dip->disklabel.d_secpercyl == 0) {
249 		dip->disklabel.d_secpercyl = 100;
250 		/* as long as it's not 0, since readdisklabel divides by it */
251 	}
252 
253 	strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM",
254 	    sizeof(dip->disklabel.d_typename));
255 	dip->disklabel.d_type = DTYPE_ATAPI;
256 
257 	strncpy(dip->disklabel.d_packname, "fictitious",
258 	    sizeof(dip->disklabel.d_packname));
259 	dip->disklabel.d_secperunit = 100;
260 	dip->disklabel.d_rpm = 300;
261 	dip->disklabel.d_interleave = 1;
262 
263 	dip->disklabel.d_bbsize = 2048;
264 	dip->disklabel.d_sbsize = 2048;
265 
266 	/* 'a' partition covering the "whole" disk */
267 	dip->disklabel.d_partitions[0].p_offset = 0;
268 	dip->disklabel.d_partitions[0].p_size = 100;
269 	dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED;
270 
271 	/* The raw partition is special */
272 	dip->disklabel.d_partitions[RAW_PART].p_offset = 0;
273 	dip->disklabel.d_partitions[RAW_PART].p_size = 100;
274 	dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
275 
276 	dip->disklabel.d_npartitions = MAXPARTITIONS;
277 
278 	dip->disklabel.d_magic = DISKMAGIC;
279 	dip->disklabel.d_magic2 = DISKMAGIC;
280 	dip->disklabel.d_checksum = dkcksum(&dip->disklabel);
281 
282 	/* Add to queue of disks */
283 	TAILQ_INSERT_TAIL(&disklist, dip, list);
284 }
285 
286 
287 /* Find info on given BIOS disk */
288 struct diskinfo *
289 dklookup(int dev)
290 {
291 	struct diskinfo *dip;
292 
293 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
294 		if (dip->bios_info.bios_number == dev)
295 			return dip;
296 
297 	return NULL;
298 }
299 
300 void
301 dump_diskinfo(void)
302 {
303 	struct diskinfo *dip;
304 
305 	printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
306 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
307 		bios_diskinfo_t *bdi = &dip->bios_info;
308 		int d = bdi->bios_number;
309 		int u = d & 0x7f;
310 		char c;
311 
312 		if (bdi->flags & BDI_EL_TORITO) {
313 			c = 'c';
314 			u = 0;
315 		} else {
316 		    	c = (d & 0x80) ? 'h' : 'f';
317 		}
318 
319 		printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n",
320 		    c, u, d,
321 		    (bdi->flags & BDI_BADLABEL)?"*none*":"label",
322 		    bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
323 		    bdi->flags, bdi->checksum);
324 	}
325 }
326 
327 /* Find BIOS portion on given BIOS disk
328  * XXX - Use dklookup() instead.
329  */
330 bios_diskinfo_t *
331 bios_dklookup(int dev)
332 {
333 	struct diskinfo *dip;
334 
335 	dip = dklookup(dev);
336 	if (dip)
337 		return &dip->bios_info;
338 
339 	return NULL;
340 }
341 
342 /*
343  * Checksum one more block on all harddrives
344  *
345  * Use the adler32() function from libz,
346  * as it is quick, small, and available.
347  */
348 int
349 disksum(int blk)
350 {
351 	struct diskinfo *dip, *dip2;
352 	int st, reprobe = 0;
353 	char *buf;
354 
355 	buf = alloca(DEV_BSIZE);
356 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
357 		bios_diskinfo_t *bdi = &dip->bios_info;
358 
359 		/* Skip this disk if it is not a HD or has had an I/O error */
360 		if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID)
361 			continue;
362 
363 		/* Adler32 checksum */
364 		st = biosd_io(F_READ, bdi, blk, 1, buf);
365 		if (st) {
366 			bdi->flags |= BDI_INVALID;
367 			continue;
368 		}
369 		bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
370 
371 		for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
372 				dip2 = TAILQ_NEXT(dip2, list)) {
373 			bios_diskinfo_t *bd = &dip2->bios_info;
374 			if ((bd->bios_number & 0x80) &&
375 			    !(bd->flags & BDI_INVALID) &&
376 			    bdi->checksum == bd->checksum)
377 				reprobe = 1;
378 		}
379 	}
380 
381 	return reprobe;
382 }
383