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