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