haven: add sysfs and debug interfaces

This patch adds the /sys/hypervisor sysfs interface for the Haven
hypervisor. When running hosted on the Haven hypervisor, this interface
provides the Haven API and version information, and an interface for
controlling debug features. Haven also provides a call to disable its
UART driver. Disabling Haven's UART driver is exposed through a Kconfig
option.

Change-Id: I2a5ca7b01487e01e2d1dd56f5d15e826e70af081
Signed-off-by: Elliot Berman <eberman@codeaurora.org>
This commit is contained in:
Elliot Berman 2020-04-06 10:46:29 -07:00
commit ea1de79279
6 changed files with 289 additions and 2 deletions

View file

@ -0,0 +1,32 @@
What: /sys/hypervisor/type
Date: April 2020
KernelVersion: 5.4.25
Description: If running under Haven:
Type of hypervisor:
"haven": Haven hypervisor
What: /sys/hypervisor/version/api
Date: April 2020
KernelVersion: 5.4.25
Description: If running under Haven:
The Haven API version.
What: /sys/hypervisor/version/variant
Date: April 2020
KernelVersion: 5.4.25
Description: If running under Haven:
The Haven variant (build) version.
What: /sys/kernel/debug/haven/trace_set
Date: April 2020
KernelVersion: 5.4.25
Description: If running under Haven with Trace Control support:
When reading, gets active trace class flags as a hexadecimal.
When writing, adds to the trace class flags.
What: /sys/kernel/debug/haven/trace_clear
Date: April 2020
KernelVersion: 5.4.25
Description: If running under Haven with Trace Control support:
When reading, gets active trace class flags as a hexadecimal.
When writing, clears the specified trace class flags.

View file

@ -2,6 +2,7 @@
menuconfig HAVEN_DRIVERS
bool "Haven Virtualization drivers"
depends on ARM64
help
The Haven drivers are the helper interfaces that runs on the
virtual machines that provides support such as memory/device
@ -14,6 +15,27 @@ menuconfig HAVEN_DRIVERS
if HAVEN_DRIVERS
config HH_CTRL
tristate "Create Haven entries under /sys/hypervisor"
depends on SYSFS
select SYS_HYPERVISOR
help
Create entries under /sys/hypervisor for the Haven hypervisor.
The driver also provides a facility for controlling
hypervisor debug features.
See Documentation/ABI/testing/sysfs-hypervisor-haven for more details.
if HH_CTRL
config HH_DISABLE_UART
bool "Disable Haven UART print during kernel boot"
default y
help
The hypervisor UART driver is enabled at boot,
however its use may conflict with use of the
Linux driver. This config requests Haven to
disable its UART driver.
endif
config HH_MSGQ
tristate "Haven Message Queue driver"
help

View file

@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_HH_CTRL) += hh_ctrl.o
obj-$(CONFIG_HH_MSGQ) += hh_msgq.o
obj-$(CONFIG_HH_RM_DRV) += hh_rm_drv.o
hh_rm_drv-y := hh_rm_core.o hh_rm_iface.o

View file

