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