diskprobe.c revision 1.1
1/*	$OpenBSD: diskprobe.c,v 1.1 2004/02/03 12:09:47 mickey 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 */
47static int disksum(int);
48
49/* List of disk devices we found/probed */
50struct disklist_lh disklist;
51
52/* Pointer to boot device */
53struct diskinfo *bootdev_dip;
54
55extern int debug;
56
57/* Probe for all BIOS floppies */
58static void
59floppyprobe(void)
60{
61	struct diskinfo *dip;
62	int i;
63
64	/* Floppies */
65	for(i = 0; i < 4; i++) {
66		dip = alloc(sizeof(struct diskinfo));
67		bzero(dip, sizeof(*dip));
68
69		if(bios_getdiskinfo(i, &dip->bios_info)) {
70#ifdef BIOS_DEBUG
71			if (debug)
72				printf(" <!fd%u>", i);
73#endif
74			free(dip, 0);
75			break;
76		}
77
78		printf(" fd%u", i);
79
80		/* Fill out best we can - (fd?) */
81		dip->bios_info.bsd_dev = MAKEBOOTDEV(2, 0, 0, i, RAW_PART);
82
83		/*
84		 * Delay reading the disklabel until we're sure we want
85		 * to boot from the floppy. Doing this avoids a delay
86		 * (sometimes very long) when trying to read the label
87		 * and the drive is unplugged.
88		 */
89		dip->bios_info.flags |= BDI_BADLABEL;
90
91		/* Add to queue of disks */
92		TAILQ_INSERT_TAIL(&disklist, dip, list);
93	}
94}
95
96
97/* Probe for all BIOS hard disks */
98static void
99hardprobe(void)
100{
101	struct diskinfo *dip;
102	int i;
103	u_int bsdunit, type;
104	u_int scsi = 0, ide = 0;
105	const char *dc = (const char *)((0x40 << 4) + 0x75);
106
107	/* Hard disks */
108	for (i = 0x80; i < (0x80 + *dc); i++) {
109		dip = alloc(sizeof(struct diskinfo));
110		bzero(dip, sizeof(*dip));
111
112		if(bios_getdiskinfo(i, &dip->bios_info)) {
113#ifdef BIOS_DEBUG
114			if (debug)
115				printf(" <!hd%u>", i&0x7f);
116#endif
117			free(dip, 0);
118			break;
119		}
120
121		printf(" hd%u%s", i&0x7f, (dip->bios_info.bios_edd > 0?"+":""));
122
123		/* Try to find the label, to figure out device type */
124		if((bios_getdisklabel(&dip->bios_info, &dip->disklabel)) ) {
125			printf("*");
126			bsdunit = ide++;
127			type = 0;	/* XXX let it be IDE */
128		} else {
129			/* Best guess */
130			switch (dip->disklabel.d_type) {
131			case DTYPE_SCSI:
132				type = 4;
133				bsdunit = scsi++;
134				dip->bios_info.flags |= BDI_GOODLABEL;
135				break;
136
137			case DTYPE_ESDI:
138			case DTYPE_ST506:
139				type = 0;
140				bsdunit = ide++;
141				dip->bios_info.flags |= BDI_GOODLABEL;
142				break;
143
144			default:
145				dip->bios_info.flags |= BDI_BADLABEL;
146				type = 0;	/* XXX Suggest IDE */
147				bsdunit = ide++;
148			}
149		}
150
151		dip->bios_info.checksum = 0; /* just in case */
152		/* Fill out best we can */
153		dip->bios_info.bsd_dev = MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART);
154
155		/* Add to queue of disks */
156		TAILQ_INSERT_TAIL(&disklist, dip, list);
157	}
158}
159
160
161/* Probe for all BIOS supported disks */
162u_int32_t bios_cksumlen;
163void
164diskprobe(void)
165{
166	struct diskinfo *dip;
167	int i;
168
169	/* These get passed to kernel */
170	bios_diskinfo_t *bios_diskinfo;
171
172	/* Init stuff */
173	printf("disk:");
174	TAILQ_INIT(&disklist);
175
176	/* Do probes */
177	floppyprobe();
178#ifdef BIOS_DEBUG
179	if (debug)
180		printf(";");
181#endif
182	hardprobe();
183
184	/* Checksumming of hard disks */
185	for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
186		;
187	bios_cksumlen = i;
188
189	/* Get space for passing bios_diskinfo stuff to kernel */
190	for(i = 0, dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
191		i++;
192	bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t));
193
194	/* Copy out the bios_diskinfo stuff */
195	for(i = 0, dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
196		bios_diskinfo[i++] = dip->bios_info;
197
198	bios_diskinfo[i++].bios_number = -1;
199	/* Register for kernel use */
200	addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen);
201	addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t), bios_diskinfo);
202
203	printf("\n");
204}
205
206
207/* Find info on given BIOS disk */
208struct diskinfo *
209dklookup(int dev)
210{
211	struct diskinfo *dip;
212
213	for(dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
214		if(dip->bios_info.bios_number == dev)
215			return(dip);
216
217	return(NULL);
218}
219
220void
221dump_diskinfo(void)
222{
223	struct diskinfo *dip;
224
225	printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
226	for(dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)){
227		bios_diskinfo_t *bdi = &dip->bios_info;
228		int d = bdi->bios_number;
229
230		printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n",
231		    (d & 0x80)?'h':'f', d & 0x7F, d,
232			(bdi->flags & BDI_BADLABEL)?"*none*":"label",
233		    bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
234		    bdi->flags, bdi->checksum);
235	}
236}
237
238/* Find BIOS portion on given BIOS disk
239 * XXX - Use dklookup() instead.
240 */
241bios_diskinfo_t *
242bios_dklookup(int dev)
243{
244	struct diskinfo *dip;
245
246	dip = dklookup(dev);
247	if(dip)
248		return(&dip->bios_info);
249
250	return(NULL);
251}
252
253/*
254 * Checksum one more block on all harddrives
255 *
256 * Use the adler32() function from libz,
257 * as it is quick, small, and available.
258 */
259int
260disksum(int blk)
261{
262	struct diskinfo *dip, *dip2;
263	int st, reprobe = 0;
264	char *buf;
265
266	buf = alloca(DEV_BSIZE);
267	for(dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)){
268		bios_diskinfo_t *bdi = &dip->bios_info;
269
270		/* Skip this disk if it is not a HD or has had an I/O error */
271		if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID)
272			continue;
273
274		/* Adler32 checksum */
275		st = biosd_io(F_READ, bdi, blk, 1, buf);
276		if (st) {
277			bdi->flags |= BDI_INVALID;
278			continue;
279		}
280		bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
281
282		for(dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
283				dip2 = TAILQ_NEXT(dip2, list)){
284			bios_diskinfo_t *bd = &dip2->bios_info;
285			if ((bd->bios_number & 0x80) &&
286			    !(bd->flags & BDI_INVALID) &&
287			    bdi->checksum == bd->checksum)
288				reprobe = 1;
289		}
290	}
291
292	return (reprobe);
293}
294
295