mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 11:37:06 +02:00
Merge "gunyah: Adding basic initial files needed for vm on Pineapple"
This commit is contained in:
commit
cf6c961594
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
13
arch/arm64/gunyah/Kconfig
Normal file
13
arch/arm64/gunyah/Kconfig
Normal file
|
|
@ -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
|
||||
|
||||
4
arch/arm64/gunyah/Makefile
Normal file
4
arch/arm64/gunyah/Makefile
Normal file
|
|
@ -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
|
||||
21
arch/arm64/gunyah/gh_arm.c
Normal file
21
arch/arm64/gunyah/gh_arm.c
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
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");
|
||||
127
arch/arm64/gunyah/irq.c
Normal file
127
arch/arm64/gunyah/irq.c
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <linux/gunyah/gh_rm_drv.h>
|
||||
|
||||
#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);
|
||||
51
arch/arm64/gunyah/reset.c
Normal file
51
arch/arm64/gunyah/reset.c
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gunyah/gh_rm_drv.h>
|
||||
#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);
|
||||
|
||||
48
arch/arm64/gunyah/reset.h
Normal file
48
arch/arm64/gunyah/reset.h
Normal file
|
|
@ -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
|
||||
64
arch/arm64/include/asm/gunyah/hcall.h
Normal file
64
arch/arm64/include/asm/gunyah/hcall.h
Normal file
|
|
@ -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 <linux/types.h>
|
||||
|
||||
#include <linux/gunyah/hcall_common.h>
|
||||
|
||||
/**
|
||||
* _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
|
||||
Loading…
Reference in New Issue
Block a user