xref: /netbsd-src/sys/arch/zaurus/stand/zboot/diskprobe.c (revision c182898b0d016152faf559e0cff802d7d5b065ef)
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