1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* mdesc.c: Sun4V machine description handling. |
3 | * |
4 | * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> |
5 | */ |
6 | #include <linux/kernel.h> |
7 | #include <linux/types.h> |
8 | #include <linux/log2.h> |
9 | #include <linux/list.h> |
10 | #include <linux/slab.h> |
11 | #include <linux/mm.h> |
12 | #include <linux/miscdevice.h> |
13 | #include <linux/memblock.h> |
14 | #include <linux/export.h> |
15 | #include <linux/refcount.h> |
16 | |
17 | #include <asm/cpudata.h> |
18 | #include <asm/hypervisor.h> |
19 | #include <asm/mdesc.h> |
20 | #include <asm/prom.h> |
21 | #include <linux/uaccess.h> |
22 | #include <asm/oplib.h> |
23 | #include <asm/smp.h> |
24 | #include <asm/adi.h> |
25 | |
26 | /* Unlike the OBP device tree, the machine description is a full-on |
27 | * DAG. An arbitrary number of ARCs are possible from one |
28 | * node to other nodes and thus we can't use the OBP device_node |
29 | * data structure to represent these nodes inside of the kernel. |
30 | * |
31 | * Actually, it isn't even a DAG, because there are back pointers |
32 | * which create cycles in the graph. |
33 | * |
34 | * mdesc_hdr and mdesc_elem describe the layout of the data structure |
35 | * we get from the Hypervisor. |
36 | */ |
37 | struct mdesc_hdr { |
38 | u32 version; /* Transport version */ |
39 | u32 node_sz; /* node block size */ |
40 | u32 name_sz; /* name block size */ |
41 | u32 data_sz; /* data block size */ |
42 | char data[]; |
43 | } __attribute__((aligned(16))); |
44 | |
45 | struct mdesc_elem { |
46 | u8 tag; |
47 | #define MD_LIST_END 0x00 |
48 | #define MD_NODE 0x4e |
49 | #define MD_NODE_END 0x45 |
50 | #define MD_NOOP 0x20 |
51 | #define MD_PROP_ARC 0x61 |
52 | #define MD_PROP_VAL 0x76 |
53 | #define MD_PROP_STR 0x73 |
54 | #define MD_PROP_DATA 0x64 |
55 | u8 name_len; |
56 | u16 resv; |
57 | u32 name_offset; |
58 | union { |
59 | struct { |
60 | u32 data_len; |
61 | u32 data_offset; |
62 | } data; |
63 | u64 val; |
64 | } d; |
65 | }; |
66 | |
67 | struct mdesc_mem_ops { |
68 | struct mdesc_handle *(*alloc)(unsigned int mdesc_size); |
69 | void (*free)(struct mdesc_handle *handle); |
70 | }; |
71 | |
72 | struct mdesc_handle { |
73 | struct list_head list; |
74 | struct mdesc_mem_ops *mops; |
75 | void *self_base; |
76 | refcount_t refcnt; |
77 | unsigned int handle_size; |
78 | struct mdesc_hdr mdesc; |
79 | }; |
80 | |
81 | typedef int (*mdesc_node_info_get_f)(struct mdesc_handle *, u64, |
82 | union md_node_info *); |
83 | typedef void (*mdesc_node_info_rel_f)(union md_node_info *); |
84 | typedef bool (*mdesc_node_match_f)(union md_node_info *, union md_node_info *); |
85 | |
86 | struct md_node_ops { |
87 | char *name; |
88 | mdesc_node_info_get_f get_info; |
89 | mdesc_node_info_rel_f rel_info; |
90 | mdesc_node_match_f node_match; |
91 | }; |
92 | |
93 | static int get_vdev_port_node_info(struct mdesc_handle *md, u64 node, |
94 | union md_node_info *node_info); |
95 | static void rel_vdev_port_node_info(union md_node_info *node_info); |
96 | static bool vdev_port_node_match(union md_node_info *a_node_info, |
97 | union md_node_info *b_node_info); |
98 | |
99 | static int get_ds_port_node_info(struct mdesc_handle *md, u64 node, |
100 | union md_node_info *node_info); |
101 | static void rel_ds_port_node_info(union md_node_info *node_info); |
102 | static bool ds_port_node_match(union md_node_info *a_node_info, |
103 | union md_node_info *b_node_info); |
104 | |
105 | /* supported node types which can be registered */ |
106 | static struct md_node_ops md_node_ops_table[] = { |
107 | {.name: "virtual-device-port" , .get_info: get_vdev_port_node_info, |
108 | .rel_info: rel_vdev_port_node_info, .node_match: vdev_port_node_match}, |
109 | {"domain-services-port" , get_ds_port_node_info, |
110 | rel_ds_port_node_info, ds_port_node_match}, |
111 | {NULL, NULL, NULL, NULL} |
112 | }; |
113 | |
114 | static void mdesc_get_node_ops(const char *node_name, |
115 | mdesc_node_info_get_f *get_info_f, |
116 | mdesc_node_info_rel_f *rel_info_f, |
117 | mdesc_node_match_f *match_f) |
118 | { |
119 | int i; |
120 | |
121 | if (get_info_f) |
122 | *get_info_f = NULL; |
123 | |
124 | if (rel_info_f) |
125 | *rel_info_f = NULL; |
126 | |
127 | if (match_f) |
128 | *match_f = NULL; |
129 | |
130 | if (!node_name) |
131 | return; |
132 | |
133 | for (i = 0; md_node_ops_table[i].name != NULL; i++) { |
134 | if (strcmp(md_node_ops_table[i].name, node_name) == 0) { |
135 | if (get_info_f) |
136 | *get_info_f = md_node_ops_table[i].get_info; |
137 | |
138 | if (rel_info_f) |
139 | *rel_info_f = md_node_ops_table[i].rel_info; |
140 | |
141 | if (match_f) |
142 | *match_f = md_node_ops_table[i].node_match; |
143 | |
144 | break; |
145 | } |
146 | } |
147 | } |
148 | |
149 | static void mdesc_handle_init(struct mdesc_handle *hp, |
150 | unsigned int handle_size, |
151 | void *base) |
152 | { |
153 | BUG_ON(((unsigned long)&hp->mdesc) & (16UL - 1)); |
154 | |
155 | memset(hp, 0, handle_size); |
156 | INIT_LIST_HEAD(list: &hp->list); |
157 | hp->self_base = base; |
158 | refcount_set(r: &hp->refcnt, n: 1); |
159 | hp->handle_size = handle_size; |
160 | } |
161 | |
162 | static struct mdesc_handle * __init mdesc_memblock_alloc(unsigned int mdesc_size) |
163 | { |
164 | unsigned int handle_size, alloc_size; |
165 | struct mdesc_handle *hp; |
166 | unsigned long paddr; |
167 | |
168 | handle_size = (sizeof(struct mdesc_handle) - |
169 | sizeof(struct mdesc_hdr) + |
170 | mdesc_size); |
171 | alloc_size = PAGE_ALIGN(handle_size); |
172 | |
173 | paddr = memblock_phys_alloc(size: alloc_size, PAGE_SIZE); |
174 | |
175 | hp = NULL; |
176 | if (paddr) { |
177 | hp = __va(paddr); |
178 | mdesc_handle_init(hp, handle_size, base: hp); |
179 | } |
180 | return hp; |
181 | } |
182 | |
183 | static void __init mdesc_memblock_free(struct mdesc_handle *hp) |
184 | { |
185 | unsigned int alloc_size; |
186 | unsigned long start; |
187 | |
188 | BUG_ON(refcount_read(&hp->refcnt) != 0); |
189 | BUG_ON(!list_empty(&hp->list)); |
190 | |
191 | alloc_size = PAGE_ALIGN(hp->handle_size); |
192 | start = __pa(hp); |
193 | memblock_free_late(base: start, size: alloc_size); |
194 | } |
195 | |
196 | static struct mdesc_mem_ops memblock_mdesc_ops = { |
197 | .alloc = mdesc_memblock_alloc, |
198 | .free = mdesc_memblock_free, |
199 | }; |
200 | |
201 | static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size) |
202 | { |
203 | unsigned int handle_size; |
204 | struct mdesc_handle *hp; |
205 | unsigned long addr; |
206 | void *base; |
207 | |
208 | handle_size = (sizeof(struct mdesc_handle) - |
209 | sizeof(struct mdesc_hdr) + |
210 | mdesc_size); |
211 | base = kmalloc(size: handle_size + 15, GFP_KERNEL | __GFP_RETRY_MAYFAIL); |
212 | if (!base) |
213 | return NULL; |
214 | |
215 | addr = (unsigned long)base; |
216 | addr = (addr + 15UL) & ~15UL; |
217 | hp = (struct mdesc_handle *) addr; |
218 | |
219 | mdesc_handle_init(hp, handle_size, base); |
220 | |
221 | return hp; |
222 | } |
223 | |
224 | static void mdesc_kfree(struct mdesc_handle *hp) |
225 | { |
226 | BUG_ON(refcount_read(&hp->refcnt) != 0); |
227 | BUG_ON(!list_empty(&hp->list)); |
228 | |
229 | kfree(objp: hp->self_base); |
230 | } |
231 | |
232 | static struct mdesc_mem_ops kmalloc_mdesc_memops = { |
233 | .alloc = mdesc_kmalloc, |
234 | .free = mdesc_kfree, |
235 | }; |
236 | |
237 | static struct mdesc_handle *mdesc_alloc(unsigned int mdesc_size, |
238 | struct mdesc_mem_ops *mops) |
239 | { |
240 | struct mdesc_handle *hp = mops->alloc(mdesc_size); |
241 | |
242 | if (hp) |
243 | hp->mops = mops; |
244 | |
245 | return hp; |
246 | } |
247 | |
248 | static void mdesc_free(struct mdesc_handle *hp) |
249 | { |
250 | hp->mops->free(hp); |
251 | } |
252 | |
253 | static struct mdesc_handle *cur_mdesc; |
254 | static LIST_HEAD(mdesc_zombie_list); |
255 | static DEFINE_SPINLOCK(mdesc_lock); |
256 | |
257 | struct mdesc_handle *mdesc_grab(void) |
258 | { |
259 | struct mdesc_handle *hp; |
260 | unsigned long flags; |
261 | |
262 | spin_lock_irqsave(&mdesc_lock, flags); |
263 | hp = cur_mdesc; |
264 | if (hp) |
265 | refcount_inc(r: &hp->refcnt); |
266 | spin_unlock_irqrestore(lock: &mdesc_lock, flags); |
267 | |
268 | return hp; |
269 | } |
270 | EXPORT_SYMBOL(mdesc_grab); |
271 | |
272 | void mdesc_release(struct mdesc_handle *hp) |
273 | { |
274 | unsigned long flags; |
275 | |
276 | spin_lock_irqsave(&mdesc_lock, flags); |
277 | if (refcount_dec_and_test(r: &hp->refcnt)) { |
278 | list_del_init(entry: &hp->list); |
279 | hp->mops->free(hp); |
280 | } |
281 | spin_unlock_irqrestore(lock: &mdesc_lock, flags); |
282 | } |
283 | EXPORT_SYMBOL(mdesc_release); |
284 | |
285 | static DEFINE_MUTEX(mdesc_mutex); |
286 | static struct mdesc_notifier_client *client_list; |
287 | |
288 | void mdesc_register_notifier(struct mdesc_notifier_client *client) |
289 | { |
290 | bool supported = false; |
291 | u64 node; |
292 | int i; |
293 | |
294 | mutex_lock(&mdesc_mutex); |
295 | |
296 | /* check to see if the node is supported for registration */ |
297 | for (i = 0; md_node_ops_table[i].name != NULL; i++) { |
298 | if (strcmp(md_node_ops_table[i].name, client->node_name) == 0) { |
299 | supported = true; |
300 | break; |
301 | } |
302 | } |
303 | |
304 | if (!supported) { |
305 | pr_err("MD: %s node not supported\n" , client->node_name); |
306 | mutex_unlock(lock: &mdesc_mutex); |
307 | return; |
308 | } |
309 | |
310 | client->next = client_list; |
311 | client_list = client; |
312 | |
313 | mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name) |
314 | client->add(cur_mdesc, node, client->node_name); |
315 | |
316 | mutex_unlock(lock: &mdesc_mutex); |
317 | } |
318 | |
319 | static const u64 *parent_cfg_handle(struct mdesc_handle *hp, u64 node) |
320 | { |
321 | const u64 *id; |
322 | u64 a; |
323 | |
324 | id = NULL; |
325 | mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) { |
326 | u64 target; |
327 | |
328 | target = mdesc_arc_target(hp, a); |
329 | id = mdesc_get_property(hp, target, |
330 | "cfg-handle" , NULL); |
331 | if (id) |
332 | break; |
333 | } |
334 | |
335 | return id; |
336 | } |
337 | |
338 | static int get_vdev_port_node_info(struct mdesc_handle *md, u64 node, |
339 | union md_node_info *node_info) |
340 | { |
341 | const u64 *parent_cfg_hdlp; |
342 | const char *name; |
343 | const u64 *idp; |
344 | |
345 | /* |
346 | * Virtual device nodes are distinguished by: |
347 | * 1. "id" property |
348 | * 2. "name" property |
349 | * 3. parent node "cfg-handle" property |
350 | */ |
351 | idp = mdesc_get_property(md, node, "id" , NULL); |
352 | name = mdesc_get_property(md, node, "name" , NULL); |
353 | parent_cfg_hdlp = parent_cfg_handle(hp: md, node); |
354 | |
355 | if (!idp || !name || !parent_cfg_hdlp) |
356 | return -1; |
357 | |
358 | node_info->vdev_port.id = *idp; |
359 | node_info->vdev_port.name = kstrdup_const(s: name, GFP_KERNEL); |
360 | if (!node_info->vdev_port.name) |
361 | return -1; |
362 | node_info->vdev_port.parent_cfg_hdl = *parent_cfg_hdlp; |
363 | |
364 | return 0; |
365 | } |
366 | |
367 | static void rel_vdev_port_node_info(union md_node_info *node_info) |
368 | { |
369 | if (node_info && node_info->vdev_port.name) { |
370 | kfree_const(x: node_info->vdev_port.name); |
371 | node_info->vdev_port.name = NULL; |
372 | } |
373 | } |
374 | |
375 | static bool vdev_port_node_match(union md_node_info *a_node_info, |
376 | union md_node_info *b_node_info) |
377 | { |
378 | if (a_node_info->vdev_port.id != b_node_info->vdev_port.id) |
379 | return false; |
380 | |
381 | if (a_node_info->vdev_port.parent_cfg_hdl != |
382 | b_node_info->vdev_port.parent_cfg_hdl) |
383 | return false; |
384 | |
385 | if (strncmp(a_node_info->vdev_port.name, |
386 | b_node_info->vdev_port.name, MDESC_MAX_STR_LEN) != 0) |
387 | return false; |
388 | |
389 | return true; |
390 | } |
391 | |
392 | static int get_ds_port_node_info(struct mdesc_handle *md, u64 node, |
393 | union md_node_info *node_info) |
394 | { |
395 | const u64 *idp; |
396 | |
397 | /* DS port nodes use the "id" property to distinguish them */ |
398 | idp = mdesc_get_property(md, node, "id" , NULL); |
399 | if (!idp) |
400 | return -1; |
401 | |
402 | node_info->ds_port.id = *idp; |
403 | |
404 | return 0; |
405 | } |
406 | |
407 | static void rel_ds_port_node_info(union md_node_info *node_info) |
408 | { |
409 | } |
410 | |
411 | static bool ds_port_node_match(union md_node_info *a_node_info, |
412 | union md_node_info *b_node_info) |
413 | { |
414 | if (a_node_info->ds_port.id != b_node_info->ds_port.id) |
415 | return false; |
416 | |
417 | return true; |
418 | } |
419 | |
420 | /* Run 'func' on nodes which are in A but not in B. */ |
421 | static void invoke_on_missing(const char *name, |
422 | struct mdesc_handle *a, |
423 | struct mdesc_handle *b, |
424 | void (*func)(struct mdesc_handle *, u64, |
425 | const char *node_name)) |
426 | { |
427 | mdesc_node_info_get_f get_info_func; |
428 | mdesc_node_info_rel_f rel_info_func; |
429 | mdesc_node_match_f node_match_func; |
430 | union md_node_info a_node_info; |
431 | union md_node_info b_node_info; |
432 | bool found; |
433 | u64 a_node; |
434 | u64 b_node; |
435 | int rv; |
436 | |
437 | /* |
438 | * Find the get_info, rel_info and node_match ops for the given |
439 | * node name |
440 | */ |
441 | mdesc_get_node_ops(node_name: name, get_info_f: &get_info_func, rel_info_f: &rel_info_func, |
442 | match_f: &node_match_func); |
443 | |
444 | /* If we didn't find a match, the node type is not supported */ |
445 | if (!get_info_func || !rel_info_func || !node_match_func) { |
446 | pr_err("MD: %s node type is not supported\n" , name); |
447 | return; |
448 | } |
449 | |
450 | mdesc_for_each_node_by_name(a, a_node, name) { |
451 | found = false; |
452 | |
453 | rv = get_info_func(a, a_node, &a_node_info); |
454 | if (rv != 0) { |
455 | pr_err("MD: Cannot find 1 or more required match properties for %s node.\n" , |
456 | name); |
457 | continue; |
458 | } |
459 | |
460 | /* Check each node in B for node matching a_node */ |
461 | mdesc_for_each_node_by_name(b, b_node, name) { |
462 | rv = get_info_func(b, b_node, &b_node_info); |
463 | if (rv != 0) |
464 | continue; |
465 | |
466 | if (node_match_func(&a_node_info, &b_node_info)) { |
467 | found = true; |
468 | rel_info_func(&b_node_info); |
469 | break; |
470 | } |
471 | |
472 | rel_info_func(&b_node_info); |
473 | } |
474 | |
475 | rel_info_func(&a_node_info); |
476 | |
477 | if (!found) |
478 | func(a, a_node, name); |
479 | } |
480 | } |
481 | |
482 | static void notify_one(struct mdesc_notifier_client *p, |
483 | struct mdesc_handle *old_hp, |
484 | struct mdesc_handle *new_hp) |
485 | { |
486 | invoke_on_missing(name: p->node_name, a: old_hp, b: new_hp, func: p->remove); |
487 | invoke_on_missing(name: p->node_name, a: new_hp, b: old_hp, func: p->add); |
488 | } |
489 | |
490 | static void mdesc_notify_clients(struct mdesc_handle *old_hp, |
491 | struct mdesc_handle *new_hp) |
492 | { |
493 | struct mdesc_notifier_client *p = client_list; |
494 | |
495 | while (p) { |
496 | notify_one(p, old_hp, new_hp); |
497 | p = p->next; |
498 | } |
499 | } |
500 | |
501 | void mdesc_update(void) |
502 | { |
503 | unsigned long len, real_len, status; |
504 | struct mdesc_handle *hp, *orig_hp; |
505 | unsigned long flags; |
506 | |
507 | mutex_lock(&mdesc_mutex); |
508 | |
509 | (void) sun4v_mach_desc(0UL, 0UL, &len); |
510 | |
511 | hp = mdesc_alloc(mdesc_size: len, mops: &kmalloc_mdesc_memops); |
512 | if (!hp) { |
513 | printk(KERN_ERR "MD: mdesc alloc fails\n" ); |
514 | goto out; |
515 | } |
516 | |
517 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); |
518 | if (status != HV_EOK || real_len > len) { |
519 | printk(KERN_ERR "MD: mdesc reread fails with %lu\n" , |
520 | status); |
521 | refcount_dec(r: &hp->refcnt); |
522 | mdesc_free(hp); |
523 | goto out; |
524 | } |
525 | |
526 | spin_lock_irqsave(&mdesc_lock, flags); |
527 | orig_hp = cur_mdesc; |
528 | cur_mdesc = hp; |
529 | spin_unlock_irqrestore(lock: &mdesc_lock, flags); |
530 | |
531 | mdesc_notify_clients(old_hp: orig_hp, new_hp: hp); |
532 | |
533 | spin_lock_irqsave(&mdesc_lock, flags); |
534 | if (refcount_dec_and_test(r: &orig_hp->refcnt)) |
535 | mdesc_free(hp: orig_hp); |
536 | else |
537 | list_add(new: &orig_hp->list, head: &mdesc_zombie_list); |
538 | spin_unlock_irqrestore(lock: &mdesc_lock, flags); |
539 | |
540 | out: |
541 | mutex_unlock(lock: &mdesc_mutex); |
542 | } |
543 | |
544 | u64 mdesc_get_node(struct mdesc_handle *hp, const char *node_name, |
545 | union md_node_info *node_info) |
546 | { |
547 | mdesc_node_info_get_f get_info_func; |
548 | mdesc_node_info_rel_f rel_info_func; |
549 | mdesc_node_match_f node_match_func; |
550 | union md_node_info hp_node_info; |
551 | u64 hp_node; |
552 | int rv; |
553 | |
554 | if (hp == NULL || node_name == NULL || node_info == NULL) |
555 | return MDESC_NODE_NULL; |
556 | |
557 | /* Find the ops for the given node name */ |
558 | mdesc_get_node_ops(node_name, get_info_f: &get_info_func, rel_info_f: &rel_info_func, |
559 | match_f: &node_match_func); |
560 | |
561 | /* If we didn't find ops for the given node name, it is not supported */ |
562 | if (!get_info_func || !rel_info_func || !node_match_func) { |
563 | pr_err("MD: %s node is not supported\n" , node_name); |
564 | return -EINVAL; |
565 | } |
566 | |
567 | mdesc_for_each_node_by_name(hp, hp_node, node_name) { |
568 | rv = get_info_func(hp, hp_node, &hp_node_info); |
569 | if (rv != 0) |
570 | continue; |
571 | |
572 | if (node_match_func(node_info, &hp_node_info)) |
573 | break; |
574 | |
575 | rel_info_func(&hp_node_info); |
576 | } |
577 | |
578 | rel_info_func(&hp_node_info); |
579 | |
580 | return hp_node; |
581 | } |
582 | EXPORT_SYMBOL(mdesc_get_node); |
583 | |
584 | int mdesc_get_node_info(struct mdesc_handle *hp, u64 node, |
585 | const char *node_name, union md_node_info *node_info) |
586 | { |
587 | mdesc_node_info_get_f get_info_func; |
588 | int rv; |
589 | |
590 | if (hp == NULL || node == MDESC_NODE_NULL || |
591 | node_name == NULL || node_info == NULL) |
592 | return -EINVAL; |
593 | |
594 | /* Find the get_info op for the given node name */ |
595 | mdesc_get_node_ops(node_name, get_info_f: &get_info_func, NULL, NULL); |
596 | |
597 | /* If we didn't find a get_info_func, the node name is not supported */ |
598 | if (get_info_func == NULL) { |
599 | pr_err("MD: %s node is not supported\n" , node_name); |
600 | return -EINVAL; |
601 | } |
602 | |
603 | rv = get_info_func(hp, node, node_info); |
604 | if (rv != 0) { |
605 | pr_err("MD: Cannot find 1 or more required match properties for %s node.\n" , |
606 | node_name); |
607 | return -1; |
608 | } |
609 | |
610 | return 0; |
611 | } |
612 | EXPORT_SYMBOL(mdesc_get_node_info); |
613 | |
614 | static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) |
615 | { |
616 | return (struct mdesc_elem *) mdesc->data; |
617 | } |
618 | |
619 | static void *name_block(struct mdesc_hdr *mdesc) |
620 | { |
621 | return ((void *) node_block(mdesc)) + mdesc->node_sz; |
622 | } |
623 | |
624 | static void *data_block(struct mdesc_hdr *mdesc) |
625 | { |
626 | return ((void *) name_block(mdesc)) + mdesc->name_sz; |
627 | } |
628 | |
629 | u64 mdesc_node_by_name(struct mdesc_handle *hp, |
630 | u64 from_node, const char *name) |
631 | { |
632 | struct mdesc_elem *ep = node_block(mdesc: &hp->mdesc); |
633 | const char *names = name_block(mdesc: &hp->mdesc); |
634 | u64 last_node = hp->mdesc.node_sz / 16; |
635 | u64 ret; |
636 | |
637 | if (from_node == MDESC_NODE_NULL) { |
638 | ret = from_node = 0; |
639 | } else if (from_node >= last_node) { |
640 | return MDESC_NODE_NULL; |
641 | } else { |
642 | ret = ep[from_node].d.val; |
643 | } |
644 | |
645 | while (ret < last_node) { |
646 | if (ep[ret].tag != MD_NODE) |
647 | return MDESC_NODE_NULL; |
648 | if (!strcmp(names + ep[ret].name_offset, name)) |
649 | break; |
650 | ret = ep[ret].d.val; |
651 | } |
652 | if (ret >= last_node) |
653 | ret = MDESC_NODE_NULL; |
654 | return ret; |
655 | } |
656 | EXPORT_SYMBOL(mdesc_node_by_name); |
657 | |
658 | const void *mdesc_get_property(struct mdesc_handle *hp, u64 node, |
659 | const char *name, int *lenp) |
660 | { |
661 | const char *names = name_block(mdesc: &hp->mdesc); |
662 | u64 last_node = hp->mdesc.node_sz / 16; |
663 | void *data = data_block(mdesc: &hp->mdesc); |
664 | struct mdesc_elem *ep; |
665 | |
666 | if (node == MDESC_NODE_NULL || node >= last_node) |
667 | return NULL; |
668 | |
669 | ep = node_block(mdesc: &hp->mdesc) + node; |
670 | ep++; |
671 | for (; ep->tag != MD_NODE_END; ep++) { |
672 | void *val = NULL; |
673 | int len = 0; |
674 | |
675 | switch (ep->tag) { |
676 | case MD_PROP_VAL: |
677 | val = &ep->d.val; |
678 | len = 8; |
679 | break; |
680 | |
681 | case MD_PROP_STR: |
682 | case MD_PROP_DATA: |
683 | val = data + ep->d.data.data_offset; |
684 | len = ep->d.data.data_len; |
685 | break; |
686 | |
687 | default: |
688 | break; |
689 | } |
690 | if (!val) |
691 | continue; |
692 | |
693 | if (!strcmp(names + ep->name_offset, name)) { |
694 | if (lenp) |
695 | *lenp = len; |
696 | return val; |
697 | } |
698 | } |
699 | |
700 | return NULL; |
701 | } |
702 | EXPORT_SYMBOL(mdesc_get_property); |
703 | |
704 | u64 mdesc_next_arc(struct mdesc_handle *hp, u64 from, const char *arc_type) |
705 | { |
706 | struct mdesc_elem *ep, *base = node_block(mdesc: &hp->mdesc); |
707 | const char *names = name_block(mdesc: &hp->mdesc); |
708 | u64 last_node = hp->mdesc.node_sz / 16; |
709 | |
710 | if (from == MDESC_NODE_NULL || from >= last_node) |
711 | return MDESC_NODE_NULL; |
712 | |
713 | ep = base + from; |
714 | |
715 | ep++; |
716 | for (; ep->tag != MD_NODE_END; ep++) { |
717 | if (ep->tag != MD_PROP_ARC) |
718 | continue; |
719 | |
720 | if (strcmp(names + ep->name_offset, arc_type)) |
721 | continue; |
722 | |
723 | return ep - base; |
724 | } |
725 | |
726 | return MDESC_NODE_NULL; |
727 | } |
728 | EXPORT_SYMBOL(mdesc_next_arc); |
729 | |
730 | u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc) |
731 | { |
732 | struct mdesc_elem *ep, *base = node_block(mdesc: &hp->mdesc); |
733 | |
734 | ep = base + arc; |
735 | |
736 | return ep->d.val; |
737 | } |
738 | EXPORT_SYMBOL(mdesc_arc_target); |
739 | |
740 | const char *mdesc_node_name(struct mdesc_handle *hp, u64 node) |
741 | { |
742 | struct mdesc_elem *ep, *base = node_block(mdesc: &hp->mdesc); |
743 | const char *names = name_block(mdesc: &hp->mdesc); |
744 | u64 last_node = hp->mdesc.node_sz / 16; |
745 | |
746 | if (node == MDESC_NODE_NULL || node >= last_node) |
747 | return NULL; |
748 | |
749 | ep = base + node; |
750 | if (ep->tag != MD_NODE) |
751 | return NULL; |
752 | |
753 | return names + ep->name_offset; |
754 | } |
755 | EXPORT_SYMBOL(mdesc_node_name); |
756 | |
757 | static u64 max_cpus = 64; |
758 | |
759 | static void __init report_platform_properties(void) |
760 | { |
761 | struct mdesc_handle *hp = mdesc_grab(); |
762 | u64 pn = mdesc_node_by_name(hp, MDESC_NODE_NULL, "platform" ); |
763 | const char *s; |
764 | const u64 *v; |
765 | |
766 | if (pn == MDESC_NODE_NULL) { |
767 | prom_printf("No platform node in machine-description.\n" ); |
768 | prom_halt(); |
769 | } |
770 | |
771 | s = mdesc_get_property(hp, pn, "banner-name" , NULL); |
772 | printk("PLATFORM: banner-name [%s]\n" , s); |
773 | s = mdesc_get_property(hp, pn, "name" , NULL); |
774 | printk("PLATFORM: name [%s]\n" , s); |
775 | |
776 | v = mdesc_get_property(hp, pn, "hostid" , NULL); |
777 | if (v) |
778 | printk("PLATFORM: hostid [%08llx]\n" , *v); |
779 | v = mdesc_get_property(hp, pn, "serial#" , NULL); |
780 | if (v) |
781 | printk("PLATFORM: serial# [%08llx]\n" , *v); |
782 | v = mdesc_get_property(hp, pn, "stick-frequency" , NULL); |
783 | printk("PLATFORM: stick-frequency [%08llx]\n" , *v); |
784 | v = mdesc_get_property(hp, pn, "mac-address" , NULL); |
785 | if (v) |
786 | printk("PLATFORM: mac-address [%llx]\n" , *v); |
787 | v = mdesc_get_property(hp, pn, "watchdog-resolution" , NULL); |
788 | if (v) |
789 | printk("PLATFORM: watchdog-resolution [%llu ms]\n" , *v); |
790 | v = mdesc_get_property(hp, pn, "watchdog-max-timeout" , NULL); |
791 | if (v) |
792 | printk("PLATFORM: watchdog-max-timeout [%llu ms]\n" , *v); |
793 | v = mdesc_get_property(hp, pn, "max-cpus" , NULL); |
794 | if (v) { |
795 | max_cpus = *v; |
796 | printk("PLATFORM: max-cpus [%llu]\n" , max_cpus); |
797 | } |
798 | |
799 | #ifdef CONFIG_SMP |
800 | { |
801 | int max_cpu, i; |
802 | |
803 | if (v) { |
804 | max_cpu = *v; |
805 | if (max_cpu > NR_CPUS) |
806 | max_cpu = NR_CPUS; |
807 | } else { |
808 | max_cpu = NR_CPUS; |
809 | } |
810 | for (i = 0; i < max_cpu; i++) |
811 | set_cpu_possible(cpu: i, possible: true); |
812 | } |
813 | #endif |
814 | |
815 | mdesc_release(hp); |
816 | } |
817 | |
818 | static void fill_in_one_cache(cpuinfo_sparc *c, struct mdesc_handle *hp, u64 mp) |
819 | { |
820 | const u64 *level = mdesc_get_property(hp, mp, "level" , NULL); |
821 | const u64 *size = mdesc_get_property(hp, mp, "size" , NULL); |
822 | const u64 *line_size = mdesc_get_property(hp, mp, "line-size" , NULL); |
823 | const char *type; |
824 | int type_len; |
825 | |
826 | type = mdesc_get_property(hp, mp, "type" , &type_len); |
827 | |
828 | switch (*level) { |
829 | case 1: |
830 | if (of_find_in_proplist(type, "instn" , type_len)) { |
831 | c->icache_size = *size; |
832 | c->icache_line_size = *line_size; |
833 | } else if (of_find_in_proplist(type, "data" , type_len)) { |
834 | c->dcache_size = *size; |
835 | c->dcache_line_size = *line_size; |
836 | } |
837 | break; |
838 | |
839 | case 2: |
840 | c->ecache_size = *size; |
841 | c->ecache_line_size = *line_size; |
842 | break; |
843 | |
844 | default: |
845 | break; |
846 | } |
847 | |
848 | if (*level == 1) { |
849 | u64 a; |
850 | |
851 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { |
852 | u64 target = mdesc_arc_target(hp, a); |
853 | const char *name = mdesc_node_name(hp, target); |
854 | |
855 | if (!strcmp(name, "cache" )) |
856 | fill_in_one_cache(c, hp, target); |
857 | } |
858 | } |
859 | } |
860 | |
861 | static void find_back_node_value(struct mdesc_handle *hp, u64 node, |
862 | char *srch_val, |
863 | void (*func)(struct mdesc_handle *, u64, int), |
864 | u64 val, int depth) |
865 | { |
866 | u64 arc; |
867 | |
868 | /* Since we have an estimate of recursion depth, do a sanity check. */ |
869 | if (depth == 0) |
870 | return; |
871 | |
872 | mdesc_for_each_arc(arc, hp, node, MDESC_ARC_TYPE_BACK) { |
873 | u64 n = mdesc_arc_target(hp, arc); |
874 | const char *name = mdesc_node_name(hp, n); |
875 | |
876 | if (!strcmp(srch_val, name)) |
877 | (*func)(hp, n, val); |
878 | |
879 | find_back_node_value(hp, node: n, srch_val, func, val, depth: depth-1); |
880 | } |
881 | } |
882 | |
883 | static void __mark_core_id(struct mdesc_handle *hp, u64 node, |
884 | int core_id) |
885 | { |
886 | const u64 *id = mdesc_get_property(hp, node, "id" , NULL); |
887 | |
888 | if (*id < num_possible_cpus()) |
889 | cpu_data(*id).core_id = core_id; |
890 | } |
891 | |
892 | static void __mark_max_cache_id(struct mdesc_handle *hp, u64 node, |
893 | int max_cache_id) |
894 | { |
895 | const u64 *id = mdesc_get_property(hp, node, "id" , NULL); |
896 | |
897 | if (*id < num_possible_cpus()) { |
898 | cpu_data(*id).max_cache_id = max_cache_id; |
899 | |
900 | /** |
901 | * On systems without explicit socket descriptions socket |
902 | * is max_cache_id |
903 | */ |
904 | cpu_data(*id).sock_id = max_cache_id; |
905 | } |
906 | } |
907 | |
908 | static void mark_core_ids(struct mdesc_handle *hp, u64 mp, |
909 | int core_id) |
910 | { |
911 | find_back_node_value(hp, node: mp, srch_val: "cpu" , func: __mark_core_id, val: core_id, depth: 10); |
912 | } |
913 | |
914 | static void mark_max_cache_ids(struct mdesc_handle *hp, u64 mp, |
915 | int max_cache_id) |
916 | { |
917 | find_back_node_value(hp, node: mp, srch_val: "cpu" , func: __mark_max_cache_id, |
918 | val: max_cache_id, depth: 10); |
919 | } |
920 | |
921 | static void set_core_ids(struct mdesc_handle *hp) |
922 | { |
923 | int idx; |
924 | u64 mp; |
925 | |
926 | idx = 1; |
927 | |
928 | /* Identify unique cores by looking for cpus backpointed to by |
929 | * level 1 instruction caches. |
930 | */ |
931 | mdesc_for_each_node_by_name(hp, mp, "cache" ) { |
932 | const u64 *level; |
933 | const char *type; |
934 | int len; |
935 | |
936 | level = mdesc_get_property(hp, mp, "level" , NULL); |
937 | if (*level != 1) |
938 | continue; |
939 | |
940 | type = mdesc_get_property(hp, mp, "type" , &len); |
941 | if (!of_find_in_proplist(type, "instn" , len)) |
942 | continue; |
943 | |
944 | mark_core_ids(hp, mp, core_id: idx); |
945 | idx++; |
946 | } |
947 | } |
948 | |
949 | static int set_max_cache_ids_by_cache(struct mdesc_handle *hp, int level) |
950 | { |
951 | u64 mp; |
952 | int idx = 1; |
953 | int fnd = 0; |
954 | |
955 | /** |
956 | * Identify unique highest level of shared cache by looking for cpus |
957 | * backpointed to by shared level N caches. |
958 | */ |
959 | mdesc_for_each_node_by_name(hp, mp, "cache" ) { |
960 | const u64 *cur_lvl; |
961 | |
962 | cur_lvl = mdesc_get_property(hp, mp, "level" , NULL); |
963 | if (*cur_lvl != level) |
964 | continue; |
965 | mark_max_cache_ids(hp, mp, max_cache_id: idx); |
966 | idx++; |
967 | fnd = 1; |
968 | } |
969 | return fnd; |
970 | } |
971 | |
972 | static void set_sock_ids_by_socket(struct mdesc_handle *hp, u64 mp) |
973 | { |
974 | int idx = 1; |
975 | |
976 | mdesc_for_each_node_by_name(hp, mp, "socket" ) { |
977 | u64 a; |
978 | |
979 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { |
980 | u64 t = mdesc_arc_target(hp, a); |
981 | const char *name; |
982 | const u64 *id; |
983 | |
984 | name = mdesc_node_name(hp, t); |
985 | if (strcmp(name, "cpu" )) |
986 | continue; |
987 | |
988 | id = mdesc_get_property(hp, t, "id" , NULL); |
989 | if (*id < num_possible_cpus()) |
990 | cpu_data(*id).sock_id = idx; |
991 | } |
992 | idx++; |
993 | } |
994 | } |
995 | |
996 | static void set_sock_ids(struct mdesc_handle *hp) |
997 | { |
998 | u64 mp; |
999 | |
1000 | /** |
1001 | * Find the highest level of shared cache which pre-T7 is also |
1002 | * the socket. |
1003 | */ |
1004 | if (!set_max_cache_ids_by_cache(hp, level: 3)) |
1005 | set_max_cache_ids_by_cache(hp, level: 2); |
1006 | |
1007 | /* If machine description exposes sockets data use it.*/ |
1008 | mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "sockets" ); |
1009 | if (mp != MDESC_NODE_NULL) |
1010 | set_sock_ids_by_socket(hp, mp); |
1011 | } |
1012 | |
1013 | static void mark_proc_ids(struct mdesc_handle *hp, u64 mp, int proc_id) |
1014 | { |
1015 | u64 a; |
1016 | |
1017 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) { |
1018 | u64 t = mdesc_arc_target(hp, a); |
1019 | const char *name; |
1020 | const u64 *id; |
1021 | |
1022 | name = mdesc_node_name(hp, t); |
1023 | if (strcmp(name, "cpu" )) |
1024 | continue; |
1025 | |
1026 | id = mdesc_get_property(hp, t, "id" , NULL); |
1027 | if (*id < NR_CPUS) |
1028 | cpu_data(*id).proc_id = proc_id; |
1029 | } |
1030 | } |
1031 | |
1032 | static void __set_proc_ids(struct mdesc_handle *hp, const char *exec_unit_name) |
1033 | { |
1034 | int idx; |
1035 | u64 mp; |
1036 | |
1037 | idx = 0; |
1038 | mdesc_for_each_node_by_name(hp, mp, exec_unit_name) { |
1039 | const char *type; |
1040 | int len; |
1041 | |
1042 | type = mdesc_get_property(hp, mp, "type" , &len); |
1043 | if (!of_find_in_proplist(type, "int" , len) && |
1044 | !of_find_in_proplist(type, "integer" , len)) |
1045 | continue; |
1046 | |
1047 | mark_proc_ids(hp, mp, proc_id: idx); |
1048 | idx++; |
1049 | } |
1050 | } |
1051 | |
1052 | static void set_proc_ids(struct mdesc_handle *hp) |
1053 | { |
1054 | __set_proc_ids(hp, exec_unit_name: "exec_unit" ); |
1055 | __set_proc_ids(hp, exec_unit_name: "exec-unit" ); |
1056 | } |
1057 | |
1058 | static void get_one_mondo_bits(const u64 *p, unsigned int *mask, |
1059 | unsigned long def, unsigned long max) |
1060 | { |
1061 | u64 val; |
1062 | |
1063 | if (!p) |
1064 | goto use_default; |
1065 | val = *p; |
1066 | |
1067 | if (!val || val >= 64) |
1068 | goto use_default; |
1069 | |
1070 | if (val > max) |
1071 | val = max; |
1072 | |
1073 | *mask = ((1U << val) * 64U) - 1U; |
1074 | return; |
1075 | |
1076 | use_default: |
1077 | *mask = ((1U << def) * 64U) - 1U; |
1078 | } |
1079 | |
1080 | static void get_mondo_data(struct mdesc_handle *hp, u64 mp, |
1081 | struct trap_per_cpu *tb) |
1082 | { |
1083 | static int printed; |
1084 | const u64 *val; |
1085 | |
1086 | val = mdesc_get_property(hp, mp, "q-cpu-mondo-#bits" , NULL); |
1087 | get_one_mondo_bits(p: val, mask: &tb->cpu_mondo_qmask, def: 7, ilog2(max_cpus * 2)); |
1088 | |
1089 | val = mdesc_get_property(hp, mp, "q-dev-mondo-#bits" , NULL); |
1090 | get_one_mondo_bits(p: val, mask: &tb->dev_mondo_qmask, def: 7, max: 8); |
1091 | |
1092 | val = mdesc_get_property(hp, mp, "q-resumable-#bits" , NULL); |
1093 | get_one_mondo_bits(p: val, mask: &tb->resum_qmask, def: 6, max: 7); |
1094 | |
1095 | val = mdesc_get_property(hp, mp, "q-nonresumable-#bits" , NULL); |
1096 | get_one_mondo_bits(p: val, mask: &tb->nonresum_qmask, def: 2, max: 2); |
1097 | if (!printed++) { |
1098 | pr_info("SUN4V: Mondo queue sizes " |
1099 | "[cpu(%u) dev(%u) r(%u) nr(%u)]\n" , |
1100 | tb->cpu_mondo_qmask + 1, |
1101 | tb->dev_mondo_qmask + 1, |
1102 | tb->resum_qmask + 1, |
1103 | tb->nonresum_qmask + 1); |
1104 | } |
1105 | } |
1106 | |
1107 | static void *mdesc_iterate_over_cpus(void *(*func)(struct mdesc_handle *, u64, int, void *), void *arg, cpumask_t *mask) |
1108 | { |
1109 | struct mdesc_handle *hp = mdesc_grab(); |
1110 | void *ret = NULL; |
1111 | u64 mp; |
1112 | |
1113 | mdesc_for_each_node_by_name(hp, mp, "cpu" ) { |
1114 | const u64 *id = mdesc_get_property(hp, mp, "id" , NULL); |
1115 | int cpuid = *id; |
1116 | |
1117 | #ifdef CONFIG_SMP |
1118 | if (cpuid >= NR_CPUS) { |
1119 | printk(KERN_WARNING "Ignoring CPU %d which is " |
1120 | ">= NR_CPUS (%d)\n" , |
1121 | cpuid, NR_CPUS); |
1122 | continue; |
1123 | } |
1124 | if (!cpumask_test_cpu(cpuid, mask)) |
1125 | continue; |
1126 | #endif |
1127 | |
1128 | ret = func(hp, mp, cpuid, arg); |
1129 | if (ret) |
1130 | goto out; |
1131 | } |
1132 | out: |
1133 | mdesc_release(hp); |
1134 | return ret; |
1135 | } |
1136 | |
1137 | static void *record_one_cpu(struct mdesc_handle *hp, u64 mp, int cpuid, |
1138 | void *arg) |
1139 | { |
1140 | ncpus_probed++; |
1141 | #ifdef CONFIG_SMP |
1142 | set_cpu_present(cpu: cpuid, present: true); |
1143 | #endif |
1144 | return NULL; |
1145 | } |
1146 | |
1147 | void mdesc_populate_present_mask(cpumask_t *mask) |
1148 | { |
1149 | if (tlb_type != hypervisor) |
1150 | return; |
1151 | |
1152 | ncpus_probed = 0; |
1153 | mdesc_iterate_over_cpus(func: record_one_cpu, NULL, mask); |
1154 | } |
1155 | |
1156 | static void * __init check_one_pgsz(struct mdesc_handle *hp, u64 mp, int cpuid, void *arg) |
1157 | { |
1158 | const u64 *pgsz_prop = mdesc_get_property(hp, mp, "mmu-page-size-list" , NULL); |
1159 | unsigned long *pgsz_mask = arg; |
1160 | u64 val; |
1161 | |
1162 | val = (HV_PGSZ_MASK_8K | HV_PGSZ_MASK_64K | |
1163 | HV_PGSZ_MASK_512K | HV_PGSZ_MASK_4MB); |
1164 | if (pgsz_prop) |
1165 | val = *pgsz_prop; |
1166 | |
1167 | if (!*pgsz_mask) |
1168 | *pgsz_mask = val; |
1169 | else |
1170 | *pgsz_mask &= val; |
1171 | return NULL; |
1172 | } |
1173 | |
1174 | void __init mdesc_get_page_sizes(cpumask_t *mask, unsigned long *pgsz_mask) |
1175 | { |
1176 | *pgsz_mask = 0; |
1177 | mdesc_iterate_over_cpus(func: check_one_pgsz, arg: pgsz_mask, mask); |
1178 | } |
1179 | |
1180 | static void *fill_in_one_cpu(struct mdesc_handle *hp, u64 mp, int cpuid, |
1181 | void *arg) |
1182 | { |
1183 | const u64 *cfreq = mdesc_get_property(hp, mp, "clock-frequency" , NULL); |
1184 | struct trap_per_cpu *tb; |
1185 | cpuinfo_sparc *c; |
1186 | u64 a; |
1187 | |
1188 | #ifndef CONFIG_SMP |
1189 | /* On uniprocessor we only want the values for the |
1190 | * real physical cpu the kernel booted onto, however |
1191 | * cpu_data() only has one entry at index 0. |
1192 | */ |
1193 | if (cpuid != real_hard_smp_processor_id()) |
1194 | return NULL; |
1195 | cpuid = 0; |
1196 | #endif |
1197 | |
1198 | c = &cpu_data(cpuid); |
1199 | c->clock_tick = *cfreq; |
1200 | |
1201 | tb = &trap_block[cpuid]; |
1202 | get_mondo_data(hp, mp, tb); |
1203 | |
1204 | mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) { |
1205 | u64 j, t = mdesc_arc_target(hp, a); |
1206 | const char *t_name; |
1207 | |
1208 | t_name = mdesc_node_name(hp, t); |
1209 | if (!strcmp(t_name, "cache" )) { |
1210 | fill_in_one_cache(c, hp, t); |
1211 | continue; |
1212 | } |
1213 | |
1214 | mdesc_for_each_arc(j, hp, t, MDESC_ARC_TYPE_FWD) { |
1215 | u64 n = mdesc_arc_target(hp, j); |
1216 | const char *n_name; |
1217 | |
1218 | n_name = mdesc_node_name(hp, n); |
1219 | if (!strcmp(n_name, "cache" )) |
1220 | fill_in_one_cache(c, hp, n); |
1221 | } |
1222 | } |
1223 | |
1224 | c->core_id = 0; |
1225 | c->proc_id = -1; |
1226 | |
1227 | return NULL; |
1228 | } |
1229 | |
1230 | void mdesc_fill_in_cpu_data(cpumask_t *mask) |
1231 | { |
1232 | struct mdesc_handle *hp; |
1233 | |
1234 | mdesc_iterate_over_cpus(func: fill_in_one_cpu, NULL, mask); |
1235 | |
1236 | hp = mdesc_grab(); |
1237 | |
1238 | set_core_ids(hp); |
1239 | set_proc_ids(hp); |
1240 | set_sock_ids(hp); |
1241 | |
1242 | mdesc_release(hp); |
1243 | |
1244 | smp_fill_in_sib_core_maps(); |
1245 | } |
1246 | |
1247 | /* mdesc_open() - Grab a reference to mdesc_handle when /dev/mdesc is |
1248 | * opened. Hold this reference until /dev/mdesc is closed to ensure |
1249 | * mdesc data structure is not released underneath us. Store the |
1250 | * pointer to mdesc structure in private_data for read and seek to use |
1251 | */ |
1252 | static int mdesc_open(struct inode *inode, struct file *file) |
1253 | { |
1254 | struct mdesc_handle *hp = mdesc_grab(); |
1255 | |
1256 | if (!hp) |
1257 | return -ENODEV; |
1258 | |
1259 | file->private_data = hp; |
1260 | |
1261 | return 0; |
1262 | } |
1263 | |
1264 | static ssize_t mdesc_read(struct file *file, char __user *buf, |
1265 | size_t len, loff_t *offp) |
1266 | { |
1267 | struct mdesc_handle *hp = file->private_data; |
1268 | unsigned char *mdesc; |
1269 | int bytes_left, count = len; |
1270 | |
1271 | if (*offp >= hp->handle_size) |
1272 | return 0; |
1273 | |
1274 | bytes_left = hp->handle_size - *offp; |
1275 | if (count > bytes_left) |
1276 | count = bytes_left; |
1277 | |
1278 | mdesc = (unsigned char *)&hp->mdesc; |
1279 | mdesc += *offp; |
1280 | if (!copy_to_user(to: buf, from: mdesc, n: count)) { |
1281 | *offp += count; |
1282 | return count; |
1283 | } else { |
1284 | return -EFAULT; |
1285 | } |
1286 | } |
1287 | |
1288 | static loff_t mdesc_llseek(struct file *file, loff_t offset, int whence) |
1289 | { |
1290 | struct mdesc_handle *hp = file->private_data; |
1291 | |
1292 | return no_seek_end_llseek_size(file, offset, whence, hp->handle_size); |
1293 | } |
1294 | |
1295 | /* mdesc_close() - /dev/mdesc is being closed, release the reference to |
1296 | * mdesc structure. |
1297 | */ |
1298 | static int mdesc_close(struct inode *inode, struct file *file) |
1299 | { |
1300 | mdesc_release(file->private_data); |
1301 | return 0; |
1302 | } |
1303 | |
1304 | static const struct file_operations mdesc_fops = { |
1305 | .open = mdesc_open, |
1306 | .read = mdesc_read, |
1307 | .llseek = mdesc_llseek, |
1308 | .release = mdesc_close, |
1309 | .owner = THIS_MODULE, |
1310 | }; |
1311 | |
1312 | static struct miscdevice mdesc_misc = { |
1313 | .minor = MISC_DYNAMIC_MINOR, |
1314 | .name = "mdesc" , |
1315 | .fops = &mdesc_fops, |
1316 | }; |
1317 | |
1318 | static int __init mdesc_misc_init(void) |
1319 | { |
1320 | return misc_register(misc: &mdesc_misc); |
1321 | } |
1322 | |
1323 | __initcall(mdesc_misc_init); |
1324 | |
1325 | void __init sun4v_mdesc_init(void) |
1326 | { |
1327 | struct mdesc_handle *hp; |
1328 | unsigned long len, real_len, status; |
1329 | |
1330 | (void) sun4v_mach_desc(0UL, 0UL, &len); |
1331 | |
1332 | printk("MDESC: Size is %lu bytes.\n" , len); |
1333 | |
1334 | hp = mdesc_alloc(mdesc_size: len, mops: &memblock_mdesc_ops); |
1335 | if (hp == NULL) { |
1336 | prom_printf("MDESC: alloc of %lu bytes failed.\n" , len); |
1337 | prom_halt(); |
1338 | } |
1339 | |
1340 | status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); |
1341 | if (status != HV_EOK || real_len > len) { |
1342 | prom_printf("sun4v_mach_desc fails, err(%lu), " |
1343 | "len(%lu), real_len(%lu)\n" , |
1344 | status, len, real_len); |
1345 | mdesc_free(hp); |
1346 | prom_halt(); |
1347 | } |
1348 | |
1349 | cur_mdesc = hp; |
1350 | |
1351 | mdesc_adi_init(); |
1352 | report_platform_properties(); |
1353 | } |
1354 | |