1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <varargs.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <alloca.h>
35 #include <sys/systeminfo.h>
36 #include <sys/utsname.h>
37 #include <sys/openpromio.h>
38 #include <kstat.h>
39 #include <libintl.h>
40 #include "pdevinfo.h"
41 #include "display.h"
42 #include "display_sun4v.h"
43 #include "libprtdiag.h"
44 
45 #if !defined(TEXT_DOMAIN)
46 #define	TEXT_DOMAIN	"SYS_TEST"
47 #endif
48 
49 /*
50  * Global variables
51  */
52 char	*progname;
53 char	*promdev = "/dev/openprom";
54 int	print_flag = 1;
55 int	logging = 0;
56 
57 /*
58  * This file represents the splitting out of some functionality
59  * of prtdiag due to the port to the sun4v platform. The PROM
60  * tree-walking functions which contain sun4v specifics were moved
61  * into this module.
62  */
63 
64 extern int get_id(Prom_node *);
65 
66 /* Function prototypes */
67 Prom_node *sun4v_walk(Sys_tree *, Prom_node *, int);
68 picl_errno_t sun4v_get_node_by_name(picl_nodehdl_t, char *, picl_nodehdl_t *);
69 
70 /*
71  * do_prominfo() is called from main() in usr/src/cmd/prtdiag/main.c
72  *
73  * This is the starting point for all platforms. However, this function
74  * can be overlayed by writing a do_prominfo() function
75  * in the libprtdiag_psr for a particular platform.
76  *
77  */
78 int
do_prominfo(int syserrlog,char * pgname,int log_flag,int prt_flag)79 do_prominfo(int syserrlog, char *pgname, int log_flag, int prt_flag)
80 {
81 	Sys_tree sys_tree;		/* system information */
82 	Prom_node *root_node;		/* root node of OBP device tree */
83 	picl_nodehdl_t	rooth;		/* root PICL node for IO display */
84 	picl_nodehdl_t plafh;		/* Platform PICL node for IO display */
85 
86 	picl_errno_t err;
87 
88 	err = picl_initialize();
89 	if (err != PICL_SUCCESS) {
90 		(void) fprintf(stderr, EM_INIT_FAIL, picl_strerror(err));
91 		exit(1);
92 	}
93 
94 	/* set the global flags */
95 	progname = pgname;
96 	logging = log_flag;
97 	print_flag = prt_flag;
98 
99 	/* set the the system tree fields */
100 	sys_tree.sys_mem = NULL;
101 	sys_tree.boards = NULL;
102 	sys_tree.bd_list = NULL;
103 	sys_tree.board_cnt = 0;
104 
105 	if (promopen(O_RDONLY))  {
106 		exit(_error(dgettext(TEXT_DOMAIN, "openeepr device "
107 			"open failed")));
108 	}
109 
110 	if (is_openprom() == 0)  {
111 		(void) fprintf(stderr, "%s",
112 			dgettext(TEXT_DOMAIN, "System architecture "
113 			    "does not support this option of this "
114 			    "command.\n"));
115 		return (2);
116 	}
117 
118 	if (next(0) == 0) {
119 		return (2);
120 	}
121 
122 	root_node = sun4v_walk(&sys_tree, NULL, next(0));
123 	promclose();
124 
125 	err = picl_get_root(&rooth);
126 	if (err != PICL_SUCCESS) {
127 		(void) fprintf(stderr, EM_GET_ROOT_FAIL, picl_strerror(err));
128 		exit(1);
129 	}
130 
131 	err = sun4v_get_node_by_name(rooth, PICL_NODE_PLATFORM, &plafh);
132 	if (err != PICL_SUCCESS)
133 		return (err);
134 
135 	return (sun4v_display(&sys_tree, root_node, syserrlog, plafh));
136 
137 }
138 
139 /*
140  * sun4v_Walk the PROM device tree and build the system tree and root tree.
141  * Nodes that have a board number property are placed in the board
142  * structures for easier processing later. Child nodes are placed
143  * under their parents.
144  */
145 Prom_node *
sun4v_walk(Sys_tree * tree,Prom_node * root,int id)146 sun4v_walk(Sys_tree *tree, Prom_node *root, int id)
147 {
148 	register int curnode;
149 	Prom_node *pnode;
150 	char *name;
151 	char *type;
152 	char *compatible;
153 	int board_node = 0;
154 
155 	/* allocate a node for this level */
156 	if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) ==
157 	    NULL) {
158 		perror("malloc");
159 		exit(2);	/* program errors cause exit 2 */
160 	}
161 
162 	/* assign parent Prom_node */
163 	pnode->parent = root;
164 	pnode->sibling = NULL;
165 	pnode->child = NULL;
166 
167 	/* read properties for this node */
168 	dump_node(pnode);
169 
170 	/*
171 	 * Place a node in a 'board' if it has 'board'-ness. The definition
172 	 * is that all nodes that are children of root should have a
173 	 * board# property. But the PROM tree does not exactly follow
174 	 * this. This is where we start hacking.
175 	 *
176 	 * PCI to PCI bridges also have the name "pci", but with different
177 	 * model property values.  They should not be put under 'board'.
178 	 */
179 	name = get_node_name(pnode);
180 	type = get_node_type(pnode);
181 	compatible = (char *)get_prop_val(find_prop(pnode, "compatible"));
182 
183 #ifdef DEBUG
184 	if (name != NULL)
185 		printf("name=%s ", name);
186 	if (type != NULL)
187 		printf("type=%s ", type);
188 	printf("\n");
189 #endif
190 	if (compatible == NULL)
191 		compatible = "";
192 	if (type == NULL)
193 		type = "";
194 	if (name != NULL) {
195 		if (has_board_num(pnode)) {
196 			add_node(tree, pnode);
197 			board_node = 1;
198 #ifdef DEBUG
199 			printf("ADDED BOARD name=%s type=%s compatible=%s\n",
200 				name, type, compatible);
201 #endif
202 		} else if (strcmp(type, "cpu") == 0) {
203 			add_node(tree, pnode);
204 			board_node = 1;
205 #ifdef DEBUG
206 			printf("ADDED BOARD name=%s type=%s compatible=%s\n",
207 				name, type, compatible);
208 #endif
209 		}
210 #ifdef DEBUG
211 		else
212 			printf("node not added: name=%s type=%s\n", name, type);
213 #endif
214 	}
215 
216 	if (curnode = child(id)) {
217 		pnode->child = sun4v_walk(tree, pnode, curnode);
218 	}
219 
220 	if (curnode = next(id)) {
221 		if (board_node) {
222 			return (sun4v_walk(tree, root, curnode));
223 		} else {
224 			pnode->sibling = sun4v_walk(tree, root, curnode);
225 		}
226 	}
227 
228 	if (board_node) {
229 		return (NULL);
230 	} else {
231 		return (pnode);
232 	}
233 }
234 
235 /*
236  * search children to get the node by the nodename
237  */
238 picl_errno_t
sun4v_get_node_by_name(picl_nodehdl_t rooth,char * name,picl_nodehdl_t * nodeh)239 sun4v_get_node_by_name(picl_nodehdl_t rooth, char *name,
240     picl_nodehdl_t *nodeh)
241 {
242 	picl_nodehdl_t	childh;
243 	int		err;
244 	char		*nodename;
245 
246 	nodename = alloca(strlen(name) + 1);
247 	if (nodename == NULL)
248 		return (PICL_FAILURE);
249 
250 	err = picl_get_propval_by_name(rooth, PICL_PROP_CHILD, &childh,
251 	    sizeof (picl_nodehdl_t));
252 
253 	while (err == PICL_SUCCESS) {
254 		err = picl_get_propval_by_name(childh, PICL_PROP_NAME,
255 		    nodename, (strlen(name) + 1));
256 		if (err != PICL_SUCCESS) {
257 			err = picl_get_propval_by_name(childh, PICL_PROP_PEER,
258 				&childh, sizeof (picl_nodehdl_t));
259 			continue;
260 		}
261 
262 		if (strcmp(nodename, name) == 0) {
263 			*nodeh = childh;
264 			return (PICL_SUCCESS);
265 		}
266 
267 		err = picl_get_propval_by_name(childh, PICL_PROP_PEER,
268 		    &childh, sizeof (picl_nodehdl_t));
269 	}
270 
271 	return (err);
272 }
273 
274 int
get_id(Prom_node * node)275 get_id(Prom_node *node)
276 {
277 #ifdef	lint
278 	node = node;
279 #endif
280 
281 	/*
282 	 * This function is intentionally empty
283 	 */
284 	return (0);
285 }
286