summaryrefslogtreecommitdiff
path: root/drivers/xen/sys-hypervisor.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/xen/sys-hypervisor.c')
-rw-r--r--drivers/xen/sys-hypervisor.c310
1 files changed, 260 insertions, 50 deletions
diff --git a/drivers/xen/sys-hypervisor.c b/drivers/xen/sys-hypervisor.c
index 96453f8a85c5..2f880374b463 100644
--- a/drivers/xen/sys-hypervisor.c
+++ b/drivers/xen/sys-hypervisor.c
@@ -1,15 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* copyright (c) 2006 IBM Corporation
* Authored by: Mike D. Day <ncmike@us.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/slab.h>
#include <linux/kernel.h>
-#include <linux/module.h>
+#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/err.h>
@@ -20,19 +17,24 @@
#include <xen/xenbus.h>
#include <xen/interface/xen.h>
#include <xen/interface/version.h>
+#ifdef CONFIG_XEN_HAVE_VPMU
+#include <xen/interface/xenpmu.h>
+#endif
#define HYPERVISOR_ATTR_RO(_name) \
-static struct hyp_sysfs_attr _name##_attr = __ATTR_RO(_name)
+static struct hyp_sysfs_attr _name##_attr = __ATTR_RO(_name)
#define HYPERVISOR_ATTR_RW(_name) \
-static struct hyp_sysfs_attr _name##_attr = \
- __ATTR(_name, 0644, _name##_show, _name##_store)
+static struct hyp_sysfs_attr _name##_attr = __ATTR_RW(_name)
struct hyp_sysfs_attr {
struct attribute attr;
ssize_t (*show)(struct hyp_sysfs_attr *, char *);
ssize_t (*store)(struct hyp_sysfs_attr *, const char *, size_t);
- void *hyp_attr_data;
+ union {
+ void *hyp_attr_data;
+ unsigned long hyp_attr_value;
+ };
};
static ssize_t type_show(struct hyp_sysfs_attr *attr, char *buffer)
@@ -47,9 +49,33 @@ static int __init xen_sysfs_type_init(void)
return sysfs_create_file(hypervisor_kobj, &type_attr.attr);
}
-static void xen_sysfs_type_destroy(void)
+static ssize_t guest_type_show(struct hyp_sysfs_attr *attr, char *buffer)
{
- sysfs_remove_file(hypervisor_kobj, &type_attr.attr);
+ const char *type;
+
+ switch (xen_domain_type) {
+ case XEN_NATIVE:
+ /* ARM only. */
+ type = "Xen";
+ break;
+ case XEN_PV_DOMAIN:
+ type = "PV";
+ break;
+ case XEN_HVM_DOMAIN:
+ type = xen_pvh_domain() ? "PVH" : "HVM";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sprintf(buffer, "%s\n", type);
+}
+
+HYPERVISOR_ATTR_RO(guest_type);
+
+static int __init xen_sysfs_guest_type_init(void)
+{
+ return sysfs_create_file(hypervisor_kobj, &guest_type_attr.attr);
}
/* xen version attributes */
@@ -108,11 +134,6 @@ static int __init xen_sysfs_version_init(void)
return sysfs_create_group(hypervisor_kobj, &version_group);
}
-static void xen_sysfs_version_destroy(void)
-{
- sysfs_remove_group(hypervisor_kobj, &version_group);
-}
-
/* UUID */
static ssize_t uuid_show_fallback(struct hyp_sysfs_attr *attr, char *buffer)
@@ -154,11 +175,6 @@ static int __init xen_sysfs_uuid_init(void)
return sysfs_create_file(hypervisor_kobj, &uuid_attr.attr);
}
-static void xen_sysfs_uuid_destroy(void)
-{
- sysfs_remove_file(hypervisor_kobj, &uuid_attr.attr);
-}
-
/* xen compilation attributes */
static ssize_t compiler_show(struct hyp_sysfs_attr *attr, char *buffer)
@@ -227,16 +243,11 @@ static const struct attribute_group xen_compilation_group = {
.attrs = xen_compile_attrs,
};
-static int __init xen_compilation_init(void)
+static int __init xen_sysfs_compilation_init(void)
{
return sysfs_create_group(hypervisor_kobj, &xen_compilation_group);
}
-static void xen_compilation_destroy(void)
-{
- sysfs_remove_group(hypervisor_kobj, &xen_compilation_group);
-}
-
/* xen properties info */
static ssize_t capabilities_show(struct hyp_sysfs_attr *attr, char *buffer)
@@ -344,12 +355,40 @@ static ssize_t features_show(struct hyp_sysfs_attr *attr, char *buffer)
HYPERVISOR_ATTR_RO(features);
+static ssize_t buildid_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ ssize_t ret;
+ struct xen_build_id *buildid;
+
+ ret = HYPERVISOR_xen_version(XENVER_build_id, NULL);
+ if (ret < 0) {
+ if (ret == -EPERM)
+ ret = sprintf(buffer, "<denied>");
+ return ret;
+ }
+
+ buildid = kmalloc(sizeof(*buildid) + ret, GFP_KERNEL);
+ if (!buildid)
+ return -ENOMEM;
+
+ buildid->len = ret;
+ ret = HYPERVISOR_xen_version(XENVER_build_id, buildid);
+ if (ret > 0)
+ ret = sprintf(buffer, "%s", buildid->buf);
+ kfree(buildid);
+
+ return ret;
+}
+
+HYPERVISOR_ATTR_RO(buildid);
+
static struct attribute *xen_properties_attrs[] = {
&capabilities_attr.attr,
&changeset_attr.attr,
&virtual_start_attr.attr,
&pagesize_attr.attr,
&features_attr.attr,
+ &buildid_attr.attr,
NULL
};
@@ -358,16 +397,180 @@ static const struct attribute_group xen_properties_group = {
.attrs = xen_properties_attrs,
};
-static int __init xen_properties_init(void)
+static int __init xen_sysfs_properties_init(void)
{
return sysfs_create_group(hypervisor_kobj, &xen_properties_group);
}
-static void xen_properties_destroy(void)
+#define FLAG_UNAME "unknown"
+#define FLAG_UNAME_FMT FLAG_UNAME "%02u"
+#define FLAG_UNAME_MAX sizeof(FLAG_UNAME "XX")
+#define FLAG_COUNT (sizeof(xen_start_flags) * BITS_PER_BYTE)
+static_assert(sizeof(xen_start_flags) <=
+ sizeof_field(struct hyp_sysfs_attr, hyp_attr_value));
+
+static ssize_t flag_show(struct hyp_sysfs_attr *attr, char *buffer)
{
- sysfs_remove_group(hypervisor_kobj, &xen_properties_group);
+ char *p = buffer;
+
+ *p++ = '0' + ((xen_start_flags & attr->hyp_attr_value) != 0);
+ *p++ = '\n';
+ return p - buffer;
+}
+
+#define FLAG_NODE(flag, node) \
+ [ilog2(flag)] = { \
+ .attr = { .name = #node, .mode = 0444 },\
+ .show = flag_show, \
+ .hyp_attr_value = flag \
+ }
+
+/*
+ * Add new, known flags here. No other changes are required, but
+ * note that each known flag wastes one entry in flag_unames[].
+ * The code/complexity machinations to avoid this isn't worth it
+ * for a few entries, but keep it in mind.
+ */
+static struct hyp_sysfs_attr flag_attrs[FLAG_COUNT] = {
+ FLAG_NODE(SIF_PRIVILEGED, privileged),
+ FLAG_NODE(SIF_INITDOMAIN, initdomain)
+};
+static struct attribute_group xen_flags_group = {
+ .name = "start_flags",
+ .attrs = (struct attribute *[FLAG_COUNT + 1]){}
+};
+static char flag_unames[FLAG_COUNT][FLAG_UNAME_MAX];
+
+static int __init xen_sysfs_flags_init(void)
+{
+ for (unsigned fnum = 0; fnum != FLAG_COUNT; fnum++) {
+ if (likely(flag_attrs[fnum].attr.name == NULL)) {
+ sprintf(flag_unames[fnum], FLAG_UNAME_FMT, fnum);
+ flag_attrs[fnum].attr.name = flag_unames[fnum];
+ flag_attrs[fnum].attr.mode = 0444;
+ flag_attrs[fnum].show = flag_show;
+ flag_attrs[fnum].hyp_attr_value = 1 << fnum;
+ }
+ xen_flags_group.attrs[fnum] = &flag_attrs[fnum].attr;
+ }
+ return sysfs_create_group(hypervisor_kobj, &xen_flags_group);
}
+#ifdef CONFIG_XEN_HAVE_VPMU
+struct pmu_mode {
+ const char *name;
+ uint32_t mode;
+};
+
+static struct pmu_mode pmu_modes[] = {
+ {"off", XENPMU_MODE_OFF},
+ {"self", XENPMU_MODE_SELF},
+ {"hv", XENPMU_MODE_HV},
+ {"all", XENPMU_MODE_ALL}
+};
+
+static ssize_t pmu_mode_store(struct hyp_sysfs_attr *attr,
+ const char *buffer, size_t len)
+{
+ int ret;
+ struct xen_pmu_params xp;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pmu_modes); i++) {
+ if (strncmp(buffer, pmu_modes[i].name, len - 1) == 0) {
+ xp.val = pmu_modes[i].mode;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(pmu_modes))
+ return -EINVAL;
+
+ xp.version.maj = XENPMU_VER_MAJ;
+ xp.version.min = XENPMU_VER_MIN;
+ ret = HYPERVISOR_xenpmu_op(XENPMU_mode_set, &xp);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t pmu_mode_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int ret;
+ struct xen_pmu_params xp;
+ int i;
+ uint32_t mode;
+
+ xp.version.maj = XENPMU_VER_MAJ;
+ xp.version.min = XENPMU_VER_MIN;
+ ret = HYPERVISOR_xenpmu_op(XENPMU_mode_get, &xp);
+ if (ret)
+ return ret;
+
+ mode = (uint32_t)xp.val;
+ for (i = 0; i < ARRAY_SIZE(pmu_modes); i++) {
+ if (mode == pmu_modes[i].mode)
+ return sprintf(buffer, "%s\n", pmu_modes[i].name);
+ }
+
+ return -EINVAL;
+}
+HYPERVISOR_ATTR_RW(pmu_mode);
+
+static ssize_t pmu_features_store(struct hyp_sysfs_attr *attr,
+ const char *buffer, size_t len)
+{
+ int ret;
+ uint32_t features;
+ struct xen_pmu_params xp;
+
+ ret = kstrtou32(buffer, 0, &features);
+ if (ret)
+ return ret;
+
+ xp.val = features;
+ xp.version.maj = XENPMU_VER_MAJ;
+ xp.version.min = XENPMU_VER_MIN;
+ ret = HYPERVISOR_xenpmu_op(XENPMU_feature_set, &xp);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t pmu_features_show(struct hyp_sysfs_attr *attr, char *buffer)
+{
+ int ret;
+ struct xen_pmu_params xp;
+
+ xp.version.maj = XENPMU_VER_MAJ;
+ xp.version.min = XENPMU_VER_MIN;
+ ret = HYPERVISOR_xenpmu_op(XENPMU_feature_get, &xp);
+ if (ret)
+ return ret;
+
+ return sprintf(buffer, "0x%x\n", (uint32_t)xp.val);
+}
+HYPERVISOR_ATTR_RW(pmu_features);
+
+static struct attribute *xen_pmu_attrs[] = {
+ &pmu_mode_attr.attr,
+ &pmu_features_attr.attr,
+ NULL
+};
+
+static const struct attribute_group xen_pmu_group = {
+ .name = "pmu",
+ .attrs = xen_pmu_attrs,
+};
+
+static int __init xen_sysfs_pmu_init(void)
+{
+ return sysfs_create_group(hypervisor_kobj, &xen_pmu_group);
+}
+#endif
+
static int __init hyper_sysfs_init(void)
{
int ret;
@@ -378,44 +581,51 @@ static int __init hyper_sysfs_init(void)
ret = xen_sysfs_type_init();
if (ret)
goto out;
+ ret = xen_sysfs_guest_type_init();
+ if (ret)
+ goto guest_type_out;
ret = xen_sysfs_version_init();
if (ret)
goto version_out;
- ret = xen_compilation_init();
+ ret = xen_sysfs_compilation_init();
if (ret)
goto comp_out;
ret = xen_sysfs_uuid_init();
if (ret)
goto uuid_out;
- ret = xen_properties_init();
+ ret = xen_sysfs_properties_init();
if (ret)
goto prop_out;
-
+ ret = xen_sysfs_flags_init();
+ if (ret)
+ goto flags_out;
+#ifdef CONFIG_XEN_HAVE_VPMU
+ if (xen_initial_domain()) {
+ ret = xen_sysfs_pmu_init();
+ if (ret) {
+ sysfs_remove_group(hypervisor_kobj, &xen_flags_group);
+ goto flags_out;
+ }
+ }
+#endif
goto out;
+flags_out:
+ sysfs_remove_group(hypervisor_kobj, &xen_properties_group);
prop_out:
- xen_sysfs_uuid_destroy();
+ sysfs_remove_file(hypervisor_kobj, &uuid_attr.attr);
uuid_out:
- xen_compilation_destroy();
+ sysfs_remove_group(hypervisor_kobj, &xen_compilation_group);
comp_out:
- xen_sysfs_version_destroy();
+ sysfs_remove_group(hypervisor_kobj, &version_group);
version_out:
- xen_sysfs_type_destroy();
+ sysfs_remove_file(hypervisor_kobj, &guest_type_attr.attr);
+guest_type_out:
+ sysfs_remove_file(hypervisor_kobj, &type_attr.attr);
out:
return ret;
}
-
-static void __exit hyper_sysfs_exit(void)
-{
- xen_properties_destroy();
- xen_compilation_destroy();
- xen_sysfs_uuid_destroy();
- xen_sysfs_version_destroy();
- xen_sysfs_type_destroy();
-
-}
-module_init(hyper_sysfs_init);
-module_exit(hyper_sysfs_exit);
+device_initcall(hyper_sysfs_init);
static ssize_t hyp_sysfs_show(struct kobject *kobj,
struct attribute *attr,
@@ -445,7 +655,7 @@ static const struct sysfs_ops hyp_sysfs_ops = {
.store = hyp_sysfs_store,
};
-static struct kobj_type hyp_sysfs_kobj_type = {
+static const struct kobj_type hyp_sysfs_kobj_type = {
.sysfs_ops = &hyp_sysfs_ops,
};