mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-12 07:37:31 -04:00
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:
parent
d5ee68b825
commit
ea1de79279
6 changed files with 289 additions and 2 deletions
32
Documentation/ABI/testing/sysfs-hypervisor-haven
Normal file
32
Documentation/ABI/testing/sysfs-hypervisor-haven
Normal 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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
213
drivers/virt/haven/hh_ctrl.c
Normal file
213
drivers/virt/haven/hh_ctrl.c
Normal 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);
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue