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
add_string_list_prop(picl_nodehdl_t nodeh,char * name,char * strlist,unsigned int nrows)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
add_devinfo_props(picl_nodehdl_t nodeh,di_node_t di_node)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
add_reg_prop(picl_nodehdl_t pn,di_node_t dn)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
construct_cpu_node(picl_nodehdl_t plath,di_node_t dn)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
get_reg_prop(di_node_t dn,int ** pdata)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
get_cpuid(di_node_t di_node)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
find_cpu(di_node_t node,int cpuid)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
remove_cpu_candidate(picl_nodehdl_t nodeh,void * c_args)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
remove_cpus(di_node_t di_start)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
cpu_exists(picl_nodehdl_t nodeh,void * c_args)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
add_cpus(di_node_t di_node)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
update_devices(char * dev,int op)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