xref: /openbsd/sys/arch/amd64/stand/libsa/diskprobe.c (revision fc6d48fd)
1 /*	$OpenBSD: diskprobe.c,v 1.28 2024/06/04 20:31:35 krw 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 */
54 static int disksum(int);
55 
56 int bootdev_has_hibernate(void);		/* export for loadfile() */
57 
58 /* List of disk devices we found/probed */
59 struct disklist_lh disklist;
60 
61 /* Pointer to boot device */
62 struct diskinfo *bootdev_dip;
63 
64 extern int debug;
65 extern int bios_bootdev;
66 extern int bios_cddev;
67 
68 static void
diskinfo_init(struct diskinfo * dip)69 diskinfo_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 */
77 static void
floppyprobe(void)78 floppyprobe(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 */
116 static void
hardprobe(void)117 hardprobe(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 */
181 u_int32_t bios_cksumlen;
182 void
diskprobe(void)183 diskprobe(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 
229 void
cdprobe(void)230 cdprobe(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 	printf(" cd0");
244 
245 	dip->bios_info.bios_number = cddev;
246 	dip->bios_info.bios_edd = 1;		/* Use the LBA calls */
247 	dip->bios_info.flags |= BDI_GOODLABEL | BDI_EL_TORITO;
248 	dip->bios_info.checksum = 0;		 /* just in case */
249 	dip->bios_info.bsd_dev =
250 	    MAKEBOOTDEV(6, 0, 0, 0, RAW_PART);
251 
252 	/* Create an imaginary disk label */
253 	dip->disklabel.d_secsize = 2048;
254 	dip->disklabel.d_ntracks = 1;
255 	dip->disklabel.d_nsectors = 100;
256 	dip->disklabel.d_ncylinders = 1;
257 	dip->disklabel.d_secpercyl = dip->disklabel.d_ntracks *
258 	    dip->disklabel.d_nsectors;
259 
260 	strncpy(dip->disklabel.d_typename, "ATAPI CD-ROM",
261 	    sizeof(dip->disklabel.d_typename));
262 	dip->disklabel.d_type = DTYPE_ATAPI;
263 
264 	strncpy(dip->disklabel.d_packname, "fictitious",
265 	    sizeof(dip->disklabel.d_packname));
266 	DL_SETDSIZE(&dip->disklabel, 100);
267 
268 	/* 'a' partition covering the "whole" disk */
269 	DL_SETPOFFSET(&dip->disklabel.d_partitions[0], 0);
270 	DL_SETPSIZE(&dip->disklabel.d_partitions[0], 100);
271 	dip->disklabel.d_partitions[0].p_fstype = FS_UNUSED;
272 
273 	/* The raw partition is special */
274 	DL_SETPOFFSET(&dip->disklabel.d_partitions[RAW_PART], 0);
275 	DL_SETPSIZE(&dip->disklabel.d_partitions[RAW_PART], 100);
276 	dip->disklabel.d_partitions[RAW_PART].p_fstype = FS_UNUSED;
277 
278 	dip->disklabel.d_npartitions = MAXPARTITIONS;
279 
280 	dip->disklabel.d_magic = DISKMAGIC;
281 	dip->disklabel.d_magic2 = DISKMAGIC;
282 	dip->disklabel.d_checksum = dkcksum(&dip->disklabel);
283 
284 	/* Add to queue of disks */
285 	TAILQ_INSERT_TAIL(&disklist, dip, list);
286 }
287 
288 /* Find info on given BIOS disk */
289 struct diskinfo *
dklookup(int dev)290 dklookup(int dev)
291 {
292 	struct diskinfo *dip;
293 
294 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
295 		if (dip->bios_info.bios_number == dev)
296 			return dip;
297 
298 	return NULL;
299 }
300 
301 void
dump_diskinfo(void)302 dump_diskinfo(void)
303 {
304 	struct diskinfo *dip;
305 
306 	printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
307 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
308 		bios_diskinfo_t *bdi = &dip->bios_info;
309 		int d = bdi->bios_number;
310 		int u = d & 0x7f;
311 		char c;
312 
313 		if (bdi->flags & BDI_EL_TORITO) {
314 			c = 'c';
315 			u = 0;
316 		} else {
317 		    	c = (d & 0x80) ? 'h' : 'f';
318 		}
319 
320 		printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n",
321 		    c, u, d,
322 		    (bdi->flags & BDI_BADLABEL)?"*none*":"label",
323 		    bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
324 		    bdi->flags, bdi->checksum);
325 	}
326 }
327 
328 /* Find BIOS portion on given BIOS disk
329  * XXX - Use dklookup() instead.
330  */
331 bios_diskinfo_t *
bios_dklookup(int dev)332 bios_dklookup(int dev)
333 {
334 	struct diskinfo *dip;
335 
336 	dip = dklookup(dev);
337 	if (dip)
338 		return &dip->bios_info;
339 
340 	return NULL;
341 }
342 
343 /*
344  * Checksum one more block on all harddrives
345  *
346  * Use the adler32() function from libz,
347  * as it is quick, small, and available.
348  */
349 int
disksum(int blk)350 disksum(int blk)
351 {
352 	struct diskinfo *dip, *dip2;
353 	int st, reprobe = 0;
354 	char buf[DEV_BSIZE];
355 
356 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
357 		bios_diskinfo_t *bdi = &dip->bios_info;
358 
359 		/* Skip this disk if it is not a HD or has had an I/O error */
360 		if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID)
361 			continue;
362 
363 		/* Adler32 checksum */
364 		st = dip->diskio(F_READ, dip, blk, 1, buf);
365 		if (st) {
366 			bdi->flags |= BDI_INVALID;
367 			continue;
368 		}
369 		bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
370 
371 		for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
372 				dip2 = TAILQ_NEXT(dip2, list)) {
373 			bios_diskinfo_t *bd = &dip2->bios_info;
374 			if ((bd->bios_number & 0x80) &&
375 			    !(bd->flags & BDI_INVALID) &&
376 			    bdi->checksum == bd->checksum)
377 				reprobe = 1;
378 		}
379 	}
380 
381 	return reprobe;
382 }
383 
384 int
bootdev_has_hibernate(void)385 bootdev_has_hibernate(void)
386 {
387 	return ((bootdev_dip->bios_info.flags & BDI_HIBVALID)? 1 : 0);
388 }
389 
390 void
check_hibernate(struct diskinfo * dip)391 check_hibernate(struct diskinfo *dip)
392 {
393 	uint8_t buf[DEV_BSIZE];
394 	daddr_t sec;
395 	int error;
396 	union hibernate_info *hib = (union hibernate_info *)&buf;
397 
398 	/* read hibernate */
399 	if (dip->disklabel.d_partitions[1].p_fstype != FS_SWAP ||
400 	    DL_GETPSIZE(&dip->disklabel.d_partitions[1]) == 0)
401 		return;
402 
403 	sec = DL_GETPOFFSET(&dip->disklabel.d_partitions[1]) +
404 	    DL_GETPSIZE(&dip->disklabel.d_partitions[1]) - 1;
405 
406 	error = dip->strategy(dip, F_READ, DL_SECTOBLK(&dip->disklabel, sec),
407 	    sizeof buf, &buf, NULL);
408 	if (error == 0 && hib->magic == HIBERNATE_MAGIC)
409 		dip->bios_info.flags |= BDI_HIBVALID; /* Hibernate present */
410 }
411