diskprobe.c revision 1.14
1/*	$OpenBSD: diskprobe.c,v 1.14 2014/07/12 18:44:41 tedu 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
38#include <lib/libz/zlib.h>
39#include <machine/biosvar.h>
40#include <stand/boot/bootarg.h>
41
42#include "disk.h"
43#include "biosdev.h"
44#include "libsa.h"
45
46#ifdef SOFTRAID
47#include "softraid.h"
48#endif
49
50#define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE	/* Max # of blks to cksum */
51
52/* Local Prototypes */
53static int disksum(int);
54
55/* List of disk devices we found/probed */
56struct disklist_lh disklist;
57
58/* Pointer to boot device */
59struct diskinfo *bootdev_dip;
60
61extern int debug;
62extern int bios_bootdev;
63extern int bios_cddev;
64
65/* Probe for all BIOS floppies */
66static void
67floppyprobe(void)
68{
69	struct diskinfo *dip;
70	int i;
71
72	/* Floppies */
73	for (i = 0; i < 4; i++) {
74		dip = alloc(sizeof(struct diskinfo));
75		bzero(dip, sizeof(*dip));
76
77		if (bios_getdiskinfo(i, &dip->bios_info)) {
78#ifdef BIOS_DEBUG
79			if (debug)
80				printf(" <!fd%u>", i);
81#endif
82			free(dip, 0, 0);
83			break;
84		}
85
86		printf(" fd%u", i);
87
88		/* Fill out best we can - (fd?) */
89		dip->bios_info.bsd_dev = MAKEBOOTDEV(2, 0, 0, i, RAW_PART);
90
91		/*
92		 * Delay reading the disklabel until we're sure we want
93		 * to boot from the floppy. Doing this avoids a delay
94		 * (sometimes very long) when trying to read the label
95		 * and the drive is unplugged.
96		 */
97		dip->bios_info.flags |= BDI_BADLABEL;
98
99		/* Add to queue of disks */
100		TAILQ_INSERT_TAIL(&disklist, dip, list);
101	}
102}
103
104
105/* Probe for all BIOS hard disks */
106static void
107hardprobe(void)
108{
109	struct diskinfo *dip;
110	int i;
111	u_int bsdunit, type;
112	u_int scsi = 0, ide = 0;
113	const char *dc = (const char *)((0x40 << 4) + 0x75);
114
115	/* Hard disks */
116	for (i = 0x80; i < (0x80 + *dc); i++) {
117		dip = alloc(sizeof(struct diskinfo));
118		bzero(dip, sizeof(*dip));
119
120		if (bios_getdiskinfo(i, &dip->bios_info)) {
121#ifdef BIOS_DEBUG
122			if (debug)
123				printf(" <!hd%u>", i&0x7f);
124#endif
125			free(dip, 0, 0);
126			break;
127		}
128
129		printf(" hd%u%s", i&0x7f, (dip->bios_info.bios_edd > 0?"+":""));
130
131		/* Try to find the label, to figure out device type */
132		if ((bios_getdisklabel(&dip->bios_info, &dip->disklabel)) ) {
133			printf("*");
134			bsdunit = ide++;
135			type = 0;	/* XXX let it be IDE */
136		} else {
137			/* Best guess */
138			switch (dip->disklabel.d_type) {
139			case DTYPE_SCSI:
140				type = 4;
141				bsdunit = scsi++;
142				dip->bios_info.flags |= BDI_GOODLABEL;
143				break;
144
145			case DTYPE_ESDI:
146			case DTYPE_ST506:
147				type = 0;
148				bsdunit = ide++;
149				dip->bios_info.flags |= BDI_GOODLABEL;
150				break;
151
152			default:
153				dip->bios_info.flags |= BDI_BADLABEL;
154				type = 0;	/* XXX Suggest IDE */
155				bsdunit = ide++;
156			}
157		}
158
159		dip->bios_info.checksum = 0; /* just in case */
160		/* Fill out best we can */
161		dip->bios_info.bsd_dev =
162		    MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART);
163
164		/* Add to queue of disks */
165		TAILQ_INSERT_TAIL(&disklist, dip, list);
166	}
167}
168
169
170/* Probe for all BIOS supported disks */
171u_int32_t bios_cksumlen;
172void
173diskprobe(void)
174{
175	struct diskinfo *dip;
176	int i;
177
178	/* These get passed to kernel */
179	bios_diskinfo_t *bios_diskinfo;
180
181	/* Init stuff */
182	TAILQ_INIT(&disklist);
183
184	/* Do probes */
185	floppyprobe();
186#ifdef BIOS_DEBUG
187	if (debug)
188		printf(";");
189#endif
190	hardprobe();
191
192#ifdef SOFTRAID
193	srprobe();
194#endif
195
196	/* Checksumming of hard disks */
197	for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
198		;
199	bios_cksumlen = i;
200
201	/* Get space for passing bios_diskinfo stuff to kernel */
202	for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
203	    dip = TAILQ_NEXT(dip, list))
204		i++;
205	bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t));
206
207	/* Copy out the bios_diskinfo stuff */
208	for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
209	    dip = TAILQ_NEXT(dip, list))
210		bios_diskinfo[i++] = dip->bios_info;
211
212	bios_diskinfo[i++].bios_number = -1;
213	/* Register for kernel use */
214	addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen);
215	addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t),
216	    bios_diskinfo);
217}
218
219
220void
221cdprobe(void)
222{
223	struct diskinfo *dip;
224	int cddev = bios_cddev & 0xff;
225
226	/* Another BIOS boot device... */
227
228	if (bios_cddev == -1)			/* Not been set, so don't use */
229		return;
230
231	dip = alloc(sizeof(struct diskinfo));
232	bzero(dip, sizeof(*dip));
233
234#if 0
235	if (bios_getdiskinfo(cddev, &dip->bios_info)) {
236		printf(" <!cd0>");	/* XXX */
237		free(dip, 0, 0);
238		return;
239	}
240#endif
241
242	printf(" cd0");
243
244	dip->bios_info.bios_number = cddev;
245	dip->bios_info.bios_edd = 1;		/* Use the LBA calls */
246	dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO;
247	dip->bios_info.checksum = 0;		 /* just in case */
248	dip->bios_info.bsd_dev =
249	    MAKEBOOTDEV(6, 0, 0, 0, RAW_PART);
250
251	/* Create an imaginary disk label */
252	dip->disklabel.d_secsize = 2048;
253	dip->disklabel.d_ntracks = 1;
254	dip->disklabel.d_nsectors = 100;
255	dip->disklabel.d_ncylinders = 1;
256	dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks *
257	    dip->disklabel.d_nsectors;
258	if (dip->disklabel.d_secpercyl == 0) {
259		dip->disklabel.d_secpercyl = 100;
260		/* as long as it's not 0, since readdisklabel divides by it */
261	}
262
263	strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM",
264	    sizeof(dip->disklabel.d_typename));
265	dip->disklabel.d_type = DTYPE_ATAPI;
266
267	strncpy(dip->disklabel.d_packname, "fictitious",
268	    sizeof(dip->disklabel.d_packname));
269	DL_SETDSIZE(&dip->disklabel, 100);
270
271	dip->disklabel.d_bbsize = 2048;
272	dip->disklabel.d_sbsize = 2048;
273
274	/* 'a' partition covering the "whole" disk */
275	DL_SETPOFFSET(&dip->disklabel.d_partitions[0], 0);
276	DL_SETPSIZE(&dip->disklabel.d_partitions[0], 100);
277	dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED;
278
279	/* The raw partition is special */
280	DL_SETPOFFSET(&dip->disklabel.d_partitions[RAW_PART], 0);
281	DL_SETPSIZE(&dip->disklabel.d_partitions[RAW_PART], 100);
282	dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
283
284	dip->disklabel.d_npartitions = MAXPARTITIONS;
285
286	dip->disklabel.d_magic = DISKMAGIC;
287	dip->disklabel.d_magic2 = DISKMAGIC;
288	dip->disklabel.d_checksum = dkcksum(&dip->disklabel);
289
290	/* Add to queue of disks */
291	TAILQ_INSERT_TAIL(&disklist, dip, list);
292}
293
294
295/* Find info on given BIOS disk */
296struct diskinfo *
297dklookup(int dev)
298{
299	struct diskinfo *dip;
300
301	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
302		if (dip->bios_info.bios_number == dev)
303			return dip;
304
305	return NULL;
306}
307
308void
309dump_diskinfo(void)
310{
311	struct diskinfo *dip;
312
313	printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
314	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
315		bios_diskinfo_t *bdi = &dip->bios_info;
316		int d = bdi->bios_number;
317		int u = d & 0x7f;
318		char c;
319
320		if (bdi->flags & BDI_EL_TORITO) {
321			c = 'c';
322			u = 0;
323		} else {
324		    	c = (d & 0x80) ? 'h' : 'f';
325		}
326
327		printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n",
328		    c, u, d,
329		    (bdi->flags & BDI_BADLABEL)?"*none*":"label",
330		    bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
331		    bdi->flags, bdi->checksum);
332	}
333}
334
335/* Find BIOS portion on given BIOS disk
336 * XXX - Use dklookup() instead.
337 */
338bios_diskinfo_t *
339bios_dklookup(int dev)
340{
341	struct diskinfo *dip;
342
343	dip = dklookup(dev);
344	if (dip)
345		return &dip->bios_info;
346
347	return NULL;
348}
349
350/*
351 * Checksum one more block on all harddrives
352 *
353 * Use the adler32() function from libz,
354 * as it is quick, small, and available.
355 */
356int
357disksum(int blk)
358{
359	struct diskinfo *dip, *dip2;
360	int st, reprobe = 0;
361	char *buf;
362
363	buf = alloca(DEV_BSIZE);
364	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
365		bios_diskinfo_t *bdi = &dip->bios_info;
366
367		/* Skip this disk if it is not a HD or has had an I/O error */
368		if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID)
369			continue;
370
371		/* Adler32 checksum */
372		st = biosd_io(F_READ, bdi, blk, 1, buf);
373		if (st) {
374			bdi->flags |= BDI_INVALID;
375			continue;
376		}
377		bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
378
379		for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
380				dip2 = TAILQ_NEXT(dip2, list)) {
381			bios_diskinfo_t *bd = &dip2->bios_info;
382			if ((bd->bios_number & 0x80) &&
383			    !(bd->flags & BDI_INVALID) &&
384			    bdi->checksum == bd->checksum)
385				reprobe = 1;
386		}
387	}
388
389	return reprobe;
390}
391