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