linux/drivers/xen/sys-hypervisor.c
<<
>>
Prefs
   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);
 552