1 /* $NetBSD: diskprobe.c,v 1.4 2015/01/02 19:42:06 christos 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
hardprobe(char * buf,size_t bufsiz)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 hd_disk = 0;
73 u_int mmcd_disk = 0;
74 uint unit = 0;
75 int first = 1;
76 int i;
77
78 buf[0] = '\0';
79
80 /* Hard disks */
81 for (i = 0; i < __arraycount(order); i++) {
82 dip = alloc(sizeof(struct diskinfo));
83 memset(dip, 0, sizeof(*dip));
84
85 if (bios_getdiskinfo(order[i], &dip->bios_info) != NULL) {
86 dealloc(dip, 0);
87 continue;
88 }
89
90 bios_devname(order[i], devname, sizeof(devname));
91 if (order[i] & 0x80) {
92 unit = hd_disk++;
93 } else {
94 unit = mmcd_disk++;
95 }
96 snprintf(dip->devname, sizeof(dip->devname), "%s%d", devname,
97 unit);
98 strlcat(buf, dip->devname, bufsiz);
99
100 /* Try to find the label, to figure out device type. */
101 if (bios_getdisklabel(&dip->bios_info, &dip->disklabel)
102 == NULL) {
103 strlcat(buf, "*", bufsiz);
104 if (first) {
105 first = 0;
106 strlcpy(disk_devname, devname,
107 sizeof(disk_devname));
108 default_devname = disk_devname;
109 default_unit = unit;
110 default_partition = 0;
111 }
112 } else {
113 /* Best guess */
114 switch (dip->disklabel.d_type) {
115 case DKTYPE_SCSI:
116 case DKTYPE_ESDI:
117 case DKTYPE_ST506:
118 dip->bios_info.flags |= BDI_GOODLABEL;
119 break;
120
121 default:
122 dip->bios_info.flags |= BDI_BADLABEL;
123 }
124 }
125
126 /* Add to queue of disks. */
127 TAILQ_INSERT_TAIL(&disklist, dip, list);
128
129 strlcat(buf, " ", bufsiz);
130 }
131
132 /* path */
133 strlcat(buf, devname_path, bufsiz);
134 strlcat(buf, "*", bufsiz);
135 if (first) {
136 first = 0;
137 strlcpy(disk_devname, devname_path, sizeof(disk_devname));
138 default_devname = disk_devname;
139 default_unit = 0;
140 default_partition = 0;
141 }
142 }
143
144 static void
getpartitions(void)145 getpartitions(void)
146 {
147 struct linux_stat sb;
148 struct partinfo *pip;
149 char *bc, *top, *next, *p, *q;
150 int fd, off, len;
151
152 fd = uopen(_PATH_PARTITIONS, LINUX_O_RDONLY);
153 if (fd == -1)
154 return;
155
156 if (ufstat(fd, &sb) < 0) {
157 uclose(fd);
158 return;
159 }
160
161 bc = alloc(sb.lst_size + 1);
162 if (bc == NULL) {
163 printf("Could not allocate memory for %s\n", _PATH_PARTITIONS);
164 uclose(fd);
165 return;
166 }
167
168 off = 0;
169 do {
170 len = uread(fd, bc + off, 1024);
171 if (len <= 0)
172 break;
173 off += len;
174 } while (len > 0);
175 bc[off] = '\0';
176
177 uclose(fd);
178
179 /* bc now contains the whole /proc/partitions */
180 for (p = bc; *p != '\0'; p = next) {
181 top = p;
182
183 /* readline */
184 for (; *p != '\0' && *p != '\r' && *p != '\n'; p++)
185 continue;
186 if (*p == '\r') {
187 *p++ = '\0';
188 if (*p == '\n')
189 *p++ = '\0';
190 } else if (*p == '\n')
191 *p++ = '\0';
192 next = p;
193
194 /*
195 * /proc/partitions format:
196 * major minor #blocks name
197 *
198 * %d %d %d %s
199 *
200 * e.g.:
201 * major minor #blocks name
202 *
203 * 22 0 7962192 hdc
204 * 22 1 10079 hdc1
205 * 60 0 965120 mmcda
206 * 60 1 43312 mmcda1
207 */
208
209 /* trailing space */
210 for (p = top; *p == ' ' || *p == '\t'; p++)
211 continue;
212
213 /* major */
214 for (; isdigit(*p); p++)
215 continue;
216 if (*p != ' ' && *p != '\t')
217 continue; /* next line */
218 for (; *p == ' ' || *p == '\t'; p++)
219 continue;
220
221 /* minor */
222 for (; isdigit(*p); p++)
223 continue;
224 if (*p != ' ' && *p != '\t')
225 continue; /* next line */
226 for (; *p == ' ' || *p == '\t'; p++)
227 continue;
228
229 /* #blocks */
230 for (; isdigit(*p); p++)
231 continue;
232 if (*p != ' ' && *p != '\t')
233 continue; /* next line */
234 for (; *p == ' ' || *p == '\t'; p++)
235 continue;
236
237 /* name */
238 for (q = p; isalpha(*p) || isdigit(*p); p++)
239 continue;
240 if (*p != ' ' && *p != '\t' && *p != '\0')
241 continue; /* next line */
242 if (isdigit(p[-1]))
243 continue; /* next line */
244 *p = '\0';
245
246 pip = alloc(sizeof(*pip));
247 if (pip == NULL) {
248 printf("Could not allocate memory for partition\n");
249 continue; /* next line */
250 }
251 memset(pip, 0, sizeof(*pip));
252 snprintf(pip->devname, sizeof(pip->devname), "/dev/%s", q);
253 TAILQ_INSERT_TAIL(&partlist, pip, list);
254 }
255
256 dealloc(bc, 0);
257 }
258
259 /* Probe for all BIOS supported disks */
260 void
diskprobe(char * buf,size_t bufsiz)261 diskprobe(char *buf, size_t bufsiz)
262 {
263
264 /* get available disk list from /proc/partitions */
265 TAILQ_INIT(&partlist);
266 getpartitions();
267
268 /* Init stuff */
269 TAILQ_INIT(&disklist);
270
271 /* Do probes */
272 hardprobe(buf, bufsiz);
273 }
274
275 /*
276 * Find info on the disk given by major + unit number.
277 */
278 struct diskinfo *
dkdevice(const char * devname,uint unit)279 dkdevice(const char *devname, uint unit)
280 {
281 char name[MAXDEVNAME];
282 struct diskinfo *dip;
283
284 snprintf(name, sizeof(name), "%s%d", devname, unit);
285 for (dip = TAILQ_FIRST(&disklist); dip != NULL;
286 dip = TAILQ_NEXT(dip, list)) {
287 if (strcmp(name, dip->devname) == 0) {
288 return dip;
289 }
290 }
291 return NULL;
292 }
293
294 int
bios_devname(int biosdev,char * devname,int size)295 bios_devname(int biosdev, char *devname, int size)
296 {
297
298 if ((biosdev & 0x80) != 0) {
299 strlcpy(devname, devname_hd, size);
300 } else {
301 strlcpy(devname, devname_mmcd, size);
302 }
303 return 0;
304 }
305
306 /*
307 * Find the Linux device path that corresponds to the given "BIOS" disk,
308 * where 0x80 corresponds to /dev/hda, 0x81 to /dev/hdb, and so on.
309 */
310 void
bios_devpath(int dev,int part,char * p)311 bios_devpath(int dev, int part, char *p)
312 {
313 char devname[MAXDEVNAME];
314 const char *q;
315
316 *p++ = '/';
317 *p++ = 'd';
318 *p++ = 'e';
319 *p++ = 'v';
320 *p++ = '/';
321
322 bios_devname(dev, devname, sizeof(devname));
323 q = devname;
324 while (*q != '\0')
325 *p++ = *q++;
326
327 *p++ = 'a' + (dev & 0x7f);
328 if (part >= 0)
329 *p++ = '1' + part;
330 *p = '\0';
331 }
332
333 /*
334 * Fill out a bios_diskinfo_t for this device.
335 */
336 char *
bios_getdiskinfo(int dev,bios_diskinfo_t * bdi)337 bios_getdiskinfo(int dev, bios_diskinfo_t *bdi)
338 {
339 static char path[PATH_MAX];
340 struct linux_stat sb;
341 struct partinfo *pip;
342
343 memset(bdi, 0, sizeof *bdi);
344 bdi->bios_number = -1;
345
346 bios_devpath(dev, -1, path);
347
348 /* Check device name in /proc/partitions */
349 for (pip = TAILQ_FIRST(&partlist); pip != NULL;
350 pip = TAILQ_NEXT(pip, list)) {
351 if (!strcmp(path, pip->devname))
352 break;
353 }
354 if (pip == NULL)
355 return "no device node";
356
357 if (ustat(path, &sb) != 0)
358 return "no device node";
359
360 bdi->bios_number = dev;
361
362 if (bios_getdospart(bdi) < 0)
363 return "no NetBSD partition";
364
365 return NULL;
366 }
367
368 int
bios_getdospart(bios_diskinfo_t * bdi)369 bios_getdospart(bios_diskinfo_t *bdi)
370 {
371 char path[PATH_MAX];
372 char buf[DEV_BSIZE];
373 struct mbr_partition *mp;
374 int fd;
375 u_int part;
376 size_t rsize;
377
378 bios_devpath(bdi->bios_number, -1, path);
379
380 /*
381 * Give disk devices some time to become ready when the first open
382 * fails. Even when open succeeds the disk is sometimes not ready.
383 */
384 if ((fd = uopen(path, LINUX_O_RDONLY)) == -1 && errno == ENXIO) {
385 while (fd == -1 && timeout > 0) {
386 timeout--;
387 sleep(1);
388 fd = uopen(path, LINUX_O_RDONLY);
389 }
390 if (fd != -1)
391 sleep(2);
392 }
393 if (fd == -1)
394 return -1;
395
396 /* Read the disk's MBR. */
397 if (unixstrategy((void *)fd, F_READ, MBR_BBSECTOR, DEV_BSIZE, buf,
398 &rsize) != 0 || rsize != DEV_BSIZE) {
399 uclose(fd);
400 errno = EIO;
401 return -1;
402 }
403
404 /* Find NetBSD primary partition in the disk's MBR. */
405 mp = (struct mbr_partition *)&buf[MBR_PART_OFFSET];
406 for (part = 0; part < MBR_PART_COUNT; part++) {
407 if (mp[part].mbrp_type == MBR_PTYPE_NETBSD)
408 break;
409 }
410 if (part == MBR_PART_COUNT) {
411 uclose(fd);
412 errno = ERDLAB;
413 return -1;
414 }
415 uclose(fd);
416
417 return part;
418 }
419
420 char *
bios_getdisklabel(bios_diskinfo_t * bdi,struct disklabel * label)421 bios_getdisklabel(bios_diskinfo_t *bdi, struct disklabel *label)
422 {
423 char path[PATH_MAX];
424 char buf[DEV_BSIZE];
425 int part;
426 int fd;
427 size_t rsize;
428
429 part = bios_getdospart(bdi);
430 if (part < 0)
431 return "no NetBSD partition";
432
433 bios_devpath(bdi->bios_number, part, path);
434
435 /* Test if the NetBSD partition has a valid disklabel. */
436 if ((fd = uopen(path, LINUX_O_RDONLY)) != -1) {
437 char *msg = "failed to read disklabel";
438
439 if (unixstrategy((void *)fd, F_READ, LABELSECTOR,
440 DEV_BSIZE, buf, &rsize) == 0 && rsize == DEV_BSIZE)
441 msg = getdisklabel(buf, label);
442 uclose(fd);
443 /* Don't wait for other disks if this label is ok. */
444 if (msg == NULL)
445 timeout = 0;
446 return msg;
447 }
448
449 return "failed to open partition";
450 }
451