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
31static	di_prom_handle_t	ph = DI_PROM_HANDLE_NIL;
32
33typedef struct cpu_lookup {
34	di_node_t di_node;
35	picl_nodehdl_t nodeh;
36	int result;
37} cpu_lookup_t;
38
39extern int add_cpu_prop(picl_nodehdl_t node, void *args);
40extern 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 */
46int
47add_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 */
113static void
114add_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 */
253static void
254add_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 */
285static int
286construct_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 */
309static int
310get_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 */
327int
328get_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
344int
345find_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 */
373static int
374remove_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 */
412static void
413remove_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 */
447static int
448cpu_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 */
485static void
486add_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 */
524int
525update_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