1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * copyright (c) 2006 IBM Corporation |
4 | * Authored by: Mike D. Day <ncmike@us.ibm.com> |
5 | */ |
6 | |
7 | #include <linux/slab.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/init.h> |
10 | #include <linux/kobject.h> |
11 | #include <linux/err.h> |
12 | |
13 | #include <asm/xen/hypervisor.h> |
14 | #include <asm/xen/hypercall.h> |
15 | |
16 | #include <xen/xen.h> |
17 | #include <xen/xenbus.h> |
18 | #include <xen/interface/xen.h> |
19 | #include <xen/interface/version.h> |
20 | #ifdef CONFIG_XEN_HAVE_VPMU |
21 | #include <xen/interface/xenpmu.h> |
22 | #endif |
23 | |
24 | #define HYPERVISOR_ATTR_RO(_name) \ |
25 | static struct hyp_sysfs_attr _name##_attr = __ATTR_RO(_name) |
26 | |
27 | #define HYPERVISOR_ATTR_RW(_name) \ |
28 | static struct hyp_sysfs_attr _name##_attr = __ATTR_RW(_name) |
29 | |
30 | struct hyp_sysfs_attr { |
31 | struct attribute attr; |
32 | ssize_t (*show)(struct hyp_sysfs_attr *, char *); |
33 | ssize_t (*store)(struct hyp_sysfs_attr *, const char *, size_t); |
34 | union { |
35 | void *hyp_attr_data; |
36 | unsigned long hyp_attr_value; |
37 | }; |
38 | }; |
39 | |
40 | static ssize_t type_show(struct hyp_sysfs_attr *attr, char *buffer) |
41 | { |
42 | return sprintf(buf: buffer, fmt: "xen\n" ); |
43 | } |
44 | |
45 | HYPERVISOR_ATTR_RO(type); |
46 | |
47 | static int __init xen_sysfs_type_init(void) |
48 | { |
49 | return sysfs_create_file(kobj: hypervisor_kobj, attr: &type_attr.attr); |
50 | } |
51 | |
52 | static ssize_t guest_type_show(struct hyp_sysfs_attr *attr, char *buffer) |
53 | { |
54 | const char *type; |
55 | |
56 | switch (xen_domain_type) { |
57 | case XEN_NATIVE: |
58 | /* ARM only. */ |
59 | type = "Xen" ; |
60 | break; |
61 | case XEN_PV_DOMAIN: |
62 | type = "PV" ; |
63 | break; |
64 | case XEN_HVM_DOMAIN: |
65 | type = xen_pvh_domain() ? "PVH" : "HVM" ; |
66 | break; |
67 | default: |
68 | return -EINVAL; |
69 | } |
70 | |
71 | return sprintf(buf: buffer, fmt: "%s\n" , type); |
72 | } |
73 | |
74 | HYPERVISOR_ATTR_RO(guest_type); |
75 | |
76 | static int __init xen_sysfs_guest_type_init(void) |
77 | { |
78 | return sysfs_create_file(kobj: hypervisor_kobj, attr: &guest_type_attr.attr); |
79 | } |
80 | |
81 | /* xen version attributes */ |
82 | static ssize_t major_show(struct hyp_sysfs_attr *attr, char *buffer) |
83 | { |
84 | int version = HYPERVISOR_xen_version(XENVER_version, NULL); |
85 | if (version) |
86 | return sprintf(buf: buffer, fmt: "%d\n" , version >> 16); |
87 | return -ENODEV; |
88 | } |
89 | |
90 | HYPERVISOR_ATTR_RO(major); |
91 | |
92 | static ssize_t minor_show(struct hyp_sysfs_attr *attr, char *buffer) |
93 | { |
94 | int version = HYPERVISOR_xen_version(XENVER_version, NULL); |
95 | if (version) |
96 | return sprintf(buf: buffer, fmt: "%d\n" , version & 0xff); |
97 | return -ENODEV; |
98 | } |
99 | |
100 | HYPERVISOR_ATTR_RO(minor); |
101 | |
102 | static ssize_t (struct hyp_sysfs_attr *attr, char *buffer) |
103 | { |
104 | int ret = -ENOMEM; |
105 | char *; |
106 | |
107 | extra = kmalloc(XEN_EXTRAVERSION_LEN, GFP_KERNEL); |
108 | if (extra) { |
109 | ret = HYPERVISOR_xen_version(XENVER_extraversion, arg: extra); |
110 | if (!ret) |
111 | ret = sprintf(buf: buffer, fmt: "%s\n" , extra); |
112 | kfree(objp: extra); |
113 | } |
114 | |
115 | return ret; |
116 | } |
117 | |
118 | HYPERVISOR_ATTR_RO(extra); |
119 | |
120 | static struct attribute *version_attrs[] = { |
121 | &major_attr.attr, |
122 | &minor_attr.attr, |
123 | &extra_attr.attr, |
124 | NULL |
125 | }; |
126 | |
127 | static const struct attribute_group version_group = { |
128 | .name = "version" , |
129 | .attrs = version_attrs, |
130 | }; |
131 | |
132 | static int __init xen_sysfs_version_init(void) |
133 | { |
134 | return sysfs_create_group(kobj: hypervisor_kobj, grp: &version_group); |
135 | } |
136 | |
137 | /* UUID */ |
138 | |
139 | static ssize_t uuid_show_fallback(struct hyp_sysfs_attr *attr, char *buffer) |
140 | { |
141 | char *vm, *val; |
142 | int ret; |
143 | extern int xenstored_ready; |
144 | |
145 | if (!xenstored_ready) |
146 | return -EBUSY; |
147 | |
148 | vm = xenbus_read(XBT_NIL, dir: "vm" , node: "" , NULL); |
149 | if (IS_ERR(vm)) |
150 | return PTR_ERR(vm); |
151 | val = xenbus_read(XBT_NIL, vm, "uuid" , NULL); |
152 | kfree(vm); |
153 | if (IS_ERR(val)) |
154 | return PTR_ERR(val); |
155 | ret = sprintf(buffer, "%s\n" , val); |
156 | kfree(val); |
157 | return ret; |
158 | } |
159 | |
160 | static ssize_t uuid_show(struct hyp_sysfs_attr *attr, char *buffer) |
161 | { |
162 | xen_domain_handle_t uuid; |
163 | int ret; |
164 | ret = HYPERVISOR_xen_version(XENVER_guest_handle, arg: uuid); |
165 | if (ret) |
166 | return uuid_show_fallback(attr, buffer); |
167 | ret = sprintf(buf: buffer, fmt: "%pU\n" , uuid); |
168 | return ret; |
169 | } |
170 | |
171 | HYPERVISOR_ATTR_RO(uuid); |
172 | |
173 | static int __init xen_sysfs_uuid_init(void) |
174 | { |
175 | return sysfs_create_file(kobj: hypervisor_kobj, attr: &uuid_attr.attr); |
176 | } |
177 | |
178 | /* xen compilation attributes */ |
179 | |
180 | static ssize_t compiler_show(struct hyp_sysfs_attr *attr, char *buffer) |
181 | { |
182 | int ret = -ENOMEM; |
183 | struct xen_compile_info *info; |
184 | |
185 | info = kmalloc(size: sizeof(struct xen_compile_info), GFP_KERNEL); |
186 | if (info) { |
187 | ret = HYPERVISOR_xen_version(XENVER_compile_info, arg: info); |
188 | if (!ret) |
189 | ret = sprintf(buf: buffer, fmt: "%s\n" , info->compiler); |
190 | kfree(objp: info); |
191 | } |
192 | |
193 | return ret; |
194 | } |
195 | |
196 | HYPERVISOR_ATTR_RO(compiler); |
197 | |
198 | static ssize_t compiled_by_show(struct hyp_sysfs_attr *attr, char *buffer) |
199 | { |
200 | int ret = -ENOMEM; |
201 | struct xen_compile_info *info; |
202 | |
203 | info = kmalloc(size: sizeof(struct xen_compile_info), GFP_KERNEL); |
204 | if (info) { |
205 | ret = HYPERVISOR_xen_version(XENVER_compile_info, arg: info); |
206 | if (!ret) |
207 | ret = sprintf(buf: buffer, fmt: "%s\n" , info->compile_by); |
208 | kfree(objp: info); |
209 | } |
210 | |
211 | return ret; |
212 | } |
213 | |
214 | HYPERVISOR_ATTR_RO(compiled_by); |
215 | |
216 | static ssize_t compile_date_show(struct hyp_sysfs_attr *attr, char *buffer) |
217 | { |
218 | int ret = -ENOMEM; |
219 | struct xen_compile_info *info; |
220 | |
221 | info = kmalloc(size: sizeof(struct xen_compile_info), GFP_KERNEL); |
222 | if (info) { |
223 | ret = HYPERVISOR_xen_version(XENVER_compile_info, arg: info); |
224 | if (!ret) |
225 | ret = sprintf(buf: buffer, fmt: "%s\n" , info->compile_date); |
226 | kfree(objp: info); |
227 | } |
228 | |
229 | return ret; |
230 | } |
231 | |
232 | HYPERVISOR_ATTR_RO(compile_date); |
233 | |
234 | static struct attribute *xen_compile_attrs[] = { |
235 | &compiler_attr.attr, |
236 | &compiled_by_attr.attr, |
237 | &compile_date_attr.attr, |
238 | NULL |
239 | }; |
240 | |
241 | static const struct attribute_group xen_compilation_group = { |
242 | .name = "compilation" , |
243 | .attrs = xen_compile_attrs, |
244 | }; |
245 | |
246 | static int __init xen_sysfs_compilation_init(void) |
247 | { |
248 | return sysfs_create_group(kobj: hypervisor_kobj, grp: &xen_compilation_group); |
249 | } |
250 | |
251 | /* xen properties info */ |
252 | |
253 | static ssize_t capabilities_show(struct hyp_sysfs_attr *attr, char *buffer) |
254 | { |
255 | int ret = -ENOMEM; |
256 | char *caps; |
257 | |
258 | caps = kmalloc(XEN_CAPABILITIES_INFO_LEN, GFP_KERNEL); |
259 | if (caps) { |
260 | ret = HYPERVISOR_xen_version(XENVER_capabilities, arg: caps); |
261 | if (!ret) |
262 | ret = sprintf(buf: buffer, fmt: "%s\n" , caps); |
263 | kfree(objp: caps); |
264 | } |
265 | |
266 | return ret; |
267 | } |
268 | |
269 | HYPERVISOR_ATTR_RO(capabilities); |
270 | |
271 | static ssize_t changeset_show(struct hyp_sysfs_attr *attr, char *buffer) |
272 | { |
273 | int ret = -ENOMEM; |
274 | char *cset; |
275 | |
276 | cset = kmalloc(XEN_CHANGESET_INFO_LEN, GFP_KERNEL); |
277 | if (cset) { |
278 | ret = HYPERVISOR_xen_version(XENVER_changeset, arg: cset); |
279 | if (!ret) |
280 | ret = sprintf(buf: buffer, fmt: "%s\n" , cset); |
281 | kfree(objp: cset); |
282 | } |
283 | |
284 | return ret; |
285 | } |
286 | |
287 | HYPERVISOR_ATTR_RO(changeset); |
288 | |
289 | static ssize_t virtual_start_show(struct hyp_sysfs_attr *attr, char *buffer) |
290 | { |
291 | int ret = -ENOMEM; |
292 | struct xen_platform_parameters *parms; |
293 | |
294 | parms = kmalloc(size: sizeof(struct xen_platform_parameters), GFP_KERNEL); |
295 | if (parms) { |
296 | ret = HYPERVISOR_xen_version(XENVER_platform_parameters, |
297 | arg: parms); |
298 | if (!ret) |
299 | ret = sprintf(buf: buffer, fmt: "%" PRI_xen_ulong"\n" , |
300 | parms->virt_start); |
301 | kfree(objp: parms); |
302 | } |
303 | |
304 | return ret; |
305 | } |
306 | |
307 | HYPERVISOR_ATTR_RO(virtual_start); |
308 | |
309 | static ssize_t pagesize_show(struct hyp_sysfs_attr *attr, char *buffer) |
310 | { |
311 | int ret; |
312 | |
313 | ret = HYPERVISOR_xen_version(XENVER_pagesize, NULL); |
314 | if (ret > 0) |
315 | ret = sprintf(buf: buffer, fmt: "%x\n" , ret); |
316 | |
317 | return ret; |
318 | } |
319 | |
320 | HYPERVISOR_ATTR_RO(pagesize); |
321 | |
322 | static ssize_t xen_feature_show(int index, char *buffer) |
323 | { |
324 | ssize_t ret; |
325 | struct xen_feature_info info; |
326 | |
327 | info.submap_idx = index; |
328 | ret = HYPERVISOR_xen_version(XENVER_get_features, arg: &info); |
329 | if (!ret) |
330 | ret = sprintf(buf: buffer, fmt: "%08x" , info.submap); |
331 | |
332 | return ret; |
333 | } |
334 | |
335 | static ssize_t features_show(struct hyp_sysfs_attr *attr, char *buffer) |
336 | { |
337 | ssize_t len; |
338 | int i; |
339 | |
340 | len = 0; |
341 | for (i = XENFEAT_NR_SUBMAPS-1; i >= 0; i--) { |
342 | int ret = xen_feature_show(index: i, buffer: buffer + len); |
343 | if (ret < 0) { |
344 | if (len == 0) |
345 | len = ret; |
346 | break; |
347 | } |
348 | len += ret; |
349 | } |
350 | if (len > 0) |
351 | buffer[len++] = '\n'; |
352 | |
353 | return len; |
354 | } |
355 | |
356 | HYPERVISOR_ATTR_RO(features); |
357 | |
358 | static ssize_t buildid_show(struct hyp_sysfs_attr *attr, char *buffer) |
359 | { |
360 | ssize_t ret; |
361 | struct xen_build_id *buildid; |
362 | |
363 | ret = HYPERVISOR_xen_version(XENVER_build_id, NULL); |
364 | if (ret < 0) { |
365 | if (ret == -EPERM) |
366 | ret = sprintf(buf: buffer, fmt: "<denied>" ); |
367 | return ret; |
368 | } |
369 | |
370 | buildid = kmalloc(size: sizeof(*buildid) + ret, GFP_KERNEL); |
371 | if (!buildid) |
372 | return -ENOMEM; |
373 | |
374 | buildid->len = ret; |
375 | ret = HYPERVISOR_xen_version(XENVER_build_id, arg: buildid); |
376 | if (ret > 0) |
377 | ret = sprintf(buf: buffer, fmt: "%s" , buildid->buf); |
378 | kfree(objp: buildid); |
379 | |
380 | return ret; |
381 | } |
382 | |
383 | HYPERVISOR_ATTR_RO(buildid); |
384 | |
385 | static struct attribute *xen_properties_attrs[] = { |
386 | &capabilities_attr.attr, |
387 | &changeset_attr.attr, |
388 | &virtual_start_attr.attr, |
389 | &pagesize_attr.attr, |
390 | &features_attr.attr, |
391 | &buildid_attr.attr, |
392 | NULL |
393 | }; |
394 | |
395 | static const struct attribute_group xen_properties_group = { |
396 | .name = "properties" , |
397 | .attrs = xen_properties_attrs, |
398 | }; |
399 | |
400 | static int __init xen_sysfs_properties_init(void) |
401 | { |
402 | return sysfs_create_group(kobj: hypervisor_kobj, grp: &xen_properties_group); |
403 | } |
404 | |
405 | #define FLAG_UNAME "unknown" |
406 | #define FLAG_UNAME_FMT FLAG_UNAME "%02u" |
407 | #define FLAG_UNAME_MAX sizeof(FLAG_UNAME "XX") |
408 | #define FLAG_COUNT (sizeof(xen_start_flags) * BITS_PER_BYTE) |
409 | static_assert(sizeof(xen_start_flags) <= |
410 | sizeof_field(struct hyp_sysfs_attr, hyp_attr_value)); |
411 | |
412 | static ssize_t flag_show(struct hyp_sysfs_attr *attr, char *buffer) |
413 | { |
414 | char *p = buffer; |
415 | |
416 | *p++ = '0' + ((xen_start_flags & attr->hyp_attr_value) != 0); |
417 | *p++ = '\n'; |
418 | return p - buffer; |
419 | } |
420 | |
421 | #define FLAG_NODE(flag, node) \ |
422 | [ilog2(flag)] = { \ |
423 | .attr = { .name = #node, .mode = 0444 },\ |
424 | .show = flag_show, \ |
425 | .hyp_attr_value = flag \ |
426 | } |
427 | |
428 | /* |
429 | * Add new, known flags here. No other changes are required, but |
430 | * note that each known flag wastes one entry in flag_unames[]. |
431 | * The code/complexity machinations to avoid this isn't worth it |
432 | * for a few entries, but keep it in mind. |
433 | */ |
434 | static struct hyp_sysfs_attr flag_attrs[FLAG_COUNT] = { |
435 | FLAG_NODE(SIF_PRIVILEGED, privileged), |
436 | FLAG_NODE(SIF_INITDOMAIN, initdomain) |
437 | }; |
438 | static struct attribute_group xen_flags_group = { |
439 | .name = "start_flags" , |
440 | .attrs = (struct attribute *[FLAG_COUNT + 1]){} |
441 | }; |
442 | static char flag_unames[FLAG_COUNT][FLAG_UNAME_MAX]; |
443 | |
444 | static int __init xen_sysfs_flags_init(void) |
445 | { |
446 | for (unsigned fnum = 0; fnum != FLAG_COUNT; fnum++) { |
447 | if (likely(flag_attrs[fnum].attr.name == NULL)) { |
448 | sprintf(buf: flag_unames[fnum], FLAG_UNAME_FMT, fnum); |
449 | flag_attrs[fnum].attr.name = flag_unames[fnum]; |
450 | flag_attrs[fnum].attr.mode = 0444; |
451 | flag_attrs[fnum].show = flag_show; |
452 | flag_attrs[fnum].hyp_attr_value = 1 << fnum; |
453 | } |
454 | xen_flags_group.attrs[fnum] = &flag_attrs[fnum].attr; |
455 | } |
456 | return sysfs_create_group(kobj: hypervisor_kobj, grp: &xen_flags_group); |
457 | } |
458 | |
459 | #ifdef CONFIG_XEN_HAVE_VPMU |
460 | struct pmu_mode { |
461 | const char *name; |
462 | uint32_t mode; |
463 | }; |
464 | |
465 | static struct pmu_mode pmu_modes[] = { |
466 | {"off" , XENPMU_MODE_OFF}, |
467 | {"self" , XENPMU_MODE_SELF}, |
468 | {"hv" , XENPMU_MODE_HV}, |
469 | {"all" , XENPMU_MODE_ALL} |
470 | }; |
471 | |
472 | static ssize_t pmu_mode_store(struct hyp_sysfs_attr *attr, |
473 | const char *buffer, size_t len) |
474 | { |
475 | int ret; |
476 | struct xen_pmu_params xp; |
477 | int i; |
478 | |
479 | for (i = 0; i < ARRAY_SIZE(pmu_modes); i++) { |
480 | if (strncmp(buffer, pmu_modes[i].name, len - 1) == 0) { |
481 | xp.val = pmu_modes[i].mode; |
482 | break; |
483 | } |
484 | } |
485 | |
486 | if (i == ARRAY_SIZE(pmu_modes)) |
487 | return -EINVAL; |
488 | |
489 | xp.version.maj = XENPMU_VER_MAJ; |
490 | xp.version.min = XENPMU_VER_MIN; |
491 | ret = HYPERVISOR_xenpmu_op(XENPMU_mode_set, arg: &xp); |
492 | if (ret) |
493 | return ret; |
494 | |
495 | return len; |
496 | } |
497 | |
498 | static ssize_t pmu_mode_show(struct hyp_sysfs_attr *attr, char *buffer) |
499 | { |
500 | int ret; |
501 | struct xen_pmu_params xp; |
502 | int i; |
503 | uint32_t mode; |
504 | |
505 | xp.version.maj = XENPMU_VER_MAJ; |
506 | xp.version.min = XENPMU_VER_MIN; |
507 | ret = HYPERVISOR_xenpmu_op(XENPMU_mode_get, arg: &xp); |
508 | if (ret) |
509 | return ret; |
510 | |
511 | mode = (uint32_t)xp.val; |
512 | for (i = 0; i < ARRAY_SIZE(pmu_modes); i++) { |
513 | if (mode == pmu_modes[i].mode) |
514 | return sprintf(buf: buffer, fmt: "%s\n" , pmu_modes[i].name); |
515 | } |
516 | |
517 | return -EINVAL; |
518 | } |
519 | HYPERVISOR_ATTR_RW(pmu_mode); |
520 | |
521 | static ssize_t pmu_features_store(struct hyp_sysfs_attr *attr, |
522 | const char *buffer, size_t len) |
523 | { |
524 | int ret; |
525 | uint32_t features; |
526 | struct xen_pmu_params xp; |
527 | |
528 | ret = kstrtou32(s: buffer, base: 0, res: &features); |
529 | if (ret) |
530 | return ret; |
531 | |
532 | xp.val = features; |
533 | xp.version.maj = XENPMU_VER_MAJ; |
534 | xp.version.min = XENPMU_VER_MIN; |
535 | ret = HYPERVISOR_xenpmu_op(XENPMU_feature_set, arg: &xp); |
536 | if (ret) |
537 | return ret; |
538 | |
539 | return len; |
540 | } |
541 | |
542 | static ssize_t pmu_features_show(struct hyp_sysfs_attr *attr, char *buffer) |
543 | { |
544 | int ret; |
545 | struct xen_pmu_params xp; |
546 | |
547 | xp.version.maj = XENPMU_VER_MAJ; |
548 | xp.version.min = XENPMU_VER_MIN; |
549 | ret = HYPERVISOR_xenpmu_op(XENPMU_feature_get, arg: &xp); |
550 | if (ret) |
551 | return ret; |
552 | |
553 | return sprintf(buf: buffer, fmt: "0x%x\n" , (uint32_t)xp.val); |
554 | } |
555 | HYPERVISOR_ATTR_RW(pmu_features); |
556 | |
557 | static struct attribute *xen_pmu_attrs[] = { |
558 | &pmu_mode_attr.attr, |
559 | &pmu_features_attr.attr, |
560 | NULL |
561 | }; |
562 | |
563 | static const struct attribute_group xen_pmu_group = { |
564 | .name = "pmu" , |
565 | .attrs = xen_pmu_attrs, |
566 | }; |
567 | |
568 | static int __init xen_sysfs_pmu_init(void) |
569 | { |
570 | return sysfs_create_group(kobj: hypervisor_kobj, grp: &xen_pmu_group); |
571 | } |
572 | #endif |
573 | |
574 | static int __init hyper_sysfs_init(void) |
575 | { |
576 | int ret; |
577 | |
578 | if (!xen_domain()) |
579 | return -ENODEV; |
580 | |
581 | ret = xen_sysfs_type_init(); |
582 | if (ret) |
583 | goto out; |
584 | ret = xen_sysfs_guest_type_init(); |
585 | if (ret) |
586 | goto guest_type_out; |
587 | ret = xen_sysfs_version_init(); |
588 | if (ret) |
589 | goto version_out; |
590 | ret = xen_sysfs_compilation_init(); |
591 | if (ret) |
592 | goto comp_out; |
593 | ret = xen_sysfs_uuid_init(); |
594 | if (ret) |
595 | goto uuid_out; |
596 | ret = xen_sysfs_properties_init(); |
597 | if (ret) |
598 | goto prop_out; |
599 | ret = xen_sysfs_flags_init(); |
600 | if (ret) |
601 | goto flags_out; |
602 | #ifdef CONFIG_XEN_HAVE_VPMU |
603 | if (xen_initial_domain()) { |
604 | ret = xen_sysfs_pmu_init(); |
605 | if (ret) { |
606 | sysfs_remove_group(kobj: hypervisor_kobj, grp: &xen_flags_group); |
607 | goto flags_out; |
608 | } |
609 | } |
610 | #endif |
611 | goto out; |
612 | |
613 | flags_out: |
614 | sysfs_remove_group(kobj: hypervisor_kobj, grp: &xen_properties_group); |
615 | prop_out: |
616 | sysfs_remove_file(kobj: hypervisor_kobj, attr: &uuid_attr.attr); |
617 | uuid_out: |
618 | sysfs_remove_group(kobj: hypervisor_kobj, grp: &xen_compilation_group); |
619 | comp_out: |
620 | sysfs_remove_group(kobj: hypervisor_kobj, grp: &version_group); |
621 | version_out: |
622 | sysfs_remove_file(kobj: hypervisor_kobj, attr: &guest_type_attr.attr); |
623 | guest_type_out: |
624 | sysfs_remove_file(kobj: hypervisor_kobj, attr: &type_attr.attr); |
625 | out: |
626 | return ret; |
627 | } |
628 | device_initcall(hyper_sysfs_init); |
629 | |
630 | static ssize_t hyp_sysfs_show(struct kobject *kobj, |
631 | struct attribute *attr, |
632 | char *buffer) |
633 | { |
634 | struct hyp_sysfs_attr *hyp_attr; |
635 | hyp_attr = container_of(attr, struct hyp_sysfs_attr, attr); |
636 | if (hyp_attr->show) |
637 | return hyp_attr->show(hyp_attr, buffer); |
638 | return 0; |
639 | } |
640 | |
641 | static ssize_t hyp_sysfs_store(struct kobject *kobj, |
642 | struct attribute *attr, |
643 | const char *buffer, |
644 | size_t len) |
645 | { |
646 | struct hyp_sysfs_attr *hyp_attr; |
647 | hyp_attr = container_of(attr, struct hyp_sysfs_attr, attr); |
648 | if (hyp_attr->store) |
649 | return hyp_attr->store(hyp_attr, buffer, len); |
650 | return 0; |
651 | } |
652 | |
653 | static const struct sysfs_ops hyp_sysfs_ops = { |
654 | .show = hyp_sysfs_show, |
655 | .store = hyp_sysfs_store, |
656 | }; |
657 | |
658 | static const struct kobj_type hyp_sysfs_kobj_type = { |
659 | .sysfs_ops = &hyp_sysfs_ops, |
660 | }; |
661 | |
662 | static int __init hypervisor_subsys_init(void) |
663 | { |
664 | if (!xen_domain()) |
665 | return -ENODEV; |
666 | |
667 | hypervisor_kobj->ktype = &hyp_sysfs_kobj_type; |
668 | return 0; |
669 | } |
670 | device_initcall(hypervisor_subsys_init); |
671 | |