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 */
47struct partinfo {
48	char devname[MAXDEVNAME];
49	TAILQ_ENTRY(partinfo) list;
50};
51TAILQ_HEAD(partlist_lh, partinfo);
52struct partlist_lh partlist;
53
54/* Disk spin-up wait timeout. */
55static u_int timeout = 10;
56
57/* List of disk devices we found/probed */
58struct disklist_lh disklist;
59
60static char disk_devname[MAXDEVNAME];
61
62/*
63 * Probe for all hard disks.
64 */
65static void
66hardprobe(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 DTYPE_SCSI:
116			case DTYPE_ESDI:
117			case DTYPE_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
144static void
145getpartitions(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 */
260void
261diskprobe(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 */
278struct diskinfo *
279dkdevice(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
294int
295bios_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 */
310void
311bios_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 */
336char *
337bios_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
368int
369bios_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
420char *
421bios_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