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