1 /*	$OpenBSD: diskprobe.c,v 1.6 2018/12/31 11:44:57 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2008 Mark Kettenis <kettenis@openbsd.org>
5  * Copyright (c) 2014 Stefan Sperling <stsp@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/disklabel.h>
22 
23 #include <lib/libkern/funcs.h>
24 #include <lib/libsa/stand.h>
25 
26 #include "ofdev.h"
27 #include "disk.h"
28 #include "openfirm.h"
29 
30 /* List of disk devices we found/probed */
31 struct disklist_lh disklist;
32 
33 struct diskinfo *bootdev_dip;
34 
35 void
36 new_diskinfo(int node)
37 {
38 	struct diskinfo *dip;
39 	struct of_dev ofdev;
40 	int ihandle = -1;
41 	int len;
42 	const char *unit;
43 	char buf[32];
44 	int parent;
45 	int i;
46 
47 	dip = alloc(sizeof(*dip));
48 	bzero(dip, sizeof(*dip));
49 
50 	len = OF_package_to_path(node, dip->path, sizeof(dip->path));
51 	if (len < 0) {
52 		DPRINTF("could not get path for disk node %x\n", node);
53 		goto bad;
54 	} else if (len >= sizeof(dip->path)) {
55 		printf("disk device path too long: %s", dip->path);
56 		goto bad;
57 	}
58 	dip->path[len] = '\0';
59 
60 	/* If no device unit was supplied by the firmware, add it. */
61 	unit = NULL;
62 	for (i = len - 1; i >= 0; i--) {
63 		if (dip->path[i] == '/')
64 			break;
65 		else if (dip->path[i] == '@') {
66 			unit = &dip->path[i];
67 			break;
68 		}
69 	}
70 	if (unit == NULL) {
71 		parent = OF_parent(node);
72 		if (parent && OF_getprop(parent, "device_type", buf,
73 		    sizeof(buf)) > 0 && strcmp(buf, "scsi-sas") == 0)
74 			len = strlcat(dip->path, "@p0", sizeof(dip->path));
75 		else
76 			len = strlcat(dip->path, "@0", sizeof(dip->path));
77 		if (len >= sizeof(dip->path)) {
78 			printf("disk device path too long: %s", dip->path);
79 			goto bad;
80 		}
81 	}
82 
83 	DPRINTF("found disk %s\n", dip->path);
84 
85 	ihandle = OF_open(dip->path);
86 	if (ihandle == -1)
87 		goto bad;
88 
89 	bzero(&ofdev, sizeof(ofdev));
90 	ofdev.handle = ihandle;
91 	ofdev.type = OFDEV_DISK;
92 	ofdev.bsize = DEV_BSIZE;
93 	if (load_disklabel(&ofdev, &dip->disklabel) != 0)
94 		goto bad;
95 	OF_close(ihandle);
96 	TAILQ_INSERT_TAIL(&disklist, dip, list);
97 
98 	return;
99 bad:
100 	if (ihandle != -1)
101 		OF_close(ihandle);
102 	free(dip, sizeof(*dip));
103 }
104 
105 #ifdef BOOT_DEBUG
106 void dump_node(int node)
107 {
108 	char buf[32];
109 
110 	printf("node %x ", node);
111 	if (OF_getprop(node, "device_type", buf, sizeof(buf)) > 0)
112 		printf("type %s ", buf);
113 	if (OF_getprop(node, "name", buf, sizeof(buf)) > 0)
114 		printf("name %s ", buf);
115 	printf("\n");
116 }
117 #endif
118 
119 /*
120  * Hunt through the device tree for disks.  There should be no need to
121  * go more than 10 levels deep.
122  */
123 void
124 diskprobe(void)
125 {
126 	int node, child, stack[10], depth;
127 	char buf[32];
128 
129 	stack[0] = OF_peer(0);
130 	if (stack[0] == 0)
131 		return;
132 	depth = 0;
133 	TAILQ_INIT(&disklist);
134 
135 	for (;;) {
136 		node = stack[depth];
137 
138 		if (node == 0 || node == -1) {
139 			if (--depth < 0)
140 				return;
141 
142 			stack[depth] = OF_peer(stack[depth]);
143 			continue;
144 		}
145 
146 #ifdef BOOT_DEBUG
147 		dump_node(node);
148 #endif
149 		if ((OF_getprop(node, "device_type", buf, sizeof(buf)) > 0 &&
150 		    strcmp(buf, "block") == 0 &&
151 		    OF_getprop(node, "name", buf, sizeof(buf)) > 0 &&
152 		    strcmp(buf, "disk") == 0)) {
153 			new_diskinfo(node);
154 		}
155 
156 		child = OF_child(node);
157 		if (child != 0 && child != -1 && depth < 9)
158 			stack[++depth] = child;
159 		else
160 			stack[depth] = OF_peer(stack[depth]);
161 	}
162 }
163