@ -0,0 +1,213 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "haven: "
#include <linux/arm-smccc.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/of.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/haven/hcall.h>
#include <linux/haven/hh_errno.h>
#define QC_HYP_SMCCC_UART_DISABLE \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
ARM_SMCCC_OWNER_VENDOR_HYPERVISOR, 0x0)
#define QC_HYP_SMCCC_CALL_UID \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
ARM_SMCCC_OWNER_VENDOR_HYPERVISOR, 0xff01)
#define QC_HYP_SMCCC_REVISION \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \
ARM_SMCCC_OWNER_VENDOR_HYPERVISOR, 0xff03)
#define QC_HYP_UID0 0x19bd54bd
#define QC_HYP_UID1 0x0b37571b
#define QC_HYP_UID2 0x946f609b
#define QC_HYP_UID3 0x54539de6
#define HH_API_INFO_API_VERSION(x) (((x) >> 0) & 0x3fff)
#define HH_API_INFO_BIG_ENDIAN(x) (((x) >> 14) & 1)
#define HH_API_INFO_IS_64BIT(x) (((x) >> 15) & 1)
#define HH_API_INFO_VARIANT(x) (((x) >> 56) & 0xff)
#define HH_IDENTIFY_PARTITION_CSPACE(x) (((x) >> 0) & 1)
#define HH_IDENTIFY_DOORBELL(x) (((x) >> 1) & 1)
#define HH_IDENTIFY_MSGQUEUE(x) (((x) >> 2) & 1)
#define HH_IDENTIFY_VIC(x) (((x) >> 3) & 1)
#define HH_IDENTIFY_VPM(x) (((x) >> 4) & 1)
#define HH_IDENTIFY_VCPU(x) (((x) >> 5) & 1)
#define HH_IDENTIFY_MEMEXTENT(x) (((x) >> 6) & 1)
#define HH_IDENTIFY_TRACE_CTRL(x) (((x) >> 7) & 1)
#define HH_IDENTIFY_ROOTVM_CHANNEL(x) (((x) >> 16) & 1)
#define HH_IDENTIFY_SCHEDULER(x) (((x) >> 28) & 0xf)
static bool qc_hyp_calls;
static struct hh_hcall_hyp_identify_resp haven_api;
#if defined(CONFIG_HH_DISABLE_UART)
static void hh_disable_uart(void)
{
if (qc_hyp_calls)
arm_smccc_1_1_smc(QC_HYP_SMCCC_UART_DISABLE, NULL);
}
#endif
static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buffer)
{
return scnprintf(buffer, PAGE_SIZE, "haven\n");
}
static struct kobj_attribute type_attr = __ATTR_RO(type);
static ssize_t api_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buffer)
{
return scnprintf(buffer, PAGE_SIZE, "%d\n",
(int)HH_API_INFO_API_VERSION(haven_api.api_info));
}
static struct kobj_attribute api_attr = __ATTR_RO(api);
static ssize_t variant_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buffer)
{
return scnprintf(buffer, PAGE_SIZE, "%d\n",
(int)HH_API_INFO_VARIANT(haven_api.api_info));
}
static struct kobj_attribute variant_attr = __ATTR_RO(variant);
static struct attribute *version_attrs[] = { &api_attr.attr,
&variant_attr.attr, NULL };
static const struct attribute_group version_group = {
.name = "version",
.attrs = version_attrs,
};
static int __init hh_sysfs_register(void)
{
int ret;
ret = sysfs_create_file(hypervisor_kobj, &type_attr.attr);
if (ret)
return ret;
return sysfs_create_group(hypervisor_kobj, &version_group);
}
static void __exit hh_sysfs_unregister(void)
{
sysfs_remove_file(hypervisor_kobj, &type_attr.attr);
sysfs_remove_group(hypervisor_kobj, &version_group);
}
#if defined(CONFIG_DEBUG_FS)
static struct dentry *hh_dbgfs_dir;
static int hh_dbgfs_trace_class_set(void *data, u64 val)
{
return hh_remap_error(hh_hcall_trace_update_class_flags(val, 0, NULL));
}
static int hh_dbgfs_trace_class_clear(void *data, u64 val)
{
return hh_remap_error(hh_hcall_trace_update_class_flags(0, val, NULL));
}
static int hh_dbgfs_trace_class_get(void *data, u64 *val)
{
return hh_remap_error(hh_hcall_trace_update_class_flags(0, 0, val));
}
DEFINE_DEBUGFS_ATTRIBUTE(hh_dbgfs_trace_class_set_fops,
hh_dbgfs_trace_class_get,
hh_dbgfs_trace_class_set,
"0x%llx\n");
DEFINE_DEBUGFS_ATTRIBUTE(hh_dbgfs_trace_class_clear_fops,
hh_dbgfs_trace_class_get,
hh_dbgfs_trace_class_clear,
"0x%llx\n");
static int __init hh_dbgfs_register(void)
{
struct dentry *dentry;
hh_dbgfs_dir = debugfs_create_dir("haven", NULL);
if (IS_ERR_OR_NULL(hh_dbgfs_dir))
return PTR_ERR(hh_dbgfs_dir);
if (HH_IDENTIFY_TRACE_CTRL(haven_api.flags[0])) {
dentry = debugfs_create_file("trace_set", 0600, hh_dbgfs_dir,
NULL, &hh_dbgfs_trace_class_set_fops);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
dentry = debugfs_create_file("trace_clear", 0600, hh_dbgfs_dir,
NULL, &hh_dbgfs_trace_class_clear_fops);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
}
return 0;
}
static void __exit hh_dbgfs_unregister(void)
{
debugfs_remove_recursive(hh_dbgfs_dir);
}
#else /* !defined (CONFIG_DEBUG_FS) */
static inline int hh_dbgfs_register(void) { return 0; }
static inline void hh_dbgfs_unregister(void) { return 0; }
#endif
static int __init hh_ctrl_init(void)
{
int ret;
struct device_node *hyp;
struct arm_smccc_res res;
hyp = of_find_node_by_path("/hypervisor");
if (!hyp || !of_device_is_compatible(hyp, "qcom,haven-hypervisor"))
return -ENODEV;
(void)hh_hcall_hyp_identify(&haven_api);
if (HH_API_INFO_API_VERSION(haven_api.api_info) != 1) {
pr_err("unknown version\n");
return -ENODEV;
}
/* Check for ARM SMCCC VENDOR_HYP service calls by UID. */
arm_smccc_1_1_smc(QC_HYP_SMCCC_CALL_UID, &res);
if ((res.a0 == QC_HYP_UID0) && (res.a1 == QC_HYP_UID1) &&
(res.a2 == QC_HYP_UID2) && (res.a3 == QC_HYP_UID3))
qc_hyp_calls = true;
#if defined(CONFIG_HH_DISABLE_UART)
hh_disable_uart();
#endif
ret = hh_sysfs_register();
if (ret)
return ret;
ret = hh_dbgfs_register();
if (ret)
pr_warn("failed to register dbgfs: %d\n", ret);
return 0;
}
module_init(hh_ctrl_init);
static void __exit hh_ctrl_exit(void)
{
hh_sysfs_unregister();
hh_dbgfs_unregister();
}
module_exit(hh_ctrl_exit);

