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

Commit e8c9c4f0 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "arm64: defconfig: Enable Haven HVC Driver"

parents 0e3751b0 82e1a7c6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -135,6 +135,8 @@ CONFIG_HAVEN_DRIVERS=y
CONFIG_HH_MSGQ=m
CONFIG_HH_RM_DRV=m
CONFIG_HH_DBL=m
CONFIG_HVC_HAVEN=m
# CONFIG_HVC_HAVEN_CONSOLE is not set
CONFIG_CFI_PERMISSIVE=y
# CONFIG_SND_SOC_WCD9335 is not set
# CONFIG_SLIM_QCOM_CTRL is not set
+1 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ CONFIG_EDAC_QCOM=y
CONFIG_EDAC_QCOM_LLCC_PANIC_ON_UE=y
CONFIG_QCOM_DEVFREQ_ICC=y
CONFIG_QCOM_LLCC_PMU=y
CONFIG_QCOM_GUESTVM=y
CONFIG_QTI_TZ_LOG=y
CONFIG_QCOM_MEMORY_DUMP_V2=y
CONFIG_MSM_REMOTEQDSS=y
+9 −0
Original line number Diff line number Diff line
@@ -716,4 +716,13 @@ config QCOM_SMCINVOKE
	 communication between QTI Secure Execution Environment (QSEE)
	 and high level operating system. It exposes APIs for both
	 userspace and kernel clients.

config QCOM_GUESTVM
	tristate "Enable Guest VM to be loaded by PIL"
	help
	   This driver invokes Peripheral Image Loader to load images of
	   any guest Virtual Machine (VM). It also communicates with the
	   Resource Manager driver to start the boot of VMs once it has
	   successfully loaded the VM images in the designated memory.

endmenu
+1 −0
Original line number Diff line number Diff line
@@ -67,3 +67,4 @@ obj-$(CONFIG_QCOM_WATCHDOG) += qcom_watchdog.o
obj-$(CONFIG_MSM_SPCOM) += spcom.o
obj-$(CONFIG_QCOM_FSA4480_I2C) += fsa4480-i2c.o
obj-$(CONFIG_QCOM_EUD) += eud.o
obj-$(CONFIG_QCOM_GUESTVM) += guestvm_loader.o
+197 −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.
 */

#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/haven/hh_common.h>
#include <linux/haven/hh_rm_drv.h>

#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/subsystem_restart.h>

#define MAX_LEN 256

const static struct {
	enum hh_vm_names val;
	const char *str;
} conversion[] = {
	{HH_PRIMARY_VM, "pvm"},
	{HH_TRUSTED_VM, "trustedvm"},
};

static struct kobj_type guestvm_kobj_type = {
	.sysfs_ops = &kobj_sysfs_ops,
};

struct guestvm_loader_private {
	struct work_struct vm_loader_work;
	struct kobject vm_loader_kobj;
	struct device *dev;
	char vm_name[MAX_LEN];
	void *vm_loaded;
	int vmid;
};

static inline enum hh_vm_names get_hh_vm_name(const char *str)
{
	int vmid;

	for (vmid = 0; vmid < ARRAY_SIZE(conversion); ++vmid) {
		if (!strcmp(str, conversion[vmid].str))
			return conversion[vmid].val;
	}
	return HH_VM_MAX;
}

static void guestvm_loader_rm_notifier(struct work_struct *vm_loader_work)
{
	struct guestvm_loader_private *priv;
	int ret = 0;

	priv = container_of(vm_loader_work, struct guestvm_loader_private,
				vm_loader_work);
	priv->vmid = hh_rm_vm_alloc_vmid(get_hh_vm_name(priv->vm_name));
	if (priv->vmid == HH_VM_MAX) {
		dev_err(priv->dev, "Couldn't get vmid.\n");
		return;
	}
	ret = hh_rm_vm_start(priv->vmid);
	if (ret)
		dev_err(priv->dev, "VM start has failed with %d.\n", ret);
}

