diskprobe.c revision 1.3
1/*	$OpenBSD: diskprobe.c,v 1.3 2004/08/21 18:53:38 tom 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;
56extern int bios_bootdev;
57extern int bios_cddev;
58
59/* Probe for all BIOS floppies */
60static void
61floppyprobe(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 */
100static void
101hardprobe(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 = MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART);
156
157		/* Add to queue of disks */
158		TAILQ_INSERT_TAIL(&disklist, dip, list);
159	}
160}
161
162
163/* Probe for all BIOS supported disks */
164u_int32_t bios_cksumlen;
165void
166diskprobe(void)
167{
168	struct diskinfo *dip;
169	int i;
170
171	/* These get passed to kernel */
172	bios_diskinfo_t *bios_diskinfo;
173
174	/* Init stuff */
175	TAILQ_INIT(&disklist);
176
177	/* Do probes */
178	floppyprobe();
179#ifdef BIOS_DEBUG
180	if (debug)
181		printf(";");
182#endif
183	hardprobe();
184
185	/* Checksumming of hard disks */
186	for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
187		;
188	bios_cksumlen = i;
189
190	/* Get space for passing bios_diskinfo stuff to kernel */
191	for(i = 0, dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
192		i++;
193	bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t));
194
195	/* Copy out the bios_diskinfo stuff */
196	for(i = 0, dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
197		bios_diskinfo[i++] = dip->bios_info;
198
199	bios_diskinfo[i++].bios_number = -1;
200	/* Register for kernel use */
201	addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen);
202	addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t), bios_diskinfo);
203}
204
205
206void
207cdprobe(void)
208{
209	struct diskinfo *dip;
210	int cddev = bios_cddev & 0xff;
211
212	/* Another BIOS boot device... */
213
214	if (bios_cddev == -1)			/* Not been set, so don't use */
215		return;
216
217	dip = alloc(sizeof(struct diskinfo));
218	bzero(dip, sizeof(*dip));
219
220#if 0
221	if (bios_getdiskinfo(cddev, &dip->bios_info)) {
222		printf(" <!cd0>");	/* XXX */
223		free(dip, 0);
224		return;
225	}
226#endif
227
228	printf(" cd0");
229
230	dip->bios_info.bios_number = cddev;
231	dip->bios_info.bios_edd = 1;		/* Use the LBA calls */
232	dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO;
233	dip->bios_info.checksum = 0;		 /* just in case */
234	dip->bios_info.bsd_dev =
235	    MAKEBOOTDEV(0, 0, 0, 0xff, RAW_PART);
236
237	/* Create an imaginary disk label */
238	dip->disklabel.d_secsize = 2048;
239	dip->disklabel.d_ntracks = 1;
240	dip->disklabel.d_nsectors = 100;
241	dip->disklabel.d_ncylinders = 1;
242	dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks *
243	    dip->disklabel.d_nsectors;
244	if (dip->disklabel.d_secpercyl == 0) {
245		dip->disklabel.d_secpercyl = 100;
246		/* as long as it's not 0, since readdisklabel divides by it */
247	}
248
249	strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM",
250	    sizeof(dip->disklabel.d_typename));
251	dip->disklabel.d_type = DTYPE_ATAPI;
252
253	strncpy(dip->disklabel.d_packname, "fictitious",
254	    sizeof(dip->disklabel.d_packname));
255	dip->disklabel.d_secperunit = 100;
256	dip->disklabel.d_rpm = 300;
257	dip->disklabel.d_interleave = 1;
258	dip->disklabel.d_flags = D_REMOVABLE;
259
260	dip->disklabel.d_bbsize = 2048;
261	dip->disklabel.d_sbsize = 2048;
262
263	dip->disklabel.d_magic = DISKMAGIC;
264	dip->disklabel.d_magic2 = DISKMAGIC;
265	dip->disklabel.d_checksum = dkcksum(&dip->disklabel);
266
267	/* 'a' partition covering the "whole" disk */
268	dip->disklabel.d_partitions[0].p_offset = 0;
269	dip->disklabel.d_partitions[0].p_size = 100;
270	dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED;
271
272	/* The raw partition is special */
273	dip->disklabel.d_partitions[RAW_PART].p_offset = 0;
274	dip->disklabel.d_partitions[RAW_PART].p_size = 100;
275	dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
276
277	dip->disklabel.d_npartitions = RAW_PART + 1;
278
279	/* Add to queue of disks */
280	TAILQ_INSERT_TAIL(&disklist, dip, list);
281}
282
283
284/* Find info on given BIOS disk */
285struct diskinfo *
286dklookup(int dev)
287{
288	struct diskinfo *dip;
289
290	for(dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
291		if(dip->bios_info.bios_number == dev)
292			return(dip);
293
294	return(NULL);
295}
296
297void
298dump_diskinfo(void)
299{
300	struct diskinfo *dip;
301
302	printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
303	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
304		bios_diskinfo_t *bdi = &dip->bios_info;
305		int d = bdi->bios_number;
306		int u = d & 0x7f;
307		char c;
308
309		if (bdi->flags & BDI_EL_TORITO) {
310			c = 'c';
311			u = 0;
312		} else {
313		    	c = (d & 0x80) ? 'h' : 'f';
314		}
315
316		printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n",
317		    c, u, d,
318		    (bdi->flags & BDI_BADLABEL)?"*none*":"label",
319		    bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
320		    bdi->flags, bdi->checksum);
321	}
322}
323
324/* Find BIOS portion on given BIOS disk
325 * XXX - Use dklookup() instead.
326 */
327bios_diskinfo_t *
328bios_dklookup(int dev)
329{
330	struct diskinfo *dip;
331
332	dip = dklookup(dev);
333	if(dip)
334		return(&dip->bios_info);
335
336	return(NULL);
337}
338
339/*
340 * Checksum one more block on all harddrives
341 *
342 * Use the adler32() function from libz,
343 * as it is quick, small, and available.
344 */
345int
346disksum(int blk)
347{
348	struct diskinfo *dip, *dip2;
349	int st, reprobe = 0;
350	char *buf;
351
352	buf = alloca(DEV_BSIZE);
353	for(dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)){
354		bios_diskinfo_t *bdi = &dip->bios_info;
355
356		/* Skip this disk if it is not a HD or has had an I/O error */
357		if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID)
358			continue;
359
360		/* Adler32 checksum */
361		st = biosd_io(F_READ, bdi, blk, 1, buf);
362		if (st) {
363			bdi->flags |= BDI_INVALID;
364			continue;
365		}
366		bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
367
368		for(dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
369				dip2 = TAILQ_NEXT(dip2, list)){
370			bios_diskinfo_t *bd = &dip2->bios_info;
371			if ((bd->bios_number & 0x80) &&
372			    !(bd->flags & BDI_INVALID) &&
373			    bdi->checksum == bd->checksum)
374				reprobe = 1;
375		}
376	}
377
378	return (reprobe);
379}
380