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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include "mdescplugin.h"
28 
29 static	di_prom_handle_t	ph = DI_PROM_HANDLE_NIL;
30 
31 typedef struct cpu_lookup {
32 	di_node_t di_node;
33 	picl_nodehdl_t nodeh;
34 	int result;
35 } cpu_lookup_t;
36 
37 extern int add_cpu_prop(picl_nodehdl_t node, void *args);
38 extern md_t *mdesc_devinit(void);
39 
40 /*
41  * This function is identical to the one in the picldevtree plugin.
42  * Unfortunately we can't just reuse that code.
43  */
44 int
add_string_list_prop(picl_nodehdl_t nodeh,char * name,char * strlist,unsigned int nrows)45 add_string_list_prop(picl_nodehdl_t nodeh, char *name, char *strlist,
46     unsigned int nrows)
47 {
48 	ptree_propinfo_t	propinfo;
49 	picl_prophdl_t		proph;
50 	picl_prophdl_t		tblh;
51 	int			err;
52 	unsigned int		i;
53 	unsigned int		j;
54 	picl_prophdl_t		*proprow;
55 	int			len;
56 
57 #define	NCOLS_IN_STRING_TABLE	1
58 
59 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
60 	    PICL_PTYPE_TABLE, PICL_READ, sizeof (picl_prophdl_t), name,
61 	    NULL, NULL);
62 	if (err != PICL_SUCCESS)
63 		return (err);
64 
65 	err = ptree_create_table(&tblh);
66 	if (err != PICL_SUCCESS)
67 		return (err);
68 
69 	err = ptree_create_and_add_prop(nodeh, &propinfo, &tblh, &proph);
70 	if (err != PICL_SUCCESS)
71 		return (err);
72 
73 	proprow = alloca(sizeof (picl_prophdl_t) * nrows);
74 	if (proprow == NULL) {
75 		(void) ptree_destroy_prop(proph);
76 		return (PICL_FAILURE);
77 	}
78 
79 	for (j = 0; j < nrows; ++j) {
80 		len = strlen(strlist) + 1;
81 		err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
82 		    PICL_PTYPE_CHARSTRING, PICL_READ, len, name,
83 		    NULL, NULL);
84 		if (err != PICL_SUCCESS)
85 			break;
86 		err = ptree_create_prop(&propinfo, strlist, &proprow[j]);
87 		if (err != PICL_SUCCESS)
88 			break;
89 		strlist += len;
90 		err = ptree_add_row_to_table(tblh, NCOLS_IN_STRING_TABLE,
91 		    &proprow[j]);
92 		if (err != PICL_SUCCESS)
93 			break;
94 	}
95 
96 	if (err != PICL_SUCCESS) {
97 		for (i = 0; i < j; ++i)
98 			(void) ptree_destroy_prop(proprow[i]);
99 		(void) ptree_delete_prop(proph);
100 		(void) ptree_destroy_prop(proph);
101 		return (err);
102 	}
103 
104 	return (PICL_SUCCESS);
105 }
106 
107 /*
108  * This function is identical to the one in the picldevtree plugin.
109  * Unfortunately we can't just reuse that code.
110  */
111 static void
add_devinfo_props(picl_nodehdl_t nodeh,di_node_t di_node)112 add_devinfo_props(picl_nodehdl_t nodeh, di_node_t di_node)
113 {
114 	int			instance;
115 	char			*di_val;
116 	di_prop_t		di_prop;
117 	int			di_ptype;
118 	ptree_propinfo_t	propinfo;
119 
120 	instance = di_instance(di_node);
121 	(void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
122 	    PICL_PTYPE_INT, PICL_READ, sizeof (instance), PICL_PROP_INSTANCE,
123 	    NULL, NULL);
124 	(void) ptree_create_and_add_prop(nodeh, &propinfo, &instance, NULL);
125 
126 	di_val = di_bus_addr(di_node);
127 	if (di_val) {
128 		(void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
129 		    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1,
130 		    PICL_PROP_BUS_ADDR, NULL, NULL);
131 		(void) ptree_create_and_add_prop(nodeh, &propinfo, di_val,
132 		    NULL);
133 	}
134 
135 	di_val = di_binding_name(di_node);
136 	if (di_val) {
137 		(void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
138 		    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1,
139 		    PICL_PROP_BINDING_NAME, NULL, NULL);
140 		(void) ptree_create_and_add_prop(nodeh, &propinfo, di_val,
141 		    NULL);
142 	}
143 
144 	di_val = di_driver_name(di_node);
145 	if (di_val) {
146 		(void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
147 		    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1,
148 		    PICL_PROP_DRIVER_NAME, NULL, NULL);
149 		(void) ptree_create_and_add_prop(nodeh, &propinfo, di_val,
150 		    NULL);
151 	}
152 
153 	di_val = di_devfs_path(di_node);
154 	if (di_val) {
155 		(void) ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
156 		    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(di_val) + 1,
157 		    PICL_PROP_DEVFS_PATH, NULL, NULL);
158 		(void) ptree_create_and_add_prop(nodeh, &propinfo, di_val,
159 		    NULL);
160 		di_devfs_path_free(di_val);
161 	}
162 
163 	for (di_prop = di_prop_next(di_node, DI_PROP_NIL);
164 	    di_prop != DI_PROP_NIL;
165 	    di_prop = di_prop_next(di_node, di_prop)) {
166 
167 		di_val = di_prop_name(di_prop);
168 		di_ptype = di_prop_type(di_prop);
169 		switch (di_ptype) {
170 		case DI_PROP_TYPE_BOOLEAN:
171 			(void) ptree_init_propinfo(&propinfo,
172 			    PTREE_PROPINFO_VERSION, PICL_PTYPE_VOID,
173 			    PICL_READ, (size_t)0, di_val, NULL, NULL);
174 			(void) ptree_create_and_add_prop(nodeh, &propinfo,
175 			    NULL, NULL);
176 			break;
177 		case DI_PROP_TYPE_INT: {
178 			int	*idata;
179 			int	len;
180 
181 			len = di_prop_ints(di_prop, &idata);
182 			if (len < 0)
183 				/* Recieved error, so ignore prop */
184 				break;
185 
186 			if (len == 1)
187 				(void) ptree_init_propinfo(&propinfo,
188 				    PTREE_PROPINFO_VERSION, PICL_PTYPE_INT,
189 				    PICL_READ, len * sizeof (int), di_val,
190 				    NULL, NULL);
191 			else
192 				(void) ptree_init_propinfo(&propinfo,
193 				    PTREE_PROPINFO_VERSION,
194 				    PICL_PTYPE_BYTEARRAY, PICL_READ,
195 				    len * sizeof (int), di_val,
196 				    NULL, NULL);
197 
198 			(void) ptree_create_and_add_prop(nodeh, &propinfo,
199 			    idata, NULL);
200 		}
201 		break;
202 		case DI_PROP_TYPE_STRING: {
203 			char	*sdata;
204 			int	len;
205 
206 			len = di_prop_strings(di_prop, &sdata);
207 			if (len < 0)
208 				break;
209 
210 			if (len == 1) {
211 				(void) ptree_init_propinfo(&propinfo,
212 				    PTREE_PROPINFO_VERSION,
213 				    PICL_PTYPE_CHARSTRING, PICL_READ,
214 				    strlen(sdata) + 1, di_val,
215 				    NULL, NULL);
216 				(void) ptree_create_and_add_prop(nodeh,
217 				    &propinfo, sdata, NULL);
218 			} else {
219 				(void) add_string_list_prop(nodeh, di_val,
220 				    sdata, len);
221 			}
222 		}
223 		break;
224 		case DI_PROP_TYPE_BYTE: {
225 			int		len;
226 			unsigned char *bdata;
227 
228 			len = di_prop_bytes(di_prop, &bdata);
229 			if (len < 0)
230 				break;
231 			(void) ptree_init_propinfo(&propinfo,
232 			    PTREE_PROPINFO_VERSION, PICL_PTYPE_BYTEARRAY,
233 			    PICL_READ, len, di_val, NULL, NULL);
234 			(void) ptree_create_and_add_prop(nodeh, &propinfo,
235 			    bdata, NULL);
236 		}
237 		break;
238 		case DI_PROP_TYPE_UNKNOWN:
239 			break;
240 		case DI_PROP_TYPE_UNDEF_IT:
241 			break;
242 		default:
243 			break;
244 		}
245 	}
246 }
247 
248 /*
249  * add OBP_REG property to picl cpu node if it's not already there.
250  */
251 static void
add_reg_prop(picl_nodehdl_t pn,di_node_t dn)252 add_reg_prop(picl_nodehdl_t pn, di_node_t dn)
253 {
254 	int 			reg_prop[SUN4V_CPU_REGSIZE];
255 	int 			status;
256 	int 			dlen;
257 	int			*pdata;
258 	ptree_propinfo_t	propinfo;
259 
260 	status = ptree_get_propval_by_name(pn, OBP_REG, reg_prop,
261 	    sizeof (reg_prop));
262 	if (status == PICL_SUCCESS) {
263 		return;
264 	}
265 	dlen = di_prom_prop_lookup_ints(ph, dn, OBP_REG, &pdata);
266 	if (dlen < 0) {
267 		return;
268 	}
269 	status = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
270 	    PICL_PTYPE_BYTEARRAY, PICL_READ, dlen * sizeof (int), OBP_REG,
271 	    NULL, NULL);
272 	if (status != PICL_SUCCESS) {
273 		return;
274 	}
275 	(void) ptree_create_and_add_prop(pn, &propinfo, pdata, NULL);
276 }
277 
278 /*
279  * Create a  picl node of type cpu and fill it.
280  * properties are filled from both the device tree and the
281  * Machine description.
282  */
283 static int
construct_cpu_node(picl_nodehdl_t plath,di_node_t dn)284 construct_cpu_node(picl_nodehdl_t plath, di_node_t dn)
285 {
286 	int		err;
287 	char		*nodename;
288 	picl_nodehdl_t	anodeh;
289 
290 	nodename = di_node_name(dn);	/* PICL_PROP_NAME */
291 
292 	err = ptree_create_and_add_node(plath, nodename, PICL_CLASS_CPU,
293 	    &anodeh);
294 	if (err != PICL_SUCCESS)
295 		return (err);
296 
297 	add_devinfo_props(anodeh, dn);
298 	add_reg_prop(anodeh, dn);
299 	(void) add_cpu_prop(anodeh, NULL);
300 
301 	return (err);
302 }
303 
304 /*
305  * Given a devinfo node find its reg property.
306  */
307 static int
get_reg_prop(di_node_t dn,int ** pdata)308 get_reg_prop(di_node_t dn, int **pdata)
309 {
310 	int dret = 0;
311 
312 	dret = di_prop_lookup_ints(DDI_DEV_T_ANY, dn, OBP_REG, pdata);
313 	if (dret > 0)
314 		return (dret);
315 
316 	if (!ph)
317 		return (0);
318 	dret = di_prom_prop_lookup_ints(ph, dn, OBP_REG, pdata);
319 	return (dret < 0? 0 : dret);
320 }
321 
322 /*
323  * Given a devinfo cpu node find its cpuid property.
324  */
325 int
get_cpuid(di_node_t di_node)326 get_cpuid(di_node_t di_node)
327 {
328 	int	len;
329 	int	*idata;
330 	int	dcpuid = -1;
331 
332 	len = get_reg_prop(di_node, &idata);
333 
334 	if (len != SUN4V_CPU_REGSIZE)
335 		return (dcpuid);
336 	if (len == SUN4V_CPU_REGSIZE)
337 		dcpuid = CFGHDL_TO_CPUID(idata[0]);
338 
339 	return (dcpuid);
340 }
341 
342 int
find_cpu(di_node_t node,int cpuid)343 find_cpu(di_node_t node, int cpuid)
344 {
345 	int	dcpuid;
346 	di_node_t cnode;
347 	char	*nodename;
348 
349 	for (cnode = di_child_node(node); cnode != DI_NODE_NIL;
350 	    cnode = di_sibling_node(cnode)) {
351 		nodename = di_node_name(cnode);
352 		if (nodename == NULL)
353 			continue;
354 		if (strcmp(nodename, OBP_CPU) == 0) {
355 			dcpuid = get_cpuid(cnode);
356 			if (dcpuid == cpuid) {
357 				return (1);
358 			}
359 		}
360 	}
361 	return (0);
362 }
363 
364 /*
365  * Callback to the ptree walk function during remove_cpus.
366  * As a part of the args receives a picl nodeh, searches
367  * the device tree for a cpu whose cpuid matches the picl cpu node.
368  * Sets arg struct's result to 1 if it failed to match and terminates
369  * the walk.
370  */
371 static int
remove_cpu_candidate(picl_nodehdl_t nodeh,void * c_args)372 remove_cpu_candidate(picl_nodehdl_t nodeh, void *c_args)
373 {
374 	di_node_t	di_node;
375 	cpu_lookup_t	*cpu_arg;
376 	int	err;
377 	int	pcpuid;
378 	int reg_prop[SUN4V_CPU_REGSIZE];
379 
380 	if (c_args == NULL)
381 		return (PICL_INVALIDARG);
382 
383 	cpu_arg = c_args;
384 	di_node = cpu_arg->di_node;
385 
386 	err = ptree_get_propval_by_name(nodeh, OBP_REG, reg_prop,
387 	    sizeof (reg_prop));
388 
389 	if (err != PICL_SUCCESS) {
390 		return (PICL_WALK_CONTINUE);
391 	}
392 
393 	pcpuid = CFGHDL_TO_CPUID(reg_prop[0]);
394 
395 	if (!find_cpu(di_node, pcpuid)) {
396 		cpu_arg->result = 1;
397 		cpu_arg->nodeh = nodeh;
398 		return (PICL_WALK_TERMINATE);
399 	}
400 
401 	cpu_arg->result = 0;
402 	return (PICL_WALK_CONTINUE);
403 }
404 
405 /*
406  * Given the start node of the device tree.
407  * find all cpus in the picl tree that don't have
408  * device tree counterparts and remove them.
409  */
410 static void
remove_cpus(di_node_t di_start)411 remove_cpus(di_node_t di_start)
412 {
413 	int		err;
414 	picl_nodehdl_t	plath;
415 	cpu_lookup_t	cpu_arg;
416 
417 	err = ptree_get_node_by_path(PLATFORM_PATH, &plath);
418 	if (err != PICL_SUCCESS)
419 		return;
420 
421 	do {
422 		cpu_arg.di_node = di_start;
423 		cpu_arg.nodeh = 0;
424 		cpu_arg.result = 0;
425 
426 		if (ptree_walk_tree_by_class(plath,
427 		    PICL_CLASS_CPU, &cpu_arg, remove_cpu_candidate)
428 		    != PICL_SUCCESS)
429 			return;
430 
431 		if (cpu_arg.result == 1) {
432 			err = ptree_delete_node(cpu_arg.nodeh);
433 			if (err == PICL_SUCCESS)
434 				ptree_destroy_node(cpu_arg.nodeh);
435 		}
436 	} while (cpu_arg.result);
437 }
438 
439 /*
440  * Callback to the ptree walk function during add_cpus.
441  * As a part of the args receives a cpu di_node, compares
442  * each picl cpu node's cpuid to the device tree node's cpuid.
443  * Sets arg struct's result to 1 on a match.
444  */
445 static int
cpu_exists(picl_nodehdl_t nodeh,void * c_args)446 cpu_exists(picl_nodehdl_t nodeh, void *c_args)
447 {
448 	di_node_t	di_node;
449 	cpu_lookup_t	*cpu_arg;
450 	int	err;
451 	int	dcpuid, pcpuid;
452 	int reg_prop[4];
453 
454 	if (c_args == NULL)
455 		return (PICL_INVALIDARG);
456 
457 	cpu_arg = c_args;
458 	di_node = cpu_arg->di_node;
459 	dcpuid = get_cpuid(di_node);
460 
461 	err = ptree_get_propval_by_name(nodeh, OBP_REG, reg_prop,
462 	    sizeof (reg_prop));
463 
464 	if (err != PICL_SUCCESS)
465 		return (PICL_WALK_CONTINUE);
466 
467 	pcpuid = CFGHDL_TO_CPUID(reg_prop[0]);
468 
469 	if (dcpuid == pcpuid) {
470 		cpu_arg->result = 1;
471 		return (PICL_WALK_TERMINATE);
472 	}
473 
474 	cpu_arg->result = 0;
475 	return (PICL_WALK_CONTINUE);
476 }
477 
478 /*
479  * Given the root node of the device tree.
480  * compare it to the picl tree and add to it cpus
481  * that are new.
482  */
483 static void
add_cpus(di_node_t di_node)484 add_cpus(di_node_t di_node)
485 {
486 	int		err;
487 	di_node_t	cnode;
488 	picl_nodehdl_t	plath;
489 	cpu_lookup_t	cpu_arg;
490 	char		*nodename;
491 
492 	err = ptree_get_node_by_path(PLATFORM_PATH, &plath);
493 	if (err != PICL_SUCCESS)
494 		return;
495 
496 	for (cnode = di_child_node(di_node); cnode != DI_NODE_NIL;
497 	    cnode = di_sibling_node(cnode)) {
498 		nodename = di_node_name(cnode);
499 		if (nodename == NULL)
500 			continue;
501 		if (strcmp(nodename, OBP_CPU) == 0) {
502 			cpu_arg.di_node = cnode;
503 
504 			if (ptree_walk_tree_by_class(plath,
505 			    PICL_CLASS_CPU, &cpu_arg, cpu_exists)
506 			    != PICL_SUCCESS)
507 				return;
508 
509 			if (cpu_arg.result == 0)
510 				/*
511 				 * Didn't find a matching cpu, add it.
512 				 */
513 				(void) construct_cpu_node(plath,
514 				    cnode);
515 		}
516 	}
517 }
518 
519 /*
520  * Handle DR events. Only supports cpu add and remove.
521  */
522 int
update_devices(char * dev,int op)523 update_devices(char *dev, int op)
524 {
525 	di_node_t	di_root;
526 
527 	if ((di_root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL)
528 		return (PICL_FAILURE);
529 
530 	if ((ph = di_prom_init()) == NULL)
531 		return (PICL_FAILURE);
532 
533 	if (op == DEV_ADD) {
534 		if (strcmp(dev, OBP_CPU) == 0)
535 			add_cpus(di_root);
536 	}
537 
538 	if (op == DEV_REMOVE) {
539 		if (strcmp(dev, OBP_CPU) == 0)
540 			remove_cpus(di_root);
541 	}
542 
543 	di_fini(di_root);
544 	di_prom_fini(ph);
545 	return (PICL_SUCCESS);
546 }
547