static ssize_t guestvm_load_start(struct kobject *kobj,
	struct kobj_attribute *attr,
	const char *buf,
	size_t count)
{
	struct guestvm_loader_private *priv;
	int ret = 0;
	bool boot = false;

	ret = kstrtobool(buf, &boot);
	if (ret)
		return -EINVAL;

	priv = container_of(kobj, struct guestvm_loader_private,
				vm_loader_kobj);
	if (ret != 1 || priv->vm_loaded) {
		dev_err(priv->dev, "invalid arguments for guestvm_loader.\n");
		return -EINVAL;
	}

	if (boot) {
		priv->vm_loaded = subsystem_get(priv->vm_name);
		if (IS_ERR(priv->vm_loaded)) {
			ret = (int)(PTR_ERR(priv->vm_loaded));
			dev_err(priv->dev,
				"subsystem_get failed with error %d\n", ret);
			priv->vm_loaded = NULL;
			return ret;
		}
		schedule_work(&priv->vm_loader_work);
	}

	return ret;
}
static struct kobj_attribute guestvm_loader_attribute =
__ATTR(boot_guestvm, 0220, NULL, guestvm_load_start);

static struct attribute *attrs[] = {
	&guestvm_loader_attribute.attr,
	NULL,
};

static struct attribute_group attr_group = {
	.attrs = attrs,
};

static int guestvm_loader_probe(struct platform_device *pdev)
{
	struct guestvm_loader_private *priv = NULL;
	const char *sub_sys;
	int ret = 0;

	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	platform_set_drvdata(pdev, priv);
	priv->dev = &pdev->dev;

	ret = of_property_read_string(pdev->dev.of_node, "image_to_be_loaded",
		 &sub_sys);
	if (ret)
		return -EINVAL;
	strlcpy(priv->vm_name, sub_sys, sizeof(priv->vm_name));

	INIT_WORK(&priv->vm_loader_work, guestvm_loader_rm_notifier);

	ret = kobject_init_and_add(&priv->vm_loader_kobj, &guestvm_kobj_type,
				   kernel_kobj, "load_guestvm");
	if (ret) {
		dev_err(&pdev->dev, "sysfs create and add failed\n");
		ret = -ENOMEM;
		goto error_return;
	}

	ret = sysfs_create_group(&priv->vm_loader_kobj, &attr_group);
	if (ret) {
		dev_err(&pdev->dev, "sysfs create group failed %d\n", ret);
		goto error_return;
	}

	return 0;

error_return:
	if (kobject_name(&priv->vm_loader_kobj) != NULL) {
		kobject_del(&priv->vm_loader_kobj);
		kobject_put(&priv->vm_loader_kobj);

		memset(&priv->vm_loader_kobj, 0, sizeof(priv->vm_loader_kobj));
	}

	return ret;
}

static int guestvm_loader_remove(struct platform_device *pdev)
{
	struct guestvm_loader_private *priv = platform_get_drvdata(pdev);

	if (priv->vm_loaded)
		subsystem_put(priv->vm_loaded);

	if (kobject_name(&priv->vm_loader_kobj) != NULL) {
		kobject_del(&priv->vm_loader_kobj);
		kobject_put(&priv->vm_loader_kobj);

		memset(&priv->vm_loader_kobj, 0, sizeof(priv->vm_loader_kobj));
	}

	return 0;
}

static const struct of_device_id guestvm_loader_match_table[] = {
	{ .compatible = "qcom,guestvm-loader" },
	{},
};

static struct platform_driver guestvm_loader_driver = {
	.probe = guestvm_loader_probe,
	.remove = guestvm_loader_remove,
	.driver = {
		.name = "qcom_guestvm_loader",
		.of_match_table = guestvm_loader_match_table,
	},
};

module_platform_driver(guestvm_loader_driver);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. GuestVM loader");
MODULE_LICENSE("GPL v2");
Loading