xref: /openbsd-src/sys/arch/amd64/stand/efiboot/diskprobe.c (revision fc6d48fd3b921041134581a7d748516fe2a9e57f)
1 /*	$OpenBSD: diskprobe.c,v 1.3 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 #include "efidev.h"
51 
52 #define MAX_CKSUMLEN MAXBSIZE / DEV_BSIZE	/* Max # of blks to cksum */
53 
54 /* Local Prototypes */
55 static int disksum(int);
56 
57 int bootdev_has_hibernate(void);		/* export for loadfile() */
58 
59 /* List of disk devices we found/probed */
60 struct disklist_lh disklist;
61 
62 /* Pointer to boot device */
63 struct diskinfo *bootdev_dip;
64 
65 extern int debug;
66 extern int bios_bootdev;
67 extern int bios_cddev;
68 
69 static void
efi_hardprobe(void)70 efi_hardprobe(void)
71 {
72 	int		 n;
73 	struct diskinfo	*dip, *dipt;
74 	u_int		 bsdunit, type = 0;
75 	u_int		 scsi= 0, ide = 0, atapi = 0;
76 	extern struct disklist_lh
77 			 efi_disklist;
78 
79 	n = 0;
80 	TAILQ_FOREACH_SAFE(dip, &efi_disklist, list, dipt) {
81 		TAILQ_REMOVE(&efi_disklist, dip, list);
82 		n = scsi + ide;
83 
84 		/* Try to find the label, to figure out device type */
85 		if ((efi_getdisklabel(dip->efi_info, &dip->disklabel))) {
86 			type = 0;
87 			printf(" hd%d*", n);
88 			bsdunit = ide++;
89 		} else {
90 			/* Best guess */
91 			switch (dip->disklabel.d_type) {
92 			case DTYPE_SCSI:
93 				type = 4;
94 				bsdunit = scsi++;
95 				dip->bios_info.flags |= BDI_GOODLABEL;
96 				break;
97 
98 			case DTYPE_ESDI:
99 			case DTYPE_ST506:
100 				type = 0;
101 				bsdunit = ide++;
102 				dip->bios_info.flags |= BDI_GOODLABEL;
103 				break;
104 
105 			case DTYPE_ATAPI:
106 				type = 6;
107 				n = atapi;
108 				bsdunit = atapi++;
109 				dip->bios_info.flags |= BDI_GOODLABEL
110 				    | BDI_EL_TORITO;
111 				break;
112 
113 			default:
114 				dip->bios_info.flags |= BDI_BADLABEL;
115 				type = 0;	/* XXX Suggest IDE */
116 				bsdunit = ide++;
117 			}
118 			printf(" %cd%d", (type == 6)? 'c' : 'h', n);
119 		}
120 		if (type != 6)
121 			dip->bios_info.bios_number = 0x80 | n;
122 		else
123 			dip->bios_info.bios_number = 0xe0 | n;
124 
125 		dip->bios_info.checksum = 0; /* just in case */
126 		/* Fill out best we can */
127 		dip->bsddev = dip->bios_info.bsd_dev =
128 		    MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART);
129 		check_hibernate(dip);
130 
131 		/* Add to queue of disks */
132 		TAILQ_INSERT_TAIL(&disklist, dip, list);
133 		n++;
134 	}
135 }
136 
137 /* Probe for all BIOS supported disks */
138 u_int32_t bios_cksumlen;
139 void
diskprobe(void)140 diskprobe(void)
141 {
142 	struct diskinfo *dip;
143 	int i;
144 
145 	/* These get passed to kernel */
146 	bios_diskinfo_t *bios_diskinfo;
147 
148 	/* Init stuff */
149 	TAILQ_INIT(&disklist);
150 
151 	efi_hardprobe();
152 
153 #ifdef SOFTRAID
154 	srprobe();
155 #endif
156 
157 	/* Checksumming of hard disks */
158 	for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
159 		;
160 	bios_cksumlen = i;
161 
162 	/* Get space for passing bios_diskinfo stuff to kernel */
163 	for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
164 	    dip = TAILQ_NEXT(dip, list))
165 		i++;
166 	bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t));
167 
168 	/* Copy out the bios_diskinfo stuff */
169 	for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
170 	    dip = TAILQ_NEXT(dip, list))
171 		bios_diskinfo[i++] = dip->bios_info;
172 
173 	bios_diskinfo[i++].bios_number = -1;
174 	/* Register for kernel use */
175 	addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen);
176 	addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t),
177 	    bios_diskinfo);
178 }
179 
180 /* Find info on given BIOS disk */
181 struct diskinfo *
dklookup(int dev)182 dklookup(int dev)
183 {
184 	struct diskinfo *dip;
185 
186 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list))
187 		if (dip->bios_info.bios_number == dev)
188 			return dip;
189 
190 	return NULL;
191 }
192 
193 void
dump_diskinfo(void)194 dump_diskinfo(void)
195 {
196 	struct diskinfo *dip;
197 
198 	printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
199 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
200 		bios_diskinfo_t *bdi = &dip->bios_info;
201 		int d = bdi->bios_number;
202 		int u = d & 0x7f;
203 		char c;
204 
205 		if (bdi->flags & BDI_EL_TORITO) {
206 			c = 'c';
207 			u = 0;
208 		} else {
209 		    	c = (d & 0x80) ? 'h' : 'f';
210 		}
211 
212 		printf("%cd%d\t0x%x\t%s\t%d\t%d\t%d\t0x%x\t0x%x\n",
213 		    c, u, d,
214 		    (bdi->flags & BDI_BADLABEL)?"*none*":"label",
215 		    bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
216 		    bdi->flags, bdi->checksum);
217 	}
218 }
219 
220 /* Find BIOS portion on given BIOS disk
221  * XXX - Use dklookup() instead.
222  */
223 bios_diskinfo_t *
bios_dklookup(int dev)224 bios_dklookup(int dev)
225 {
226 	struct diskinfo *dip;
227 
228 	dip = dklookup(dev);
229 	if (dip)
230 		return &dip->bios_info;
231 
232 	return NULL;
233 }
234 
235 /*
236  * Checksum one more block on all harddrives
237  *
238  * Use the adler32() function from libz,
239  * as it is quick, small, and available.
240  */
241 int
disksum(int blk)242 disksum(int blk)
243 {
244 	struct diskinfo *dip, *dip2;
245 	int st, reprobe = 0;
246 	char buf[DEV_BSIZE];
247 
248 	for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
249 		bios_diskinfo_t *bdi = &dip->bios_info;
250 
251 		/* Skip this disk if it is not a HD or has had an I/O error */
252 		if (!(bdi->bios_number & 0x80) || bdi->flags & BDI_INVALID)
253 			continue;
254 
255 		/* Adler32 checksum */
256 		st = dip->diskio(F_READ, dip, blk, 1, buf);
257 		if (st) {
258 			bdi->flags |= BDI_INVALID;
259 			continue;
260 		}
261 		bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
262 
263 		for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
264 				dip2 = TAILQ_NEXT(dip2, list)) {
265 			bios_diskinfo_t *bd = &dip2->bios_info;
266 			if ((bd->bios_number & 0x80) &&
267 			    !(bd->flags & BDI_INVALID) &&
268 			    bdi->checksum == bd->checksum)
269 				reprobe = 1;
270 		}
271 	}
272 
273 	return reprobe;
274 }
275 
276 int
bootdev_has_hibernate(void)277 bootdev_has_hibernate(void)
278 {
279 	return ((bootdev_dip->bios_info.flags & BDI_HIBVALID)? 1 : 0);
280 }
281 
282 void
check_hibernate(struct diskinfo * dip)283 check_hibernate(struct diskinfo *dip)
284 {
285 	uint8_t buf[DEV_BSIZE];
286 	daddr_t sec;
287 	int error;
288 	union hibernate_info *hib = (union hibernate_info *)&buf;
289 
290 	/* read hibernate */
291 	if (dip->disklabel.d_partitions[1].p_fstype != FS_SWAP ||
292 	    DL_GETPSIZE(&dip->disklabel.d_partitions[1]) == 0)
293 		return;
294 
295 	sec = DL_GETPOFFSET(&dip->disklabel.d_partitions[1]) +
296 	    DL_GETPSIZE(&dip->disklabel.d_partitions[1]) - 1;
297 
298 	error = dip->strategy(dip, F_READ, DL_SECTOBLK(&dip->disklabel, sec),
299 	    sizeof buf, &buf, NULL);
300 	if (error == 0 && hib->magic == HIBERNATE_MAGIC)
301 		dip->bios_info.flags |= BDI_HIBVALID; /* Hibernate present */
302 }
303