From 07219d4f2b0db21c70562e3db6443e36fac0d409 Mon Sep 17 00:00:00 2001 From: Peng Yang Date: Fri, 1 Jul 2022 21:09:46 -0700 Subject: [PATCH] gunyah: Adding basic initial files needed for vm on Pineapple Adding basic library, Kconfig, make and arch specific files needed for vm on Pineapple. This is just snapshot of kalama's basic initial files from 'commit 5beb98404269 ("Merge "i3c: i3c-master-msm-geni: Fix IBI for invalid device OR handler"")'from msm-5.15 branch. Change-Id: I958bcbda1136fb7b7e6d957f303af8cf7773322e Signed-off-by: Peng Yang --- arch/arm64/Kbuild | 1 + arch/arm64/Kconfig | 2 + arch/arm64/Kconfig.platforms | 9 ++ arch/arm64/gunyah/Kconfig | 13 +++ arch/arm64/gunyah/Makefile | 4 + arch/arm64/gunyah/gh_arm.c | 21 +++++ arch/arm64/gunyah/irq.c | 127 ++++++++++++++++++++++++++ arch/arm64/gunyah/reset.c | 51 +++++++++++ arch/arm64/gunyah/reset.h | 48 ++++++++++ arch/arm64/include/asm/gunyah/hcall.h | 64 +++++++++++++ 10 files changed, 340 insertions(+) create mode 100644 arch/arm64/gunyah/Kconfig create mode 100644 arch/arm64/gunyah/Makefile create mode 100644 arch/arm64/gunyah/gh_arm.c create mode 100644 arch/arm64/gunyah/irq.c create mode 100644 arch/arm64/gunyah/reset.c create mode 100644 arch/arm64/gunyah/reset.h create mode 100644 arch/arm64/include/asm/gunyah/hcall.h diff --git a/arch/arm64/Kbuild b/arch/arm64/Kbuild index 5bfbf7d79c99..b03e545c9ddf 100644 --- a/arch/arm64/Kbuild +++ b/arch/arm64/Kbuild @@ -4,6 +4,7 @@ obj-$(CONFIG_KVM) += kvm/ obj-$(CONFIG_XEN) += xen/ obj-$(subst m,y,$(CONFIG_HYPERV)) += hyperv/ obj-$(CONFIG_CRYPTO) += crypto/ +obj-$(CONFIG_GUNYAH_DRIVERS) += gunyah/ # for cleaning subdir- += boot diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 1652a9800ebe..ff44433811d6 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2186,6 +2186,8 @@ source "drivers/acpi/Kconfig" source "arch/arm64/kvm/Kconfig" +source "arch/arm64/gunyah/Kconfig" + if CRYPTO source "arch/arm64/crypto/Kconfig" endif # CRYPTO diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index a2fccbaf4829..3f64d193daba 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -209,6 +209,15 @@ config ARCH_QCOM help This enables support for the ARMv8 based Qualcomm chipsets. +config ARCH_QTI_VM + bool "Enable Virtual Machines Support for Qualcomm Technologies, Inc." + depends on ARCH_QCOM + help + This enables support for the Qualcomm Technologies, Inc.'s + Virtual Machines. If you wish to build a kernel that doesn't + require VM support or if you are unsure, + say 'N' here. + config ARCH_PINEAPPLE bool "Enable support for Qualcomm Technologies, Inc. Pineapple" depends on ARCH_QCOM diff --git a/arch/arm64/gunyah/Kconfig b/arch/arm64/gunyah/Kconfig new file mode 100644 index 000000000000..327bfa3cad16 --- /dev/null +++ b/arch/arm64/gunyah/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menu "Gunyah arm64 drivers" +config GH_ARM64_DRV + tristate "Gunyah arm64 support" + depends on ARM64 + help + Gunyah drivers need arch specific support. Add support for + ARM64 features required by Gunyah virtualizaton drivers. All + arm64 specific features like GIC and reset reasons are handled + by drivers under this config. +endmenu + diff --git a/arch/arm64/gunyah/Makefile b/arch/arm64/gunyah/Makefile new file mode 100644 index 000000000000..2ef4ed957bc5 --- /dev/null +++ b/arch/arm64/gunyah/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_GH_ARM64_DRV) += gh_arm_drv.o +gh_arm_drv-y := gh_arm.o irq.o reset.o diff --git a/arch/arm64/gunyah/gh_arm.c b/arch/arm64/gunyah/gh_arm.c new file mode 100644 index 000000000000..62919e19584e --- /dev/null +++ b/arch/arm64/gunyah/gh_arm.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * + */ + +#include + +static int __init gh_arm_init(void) +{ + return 0; +} +module_init(gh_arm_init); + +static void __exit gh_arm_exit(void) +{ +} +module_exit(gh_arm_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Gunyah ARM64 Driver"); diff --git a/arch/arm64/gunyah/irq.c b/arch/arm64/gunyah/irq.c new file mode 100644 index 000000000000..25f6d104bdb1 --- /dev/null +++ b/arch/arm64/gunyah/irq.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * + */ + +#include +#include +#include + +#include +#include + +#define GIC_V3_SPI_MAX 1019 + +#define GH_RM_NO_IRQ_ALLOC -1 + +#define IRQ_OFFSET 32 + +static DEFINE_IDR(gh_rm_free_virq_idr); + +/** + * gh_get_irq: Get a Linux IRQ from a Gunyah-compatible vIRQ + * @virq: Gunyah-compatible vIRQ + * @type: IRQ trigger type (IRQ_TYPE_EDGE_RISING) + * @fw_handle: fw node handle + * + * Returns the mapped Linux IRQ# at Gunyah's IRQ domain (i.e. GIC SPI) + */ +int gh_get_irq(u32 virq, u32 type, struct fwnode_handle *fw_handle) +{ + struct irq_fwspec fwspec = {}; + + if (virq < IRQ_OFFSET || virq >= GIC_V3_SPI_MAX) { + pr_warn("%s: expecting an SPI from RM, but got GIC IRQ %d\n", + __func__, virq); + } + + fwspec.fwnode = fw_handle; + fwspec.param_count = 3; + fwspec.param[0] = GIC_SPI; + fwspec.param[1] = virq - IRQ_OFFSET; + fwspec.param[2] = type; + + return irq_create_fwspec_mapping(&fwspec); +} +EXPORT_SYMBOL(gh_get_irq); + +/** + * gh_get_virq: Allocate a new IRQ if RM-VM hasn't already done already + * @base_virq: The base virtual IRQ number. + * @virq: The virtual IRQ number. + * + * Returns Gunyah compatible vIRQ to bind to. + */ +int gh_get_virq(int base_virq, int virq) +{ + int ret; + + /* Get the next free vIRQ. + * Subtract IRQ_OFFSET from the base virq to get the base SPI. + * + * Assoiate the address of the idr variable itself as a lookup + * ptr. This will help us to free the virq later. + */ + ret = virq = idr_alloc(&gh_rm_free_virq_idr, + &gh_rm_free_virq_idr, + base_virq - IRQ_OFFSET, + GIC_V3_SPI_MAX, GFP_KERNEL); + if (ret < 0) + return ret; + + /* Add IRQ_OFFSET offset to make interrupt as hwirq */ + virq += IRQ_OFFSET; + + return virq; +} +EXPORT_SYMBOL(gh_get_virq); + +/** + * gh_put_virq: Deallocates a vIRQ. + * @irq: The IRQ number. + * + * Returns 0 on success and EINVAL if no IRQ was found. + */ +int gh_put_virq(int virq) +{ + void *idr_ptr; + int virq_num; + + virq_num = virq - IRQ_OFFSET; + /* If the idr_find() returns a valid ptr, it means that the + * virq was allocated by the kernel itself and not by hyp. + * Release the IRQ and free the allocation if that's true. + */ + idr_ptr = idr_find(&gh_rm_free_virq_idr, virq_num); + if (idr_ptr) { + idr_remove(&gh_rm_free_virq_idr, virq_num); + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL(gh_put_virq); + +/** + * gh_put_irq: Deallocate an Linux IRQ. + * @irq: The IRQ number. + * + * Returns 0 on success and EINVAL if no IRQ was found. + */ +int gh_put_irq(int irq) +{ + struct irq_data *irq_data; + + if (irq <= 0) + return -EINVAL; + + irq_data = irq_get_irq_data(irq); + if (!irq_data) + return -EINVAL; + + irq_dispose_mapping(irq); + + return gh_put_virq(irq_data->hwirq); +} +EXPORT_SYMBOL(gh_put_irq); diff --git a/arch/arm64/gunyah/reset.c b/arch/arm64/gunyah/reset.c new file mode 100644 index 000000000000..57e73de7bfe6 --- /dev/null +++ b/arch/arm64/gunyah/reset.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * + */ + +#include +#include "reset.h" + +/** + * gh_arch_validate_vm_exited_notif: Validate the arch specific exit + * reason and provide a generic reason for further use. + * @buff_size: Size of the buffer containing the exit reason + * @hdr_size: Size of the header + * @vm_exited_payload: Struct of exit_reason + * + * If the exit reason is not valid or has an incorrect size, -EINVAL is + * returned, 0 otherwise and also provides a generic reason for exit + * which can be used by drivers. + */ +int gh_arch_validate_vm_exited_notif(size_t buff_size, size_t hdr_size, + struct gh_rm_notif_vm_exited_payload *vm_exited_payload) +{ + size_t min_buf_sz = hdr_size | sizeof(*vm_exited_payload); + + switch (vm_exited_payload->exit_type) { + case GH_RM_VM_EXIT_TYPE_PSCI_SYSTEM_RESET2: + if ((vm_exited_payload->exit_reason_size != + MAX_EXIT_REASON_SIZE) || + (buff_size != min_buf_sz + + sizeof(struct gh_vm_exit_reason_psci_sys_reset2))) { + pr_err("%s: Invalid size for type PSCI_SYSTEM_RESET2: %u\n", + __func__, buff_size - hdr_size); + return -EINVAL; + } + vm_exited_payload->exit_type = GH_RM_VM_EXIT_TYPE_SYSTEM_RESET; + fallthrough; + case GH_RM_VM_EXIT_TYPE_PSCI_SYSTEM_RESET: + vm_exited_payload->exit_type = GH_RM_VM_EXIT_TYPE_SYSTEM_RESET; + break; + case GH_RM_VM_EXIT_TYPE_PSCI_SYSTEM_OFF: + vm_exited_payload->exit_type = GH_RM_VM_EXIT_TYPE_SYSTEM_OFF; + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(gh_arch_validate_vm_exited_notif); + diff --git a/arch/arm64/gunyah/reset.h b/arch/arm64/gunyah/reset.h new file mode 100644 index 000000000000..988429e789dc --- /dev/null +++ b/arch/arm64/gunyah/reset.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * + */ + +#ifndef __RESET_H +#define __RESET_H + +#define GH_RM_VM_EXIT_TYPE_PSCI_SYSTEM_OFF 1 +#define GH_RM_VM_EXIT_TYPE_PSCI_SYSTEM_RESET 2 +#define GH_RM_VM_EXIT_TYPE_PSCI_SYSTEM_RESET2 3 + +/* GH_RM_VM_EXIT_TYPE_PSCI_SYSTEM_RESET2 */ +struct gh_vm_exit_reason_psci_sys_reset2 { + u16 exit_flags; + /* GH_PSCI_SYS_RESET2_EXIT_FLAG_* are bit representations. + * It follows similar flags model as that of VM_EXIT, but + * only if the vendor_reset field in the struct is set + */ +#define GH_PSCI_SYS_RESET2_EXIT_FLAG_TYPE 0x1 +#define GH_PSCI_SYS_RESET2_POWEROFF 0 /* Value at bit:0 */ +#define GH_PSCI_SYS_RESET2_RESTART 1 /* Value at bit:0 */ +#define GH_PSCI_SYS_RESET2_EXIT_FLAG_SYSTEM 0x2 +#define GH_PSCI_SYS_RESET2_EXIT_FLAG_WARM 0x4 +#define GH_PSCI_SYS_RESET2_EXIT_FLAG_DUMP 0x8 + + u8 exit_code; + /* Exit codes. + * It follows similar flags model as that of VM_EXIT, but + * only if the vendor_reset field in the struct is set + */ +#define GH_PSCI_SYS_RESET2_CODE_NORMAL 0 +#define GH_PSCI_SYS_RESET2_SOFTWARE_ERR 1 +#define GH_PSCI_SYS_RESET2_BUS_ERR 2 +#define GH_PSCI_SYS_RESET2_DEVICE_ERR 3 + + u8 reserved:7; + + /* If the vendor_reset is set, the above flags and codes apply. + * Else, the entire exit_reason struct is 0, which qualifies as + * PSCI_SYSTEM_WARM_RESET. Hence, first check this field before + * checking others. + */ + u8 vendor_reset:1; +} __packed; + +#endif diff --git a/arch/arm64/include/asm/gunyah/hcall.h b/arch/arm64/include/asm/gunyah/hcall.h new file mode 100644 index 000000000000..2365bb231ccf --- /dev/null +++ b/arch/arm64/include/asm/gunyah/hcall.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ +#ifndef __ASM_GH_HCALL_H +#define __ASM_GH_HCALL_H + +#include + +#include + +/** + * _gh_hcall: Performs an AArch64-specific call into hypervisor using Gunyah ABI + * @hcall_num: Hypercall function ID to invoke + * @args: Hypercall argument registers + * @resp: Pointer to location to store response + */ +static inline int _gh_hcall(const gh_hcall_fnid_t hcall_num, + const struct gh_hcall_args args, + struct gh_hcall_resp *resp) +{ + uint64_t _x18; + + register uint64_t _x0 asm("x0") = args.arg0; + register uint64_t _x1 asm("x1") = args.arg1; + register uint64_t _x2 asm("x2") = args.arg2; + register uint64_t _x3 asm("x3") = args.arg3; + register uint64_t _x4 asm("x4") = args.arg4; + register uint64_t _x5 asm("x5") = args.arg5; + register uint64_t _x6 asm("x6") = args.arg6; + register uint64_t _x7 asm("x7") = args.arg7; + + asm volatile ( +#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK) + "str x18, [%[_x18]]\n" +#endif + "hvc %[num]\n" +#if IS_ENABLED(CONFIG_SHADOW_CALL_STACK) + "ldr x18, [%[_x18]]\n" + "str xzr, [%[_x18]]\n" +#endif + : "+r"(_x0), "+r"(_x1), "+r"(_x2), "+r"(_x3), "+r"(_x4), + "+r"(_x5), "+r"(_x6), "+r"(_x7) + : [num] "i" (hcall_num), [_x18] "r"(&_x18) + : "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", +#if !IS_ENABLED(CONFIG_SHADOW_CALL_STACK) + "x18", +#endif + "memory" + ); + + resp->resp0 = _x0; + resp->resp1 = _x1; + resp->resp2 = _x2; + resp->resp3 = _x3; + resp->resp4 = _x4; + resp->resp5 = _x5; + resp->resp6 = _x6; + resp->resp7 = _x7; + + return _x0; +} + +#endif