xref: /openbsd/sys/arch/amd64/stand/libsa/diskprobe.c (revision cca36db2)
1 /*	$OpenBSD: diskprobe.c,v 1.9 2012/01/11 14:47:02 jsing Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Tobias Weingartner
5  * Copyright (c) 2012 Joel Sing <jsing@openbsd.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  */
30 
31 /* We want the disk type names from disklabel.h */
32 #undef DKTYPENAMES
33 
34 #include <sys/param.h>
35 #include <sys/queue.h>
36 #include <sys/reboot.h>
37 #include <sys/disklabel.h>
38 #include <dev/biovar.h>
39 #include <dev/softraidvar.h>
40 #include <stand/boot/bootarg.h>
41 #include <machine/biosvar.h>
42 #include <lib/libz/zlib.h>
43 #include "disk.h"
44 #include "biosdev.h"
45 #include "libsa.h"
46 
47 #define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE	/* Max # of blks to cksum */
48 
49 /* Local Prototypes */
50 static int disksum(int);
51 
52 /* List of disk devices we found/probed */
53 struct disklist_lh disklist;
54 
55 /* List of softraid volumes. */
56 struct sr_boot_volume_head sr_volumes;
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 = MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART);
162 
163 		/* Add to queue of disks */
164 		TAILQ_INSERT_TAIL(&disklist, dip, list);
165 	}
166 }
167 
168 
169 static void
170 srprobe(void)
171 {
172 	struct sr_boot_volume *bv, *bv1, *bv2;
173 	struct sr_boot_chunk *bc, *bc1, *bc2;
174 	struct sr_meta_chunk *mc;
175 	struct sr_metadata *md;
176 	struct diskinfo *dip;
177 	struct partition *pp;
178 	int i, error, volno;
179 	dev_t bsd_dev;
180 	daddr_t off;
181 
182 	/* Probe for softraid volumes. */
183 	SLIST_INIT(&sr_volumes);
184 
185 	md = alloc(SR_META_SIZE * 512);
186 
187 	TAILQ_FOREACH(dip, &disklist, list) {
188 
189 		/* Only check hard disks, skip those with I/O errors. */
190 		if ((dip->bios_info.bios_number & 0x80) == 0 ||
191 		    (dip->bios_info.flags & BDI_INVALID))
192 			continue;
193 
194 		/* Make sure disklabel has been read. */
195 		if ((dip->bios_info.flags & (BDI_BADLABEL|BDI_GOODLABEL)) == 0)
196 			continue;
197 
198 		for (i = 0; i < MAXPARTITIONS; i++) {
199 
200 			pp = &dip->disklabel.d_partitions[i];
201 			if (pp->p_fstype != FS_RAID || pp->p_size == 0)
202 				continue;
203 
204 			/* Read softraid metadata. */
205 			bzero(md, SR_META_SIZE * 512);
206 			off = DL_GETPOFFSET(pp) + SR_META_OFFSET;
207 			error = biosd_io(F_READ, &dip->bios_info, off,
208 			    SR_META_SIZE, md);
209 			if (error)
210 				continue;
211 
212 			/* Is this valid softraid metadata? */
213 			if (md->ssdi.ssd_magic != SR_MAGIC)
214 				continue;
215 
216 			/* XXX - validate checksum. */
217 
218 			/* Locate chunk-specific metadata for this chunk. */
219 			mc = (struct sr_meta_chunk *)(md + 1);
220 			mc += md->ssdi.ssd_chunk_id;
221 
222 			/* XXX - extract necessary optional metadata. */
223 
224 			bc = alloc(sizeof(struct sr_boot_chunk));
225 			bc->sbc_diskinfo = dip;
226 			bc->sbc_disk = dip->bios_info.bios_number;
227 			bc->sbc_part = 'a' + i;
228 
229 			bsd_dev = dip->bios_info.bsd_dev;
230 			bc->sbc_mm = MAKEBOOTDEV(B_TYPE(bsd_dev),
231 			    B_ADAPTOR(bsd_dev), B_CONTROLLER(bsd_dev),
232 			    B_UNIT(bsd_dev), bc->sbc_part - 'a');
233 
234 			bc->sbc_chunk_id = md->ssdi.ssd_chunk_id;
235 			bc->sbc_ondisk = md->ssd_ondisk;
236 			bc->sbc_state = mc->scm_status;
237 
238 			/* Handle key disks separately... later. */
239 			if (md->ssdi.ssd_level == SR_KEYDISK_LEVEL)
240 				continue;
241 
242 			SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
243 				if (bcmp(&md->ssdi.ssd_uuid, &bv->sbv_uuid,
244 				    sizeof(md->ssdi.ssd_uuid)) == 0)
245 					break;
246 			}
247 
248 			if (bv == NULL) {
249 				bv = alloc(sizeof(struct sr_boot_volume));
250 				bv->sbv_level = md->ssdi.ssd_level;
251 				bv->sbv_volid = md->ssdi.ssd_volid;
252 				bv->sbv_chunk_no = md->ssdi.ssd_chunk_no;
253 				bv->sbv_flags = md->ssdi.ssd_vol_flags;
254 				bv->sbv_size = md->ssdi.ssd_size;
255 				bv->sbv_data_offset = md->ssd_data_offset;
256 				bcopy(&md->ssdi.ssd_uuid, &bv->sbv_uuid,
257 				    sizeof(md->ssdi.ssd_uuid));
258 				SLIST_INIT(&bv->sbv_chunks);
259 
260 				/* Maintain volume order. */
261 				bv2 = NULL;
262 				SLIST_FOREACH(bv1, &sr_volumes, sbv_link) {
263 					if (bv1->sbv_volid > bv->sbv_volid)
264 						break;
265 					bv2 = bv1;
266 				}
267 				if (bv2 == NULL)
268 					SLIST_INSERT_HEAD(&sr_volumes, bv,
269 					    sbv_link);
270 				else
271 					SLIST_INSERT_AFTER(bv2, bv, sbv_link);
272 			}
273 
274 			/* Maintain chunk order. */
275 			bc2 = NULL;
276 			SLIST_FOREACH(bc1, &bv->sbv_chunks, sbc_link) {
277 				if (bc1->sbc_chunk_id > bc->sbc_chunk_id)
278 					break;
279 				bc2 = bc1;
280 			}
281 			if (bc2 == NULL)
282 				SLIST_INSERT_HEAD(&bv->sbv_chunks,
283 				    bc, sbc_link);
284 			else
285 				SLIST_INSERT_AFTER(bc2, bc, sbc_link);
286 
287 			bv->sbv_chunks_found++;
288 		}
289 	}
290 
291 	/*
292 	 * Assemble RAID volumes.
293 	 */
294 	volno = 0;
295 	SLIST_FOREACH(bv, &sr_volumes, sbv_link) {
296 
297 		/* Skip if this is a hotspare "volume". */
298 		if (bv->sbv_level == SR_HOTSPARE_LEVEL &&
299 		    bv->sbv_chunk_no == 1)
300 			continue;
301 
302 		/* Determine current ondisk version. */
303 		bv->sbv_ondisk = 0;
304 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
305 			if (bc->sbc_ondisk > bv->sbv_ondisk)
306 				bv->sbv_ondisk = bc->sbc_ondisk;
307 		}
308 		SLIST_FOREACH(bc, &bv->sbv_chunks, sbc_link) {
309 			if (bc->sbc_ondisk != bv->sbv_ondisk)
310 				bc->sbc_state = BIOC_SDOFFLINE;
311 		}
312 
313 		/* XXX - Check for duplicate chunks. */
314 
315 		/*
316 		 * Validate that volume has sufficient chunks for
317 		 * read-only access.
318 		 *
319 		 * XXX - check chunk states.
320 		 */
321 		bv->sbv_state = BIOC_SVOFFLINE;
322 		switch (bv->sbv_level) {
323 		case 0:
324 		case 'C':
325 		case 'c':
326 			if (bv->sbv_chunk_no == bv->sbv_chunks_found)
327 				bv->sbv_state = BIOC_SVONLINE;
328 			break;
329 
330 		case 1:
331 			if (bv->sbv_chunk_no == bv->sbv_chunks_found)
332 				bv->sbv_state = BIOC_SVONLINE;
333 			else if (bv->sbv_chunks_found > 0)
334 				bv->sbv_state = BIOC_SVDEGRADED;
335 			break;
336 		}
337 
338 		bv->sbv_unit = volno++;
339 		if (bv->sbv_state != BIOC_SVOFFLINE)
340 			printf(" sr%d%s", bv->sbv_unit,
341 			    bv->sbv_flags & BIOC_SCBOOTABLE ? "*" : "");
342 	}
343 
344 	if (md)
345 		free(md, 0);
346 }
347 
348 
349 /* Probe for all BIOS supported disks */
350 u_int32_t bios_cksumlen;
351 void
352 diskprobe(void)
353 {
354 	struct diskinfo *dip;
355 	int i;
356 
357 	/* These get passed to kernel */
358 	bios_diskinfo_t *bios_diskinfo;
359 
360 	/* Init stuff */
361 	TAILQ_INIT(&disklist);
362 
363 	/* Do probes */
364 	floppyprobe();
365 #ifdef BIOS_DEBUG
366 	if (debug)
367 		printf(";");
368 #endif
369 	hardprobe();
370 
371 	srprobe();
372 
373 	/* Checksumming of hard disks */
374 	for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
375 		;
376 	bios_cksumlen = i;
377 
378 	/* Get space for passing bios_diskinfo stuff to kernel */
379 	for(i = 0, dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
380 		i++;
381 	bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t));
382 
383 	/* Copy out the bios_diskinfo stuff */
384 	for(i = 0, dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
385 		bios_diskinfo[i++] = dip->bios_info;
386 
387 	bios_diskinfo[i++].bios_number = -1;
388 	/* Register for kernel use */
389 	addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen);
390 	addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t), bios_diskinfo);
391 }
392 
393 
394 void
395 cdprobe(void)
396 {
397 	struct diskinfo *dip;
398 	int cddev = bios_cddev & 0xff;
399 
400 	/* Another BIOS boot device... */
401 
402 	if (bios_cddev == -1)			/* Not been set, so don't use */
403 		return;
404 
405 	dip = alloc(sizeof(struct diskinfo));
406 	bzero(dip, sizeof(*dip));
407 
408 #if 0
409 	if (bios_getdiskinfo(cddev, &dip->bios_info)) {
410 		printf(" <!cd0>");	/* XXX */
411 		free(dip, 0);
412 		return;
413 	}
414 #endif
415 
416 	printf(" cd0");
417 
418 	dip->bios_info.bios_number = cddev;
419 	dip->bios_info.bios_edd = 1;		/* Use the LBA calls */
420 	dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO;
421 	dip->bios_info.checksum = 0;		 /* just in case */
422 	dip->bios_info.bsd_dev =
423 	    MAKEBOOTDEV(6, 0, 0, 0, RAW_PART);
424 
425 	/* Create an imaginary disk label */
426 	dip->disklabel.d_secsize = 2048;
427 	dip->disklabel.d_ntracks = 1;
428 	dip->disklabel.d_nsectors = 100;
429 	dip->disklabel.d_ncylinders = 1;
430 	dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks *
431 	    dip->disklabel.d_nsectors;
432 	if (dip->disklabel.d_secpercyl == 0) {
433 		dip->disklabel.d_secpercyl = 100;
434 		/* as long as it's not 0, since readdisklabel divides by it */
435 	}
436 
437 	strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM",
438 	    sizeof(dip->disklabel.d_typename));
439 	dip->disklabel.d_type = DTYPE_ATAPI;
440 
441 	strncpy(dip->disklabel.d_packname, "fictitious",
442 	    sizeof(dip->disklabel.d_packname));
443 	dip->disklabel.d_secperunit = 100;
444 
445 	dip->disklabel.d_bbsize = 2048;
446 	dip->disklabel.d_sbsize = 2048;
447 
448 	/* 'a' partition covering the "whole" disk */
449 	dip->disklabel.d_partitions[0].p_offset = 0;
450 	dip->disklabel.d_partitions[0].p_size = 100;
451 	dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED;
452 
453 	/* The raw partition is special */
454 	dip->disklabel.d_partitions[RAW_PART].p_offset = 0;
455 	dip->disklabel.d_partitions[RAW_PART].p_size = 100;
456 	dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
457 
458 	dip->disklabel.d_npartitions = MAXPARTITIONS;
459 
460 	dip->disklabel.d_magic = DISKMAGIC;
461 	dip->disklabel.d_magic2 = DISKMAGIC;
462 	dip->disklabel.d_checksum = dkcksum(&dip->disklabel);
463 
464 	/* Add to queue of disks */
465 	TAILQ_INSERT_TAIL(&disklist, dip, list);
466 }
467 
468 
469 /* Find info on given BIOS disk */
470 struct diskinfo *
471 dklookup(int dev)
472 {
473 	struct diskinfo *dip;
474 
475 	for(dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
476 		if(dip->bios_info.bios_number == dev)
477 			return(dip);
478 
479 	return(NULL);
480 }
481 
482 void
483 dump_diskinfo(void)
484 {
485 	struct diskinfo *dip;
486 
487 	printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
488 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
489 		bios_diskinfo_t *bdi = &dip->bios_info;
490 		int d = bdi->bios_number;
491 		int u = d & 0x7f;
492 		char c;
493 
494 		if (bdi->flags & BDI_EL_TORITO) {
495 			c = 'c';
496 			u = 0;
497 		} else {
498 		    	c = (d & 0x80) ? 'h' : 'f';
499 		}
500 
501 		printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n",
502 		    c, u, d,
503 		    (bdi->flags & BDI_BADLABEL)?"*none*":"label",
504 		    bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
505 		    bdi->flags, bdi->checksum);
506 	}
507 }
508 
509 /* Find BIOS portion on given BIOS disk
510  * XXX - Use dklookup() instead.
511  */
512 bios_diskinfo_t *
513 bios_dklookup(int dev)
514 {
515 	struct diskinfo *dip;
516 
517 	dip = dklookup(dev);
518 	if(dip)
519 		return(&dip->bios_info);
520 
521 	return(NULL);
522 }
523 
524 /*
525  * Checksum one more block on all harddrives
526  *
527  * Use the adler32() function from libz,
528  * as it is quick, small, and available.
529  */
530 int
531 disksum(int blk)
532 {
533 	struct diskinfo *dip, *dip2;
534 	int st, reprobe = 0;
535 	char *buf;
536 
537 	buf = alloca(DEV_BSIZE);
538 	for(dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)){
539 		bios_diskinfo_t *bdi = &dip->bios_info;
540 
541 		/* Skip this disk if it is not a HD or has had an I/O error */
542 		if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID)
543 			continue;
544 
545 		/* Adler32 checksum */
546 		st = biosd_io(F_READ, bdi, blk, 1, buf);
547 		if (st) {
548 			bdi->flags |= BDI_INVALID;
549 			continue;
550 		}
551 		bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
552 
553 		for(dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
554 				dip2 = TAILQ_NEXT(dip2, list)){
555 			bios_diskinfo_t *bd = &dip2->bios_info;
556 			if ((bd->bios_number & 0x80) &&
557 			    !(bd->flags & BDI_INVALID) &&
558 			    bdi->checksum == bd->checksum)
559 				reprobe = 1;
560 		}
561 	}
562 
563 	return (reprobe);
564 }
565