xref: /openbsd/sys/arch/amd64/stand/libsa/diskprobe.c (revision a6445c1d)
1 /*	$OpenBSD: diskprobe.c,v 1.15 2014/07/12 20:58:31 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 */
53 static int disksum(int);
54 
55 /* List of disk devices we found/probed */
56 struct disklist_lh disklist;
57 
58 /* Pointer to boot device */
59 struct diskinfo *bootdev_dip;
60 
61 extern int debug;
62 extern int bios_bootdev;
63 extern int bios_cddev;
64 
65 /* Probe for all BIOS floppies */
66 static void
67 floppyprobe(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);
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 */
106 static void
107 hardprobe(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);
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 */
171 u_int32_t bios_cksumlen;
172 void
173 diskprobe(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 
220 void
221 cdprobe(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);
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 */
296 struct diskinfo *
297 dklookup(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 
308 void
309 dump_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  */
338 bios_diskinfo_t *
339 bios_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  */
356 int
357 disksum(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