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