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