Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.4-rc2 594 lines 12 kB view raw
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) \ 28static struct hyp_sysfs_attr _name##_attr = __ATTR_RO(_name) 29 30#define HYPERVISOR_ATTR_RW(_name) \ 31static struct hyp_sysfs_attr _name##_attr = \ 32 __ATTR(_name, 0644, _name##_show, _name##_store) 33 34struct 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 41static ssize_t type_show(struct hyp_sysfs_attr *attr, char *buffer) 42{ 43 return sprintf(buffer, "xen\n"); 44} 45 46HYPERVISOR_ATTR_RO(type); 47 48static int __init xen_sysfs_type_init(void) 49{ 50 return sysfs_create_file(hypervisor_kobj, &type_attr.attr); 51} 52 53static void xen_sysfs_type_destroy(void) 54{ 55 sysfs_remove_file(hypervisor_kobj, &type_attr.attr); 56} 57 58/* xen version attributes */ 59static 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 67HYPERVISOR_ATTR_RO(major); 68 69static 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 77HYPERVISOR_ATTR_RO(minor); 78 79static 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 95HYPERVISOR_ATTR_RO(extra); 96 97static struct attribute *version_attrs[] = { 98 &major_attr.attr, 99 &minor_attr.attr, 100 &extra_attr.attr, 101 NULL 102}; 103 104static const struct attribute_group version_group = { 105 .name = "version", 106 .attrs = version_attrs, 107}; 108 109static int __init xen_sysfs_version_init(void) 110{ 111 return sysfs_create_group(hypervisor_kobj, &version_group); 112} 113 114static void xen_sysfs_version_destroy(void) 115{ 116 sysfs_remove_group(hypervisor_kobj, &version_group); 117} 118 119/* UUID */ 120 121static 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 142static 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 153HYPERVISOR_ATTR_RO(uuid); 154 155static int __init xen_sysfs_uuid_init(void) 156{ 157 return sysfs_create_file(hypervisor_kobj, &uuid_attr.attr); 158} 159 160static void xen_sysfs_uuid_destroy(void) 161{ 162 sysfs_remove_file(hypervisor_kobj, &uuid_attr.attr); 163} 164 165/* xen compilation attributes */ 166 167static 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 183HYPERVISOR_ATTR_RO(compiler); 184 185static 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 201HYPERVISOR_ATTR_RO(compiled_by); 202 203static 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 219HYPERVISOR_ATTR_RO(compile_date); 220 221static struct attribute *xen_compile_attrs[] = { 222 &compiler_attr.attr, 223 &compiled_by_attr.attr, 224 &compile_date_attr.attr, 225 NULL 226}; 227 228static const struct attribute_group xen_compilation_group = { 229 .name = "compilation", 230 .attrs = xen_compile_attrs, 231}; 232 233static int __init xen_compilation_init(void) 234{ 235 return sysfs_create_group(hypervisor_kobj, &xen_compilation_group); 236} 237 238static void xen_compilation_destroy(void) 239{ 240 sysfs_remove_group(hypervisor_kobj, &xen_compilation_group); 241} 242 243/* xen properties info */ 244 245static 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 261HYPERVISOR_ATTR_RO(capabilities); 262 263static 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 279HYPERVISOR_ATTR_RO(changeset); 280 281static 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 299HYPERVISOR_ATTR_RO(virtual_start); 300 301static 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 312HYPERVISOR_ATTR_RO(pagesize); 313 314static 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 327static 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 348HYPERVISOR_ATTR_RO(features); 349 350static 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 359static const struct attribute_group xen_properties_group = { 360 .name = "properties", 361 .attrs = xen_properties_attrs, 362}; 363 364static int __init xen_properties_init(void) 365{ 366 return sysfs_create_group(hypervisor_kobj, &xen_properties_group); 367} 368 369static void xen_properties_destroy(void) 370{ 371 sysfs_remove_group(hypervisor_kobj, &xen_properties_group); 372} 373 374#ifdef CONFIG_XEN_HAVE_VPMU 375struct pmu_mode { 376 const char *name; 377 uint32_t mode; 378}; 379 380static 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 387static 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 413static 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} 434HYPERVISOR_ATTR_RW(pmu_mode); 435 436static 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 457static 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} 470HYPERVISOR_ATTR_RW(pmu_features); 471 472static struct attribute *xen_pmu_attrs[] = { 473 &pmu_mode_attr.attr, 474 &pmu_features_attr.attr, 475 NULL 476}; 477 478static const struct attribute_group xen_pmu_group = { 479 .name = "pmu", 480 .attrs = xen_pmu_attrs, 481}; 482 483static int __init xen_pmu_init(void) 484{ 485 return sysfs_create_group(hypervisor_kobj, &xen_pmu_group); 486} 487 488static void xen_pmu_destroy(void) 489{ 490 sysfs_remove_group(hypervisor_kobj, &xen_pmu_group); 491} 492#endif 493 494static 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 527prop_out: 528 xen_sysfs_uuid_destroy(); 529uuid_out: 530 xen_compilation_destroy(); 531comp_out: 532 xen_sysfs_version_destroy(); 533version_out: 534 xen_sysfs_type_destroy(); 535out: 536 return ret; 537} 538 539static 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} 551module_init(hyper_sysfs_init); 552module_exit(hyper_sysfs_exit); 553 554static 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 565static 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 577static const struct sysfs_ops hyp_sysfs_ops = { 578 .show = hyp_sysfs_show, 579 .store = hyp_sysfs_store, 580}; 581 582static struct kobj_type hyp_sysfs_kobj_type = { 583 .sysfs_ops = &hyp_sysfs_ops, 584}; 585 586static 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} 594device_initcall(hypervisor_subsys_init);