diff options
Diffstat (limited to 'arch/sparc64/kernel/mdesc.c')
-rw-r--r-- | arch/sparc64/kernel/mdesc.c | 619 |
1 files changed, 619 insertions, 0 deletions
diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c new file mode 100644 index 000000000000..9246c2cf9574 --- /dev/null +++ b/arch/sparc64/kernel/mdesc.c | |||
@@ -0,0 +1,619 @@ | |||
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 | } | ||