Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ea1de792 authored by Elliot Berman's avatar Elliot Berman
Browse files

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: default avatarElliot Berman <eberman@codeaurora.org>
parent d5ee68b8
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
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.
+22 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
# 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 −0
Original line number Diff line number Diff line
// 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);
+2 −0
Original line number Diff line number Diff line
@@ -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
Loading