View file

@ -45,6 +45,8 @@
#define ARM_SMCCC_OWNER_SIP 2
#define ARM_SMCCC_OWNER_OEM 3
#define ARM_SMCCC_OWNER_STANDARD 4
#define ARM_SMCCC_OWNER_STANDARD_HYPERVISOR 5
#define ARM_SMCCC_OWNER_VENDOR_HYPERVISOR 6
#define ARM_SMCCC_OWNER_TRUSTED_APP 48
#define ARM_SMCCC_OWNER_TRUSTED_APP_END 49
#define ARM_SMCCC_OWNER_TRUSTED_OS 50

View file

@ -26,14 +26,14 @@ static inline int hh_hcall_hyp_identify(struct hh_hcall_hyp_identify_resp *resp)
(struct hh_hcall_args){ 0 },
&_resp);
if (!ret && resp) {
if (resp) {
resp->api_info = _resp.resp0;
resp->flags[0] = _resp.resp1;
resp->flags[1] = _resp.resp2;
resp->flags[2] = _resp.resp3;
}
return ret < 0 ? ret : 0;
return 0;
}
static inline int hh_hcall_dbl_bind(hh_capid_t dbl_capid, hh_capid_t vic_capid,
@ -283,4 +283,21 @@ static inline int hh_hcall_vcpu_affinity_set(hh_capid_t vcpu_capid,
return ret;
}
static inline int hh_hcall_trace_update_class_flags(
uint64_t set_flags, uint64_t clear_flags,
uint64_t *new_flags)
{
int ret;
struct hh_hcall_resp _resp = {0};
ret = _hh_hcall(0x603f,
(struct hh_hcall_args){ set_flags, clear_flags, 0 },
&_resp);
if ((ret != 0) && new_flags)
*new_flags = _resp.resp1;
return ret;
}
#endif