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