Commit | Line | Data |
---|---|---|
5cbc3073 DM |
1 | /* mdesc.c: Sun4V machine description handling. |
2 | * | |
3 | * Copyright (C) 2007 David S. Miller <davem@davemloft.net> | |
4 | */ | |
5 | #include <linux/kernel.h> | |
6 | #include <linux/types.h> | |
7 | #include <linux/bootmem.h> | |
8 | #include <linux/log2.h> | |
9 | ||
10 | #include <asm/hypervisor.h> | |
11 | #include <asm/mdesc.h> | |
12 | #include <asm/prom.h> | |
13 | #include <asm/oplib.h> | |
14 | #include <asm/smp.h> | |
15 | ||
16 | /* Unlike the OBP device tree, the machine description is a full-on | |
17 | * DAG. An arbitrary number of ARCs are possible from one | |
18 | * node to other nodes and thus we can't use the OBP device_node | |
19 | * data structure to represent these nodes inside of the kernel. | |
20 | * | |
21 | * Actually, it isn't even a DAG, because there are back pointers | |
22 | * which create cycles in the graph. | |
23 | * | |
24 | * mdesc_hdr and mdesc_elem describe the layout of the data structure | |
25 | * we get from the Hypervisor. | |
26 | */ | |
27 | struct mdesc_hdr { | |
28 | u32 version; /* Transport version */ | |
29 | u32 node_sz; /* node block size */ | |
30 | u32 name_sz; /* name block size */ | |
31 | u32 data_sz; /* data block size */ | |
32 | }; | |
33 | ||
34 | struct mdesc_elem { | |
35 | u8 tag; | |
36 | #define MD_LIST_END 0x00 | |
37 | #define MD_NODE 0x4e | |
38 | #define MD_NODE_END 0x45 | |
39 | #define MD_NOOP 0x20 | |
40 | #define MD_PROP_ARC 0x61 | |
41 | #define MD_PROP_VAL 0x76 | |
42 | #define MD_PROP_STR 0x73 | |
43 | #define MD_PROP_DATA 0x64 | |
44 | u8 name_len; | |
45 | u16 resv; | |
46 | u32 name_offset; | |
47 | union { | |
48 | struct { | |
49 | u32 data_len; | |
50 | u32 data_offset; | |
51 | } data; | |
52 | u64 val; | |
53 | } d; | |
54 | }; | |
55 | ||
56 | static struct mdesc_hdr *main_mdesc; | |
57 | static struct mdesc_node *allnodes; | |
58 | ||
59 | static struct mdesc_node *allnodes_tail; | |
60 | static unsigned int unique_id; | |
61 | ||
62 | static struct mdesc_node **mdesc_hash; | |
63 | static unsigned int mdesc_hash_size; | |
64 | ||
65 | static inline unsigned int node_hashfn(u64 node) | |
66 | { | |
67 | return ((unsigned int) (node ^ (node >> 8) ^ (node >> 16))) | |
68 | & (mdesc_hash_size - 1); | |
69 | } | |
70 | ||
71 | static inline void hash_node(struct mdesc_node *mp) | |
72 | { | |
73 | struct mdesc_node **head = &mdesc_hash[node_hashfn(mp->node)]; | |
74 | ||
75 | mp->hash_next = *head; | |
76 | *head = mp; | |
77 | ||
78 | if (allnodes_tail) { | |
79 | allnodes_tail->allnodes_next = mp; | |
80 | allnodes_tail = mp; | |
81 | } else { | |
82 | allnodes = allnodes_tail = mp; | |
83 | } | |
84 | } | |
85 | ||
86 | static struct mdesc_node *find_node(u64 node) | |
87 | { | |
88 | struct mdesc_node *mp = mdesc_hash[node_hashfn(node)]; | |
89 | ||
90 | while (mp) { | |
91 | if (mp->node == node) | |
92 | return mp; | |
93 | ||
94 | mp = mp->hash_next; | |
95 | } | |
96 | return NULL; | |
97 | } | |
98 | ||
99 | struct property *md_find_property(const struct mdesc_node *mp, | |
100 | const char *name, | |
101 | int *lenp) | |
102 | { | |
103 | struct property *pp; | |
104 | ||
105 | for (pp = mp->properties; pp != 0; pp = pp->next) { | |
106 | if (strcasecmp(pp->name, name) == 0) { | |
107 | if (lenp) | |
108 | *lenp = pp->length; | |
109 | break; | |
110 | } | |
111 | } | |
112 | return pp; | |
113 | } | |
114 | EXPORT_SYMBOL(md_find_property); | |
115 | ||
116 | /* | |
117 | * Find a property with a given name for a given node | |
118 | * and return the value. | |
119 | */ | |
120 | const void *md_get_property(const struct mdesc_node *mp, const char *name, | |
121 | int *lenp) | |
122 | { | |
123 | struct property *pp = md_find_property(mp, name, lenp); | |
124 | return pp ? pp->value : NULL; | |
125 | } | |
126 | EXPORT_SYMBOL(md_get_property); | |
127 | ||
128 | struct mdesc_node *md_find_node_by_name(struct mdesc_node *from, | |
129 | const char *name) | |
130 | { | |
131 | struct mdesc_node *mp; | |
132 | ||
133 | mp = from ? from->allnodes_next : allnodes; | |
134 | for (; mp != NULL; mp = mp->allnodes_next) { | |
135 | if (strcmp(mp->name, name) == 0) | |
136 | break; | |
137 | } | |
138 | return mp; | |
139 | } | |
140 | EXPORT_SYMBOL(md_find_node_by_name); | |
141 | ||
142 | static unsigned int mdesc_early_allocated; | |
143 | ||
144 | static void * __init mdesc_early_alloc(unsigned long size) | |
145 | { | |
146 | void *ret; | |
147 | ||
148 | ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL); | |
149 | if (ret == NULL) { | |
150 | prom_printf("MDESC: alloc of %lu bytes failed.\n", size); | |
151 | prom_halt(); | |
152 | } | |
153 | ||
154 | memset(ret, 0, size); | |
155 | ||
156 | mdesc_early_allocated += size; | |
157 | ||
158 | return ret; | |
159 | } | |
160 | ||
161 | static unsigned int __init count_arcs(struct mdesc_elem *ep) | |
162 | { | |
163 | unsigned int ret = 0; | |
164 | ||
165 | ep++; | |
166 | while (ep->tag != MD_NODE_END) { | |
167 | if (ep->tag == MD_PROP_ARC) | |
168 | ret++; | |
169 | ep++; | |
170 | } | |
171 | return ret; | |
172 | } | |
173 | ||
174 | static void __init mdesc_node_alloc(u64 node, struct mdesc_elem *ep, const char *names) | |
175 | { | |
176 | unsigned int num_arcs = count_arcs(ep); | |
177 | struct mdesc_node *mp; | |
178 | ||
179 | mp = mdesc_early_alloc(sizeof(*mp) + | |
180 | (num_arcs * sizeof(struct mdesc_arc))); | |
181 | mp->name = names + ep->name_offset; | |
182 | mp->node = node; | |
183 | mp->unique_id = unique_id++; | |
184 | mp->num_arcs = num_arcs; | |
185 | ||
186 | hash_node(mp); | |
187 | } | |
188 | ||
189 | static inline struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) | |
190 | { | |
191 | return (struct mdesc_elem *) (mdesc + 1); | |
192 | } | |
193 | ||
194 | static inline void *name_block(struct mdesc_hdr *mdesc) | |
195 | { | |
196 | return ((void *) node_block(mdesc)) + mdesc->node_sz; | |
197 | } | |
198 | ||
199 | static inline void *data_block(struct mdesc_hdr *mdesc) | |
200 | { | |
201 | return ((void *) name_block(mdesc)) + mdesc->name_sz; | |
202 | } | |
203 | ||
204 | /* In order to avoid recursion (the graph can be very deep) we use a | |
205 | * two pass algorithm. First we allocate all the nodes and hash them. | |
206 | * Then we iterate over each node, filling in the arcs and properties. | |
207 | */ | |
208 | static void __init build_all_nodes(struct mdesc_hdr *mdesc) | |
209 | { | |
210 | struct mdesc_elem *start, *ep; | |
211 | struct mdesc_node *mp; | |
212 | const char *names; | |
213 | void *data; | |
214 | u64 last_node; | |
215 | ||
216 | start = ep = node_block(mdesc); | |
217 | last_node = mdesc->node_sz / 16; | |
218 | ||
219 | names = name_block(mdesc); | |
220 | ||
221 | while (1) { | |
222 | u64 node = ep - start; | |
223 | ||
224 | if (ep->tag == MD_LIST_END) | |
225 | break; | |
226 | ||
227 | if (ep->tag != MD_NODE) { | |
228 | prom_printf("MDESC: Inconsistent element list.\n"); | |
229 | prom_halt(); | |
230 | } | |
231 | ||
232 | mdesc_node_alloc(node, ep, names); | |
233 | ||
234 | if (ep->d.val >= last_node) { | |
235 | printk("MDESC: Warning, early break out of node scan.\n"); | |
236 | printk("MDESC: Next node [%lu] last_node [%lu].\n", | |
237 | node, last_node); | |
238 | break; | |
239 | } | |
240 | ||
241 | ep = start + ep->d.val; | |
242 | } | |
243 | ||
244 | data = data_block(mdesc); | |
245 | for (mp = allnodes; mp; mp = mp->allnodes_next) { | |
246 | struct mdesc_elem *ep = start + mp->node; | |
247 | struct property **link = &mp->properties; | |
248 | unsigned int this_arc = 0; | |
249 | ||
250 | ep++; | |
251 | while (ep->tag != MD_NODE_END) { | |
252 | switch (ep->tag) { | |
253 | case MD_PROP_ARC: { | |
254 | struct mdesc_node *target; | |
255 | ||
256 | if (this_arc >= mp->num_arcs) { | |
257 | prom_printf("MDESC: ARC overrun [%u:%u]\n", | |
258 | this_arc, mp->num_arcs); | |
259 | prom_halt(); | |
260 | } | |
261 | target = find_node(ep->d.val); | |
262 | if (!target) { | |
263 | printk("MDESC: Warning, arc points to " | |
264 | "missing node, ignoring.\n"); | |
265 | break; | |
266 | } | |
267 | mp->arcs[this_arc].name = | |
268 | (names + ep->name_offset); | |
269 | mp->arcs[this_arc].arc = target; | |
270 | this_arc++; | |
271 | break; | |
272 | } | |
273 | ||
274 | case MD_PROP_VAL: | |
275 | case MD_PROP_STR: | |
276 | case MD_PROP_DATA: { | |
277 | struct property *p = mdesc_early_alloc(sizeof(*p)); | |
278 | ||
279 | p->unique_id = unique_id++; | |
280 | p->name = (char *) names + ep->name_offset; | |
281 | if (ep->tag == MD_PROP_VAL) { | |
282 | p->value = &ep->d.val; | |
283 | p->length = 8; | |
284 | } else { | |
285 | p->value = data + ep->d.data.data_offset; | |
286 | p->length = ep->d.data.data_len; | |
287 | } | |
288 | *link = p; | |
289 | link = &p->next; | |
290 | break; | |
291 | } | |
292 | ||
293 | case MD_NOOP: | |
294 | break; | |
295 | ||
296 | default: | |
297 | printk("MDESC: Warning, ignoring unknown tag type %02x\n", | |
298 | ep->tag); | |
299 | } | |
300 | ep++; | |
301 | } | |
302 | } | |
303 | } | |
304 | ||
305 | static unsigned int __init count_nodes(struct mdesc_hdr *mdesc) | |
306 | { | |
307 | struct mdesc_elem *ep = node_block(mdesc); | |
308 | struct mdesc_elem *end; | |
309 | unsigned int cnt = 0; | |
310 | ||
311 | end = ((void *)ep) + mdesc->node_sz; | |
312 | while (ep < end) { | |
313 | if (ep->tag == MD_NODE) | |
314 | cnt++; | |
315 | ep++; | |
316 | } | |
317 | return cnt; | |
318 | } | |
319 | ||
320 | static void __init report_platform_properties(void) | |
321 | { | |
322 | struct mdesc_node *pn = md_find_node_by_name(NULL, "platform"); | |
323 | const char *s; | |
324 | const u64 *v; | |
325 | ||
326 | if (!pn) { | |
327 | prom_printf("No platform node in machine-description.\n"); | |
328 | prom_halt(); | |
329 | } | |
330 | ||
331 | s = md_get_property(pn, "banner-name", NULL); | |
332 | printk("PLATFORM: banner-name [%s]\n", s); | |
333 | s = md_get_property(pn, "name", NULL); | |
334 | printk("PLATFORM: name [%s]\n", s); | |
335 | ||
336 | v = md_get_property(pn, "hostid", NULL); | |
337 | if (v) | |
338 | printk("PLATFORM: hostid [%08lx]\n", *v); | |
339 | v = md_get_property(pn, "serial#", NULL); | |
340 | if (v) | |
341 | printk("PLATFORM: serial# [%08lx]\n", *v); | |
342 | v = md_get_property(pn, "stick-frequency", NULL); | |
343 | printk("PLATFORM: stick-frequency [%08lx]\n", *v); | |
344 | v = md_get_property(pn, "mac-address", NULL); | |
345 | if (v) | |
346 | printk("PLATFORM: mac-address [%lx]\n", *v); | |
347 | v = md_get_property(pn, "watchdog-resolution", NULL); | |
348 | if (v) | |
349 | printk("PLATFORM: watchdog-resolution [%lu ms]\n", *v); | |
350 | v = md_get_property(pn, "watchdog-max-timeout", NULL); | |
351 | if (v) | |
352 | printk("PLATFORM: watchdog-max-timeout [%lu ms]\n", *v); | |
353 | v = md_get_property(pn, "max-cpus", NULL); | |
354 | if (v) | |
355 | printk("PLATFORM: max-cpus [%lu]\n", *v); | |
356 | } | |
357 | ||
358 | static int inline find_in_proplist(const char *list, const char *match, int len) | |
359 | { | |
360 | while (len > 0) { | |
361 | int l; | |
362 | ||
363 | if (!strcmp(list, match)) | |
364 | return 1; | |
365 | l = strlen(list) + 1; | |
366 | list += l; | |
367 | len -= l; | |
368 | } | |
369 | return 0; | |
370 | } | |
371 | ||
372 | static void __init fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_node *mp) | |
373 | { | |
374 | const u64 *level = md_get_property(mp, "level", NULL); | |
375 | const u64 *size = md_get_property(mp, "size", NULL); | |
376 | const u64 *line_size = md_get_property(mp, "line-size", NULL); | |
377 | const char *type; | |
378 | int type_len; | |
379 | ||
380 | type = md_get_property(mp, "type", &type_len); | |
381 | ||
382 | switch (*level) { | |
383 | case 1: | |
384 | if (find_in_proplist(type, "instn", type_len)) { | |
385 | c->icache_size = *size; | |
386 | c->icache_line_size = *line_size; | |
387 | } else if (find_in_proplist(type, "data", type_len)) { | |
388 | c->dcache_size = *size; | |
389 | c->dcache_line_size = *line_size; | |
390 | } | |
391 | break; | |
392 | ||
393 | case 2: | |
394 | c->ecache_size = *size; | |
395 | c->ecache_line_size = *line_size; | |
396 | break; | |
397 | ||
398 | default: | |
399 | break; | |
400 | } | |
401 | ||
402 | if (*level == 1) { | |
403 | unsigned int i; | |
404 | ||
405 | for (i = 0; i < mp->num_arcs; i++) { | |
406 | struct mdesc_node *t = mp->arcs[i].arc; | |
407 | ||
408 | if (strcmp(mp->arcs[i].name, "fwd")) | |
409 | continue; | |
410 | ||
411 | if (!strcmp(t->name, "cache")) | |
412 | fill_in_one_cache(c, t); | |
413 | } | |
414 | } | |
415 | } | |
416 | ||
417 | static void __init mark_core_ids(struct mdesc_node *mp, int core_id) | |
418 | { | |
419 | unsigned int i; | |
420 | ||
421 | for (i = 0; i < mp->num_arcs; i++) { | |
422 | struct mdesc_node *t = mp->arcs[i].arc; | |
423 | const u64 *id; | |
424 | ||
425 | if (strcmp(mp->arcs[i].name, "back")) | |
426 | continue; | |
427 | ||
428 | if (!strcmp(t->name, "cpu")) { | |
429 | id = md_get_property(t, "id", NULL); | |
430 | if (*id < NR_CPUS) | |
431 | cpu_data(*id).core_id = core_id; | |
432 | } else { | |
433 | unsigned int j; | |
434 | ||
435 | for (j = 0; j < t->num_arcs; j++) { | |
436 | struct mdesc_node *n = t->arcs[j].arc; | |
437 | ||
438 | if (strcmp(t->arcs[j].name, "back")) | |
439 | continue; | |
440 | ||
441 | if (strcmp(n->name, "cpu")) | |
442 | continue; | |
443 | ||
444 | id = md_get_property(n, "id", NULL); | |
445 | if (*id < NR_CPUS) | |
446 | cpu_data(*id).core_id = core_id; | |
447 | } | |
448 | } | |
449 | } | |
450 | } | |
451 | ||
452 | static void __init set_core_ids(void) | |
453 | { | |
454 | struct mdesc_node *mp; | |
455 | int idx; | |
456 | ||
457 | idx = 1; | |
458 | md_for_each_node_by_name(mp, "cache") { | |
459 | const u64 *level = md_get_property(mp, "level", NULL); | |
460 | const char *type; | |
461 | int len; | |
462 | ||
463 | if (*level != 1) | |
464 | continue; | |
465 | ||
466 | type = md_get_property(mp, "type", &len); | |
467 | if (!find_in_proplist(type, "instn", len)) | |
468 | continue; | |
469 | ||
470 | mark_core_ids(mp, idx); | |
471 | ||
472 | idx++; | |
473 | } | |
474 | } | |
475 | ||
476 | static void __init get_one_mondo_bits(const u64 *p, unsigned int *mask, unsigned char def) | |
477 | { | |
478 | u64 val; | |
479 | ||
480 | if (!p) | |
481 | goto use_default; | |
482 | val = *p; | |
483 | ||
484 | if (!val || val >= 64) | |
485 | goto use_default; | |
486 | ||
487 | *mask = ((1U << val) * 64U) - 1U; | |
488 | return; | |
489 | ||
490 | use_default: | |
491 | *mask = ((1U << def) * 64U) - 1U; | |
492 | } | |
493 | ||
494 | static void __init get_mondo_data(struct mdesc_node *mp, struct trap_per_cpu *tb) | |
495 | { | |
496 | const u64 *val; | |
497 | ||
498 | val = md_get_property(mp, "q-cpu-mondo-#bits", NULL); | |
499 | get_one_mondo_bits(val, &tb->cpu_mondo_qmask, 7); | |
500 | ||
501 | val = md_get_property(mp, "q-dev-mondo-#bits", NULL); | |
502 | get_one_mondo_bits(val, &tb->dev_mondo_qmask, 7); | |
503 | ||
504 | val = md_get_property(mp, "q-resumable-#bits", NULL); | |
505 | get_one_mondo_bits(val, &tb->resum_qmask, 6); | |
506 | ||
507 | val = md_get_property(mp, "q-nonresumable-#bits", NULL); | |
508 | get_one_mondo_bits(val, &tb->nonresum_qmask, 2); | |
509 | } | |
510 | ||
511 | static void __init mdesc_fill_in_cpu_data(void) | |
512 | { | |
513 | struct mdesc_node *mp; | |
514 | ||
515 | ncpus_probed = 0; | |
516 | md_for_each_node_by_name(mp, "cpu") { | |
517 | const u64 *id = md_get_property(mp, "id", NULL); | |
518 | const u64 *cfreq = md_get_property(mp, "clock-frequency", NULL); | |
519 | struct trap_per_cpu *tb; | |
520 | cpuinfo_sparc *c; | |
521 | unsigned int i; | |
522 | int cpuid; | |
523 | ||
524 | ncpus_probed++; | |
525 | ||
526 | cpuid = *id; | |
527 | ||
528 | #ifdef CONFIG_SMP | |
529 | if (cpuid >= NR_CPUS) | |
530 | continue; | |
531 | #else | |
532 | /* On uniprocessor we only want the values for the | |
533 | * real physical cpu the kernel booted onto, however | |
534 | * cpu_data() only has one entry at index 0. | |
535 | */ | |
536 | if (cpuid != real_hard_smp_processor_id()) | |
537 | continue; | |
538 | cpuid = 0; | |
539 | #endif | |
540 | ||
541 | c = &cpu_data(cpuid); | |
542 | c->clock_tick = *cfreq; | |
543 | ||
544 | tb = &trap_block[cpuid]; | |
545 | get_mondo_data(mp, tb); | |
546 | ||
547 | for (i = 0; i < mp->num_arcs; i++) { | |
548 | struct mdesc_node *t = mp->arcs[i].arc; | |
549 | unsigned int j; | |
550 | ||
551 | if (strcmp(mp->arcs[i].name, "fwd")) | |
552 | continue; | |
553 | ||
554 | if (!strcmp(t->name, "cache")) { | |
555 | fill_in_one_cache(c, t); | |
556 | continue; | |
557 | } | |
558 | ||
559 | for (j = 0; j < t->num_arcs; j++) { | |
560 | struct mdesc_node *n; | |
561 | ||
562 | n = t->arcs[j].arc; | |
563 | if (strcmp(t->arcs[j].name, "fwd")) | |
564 | continue; | |
565 | ||
566 | if (!strcmp(n->name, "cache")) | |
567 | fill_in_one_cache(c, n); | |
568 | } | |
569 | } | |
570 | ||
571 | #ifdef CONFIG_SMP | |
572 | cpu_set(cpuid, cpu_present_map); | |
573 | cpu_set(cpuid, phys_cpu_present_map); | |
574 | #endif | |
575 | ||
576 | c->core_id = 0; | |
577 | } | |
578 | ||
579 | set_core_ids(); | |
580 | ||
581 | smp_fill_in_sib_core_maps(); | |
582 | } | |
583 | ||
584 | void __init sun4v_mdesc_init(void) | |
585 | { | |
586 | unsigned long len, real_len, status; | |
587 | ||
588 | (void) sun4v_mach_desc(0UL, 0UL, &len); | |
589 | ||
590 | printk("MDESC: Size is %lu bytes.\n", len); | |
591 | ||
592 | main_mdesc = mdesc_early_alloc(len); | |
593 | ||
594 | status = sun4v_mach_desc(__pa(main_mdesc), len, &real_len); | |
595 | if (status != HV_EOK || real_len > len) { | |
596 | prom_printf("sun4v_mach_desc fails, err(%lu), " | |
597 | "len(%lu), real_len(%lu)\n", | |
598 | status, len, real_len); | |
599 | prom_halt(); | |
600 | } | |
601 | ||
602 | len = count_nodes(main_mdesc); | |
603 | printk("MDESC: %lu nodes.\n", len); | |
604 | ||
605 | len = roundup_pow_of_two(len); | |
606 | ||
607 | mdesc_hash = mdesc_early_alloc(len * sizeof(struct mdesc_node *)); | |
608 | mdesc_hash_size = len; | |
609 | ||
610 | printk("MDESC: Hash size %lu entries.\n", len); | |
611 | ||
612 | build_all_nodes(main_mdesc); | |
613 | ||
614 | printk("MDESC: Built graph with %u bytes of memory.\n", | |
615 | mdesc_early_allocated); | |
616 | ||
617 | report_platform_properties(); | |
618 | mdesc_fill_in_cpu_data(); | |
619 | } |