1 /** $MirOS: src/sys/arch/i386/stand/libsa/diskprobe.c,v 1.20 2012/01/14 21:44:26 tg Exp $ */
2 /* $OpenBSD: diskprobe.c,v 1.29 2007/06/18 22:11:20 krw Exp $ */
3
4 /*
5 * Copyright (c) 1997 Tobias Weingartner
6 * Copyright (c) 2002, 2003, 2009, 2012
7 * Thorsten "mirabilos" Glaser <tg@mirbsd.org>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 */
32
33 /* We don't want the disk type names from disklabel.h */
34 #undef DKTYPENAMES
35
36 #include <sys/param.h>
37 #include <sys/queue.h>
38 #include <sys/reboot.h>
39 #include <sys/disklabel.h>
40 #include <stand/boot/bootarg.h>
41 #include <machine/biosvar.h>
42 #include <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 static void hardprobe_one(int);
52
53 /* List of disk devices we found/probed */
54 struct disklist_lh disklist;
55
56 /* Pointer to boot device */
57 struct diskinfo *bootdev_dip = NULL;
58
59 /* Pointer to original boot device */
60 struct diskinfo *start_dip = NULL;
61
62 extern int debug;
63
64 /* Probe for all BIOS floppies */
65 static void
floppyprobe(void)66 floppyprobe(void)
67 {
68 struct diskinfo *dip;
69 int i;
70
71 /* Floppies */
72 for (i = 0; i < 4; i++) {
73 dip = alloc(sizeof(struct diskinfo));
74 bzero(dip, sizeof(*dip));
75
76 if (bios_getdiskinfo(i, &dip->bios_info)) {
77 #ifdef BIOS_DEBUG
78 if (debug)
79 printf(" <!fd%u>", i);
80 #endif
81 free(dip, 0);
82 break;
83 }
84
85 if (!start_dip && i386_biosdev == i)
86 start_dip = dip;
87
88 dip->name[0] = 'f';
89 dip->name[1] = 'd';
90 dip->name[2] = '0' + i;
91 dip->name[3] = '\0';
92 printf(" %s", dip->name);
93
94 /* Fill out best we can - (fd?) */
95 dip->bios_info.bsd_dev = MAKEBOOTDEV(2, 0, 0, i, RAW_PART);
96
97 /*
98 * Delay reading the disklabel until we're sure we want
99 * to boot from the floppy. Doing this avoids a delay
100 * (sometimes very long) when trying to read the label
101 * and the drive is unplugged.
102 */
103 dip->bios_info.flags |= BDI_BADLABEL;
104
105 /* Add to queue of disks */
106 TAILQ_INSERT_TAIL(&disklist, dip, list);
107 }
108 }
109
110 /* Probe for all BIOS hard disks */
111 static void
hardprobe(void)112 hardprobe(void)
113 {
114 int i;
115
116 if (i386_biosdev & 0x80)
117 hardprobe_one(i386_biosdev);
118 for (i = 0x80; i < 0x88; i++)
119 if (i != i386_biosdev)
120 hardprobe_one(i);
121 }
122
123 static void
hardprobe_one(int i)124 hardprobe_one(int i)
125 {
126 static unsigned int cddv = 0, ide = 0, scsi = 0;
127 struct diskinfo *dip;
128 u_int bsdunit, type;
129
130 dip = alloc(sizeof(struct diskinfo));
131 bzero(dip, sizeof(*dip));
132
133 if (bios_getdiskinfo(i, &dip->bios_info)) {
134 #ifdef BIOS_DEBUG
135 if (debug)
136 printf(" <!hd%u>", i&0x7f);
137 #endif
138 free(dip, 0);
139 return;
140 }
141
142 if (!start_dip && i386_biosdev == i)
143 start_dip = dip;
144
145 dip->name[0] = 'h';
146 dip->name[1] = 'd';
147 dip->name[2] = '0' + (i & 0x7F);
148 dip->name[3] = '\0';
149 if (dip->bios_info.flags & BDI_EL_TORITO) {
150 dip->name[0] = 'c';
151 dip->name[2] = '0' + cddv++;
152 }
153 printf(" %s%s", dip->name, dip->bios_info.flags & BDI_LBA ? "+" : "");
154
155 /* Try to find the label, to figure out device type */
156 if ((bios_getdisklabel(&dip->bios_info, &dip->disklabel)) ) {
157 if (dip->bios_info.flags & BDI_EL_TORITO) {
158 bsdunit = dip->name[2] - '0';
159 type = 6; /* CD-ROM */
160 } else {
161 printf("*");
162 bsdunit = ide++;
163 type = 0; /* XXX let it be IDE */
164 }
165 } else {
166 /* Best guess */
167 switch (dip->disklabel.d_type) {
168 case DTYPE_SCSI:
169 type = 4;
170 bsdunit = scsi++;
171 dip->bios_info.flags |= BDI_GOODLABEL;
172 break;
173
174 case DTYPE_ESDI:
175 case DTYPE_ST506:
176 type = 0;
177 bsdunit = ide++;
178 dip->bios_info.flags |= BDI_GOODLABEL;
179 break;
180
181 case DTYPE_ATAPI:
182 bsdunit = dip->name[2] - '0';
183 type = 6; /* CD-ROM */
184 dip->bios_info.flags |= BDI_GOODLABEL;
185 break;
186
187 default:
188 dip->bios_info.flags |= BDI_BADLABEL;
189 type = 0; /* XXX Suggest IDE */
190 bsdunit = ide++;
191 }
192 }
193
194 dip->bios_info.checksum = 0; /* just in case */
195 /* Fill out best we can */
196 dip->bios_info.bsd_dev =
197 MAKEBOOTDEV(type, 0, 0, bsdunit, RAW_PART);
198
199 /* Add to queue of disks */
200 TAILQ_INSERT_TAIL(&disklist, dip, list);
201 }
202
203
204 /* Probe for all BIOS supported disks */
205 u_int32_t bios_cksumlen;
206 void
diskprobe(void)207 diskprobe(void)
208 {
209 struct diskinfo *dip;
210 int i;
211
212 /* These get passed to kernel */
213 bios_diskinfo_t *bios_diskinfo;
214
215 /* Do probes */
216 floppyprobe();
217 #ifdef BIOS_DEBUG
218 if (debug)
219 printf(";");
220 #endif
221 hardprobe();
222
223 if (!start_dip)
224 start_dip = TAILQ_FIRST(&disklist);
225
226 /* Checksumming of hard disks */
227 for (i = 0; disksum(i++) && i < MAX_CKSUMLEN; )
228 ;
229 bios_cksumlen = i;
230
231 /* Get space for passing bios_diskinfo stuff to kernel */
232 for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
233 dip = TAILQ_NEXT(dip, list))
234 i++;
235 bios_diskinfo = alloc(++i * sizeof(bios_diskinfo_t));
236
237 /* Copy out the bios_diskinfo stuff */
238 for (i = 0, dip = TAILQ_FIRST(&disklist); dip;
239 dip = TAILQ_NEXT(dip, list))
240 bios_diskinfo[i++] = dip->bios_info;
241
242 bios_diskinfo[i++].bios_number = -1;
243 /* Register for kernel use */
244 addbootarg(BOOTARG_CKSUMLEN, sizeof(u_int32_t), &bios_cksumlen);
245 addbootarg(BOOTARG_DISKINFO, i * sizeof(bios_diskinfo_t),
246 bios_diskinfo);
247 }
248
249 void
dump_diskinfo(void)250 dump_diskinfo(void)
251 {
252 struct diskinfo *dip;
253
254 #ifndef SMALL_BOOT
255 printf("Disk\tBIOS#\tType\tCyls\tHeads\tSecs\tFlags\tChecksum\n");
256 #endif
257 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
258 bios_diskinfo_t *bdi = &dip->bios_info;
259
260 if (bdi->flags & BDI_NOTADISK) {
261 printf("%s\tnone\n", dip->name);
262 continue;
263 }
264 printf("%s\t0x%X\t%s\t%d\t%d\t%d\t0x%X\t0x%X\n",
265 dip->name, bdi->bios_number,
266 (bdi->flags & BDI_BADLABEL) ? "*none*" : "label",
267 bdi->bios_cylinders, bdi->bios_heads, bdi->bios_sectors,
268 bdi->flags, bdi->checksum);
269 }
270 }
271
272 /*
273 * Checksum one more block on all harddrives
274 *
275 * Use the adler32() function from libz,
276 * as it is quick, small, and available.
277 */
278 int
disksum(int blk)279 disksum(int blk)
280 {
281 struct diskinfo *dip, *dip2;
282 int st, reprobe = 0;
283 char *buf;
284
285 buf = alloc(DEV_BSIZE);
286 for (dip = TAILQ_FIRST(&disklist); dip; dip = TAILQ_NEXT(dip, list)) {
287 bios_diskinfo_t *bdi = &dip->bios_info;
288
289 /* Skip this disk if it is not a HD or has had an I/O error */
290 if (!(bdi->bios_number & 0x80) ||
291 bdi->flags & (BDI_INVALID | BDI_NOTADISK))
292 continue;
293
294 /* Adler32 checksum */
295 st = biosd_io(F_READ, bdi, blk, 1, buf);
296 if (st) {
297 bdi->flags |= BDI_INVALID;
298 continue;
299 }
300 bdi->checksum = adler32(bdi->checksum, buf, DEV_BSIZE);
301
302 for (dip2 = TAILQ_FIRST(&disklist); dip2 != dip;
303 dip2 = TAILQ_NEXT(dip2, list)) {
304 bios_diskinfo_t *bd = &dip2->bios_info;
305 if ((bd->bios_number & 0x80) &&
306 !(bd->flags & (BDI_INVALID | BDI_NOTADISK)) &&
307 bdi->checksum == bd->checksum)
308 reprobe = 1;
309 }
310 }
311 free(buf, DEV_BSIZE);
312
313 return reprobe;
314 }
315