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 */
55static int disksum(int);
56
57int bootdev_has_hibernate(void);		/* export for loadfile() */
58
59/* List of disk devices we found/probed */
60struct disklist_lh disklist;
61
62/* Pointer to boot device */
63struct diskinfo *bootdev_dip;
64
65extern int debug;
66extern int bios_bootdev;
67extern int bios_cddev;
68
69static void
70efi_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 */
138u_int32_t bios_cksumlen;
139void
140diskprobe(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 */
181struct diskinfo *
182dklookup(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
193void
194dump_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 */
223bios_diskinfo_t *
224bios_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 */
241int
242disksum(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
276int
277bootdev_has_hibernate(void)
278{
279	return ((bootdev_dip->bios_info.flags & BDI_HIBVALID)? 1 : 0);
280}
281
282void
283check_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