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