at v4.10 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/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) \ 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 53/* xen version attributes */ 54static 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 62HYPERVISOR_ATTR_RO(major); 63 64static 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 72HYPERVISOR_ATTR_RO(minor); 73 74static 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 90HYPERVISOR_ATTR_RO(extra); 91 92static struct attribute *version_attrs[] = { 93 &major_attr.attr, 94 &minor_attr.attr, 95 &extra_attr.attr, 96 NULL 97}; 98 99static const struct attribute_group version_group = { 100 .name = "version", 101 .attrs = version_attrs, 102}; 103 104static int __init xen_sysfs_version_init(void) 105{ 106 return sysfs_create_group(hypervisor_kobj, &version_group); 107} 108 109/* UUID */ 110 111static 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 132static 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 143HYPERVISOR_ATTR_RO(uuid); 144 145static 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 152static 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 168HYPERVISOR_ATTR_RO(compiler); 169 170static 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 186HYPERVISOR_ATTR_RO(compiled_by); 187 188static 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 204HYPERVISOR_ATTR_RO(compile_date); 205 206static struct attribute *xen_compile_attrs[] = { 207 &compiler_attr.attr, 208 &compiled_by_attr.attr, 209 &compile_date_attr.attr, 210 NULL 211}; 212 213static const struct attribute_group xen_compilation_group = { 214 .name = "compilation", 215 .attrs = xen_compile_attrs, 216}; 217 218static int __init xen_sysfs_compilation_init(void) 219{ 220 return sysfs_create_group(hypervisor_kobj, &xen_compilation_group); 221} 222 223/* xen properties info */ 224 225static 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 241HYPERVISOR_ATTR_RO(capabilities); 242 243static 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 259HYPERVISOR_ATTR_RO(changeset); 260 261static 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 279HYPERVISOR_ATTR_RO(virtual_start); 280 281static 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 292HYPERVISOR_ATTR_RO(pagesize); 293 294static 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 307static 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 328HYPERVISOR_ATTR_RO(features); 329 330static 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 339static const struct attribute_group xen_properties_group = { 340 .name = "properties", 341 .attrs = xen_properties_attrs, 342}; 343 344static int __init xen_sysfs_properties_init(void) 345{ 346 return sysfs_create_group(hypervisor_kobj, &xen_properties_group); 347} 348 349#ifdef CONFIG_XEN_HAVE_VPMU 350struct pmu_mode { 351 const char *name; 352 uint32_t mode; 353}; 354 355static 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 362static 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 388static 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} 409HYPERVISOR_ATTR_RW(pmu_mode); 410 411static 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 432static 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} 445HYPERVISOR_ATTR_RW(pmu_features); 446 447static struct attribute *xen_pmu_attrs[] = { 448 &pmu_mode_attr.attr, 449 &pmu_features_attr.attr, 450 NULL 451}; 452 453static const struct attribute_group xen_pmu_group = { 454 .name = "pmu", 455 .attrs = xen_pmu_attrs, 456}; 457 458static int __init xen_sysfs_pmu_init(void) 459{ 460 return sysfs_create_group(hypervisor_kobj, &xen_pmu_group); 461} 462#endif 463 464static 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_sysfs_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_sysfs_properties_init(); 484 if (ret) 485 goto prop_out; 486#ifdef CONFIG_XEN_HAVE_VPMU 487 if (xen_initial_domain()) { 488 ret = xen_sysfs_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 498prop_out: 499 sysfs_remove_file(hypervisor_kobj, &uuid_attr.attr); 500uuid_out: 501 sysfs_remove_group(hypervisor_kobj, &xen_compilation_group); 502comp_out: 503 sysfs_remove_group(hypervisor_kobj, &version_group); 504version_out: 505 sysfs_remove_file(hypervisor_kobj, &type_attr.attr); 506out: 507 return ret; 508} 509device_initcall(hyper_sysfs_init); 510 511static 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 522static 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 534static const struct sysfs_ops hyp_sysfs_ops = { 535 .show = hyp_sysfs_show, 536 .store = hyp_sysfs_store, 537}; 538 539static struct kobj_type hyp_sysfs_kobj_type = { 540 .sysfs_ops = &hyp_sysfs_ops, 541}; 542 543static 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} 551device_initcall(hypervisor_subsys_init);