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