diff --git a/Documentation/ABI/testing/sysfs-hypervisor-haven b/Documentation/ABI/testing/sysfs-hypervisor-haven new file mode 100644 index 000000000000..bd0d5da67347 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-hypervisor-haven @@ -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. diff --git a/drivers/virt/haven/Kconfig b/drivers/virt/haven/Kconfig index ce00fd916a17..58ae43c164c9 100644 --- a/drivers/virt/haven/Kconfig +++ b/drivers/virt/haven/Kconfig @@ -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 diff --git a/drivers/virt/haven/Makefile b/drivers/virt/haven/Makefile index 4aea5ea755e0..21739d8094fe 100644 --- a/drivers/virt/haven/Makefile +++ b/drivers/virt/haven/Makefile @@ -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 diff --git a/drivers/virt/haven/hh_ctrl.c b/drivers/virt/haven/hh_ctrl.c new file mode 100644 index 000000000000..b13fd07f1830 --- /dev/null +++ b/drivers/virt/haven/hh_ctrl.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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); diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 080012a6f025..7fcced1bd897 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -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 diff --git a/include/linux/haven/hcall.h b/include/linux/haven/hcall.h index a3d360406199..8843849b4607 100644 --- a/include/linux/haven/hcall.h +++ b/include/linux/haven/hcall.h @@ -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