Home | History | Annotate | Line # | Download | only in zboot
      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
     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
    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
    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 *
    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
    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
    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 *
    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
    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 *
    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