diskprobe.c revision 1.2 1 /* $NetBSD: diskprobe.c,v 1.2 2011/06/20 12:39:21 nonaka Exp $ */
2 /* $OpenBSD: diskprobe.c,v 1.3 2006/10/13 00:00:55 krw Exp $ */
3
4 /*
5 * Copyright (c) 1997 Tobias Weingartner
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 */
30
31 /* We want the disk type names from disklabel.h */
32 #undef DKTYPENAMES
33
34 #include <sys/param.h>
35 #include <sys/bootblock.h>
36 #include <sys/disklabel.h>
37 #include <sys/queue.h>
38 #include <sys/reboot.h>
39
40 #include "boot.h"
41 #include "disk.h"
42 #include "unixdev.h"
43 #include "pathnames.h"
44 #include "compat_linux.h"
45
46 /* All the info on /proc/partitions */
47 struct partinfo {
48 char devname[MAXDEVNAME];
49 TAILQ_ENTRY(partinfo) list;
50 };
51 TAILQ_HEAD(partlist_lh, partinfo);
52 struct partlist_lh partlist;
53
54 /* Disk spin-up wait timeout. */
55 static u_int timeout = 10;
56
57 /* List of disk devices we found/probed */
58 struct disklist_lh disklist;
59
60 static char disk_devname[MAXDEVNAME];
61
62 /*
63 * Probe for all hard disks.
64 */
65 static void
66 hardprobe(char *buf, size_t bufsiz)
67 {
68 /* XXX probe disks in this order */
69 static const int order[] = { 0x80, 0x82, 0x00 };
70 char devname[MAXDEVNAME];
71 struct diskinfo *dip;
72 u_int disk = 0;
73 u_int hd_disk = 0;
74 u_int mmcd_disk = 0;
75 uint unit = 0;
76 int first = 1;
77 int i;
78
79 buf[0] = '\0';
80
81 /* Hard disks */
82 for (i = 0; i < __arraycount(order); i++) {
83 dip = alloc(sizeof(struct diskinfo));
84 memset(dip, 0, sizeof(*dip));
85
86 if (bios_getdiskinfo(order[i], &dip->bios_info) != NULL) {
87 dealloc(dip, 0);
88 continue;
89 }
90
91 bios_devname(order[i], devname, sizeof(devname));
92 if (order[i] & 0x80) {
93 unit = hd_disk;
94 snprintf(dip->devname, sizeof(dip->devname), "%s%d",
95 devname, hd_disk++);
96 } else {
97 unit = mmcd_disk;
98 snprintf(dip->devname, sizeof(dip->devname), "%s%d",
99 devname, mmcd_disk++);
100 }
101 strlcat(buf, dip->devname, bufsiz);
102 disk++;
103
104 /* Try to find the label, to figure out device type. */
105 if (bios_getdisklabel(&dip->bios_info, &dip->disklabel)
106 == NULL) {
107 strlcat(buf, "*", bufsiz);
108 if (first) {
109 first = 0;
110 strlcpy(disk_devname, devname,
111 sizeof(disk_devname));
112 default_devname = disk_devname;
113 default_unit = unit;
114 }
115 } else {
116 /* Best guess */
117 switch (dip->disklabel.d_type) {
118 case DTYPE_SCSI:
119 case DTYPE_ESDI:
120 case DTYPE_ST506:
121 dip->bios_info.flags |= BDI_GOODLABEL;
122 break;
123
124 default:
125 dip->bios_info.flags |= BDI_BADLABEL;
126 }
127 }
128
129 /* Add to queue of disks. */
130 TAILQ_INSERT_TAIL(&disklist, dip, list);
131
132 strlcat(buf, " ", bufsiz);
133 }
134 if (disk == 0)
135 strlcat(buf, "none...", bufsiz);
136 }
137
138 static void
139 getpartitions(void)
140 {
141 struct linux_stat sb;
142 struct partinfo *pip;
143 char *bc, *top, *next, *p, *q;
144 int fd, off, len;
145
146 fd = uopen(_PATH_PARTITIONS, LINUX_O_RDONLY);
147 if (fd == -1)
148 return;
149
150 if (ufstat(fd, &sb) < 0) {
151 uclose(fd);
152 return;
153 }
154
155 bc = alloc(sb.lst_size + 1);
156 if (bc == NULL) {
157 printf("Could not allocate memory for %s\n", _PATH_PARTITIONS);
158 uclose(fd);
159 return;
160 }
161
162 off = 0;
163 do {
164 len = uread(fd, bc + off, 1024);
165 if (len <= 0)
166 break;
167 off += len;
168 } while (len > 0);
169 bc[off] = '\0';
170
171 uclose(fd);
172
173 /* bc now contains the whole /proc/partitions */
174 for (p = bc; *p != '\0'; p = next) {
175 top = p;
176
177 /* readline */
178 for (; *p != '\0' && *p != '\r' && *p != '\n'; p++)
179 continue;
180 if (*p == '\r') {
181 *p++ = '\0';
182 if (*p == '\n')
183 *p++ = '\0';
184 } else if (*p == '\n')
185 *p++ = '\0';
186 next = p;
187
188 /*
189 * /proc/partitions format:
190 * major minor #blocks name
191 *
192 * %d %d %d %s
193 *
194 * e.g.:
195 * major minor #blocks name
196 *
197 * 22 0 7962192 hdc
198 * 22 1 10079 hdc1
199 * 60 0 965120 mmcda
200 * 60 1 43312 mmcda1
201 */
202
203 /* trailing space */
204 for (p = top; *p == ' ' || *p == '\t'; p++)
205 continue;
206
207 /* major */
208 for (; isdigit(*p); p++)
209 continue;
210 if (*p != ' ' && *p != '\t')
211 continue; /* next line */
212 for (; *p == ' ' || *p == '\t'; p++)
213 continue;
214
215 /* minor */
216 for (; isdigit(*p); p++)
217 continue;
218 if (*p != ' ' && *p != '\t')
219 continue; /* next line */
220 for (; *p == ' ' || *p == '\t'; p++)
221 continue;
222
223 /* #blocks */
224 for (; isdigit(*p); p++)
225 continue;
226 if (*p != ' ' && *p != '\t')
227 continue; /* next line */
228 for (; *p == ' ' || *p == '\t'; p++)
229 continue;
230
231 /* name */
232 for (q = p; isalpha(*p) || isdigit(*p); p++)
233 continue;
234 if (*p != ' ' && *p != '\t' && *p != '\0')
235 continue; /* next line */
236 if (isdigit(p[-1]))
237 continue; /* next line */
238 *p = '\0';
239
240 pip = alloc(sizeof(*pip));
241 if (pip == NULL) {
242 printf("Could not allocate memory for partition\n");
243 continue; /* next line */
244 }
245 memset(pip, 0, sizeof(*pip));
246 snprintf(pip->devname, sizeof(pip->devname), "/dev/%s", q);
247 TAILQ_INSERT_TAIL(&partlist, pip, list);
248 }
249
250 dealloc(bc, 0);
251 }
252
253 /* Probe for all BIOS supported disks */
254 void
255 diskprobe(char *buf, size_t bufsiz)
256 {
257
258 /* get available disk list from /proc/partitions */
259 TAILQ_INIT(&partlist);
260 getpartitions();
261
262 /* Init stuff */
263 TAILQ_INIT(&disklist);
264
265 /* Do probes */
266 hardprobe(buf, bufsiz);
267 }
268
269 /*
270 * Find info on the disk given by major + unit number.
271 */
272 struct diskinfo *
273 dkdevice(const char *devname, uint unit)
274 {
275 char name[MAXDEVNAME];
276 struct diskinfo *dip;
277
278 snprintf(name, sizeof(name), "%s%d", devname, unit);
279 for (dip = TAILQ_FIRST(&disklist); dip != NULL;
280 dip = TAILQ_NEXT(dip, list)) {
281 if (strcmp(name, dip->devname) == 0) {
282 return dip;
283 }
284 }
285 return NULL;
286 }
287
288 int
289 bios_devname(int biosdev, char *devname, int size)
290 {
291
292 if ((biosdev & 0x80) != 0) {
293 strlcpy(devname, devname_hd, size);
294 } else {
295 strlcpy(devname, devname_mmcd, size);
296 }
297 return 0;
298 }
299
300 /*
301 * Find the Linux device path that corresponds to the given "BIOS" disk,
302 * where 0x80 corresponds to /dev/hda, 0x81 to /dev/hdb, and so on.
303 */
304 void
305 bios_devpath(int dev, int part, char *p)
306 {
307 char devname[MAXDEVNAME];
308 const char *q;
309
310 *p++ = '/';
311 *p++ = 'd';
312 *p++ = 'e';
313 *p++ = 'v';
314 *p++ = '/';
315
316 bios_devname(dev, devname, sizeof(devname));
317 q = devname;
318 while (*q != '\0')
319 *p++ = *q++;
320
321 *p++ = 'a' + (dev & 0x7f);
322 if (part >= 0)
323 *p++ = '1' + part;
324 *p = '\0';
325 }
326
327 /*
328 * Fill out a bios_diskinfo_t for this device.
329 */
330 char *
331 bios_getdiskinfo(int dev, bios_diskinfo_t *bdi)
332 {
333 static char path[PATH_MAX];
334 struct linux_stat sb;
335 struct partinfo *pip;
336
337 memset(bdi, 0, sizeof *bdi);
338 bdi->bios_number = -1;
339
340 bios_devpath(dev, -1, path);
341
342 /* Check device name in /proc/partitions */
343 for (pip = TAILQ_FIRST(&partlist); pip != NULL;
344 pip = TAILQ_NEXT(pip, list)) {
345 if (!strcmp(path, pip->devname))
346 break;
347 }
348 if (pip == NULL)
349 return "no device node";
350
351 if (ustat(path, &sb) != 0)
352 return "no device node";
353
354 bdi->bios_number = dev;
355
356 if (bios_getdospart(bdi) < 0)
357 return "no NetBSD partition";
358
359 return NULL;
360 }
361
362 int
363 bios_getdospart(bios_diskinfo_t *bdi)
364 {
365 char path[PATH_MAX];
366 char buf[DEV_BSIZE];
367 struct mbr_partition *mp;
368 int fd;
369 u_int part;
370 size_t rsize;
371
372 bios_devpath(bdi->bios_number, -1, path);
373
374 /*
375 * Give disk devices some time to become ready when the first open
376 * fails. Even when open succeeds the disk is sometimes not ready.
377 */
378 if ((fd = uopen(path, LINUX_O_RDONLY)) == -1 && errno == ENXIO) {
379 while (fd == -1 && timeout > 0) {
380 timeout--;
381 sleep(1);
382 fd = uopen(path, LINUX_O_RDONLY);
383 }
384 if (fd != -1)
385 sleep(2);
386 }
387 if (fd == -1)
388 return -1;
389
390 /* Read the disk's MBR. */
391 if (unixstrategy((void *)fd, F_READ, MBR_BBSECTOR, DEV_BSIZE, buf,
392 &rsize) != 0 || rsize != DEV_BSIZE) {
393 uclose(fd);
394 errno = EIO;
395 return -1;
396 }
397
398 /* Find NetBSD primary partition in the disk's MBR. */
399 mp = (struct mbr_partition *)&buf[MBR_PART_OFFSET];
400 for (part = 0; part < MBR_PART_COUNT; part++) {
401 if (mp[part].mbrp_type == MBR_PTYPE_NETBSD)
402 break;
403 }
404 if (part == MBR_PART_COUNT) {
405 uclose(fd);
406 errno = ERDLAB;
407 return -1;
408 }
409 uclose(fd);
410
411 return part;
412 }
413
414 char *
415 bios_getdisklabel(bios_diskinfo_t *bdi, struct disklabel *label)
416 {
417 char path[PATH_MAX];
418 char buf[DEV_BSIZE];
419 int part;
420 int fd;
421 size_t rsize;
422
423 part = bios_getdospart(bdi);
424 if (part < 0)
425 return "no NetBSD partition";
426
427 bios_devpath(bdi->bios_number, part, path);
428
429 /* Test if the NetBSD partition has a valid disklabel. */
430 if ((fd = uopen(path, LINUX_O_RDONLY)) != -1) {
431 char *msg = "failed to read disklabel";
432
433 if (unixstrategy((void *)fd, F_READ, LABELSECTOR,
434 DEV_BSIZE, buf, &rsize) == 0 && rsize == DEV_BSIZE)
435 msg = getdisklabel(buf, label);
436 uclose(fd);
437 /* Don't wait for other disks if this label is ok. */
438 if (msg == NULL)
439 timeout = 0;
440 return msg;
441 }
442
443 return "failed to open partition";
444 }
445