diff --git a/arm-add-CONFIG_SDEI-build-flag.patch b/arm-add-CONFIG_SDEI-build-flag.patch new file mode 100644 index 0000000000000000000000000000000000000000..a13f18dad3f43933d2d3480308232f2b0737c7ed --- /dev/null +++ b/arm-add-CONFIG_SDEI-build-flag.patch @@ -0,0 +1,53 @@ +From 1f3ab424f8ac93c2f30cf12c6f8161c34eb35317 Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:46 +0800 +Subject: [PATCH 04/15] arm: add CONFIG_SDEI build flag + +Integrate SDEI support for arm/aarch64 targets by default, if KVM is +enabled. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + default-configs/arm-softmmu.mak | 1 + + hw/arm/Kconfig | 4 ++++ + target/arm/Makefile.objs | 2 ++ + 3 files changed, 7 insertions(+) + +diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak +index 1f2e0e7f..fc1f2b2e 100644 +--- a/default-configs/arm-softmmu.mak ++++ b/default-configs/arm-softmmu.mak +@@ -40,3 +40,4 @@ CONFIG_FSL_IMX25=y + CONFIG_FSL_IMX7=y + CONFIG_FSL_IMX6UL=y + CONFIG_SEMIHOSTING=y ++CONFIG_SDEI=y +diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig +index 15e18b0a..b163eda9 100644 +--- a/hw/arm/Kconfig ++++ b/hw/arm/Kconfig +@@ -453,3 +453,7 @@ config ARMSSE_CPUID + + config ARMSSE_MHU + bool ++ ++config SDEI ++ bool ++ depends on KVM +diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs +index 5cafc1eb..f739ce24 100644 +--- a/target/arm/Makefile.objs ++++ b/target/arm/Makefile.objs +@@ -11,6 +11,8 @@ obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o + obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o + obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o + ++obj-$(CONFIG_SDEI) += sdei.o ++ + DECODETREE = $(SRC_PATH)/scripts/decodetree.py + + target/arm/decode-sve.inc.c: $(SRC_PATH)/target/arm/sve.decode $(DECODETREE) +-- +2.23.0 + diff --git a/arm-kvm-handle-guest-exit-of-hypercall.patch b/arm-kvm-handle-guest-exit-of-hypercall.patch new file mode 100644 index 0000000000000000000000000000000000000000..1e2985ec41508481c9289afb82954aab0aad8e56 --- /dev/null +++ b/arm-kvm-handle-guest-exit-of-hypercall.patch @@ -0,0 +1,59 @@ +From 8ab6fab137a1a99c3adb5831ca180d68f138622a Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:55 +0800 +Subject: [PATCH 13/15] arm/kvm: handle guest exit of hypercall + +Add support to handle guest exit of hypercall, and forward to SDEI +dispatcher if SDEI is enabled and it is an SDEI request. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + target/arm/kvm.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/target/arm/kvm.c b/target/arm/kvm.c +index 4f131f68..566ab461 100644 +--- a/target/arm/kvm.c ++++ b/target/arm/kvm.c +@@ -30,6 +30,7 @@ + #include "exec/address-spaces.h" + #include "hw/boards.h" + #include "qemu/log.h" ++#include "sdei.h" + + const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO +@@ -788,6 +789,19 @@ void kvm_arm_vm_state_change(void *opaque, int running, RunState state) + } + } + ++static void kvm_arm_handle_hypercall(CPUState *cs, struct kvm_run *run) ++{ ++ uint32_t func_id = run->hypercall.args[0]; ++ ++ if (sdei_enabled && ++ func_id >= SDEI_1_0_FN_BASE && func_id <= SDEI_MAX_REQ) { ++ sdei_handle_request(cs, run); ++ return; ++ } ++ ++ run->hypercall.args[0] = -1; ++} ++ + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) + { + int ret = 0; +@@ -798,6 +812,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) + ret = EXCP_DEBUG; + } /* otherwise return to guest */ + break; ++ case KVM_EXIT_HYPERCALL: ++ kvm_arm_handle_hypercall(cs, run); ++ break; + default: + qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n", + __func__, run->exit_reason); +-- +2.23.0 + diff --git a/arm-sdei-Add-SDEI-watchdog.patch b/arm-sdei-Add-SDEI-watchdog.patch new file mode 100644 index 0000000000000000000000000000000000000000..2bef9241069f17336f44057ffb29c784e96c3d40 --- /dev/null +++ b/arm-sdei-Add-SDEI-watchdog.patch @@ -0,0 +1,370 @@ +From 7ddd02a21e7afad01ff250fadb11b451accea351 Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Mon, 23 Sep 2019 15:32:49 +0800 +Subject: [PATCH 15/15] arm/sdei: Add SDEI watchdog + +Add SDEI watchdog virtual device + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + target/arm/Makefile.objs | 1 + + target/arm/sdei.c | 8 ++ + target/arm/sdei.h | 7 +- + target/arm/sdei_watchdog.c | 237 +++++++++++++++++++++++++++++++++++++ + target/arm/sdei_watchdog.h | 43 +++++++ + 5 files changed, 295 insertions(+), 1 deletion(-) + create mode 100644 target/arm/sdei_watchdog.c + create mode 100644 target/arm/sdei_watchdog.h + +diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs +index 64c8bdc8..2f459d9e 100644 +--- a/target/arm/Makefile.objs ++++ b/target/arm/Makefile.objs +@@ -12,6 +12,7 @@ obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o + obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o + + obj-$(CONFIG_SDEI) += sdei.o ++obj-$(CONFIG_SDEI) += sdei_watchdog.o + obj-$(call lnot,$(CONFIG_SDEI)) += sdei-stub.o + + +diff --git a/target/arm/sdei.c b/target/arm/sdei.c +index 16e22027..6d8232cd 100644 +--- a/target/arm/sdei.c ++++ b/target/arm/sdei.c +@@ -31,6 +31,7 @@ + #include "qemu/error-report.h" + #include "sdei.h" + #include "sdei_int.h" ++#include "sdei_watchdog.h" + #include "internals.h" + #include "hw/boards.h" + #include "hw/intc/arm_gicv3.h" +@@ -1278,6 +1279,13 @@ static sdei_single_function sdei_functions[] = { + sdei_features, + sdei_private_reset, + sdei_shared_reset, ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ sdei_set_secure_timer_period, + }; + + void sdei_handle_request(CPUState *cs, struct kvm_run *run) +diff --git a/target/arm/sdei.h b/target/arm/sdei.h +index 9bb9b977..51142af3 100644 +--- a/target/arm/sdei.h ++++ b/target/arm/sdei.h +@@ -27,7 +27,12 @@ + #include + #include "qom/cpu.h" + +-#define SDEI_MAX_REQ SDEI_1_0_FN(0x12) ++/* ++ * The max request defined in SDEI v1.0 is only 0x12 (func_id - ++ * SDEI_func_id_base), but we define request 0x19 as an internal request to set ++ * sdei watchdog period. ++ */ ++#define SDEI_MAX_REQ SDEI_1_0_FN(0x19) + + extern bool sdei_enabled; + +diff --git a/target/arm/sdei_watchdog.c b/target/arm/sdei_watchdog.c +new file mode 100644 +index 00000000..1e718dac +--- /dev/null ++++ b/target/arm/sdei_watchdog.c +@@ -0,0 +1,237 @@ ++/* ++ * ARM SDEI watchdog virtual device ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. ++ * ++ * Authors: ++ * Jingyi Wang ++ * Heyi Guo ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2 or later, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "hw/arm/virt.h" ++#include "hw/boards.h" ++#include "hw/hw.h" ++#include "hw/irq.h" ++#include "hw/sysbus.h" ++#include "migration/vmstate.h" ++#include "qapi/error.h" ++#include "qemu/timer.h" ++#include "qemu/error-report.h" ++#include "qom/object.h" ++#include "sysemu/kvm.h" ++#include "sysemu/sysemu.h" ++#include "arm-powerctl.h" ++#include "internals.h" ++#include "sdei.h" ++#include "sdei_watchdog.h" ++#include "qemu-common.h" ++#include "qemu/log.h" ++ ++#define TYPE_QEMU_SDEI_WATCHDOG "qemu_sdei_watchdog" ++#define QEMU_SDEI_WD(obj) \ ++ OBJECT_CHECK(QemuSdeiWdState, (obj), TYPE_QEMU_SDEI_WATCHDOG) ++ ++#define MILLISECONDS_PER_SECOND 1000 ++#define DEFAULT_WATCHDOG_PERIOD 10 ++/* ++ * We don't support period more than 1 day. ++ * If we increase the value, we need to reconsider the type of wd->interval, as ++ * type of "int" may cause integer overflow when being multiplied with ++ * MILLISECONDS_PER_SECOND. ++ */ ++#define MAX_WATCHDOG_PERIOD (3600 * 24) ++ ++static QemuSdeiWdState *sdei_watchdog_state; ++ ++static void sdei_watchdog_timer_callback(void *data) ++{ ++ QemuSdeiWdState *wd = data; ++ CPUState *cs; ++ timer_mod(wd->timer, ++ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + ++ wd->interval * MILLISECONDS_PER_SECOND); ++ CPU_FOREACH(cs) { ++ qemu_set_irq(wd->irqs[cs->cpu_index], 1); ++ } ++} ++ ++static void populate_timer_irqs(QemuSdeiWdState *wd) ++{ ++ int i; ++ ++ for (i = 0; i < current_machine->smp.cpus; i++) { ++ DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); ++ wd->irqs[i] = qdev_get_gpio_out_connector(cpudev, NULL, GTIMER_SEC); ++ } ++} ++ ++static void sdei_watchdog_timer_enable(QemuSdeiWdState *wd) ++{ ++ if (!wd->timer_initialized) { ++ wd->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, ++ sdei_watchdog_timer_callback, wd); ++ if (!wd->timer) { ++ qemu_log("error: Create timer failed!\n"); ++ return; ++ } ++ timer_mod(wd->timer, ++ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + ++ MILLISECONDS_PER_SECOND * wd->interval); ++ wd->timer_initialized = true; ++ populate_timer_irqs(wd); ++ } ++} ++ ++static void sdei_watchdog_timer_disable(QemuSdeiWdState *wd) ++{ ++ if (wd->timer_initialized) { ++ timer_del(wd->timer); ++ timer_free(wd->timer); ++ wd->timer_initialized = false; ++ } ++} ++ ++int64_t sdei_set_secure_timer_period(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ if (!sdei_watchdog_state) { ++ return SDEI_NOT_SUPPORTED; ++ } ++ ++ if (args[1] == 0 || args[1] > MAX_WATCHDOG_PERIOD) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ sdei_watchdog_state->interval = args[1]; ++ return SDEI_SUCCESS; ++} ++ ++static void qemu_sdei_wd_reset(void *opaque) ++{ ++ QemuSdeiWdState *wd = opaque; ++ sdei_watchdog_timer_disable(wd); ++} ++ ++static int qemu_sdei_wd_pre_save(void *opaque) ++{ ++ QemuSdeiWdState *wd = opaque; ++ ++ qemu_log("info: SDEI watchdog pre save\n"); ++ if (wd->timer_initialized) { ++ timer_del(wd->timer); ++ timer_free(wd->timer); ++ qemu_log("info: delete sdei_timer\n"); ++ } ++ ++ return 0; ++} ++ ++static int qemu_sdei_wd_post_load(void *opaque, int version_id) ++{ ++ qemu_log("info: SDEI watchdog post load\n"); ++ QemuSdeiWdState *wd = opaque; ++ ++ /* initialize the watchdog timer */ ++ if (wd->timer_initialized) { ++ wd->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, ++ sdei_watchdog_timer_callback, ++ wd); ++ if (!wd->timer) { ++ qemu_log("error: Create timer failed!\n"); ++ return -1; ++ } ++ timer_mod(wd->timer, ++ qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + ++ MILLISECONDS_PER_SECOND * wd->interval); ++ populate_timer_irqs(wd); ++ } ++ return 0; ++} ++ ++static const VMStateDescription vmstate_sdei_wd_state = { ++ .name = "qemu_sdei_wd", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .pre_save = qemu_sdei_wd_pre_save, ++ .post_load = qemu_sdei_wd_post_load, ++ .fields = (VMStateField[]) { ++ VMSTATE_BOOL(timer_initialized, QemuSdeiWdState), ++ VMSTATE_INT32(interval, QemuSdeiWdState), ++ VMSTATE_INT32(event, QemuSdeiWdState), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++static void sdei_watchdog_bind_notify(void *opaque, int irq, ++ int32_t event, bool bind) ++{ ++ QemuSdeiWdState *wd = opaque; ++ ++ if (bind) { ++ wd->event = event; ++ sdei_watchdog_timer_enable(wd); ++ } else { ++ wd->event = -1; ++ sdei_watchdog_timer_disable(wd); ++ } ++} ++ ++static void sdei_wd_initfn(Object *obj) ++{ ++ QemuSdeiWdState *wd = QEMU_SDEI_WD(obj); ++ if (sdei_watchdog_state) { ++ qemu_log("error: Only allow one SDEI watchdog instance!\n"); ++ abort(); ++ } ++ ++ sdei_watchdog_state = wd; ++ wd->timer_initialized = false; ++ wd->interval = DEFAULT_WATCHDOG_PERIOD; ++ wd->event = -1; ++ /* GIC_NR_SGIS + ARCH_TIMER_S_EL1_IRQ = GIC interrupt ID for secure timer */ ++ qemu_register_sdei_bind_notifier(sdei_watchdog_bind_notify, (void *)wd, ++ GIC_NR_SGIS + ARCH_TIMER_S_EL1_IRQ); ++ wd->irqs = g_malloc0_n(current_machine->smp.max_cpus, sizeof(qemu_irq)); ++ if (wd->irqs == NULL) { ++ qemu_log("error: Allocate memory failed!\n"); ++ abort(); ++ } ++ qemu_register_reset(qemu_sdei_wd_reset, wd); ++ qemu_log("info: qemu sdei watchdog initialized\n"); ++} ++ ++static void qemu_sdei_wd_class_init(ObjectClass *klass, void *data) ++{ ++ DeviceClass *dc = DEVICE_CLASS(klass); ++ ++ dc->desc = "SDEI_WATCHDOG_QEMU"; ++ dc->vmsd = &vmstate_sdei_wd_state; ++} ++ ++static const TypeInfo sdei_wd_qemu_info = { ++ .name = TYPE_QEMU_SDEI_WATCHDOG, ++ .parent = TYPE_DEVICE, ++ .instance_size = sizeof(QemuSdeiWdState), ++ .instance_init = sdei_wd_initfn, ++ .class_init = qemu_sdei_wd_class_init, ++}; ++ ++static void register_types(void) ++{ ++ type_register_static(&sdei_wd_qemu_info); ++} ++ ++type_init(register_types); +diff --git a/target/arm/sdei_watchdog.h b/target/arm/sdei_watchdog.h +new file mode 100644 +index 00000000..cc4adac2 +--- /dev/null ++++ b/target/arm/sdei_watchdog.h +@@ -0,0 +1,43 @@ ++/* ++ * ARM SDEI watchdog internal interfaces ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. ++ * ++ * Authors: ++ * Jingyi Wang ++ * Heyi Guo ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2 or later, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#ifndef QEMU_SDEI_WATCHDOG_H ++#define QEMU_SDEI_WATCHDOG_H ++ ++#include "qemu/osdep.h" ++#include "qemu/timer.h" ++#include "qom/object.h" ++#include "sdei_int.h" ++ ++typedef struct QemuSdeiWdState { ++ DeviceState parent_obj; ++ bool timer_initialized; ++ int interval; ++ int32_t event; ++ QEMUTimer *timer; ++ qemu_irq *irqs; ++} QemuSdeiWdState; ++ ++int64_t sdei_set_secure_timer_period(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run); ++ ++#endif +-- +2.23.0 + diff --git a/arm-sdei-add-stub-to-fix-build-failure-when-SDEI-is-.patch b/arm-sdei-add-stub-to-fix-build-failure-when-SDEI-is-.patch new file mode 100644 index 0000000000000000000000000000000000000000..ca179f43c4471b71b7938f4807e271c1cba42d28 --- /dev/null +++ b/arm-sdei-add-stub-to-fix-build-failure-when-SDEI-is-.patch @@ -0,0 +1,85 @@ +From bf343b8603a3daf9b2a3ba9ed04aff1f28ee089c Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:54 +0800 +Subject: [PATCH 12/15] arm/sdei: add stub to fix build failure when SDEI is + not enabled + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + target/arm/Makefile.objs | 2 ++ + target/arm/sdei-stub.c | 49 ++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 51 insertions(+) + create mode 100644 target/arm/sdei-stub.c + +diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs +index f739ce24..64c8bdc8 100644 +--- a/target/arm/Makefile.objs ++++ b/target/arm/Makefile.objs +@@ -12,6 +12,8 @@ obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o + obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o + + obj-$(CONFIG_SDEI) += sdei.o ++obj-$(call lnot,$(CONFIG_SDEI)) += sdei-stub.o ++ + + DECODETREE = $(SRC_PATH)/scripts/decodetree.py + +diff --git a/target/arm/sdei-stub.c b/target/arm/sdei-stub.c +new file mode 100644 +index 00000000..4eaf365d +--- /dev/null ++++ b/target/arm/sdei-stub.c +@@ -0,0 +1,49 @@ ++/* ++ * QEMU ARM SDEI specific function stubs ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. ++ * ++ * Author: Heyi Guo ++ * ++ * This work is licensed under the terms of the GNU GPL, version 2 or later. ++ * See the COPYING file in the top-level directory. ++ * ++ */ ++ ++#include "qemu/osdep.h" ++#include "sdei.h" ++ ++bool sdei_enabled; ++ ++void sdei_handle_request(CPUState *cs, struct kvm_run *run) ++{ ++ run->hypercall.args[0] = SDEI_NOT_SUPPORTED; ++ return; ++} ++ ++/* ++ * Trigger an SDEI event bound to an interrupt. ++ * Return true if event has been triggered successfully. ++ * Return false if event has not been triggered for some reason. ++ */ ++bool trigger_sdei_by_irq(int cpu, int irq) ++{ ++ return false; ++} ++ ++/* ++ * Register a notify callback for a specific interrupt bind operation; the ++ * client will be both notified by bind and unbind operation. ++ */ ++void qemu_register_sdei_bind_notifier(QemuSDEIBindNotify *func, ++ void *opaque, int irq) ++{ ++} ++ ++/* ++ * Unregister a notify callback for a specific interrupt bind operation. ++ */ ++void qemu_unregister_sdei_bind_notifier(QemuSDEIBindNotify *func, ++ void *opaque, int irq) ++{ ++} +-- +2.23.0 + diff --git a/arm-sdei-add-support-to-handle-SDEI-requests-from-gu.patch b/arm-sdei-add-support-to-handle-SDEI-requests-from-gu.patch new file mode 100644 index 0000000000000000000000000000000000000000..a48bf8f9f8eebaecd70e90431cd3bac0d3d09415 --- /dev/null +++ b/arm-sdei-add-support-to-handle-SDEI-requests-from-gu.patch @@ -0,0 +1,1108 @@ +From ccfc79d5fffcb30024ad7ba191459a3997bdae42 Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:47 +0800 +Subject: [PATCH 05/15] arm/sdei: add support to handle SDEI requests from + guest + +Add support for all interfaces defined in ARM SDEI 1.0 spec. + +http://infocenter.arm.com/help/topic/com.arm.doc.den0054a/ARM_DEN0054A_Software_Delegated_Exception_Interface.pdf + +The exit reason KVM_EXIT_HYPERCALL is used to indicate it is an +HVC/SMC forward, and the structure kvm_run->hypercall is used to pass +arguments and return values between KVM and qemu: +Input: + nr: the immediate value of SMC/HVC calls; not really used today. + args[6]: x0..x5 (This is not fully conform with SMCCC which requires + x6 as argument as well, but we can use GET_ONE_REG ioctl + for such rare case). +Return: + args[0..3]: x0..x3 as defined in SMCCC. We rely on KVM to extract + args[0..3] and write them to x0..x3 when hypercall exit + returns. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + target/arm/sdei.c | 1002 +++++++++++++++++++++++++++++++++++++++++++++ + target/arm/sdei.h | 34 ++ + 2 files changed, 1036 insertions(+) + create mode 100644 target/arm/sdei.h + +diff --git a/target/arm/sdei.c b/target/arm/sdei.c +index 6ebcc096..1fb5bcf3 100644 +--- a/target/arm/sdei.c ++++ b/target/arm/sdei.c +@@ -29,6 +29,7 @@ + #include "sysemu/sysemu.h" + #include "sysemu/reset.h" + #include "qemu/error-report.h" ++#include "sdei.h" + #include "sdei_int.h" + #include "internals.h" + #include "hw/boards.h" +@@ -42,6 +43,9 @@ + #define TYPE_ARM_SDEI "arm_sdei" + #define QEMU_SDEI(obj) OBJECT_CHECK(QemuSDEState, (obj), TYPE_ARM_SDEI) + ++#define SMCCC_RETURN_REG_COUNT 4 ++#define PSTATE_M_EL_SHIFT 2 ++ + static QemuSDEState *sde_state; + + static void qemu_sde_prop_init(QemuSDEState *s) +@@ -84,6 +88,16 @@ static void qemu_sde_cpu_init(QemuSDEState *s) + } + } + ++static inline QemuSDECpu *get_sde_cpu(QemuSDEState *s, CPUState *cs) ++{ ++ if (cs->cpu_index >= s->sdei_max_cpus) { ++ error_report("BUG: cpu index %d >= max_cpus %d", ++ cs->cpu_index, s->sdei_max_cpus); ++ return NULL; ++ } ++ return &s->sde_cpus[cs->cpu_index]; ++} ++ + static bool is_valid_event_number(int32_t event) + { + int32_t slot_id; +@@ -122,6 +136,994 @@ static QemuSDEProp *get_sde_prop_no_lock(QemuSDEState *s, int32_t event) + return &s->sde_props_state[SDEI_EVENT_TO_SLOT(event)]; + } + ++static QemuSDEProp *get_sde_prop(QemuSDEState *s, int32_t event) ++{ ++ QemuSDEProp *sde_props = s->sde_props_state; ++ ++ if (!is_valid_event_number(event)) { ++ return NULL; ++ } ++ ++ event = SDEI_EVENT_TO_SLOT(event); ++ ++ qemu_mutex_lock(&sde_props[event].lock); ++ if (sde_props[event].event_id < 0) { ++ qemu_mutex_unlock(&sde_props[event].lock); ++ return NULL; ++ } ++ return &sde_props[event]; ++} ++ ++static void put_sde_prop(QemuSDEProp *prop) ++{ ++ qemu_mutex_unlock(&prop->lock); ++} ++ ++static void sde_slot_lock(QemuSDE *sde, CPUState *cs) ++{ ++ qemu_mutex_lock(&sde->lock); ++} ++ ++static void sde_slot_unlock(QemuSDE *sde, CPUState *cs) ++{ ++ qemu_mutex_unlock(&sde->lock); ++} ++ ++/* ++ * It will always return a pointer to a preallocated sde; event number must be ++ * validated before calling this function. ++ */ ++static QemuSDE *get_sde_no_check(QemuSDEState *s, int32_t event, CPUState *cs) ++{ ++ QemuSDE **array = s->sde_cpus[cs->cpu_index].private_sde_array; ++ int32_t sde_index = SDEI_EVENT_TO_SLOT(event); ++ QemuSDE *sde; ++ ++ if (SDEI_IS_SHARED_EVENT(event)) { ++ array = s->shared_sde_array; ++ sde_index -= PRIVATE_SLOT_COUNT; ++ } ++ ++ sde = array[sde_index]; ++ sde_slot_lock(sde, cs); ++ return sde; ++} ++ ++static void put_sde(QemuSDE *sde, CPUState *cs) ++{ ++ sde_slot_unlock(sde, cs); ++} ++ ++static inline bool is_sde_nested(QemuSDECpu *sde_cpu) ++{ ++ return sde_cpu->critical_running_event >= 0 && ++ sde_cpu->normal_running_event >= 0; ++} ++ ++static int32_t get_running_sde(QemuSDEState *s, CPUState *cs) ++{ ++ QemuSDECpu *sde_cpu = get_sde_cpu(s, cs); ++ ++ if (!sde_cpu) { ++ return SDEI_INVALID_EVENT_ID; ++ } ++ ++ if (sde_cpu->critical_running_event >= 0) { ++ return sde_cpu->critical_running_event; ++ } ++ return sde_cpu->normal_running_event; ++} ++ ++static void override_return_value(CPUState *cs, uint64_t *args) ++{ ++ CPUARMState *env = &ARM_CPU(cs)->env; ++ int i; ++ ++ for (i = 0; i < SMCCC_RETURN_REG_COUNT; i++) { ++ args[i] = env->xregs[i]; ++ } ++} ++ ++static void sde_save_cpu_ctx(CPUState *cs, QemuSDECpu *sde_cpu, bool critical) ++{ ++ CPUARMState *env = &ARM_CPU(cs)->env; ++ QemuSDECpuCtx *ctx = &sde_cpu->ctx[critical ? 1 : 0]; ++ ++ memcpy(ctx->xregs, env->xregs, sizeof(ctx->xregs)); ++ ctx->pc = env->pc; ++ ctx->pstate = pstate_read(env); ++} ++ ++static void sde_restore_cpu_ctx(QemuSDEState *s, CPUState *cs, bool critical) ++{ ++ CPUARMState *env = &ARM_CPU(cs)->env; ++ QemuSDECpu *sde_cpu = get_sde_cpu(s, cs); ++ QemuSDECpuCtx *ctx; ++ ++ if (!sde_cpu) { ++ return; ++ } ++ ++ ctx = &sde_cpu->ctx[critical ? 1 : 0]; ++ ++ kvm_arch_get_registers(cs); ++ ++ env->aarch64 = ((ctx->pstate & PSTATE_nRW) == 0); ++ memcpy(env->xregs, ctx->xregs, sizeof(ctx->xregs)); ++ env->pc = ctx->pc; ++ pstate_write(env, ctx->pstate); ++ aarch64_restore_sp(env, (env->pstate & PSTATE_M) >> PSTATE_M_EL_SHIFT); ++} ++ ++static void sde_restore_cpu_ctx_for_resume(QemuSDEState *s, ++ CPUState *cs, ++ bool critical, ++ uint64_t resume_addr) ++{ ++ CPUARMState *env = &ARM_CPU(cs)->env; ++ QemuSDECpu *sde_cpu = get_sde_cpu(s, cs); ++ QemuSDECpuCtx *ctx; ++ ++ if (!sde_cpu) { ++ return; ++ } ++ ++ ctx = &sde_cpu->ctx[critical ? 1 : 0]; ++ ++ kvm_arch_get_registers(cs); ++ ++ memcpy(env->xregs, ctx->xregs, sizeof(ctx->xregs)); ++ env->pc = resume_addr; ++ env->aarch64 = 1; ++ /* Constructe pstate in pstate_read() */ ++ env->daif = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F; ++ /* Clear nRW/M[4] and M[3:0] */ ++ env->pstate &= ~(PSTATE_nRW | PSTATE_M); ++ /* Set exception mode to EL1h */ ++ env->pstate |= PSTATE_MODE_EL1h; ++ env->elr_el[1] = ctx->pc; ++ env->banked_spsr[KVM_SPSR_EL1 + 1] = ctx->pstate; ++ aarch64_restore_sp(env, 1); ++} ++ ++/* According to SDEI v1.0, GP registers x0~x3 should be set as below */ ++enum { ++ SDEI_TRIGGER_CTX_EVENT = 0, ++ SDEI_TRIGGER_CTX_EP_ARGUMENT, ++ SDEI_TRIGGER_CTX_PC, ++ SDEI_TRIGGER_CTX_PSTATE, ++} SDEITriggerContextIndex; ++ ++static void sde_build_cpu_ctx(CPUState *cs, QemuSDECpu *sde_cpu, QemuSDE *sde) ++{ ++ CPUARMState *env = &ARM_CPU(cs)->env; ++ ++ env->xregs[SDEI_TRIGGER_CTX_EVENT] = sde->prop->event_id; ++ env->xregs[SDEI_TRIGGER_CTX_EP_ARGUMENT] = sde->ep_argument; ++ env->xregs[SDEI_TRIGGER_CTX_PC] = env->pc; ++ env->xregs[SDEI_TRIGGER_CTX_PSTATE] = pstate_read(env); ++ env->pc = sde->ep_address; ++ env->aarch64 = 1; ++ /* Constructe pstate in pstate_read() */ ++ env->daif = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F; ++ /* Clear nRW/M[4] and M[3:0] */ ++ env->pstate &= ~(PSTATE_nRW | PSTATE_M); ++ /* Set exception mode to EL1h */ ++ env->pstate |= PSTATE_MODE_EL1h; ++ aarch64_restore_sp(env, 1); ++} ++ ++static void trigger_sde(CPUState *cs, run_on_cpu_data data) ++{ ++ QemuSDEState *s = sde_state; ++ QemuSDECpu *sde_cpu = get_sde_cpu(s, cs); ++ int32_t event = data.host_int; ++ QemuSDE *sde; ++ ++ if (!sde_cpu) { ++ return; ++ } ++ ++ if (sde_cpu->masked || sde_cpu->critical_running_event >= 0) { ++ return; ++ } ++ ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id == SDEI_INVALID_EVENT_ID) { ++ /* Some race condition happens! */ ++ put_sde(sde, cs); ++ return; ++ } ++ ++ if (sde_cpu->normal_running_event >= 0 && !sde->prop->is_critical) { ++ put_sde(sde, cs); ++ return; ++ } ++ ++ if (!sde->enabled || !sde->pending || sde->running) { ++ /* Some race condition happens! */ ++ put_sde(sde, cs); ++ return; ++ } ++ ++ sde->pending = false; ++ sde->running = true; ++ ++ if (sde->prop->is_critical) { ++ sde_cpu->critical_running_event = sde->prop->event_id; ++ } else { ++ sde_cpu->normal_running_event = sde->prop->event_id; ++ } ++ ++ kvm_arch_get_registers(cs); ++ sde_save_cpu_ctx(cs, sde_cpu, sde->prop->is_critical); ++ sde_build_cpu_ctx(cs, sde_cpu, sde); ++ kvm_arch_put_registers(cs, 1); ++ put_sde(sde, cs); ++} ++ ++static void dispatch_single(QemuSDEState *s, QemuSDE *sde, CPUState *cs) ++{ ++ int32_t event = sde->prop->event_id; ++ bool pending = sde->pending; ++ bool enabled = sde->enabled; ++ CPUState *target = sde->target_cpu; ++ put_sde(sde, cs); ++ ++ if (pending && enabled) { ++ async_run_on_cpu(target, trigger_sde, ++ RUN_ON_CPU_HOST_INT(event)); ++ } ++} ++ ++static bool sde_ready_to_trigger(QemuSDE *sde, CPUState *cs, bool is_critical) ++{ ++ if (sde->event_id == SDEI_INVALID_EVENT_ID) { ++ return false; ++ } ++ if (sde->prop->is_critical != is_critical) { ++ return false; ++ } ++ if (!sde->enabled || !sde->pending || sde->running || ++ sde->target_cpu != cs) { ++ return false; ++ } ++ return true; ++} ++ ++static void dispatch_cpu(QemuSDEState *s, CPUState *cs, bool is_critical) ++{ ++ QemuSDE *sde; ++ int i; ++ ++ for (i = 0; i < PRIVATE_SLOT_COUNT + SHARED_SLOT_COUNT; i++) { ++ sde = get_sde_no_check(s, i, cs); ++ if (!sde_ready_to_trigger(sde, cs, is_critical)) { ++ put_sde(sde, cs); ++ continue; ++ } ++ dispatch_single(s, sde, cs); ++ } ++} ++ ++static int32_t sdei_alloc_event_num(QemuSDEState *s, bool is_critical, ++ bool is_shared, int intid) ++{ ++ int index; ++ int start = 0; ++ int count = PRIVATE_SLOT_COUNT; ++ int32_t event; ++ QemuSDEProp *sde_props = s->sde_props_state; ++ ++ if (is_shared) { ++ start = PRIVATE_SLOT_COUNT; ++ count = PRIVATE_SLOT_COUNT + SHARED_SLOT_COUNT; ++ } ++ ++ qemu_mutex_lock(&s->sdei_interrupt_bind_lock); ++ for (index = start; index < count; index++) { ++ qemu_mutex_lock(&sde_props[index].lock); ++ if (sde_props[index].interrupt == intid) { ++ event = sde_props[index].event_id; ++ qemu_mutex_unlock(&sde_props[index].lock); ++ qemu_mutex_unlock(&s->sdei_interrupt_bind_lock); ++ return event; ++ } ++ qemu_mutex_unlock(&sde_props[index].lock); ++ } ++ ++ for (index = start; index < count; index++) { ++ qemu_mutex_lock(&sde_props[index].lock); ++ if (sde_props[index].event_id < 0) { ++ event = sde_props[index].event_id = 0x40000000 | index; ++ sde_props[index].interrupt = intid; ++ sde_props[index].is_shared = is_shared; ++ sde_props[index].is_critical = is_critical; ++ s->irq_map[intid] = event; ++ qemu_mutex_unlock(&sde_props[index].lock); ++ qemu_mutex_unlock(&s->sdei_interrupt_bind_lock); ++ return event; ++ } ++ qemu_mutex_unlock(&sde_props[index].lock); ++ } ++ qemu_mutex_unlock(&s->sdei_interrupt_bind_lock); ++ return SDEI_OUT_OF_RESOURCE; ++} ++ ++static int32_t sdei_free_event_num_locked(QemuSDEState *s, QemuSDEProp *prop) ++{ ++ if (atomic_read(&prop->refcount) > 0) { ++ return SDEI_DENIED; ++ } ++ ++ s->irq_map[prop->interrupt] = SDEI_INVALID_EVENT_ID; ++ prop->event_id = SDEI_INVALID_EVENT_ID; ++ prop->interrupt = SDEI_INVALID_INTERRUPT; ++ return SDEI_SUCCESS; ++} ++ ++typedef int64_t (*sdei_single_function)(QemuSDEState *s, ++ CPUState *cs, ++ struct kvm_run *run); ++ ++static int64_t sdei_version(QemuSDEState *s, CPUState *cs, struct kvm_run *run) ++{ ++ return (1ULL << SDEI_VERSION_MAJOR_SHIFT) | ++ (0ULL << SDEI_VERSION_MINOR_SHIFT); ++} ++ ++static int64_t unregister_single_sde(QemuSDEState *s, int32_t event, ++ CPUState *cs, bool force) ++{ ++ QemuSDE *sde; ++ QemuSDEProp *prop; ++ int ret = 0; ++ ++ prop = get_sde_prop(s, event); ++ if (!prop) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id == SDEI_INVALID_EVENT_ID) { ++ put_sde(sde, cs); ++ put_sde_prop(prop); ++ return SDEI_DENIED; ++ } ++ ++ if (sde->running && !force) { ++ sde->unregister_pending = true; ++ ret = SDEI_PENDING; ++ } else { ++ atomic_dec(&prop->refcount); ++ sde->event_id = SDEI_INVALID_EVENT_ID; ++ sde->enabled = false; ++ sde->running = false; ++ sde->pending = false; ++ sde->unregister_pending = false; ++ } ++ put_sde(sde, cs); ++ put_sde_prop(prop); ++ return ret; ++} ++ ++static int64_t sdei_private_reset_common(QemuSDEState *s, CPUState *cs, ++ bool force) ++{ ++ int64_t ret = SDEI_SUCCESS; ++ int i; ++ ++ for (i = 0; i < PRIVATE_SLOT_COUNT; i++) { ++ int64_t ret1; ++ ret1 = unregister_single_sde(s, i, cs, force); ++ /* Ignore other return values in reset interface */ ++ if (ret1 == SDEI_PENDING) { ++ ret = SDEI_DENIED; ++ } ++ } ++ ++ return ret; ++} ++ ++static int64_t sdei_shared_reset_common(QemuSDEState *s, CPUState *cs, ++ bool force) ++{ ++ int i; ++ QemuSDEProp *prop; ++ int32_t start_event = PRIVATE_SLOT_COUNT; ++ int64_t ret = SDEI_SUCCESS; ++ ++ for (i = start_event; i < PRIVATE_SLOT_COUNT + SHARED_SLOT_COUNT; i++) { ++ int64_t ret1 = unregister_single_sde(s, i, cs, force); ++ /* Ignore other return values in reset interface */ ++ if (ret1 == SDEI_PENDING) { ++ ret = SDEI_DENIED; ++ } ++ } ++ if (ret) { ++ return ret; ++ } ++ ++ qemu_mutex_lock(&s->sdei_interrupt_bind_lock); ++ for (i = 0; i < PRIVATE_SLOT_COUNT + SHARED_SLOT_COUNT; i++) { ++ prop = get_sde_prop(s, i); ++ if (!prop || prop->interrupt == SDEI_INVALID_INTERRUPT) { ++ if (prop) { ++ put_sde_prop(prop); ++ } ++ continue; ++ } ++ ret |= sdei_free_event_num_locked(s, prop); ++ put_sde_prop(prop); ++ } ++ qemu_mutex_unlock(&s->sdei_interrupt_bind_lock); ++ ++ return ret ? SDEI_DENIED : SDEI_SUCCESS; ++} ++ ++enum { ++ SDEI_EV_REGISTER_ARG_EVENT = 1, ++ SDEI_EV_REGISTER_ARG_ENTRY_POINT, ++ SDEI_EV_REGISTER_ARG_EP_ARGUMENT, ++ SDEI_EV_REGISTER_ARG_FLAGS, ++ SDEI_EV_REGISTER_ARG_AFFINITY, ++} SDEIEventRegisterArgs; ++ ++#define SDEI_EV_REGISTER_RM_MASK 1ULL ++ ++static int64_t sdei_event_register(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDE *sde; ++ QemuSDEProp *prop; ++ CPUState *target = cs; ++ uint64_t *args = (uint64_t *)run->hypercall.args; ++ int32_t event = args[SDEI_EV_REGISTER_ARG_EVENT]; ++ uint64_t rm_mode = SDEI_EVENT_REGISTER_RM_PE; ++ ++ prop = get_sde_prop(s, event); ++ if (!prop) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id != SDEI_INVALID_EVENT_ID) { ++ put_sde(sde, cs); ++ put_sde_prop(prop); ++ return SDEI_DENIED; ++ } ++ ++ if (prop->is_shared) { ++ rm_mode = args[SDEI_EV_REGISTER_ARG_FLAGS] & SDEI_EV_REGISTER_RM_MASK; ++ if (rm_mode == SDEI_EVENT_REGISTER_RM_PE) { ++ target = arm_get_cpu_by_id(args[SDEI_EV_REGISTER_ARG_AFFINITY]); ++ if (!target) { ++ put_sde(sde, cs); ++ put_sde_prop(prop); ++ return SDEI_INVALID_PARAMETERS; ++ } ++ } ++ } ++ ++ sde->target_cpu = target; ++ sde->ep_address = args[SDEI_EV_REGISTER_ARG_ENTRY_POINT]; ++ sde->ep_argument = args[SDEI_EV_REGISTER_ARG_EP_ARGUMENT]; ++ sde->prop = prop; ++ sde->routing_mode = rm_mode; ++ sde->event_id = prop->event_id; ++ ++ put_sde(sde, cs); ++ atomic_inc(&prop->refcount); ++ put_sde_prop(prop); ++ ++ return SDEI_SUCCESS; ++} ++ ++static int64_t sdei_event_enable(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDE *sde; ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ int32_t event = args[1]; ++ ++ if (!is_valid_event_number(event)) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id == SDEI_INVALID_EVENT_ID) { ++ put_sde(sde, cs); ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ sde->enabled = true; ++ dispatch_single(s, sde, cs); ++ return SDEI_SUCCESS; ++} ++ ++static int64_t sdei_event_disable(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDE *sde; ++ uint64_t *args = (uint64_t *)run->hypercall.args; ++ int32_t event = args[1]; ++ ++ if (!is_valid_event_number(event)) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id == SDEI_INVALID_EVENT_ID) { ++ put_sde(sde, cs); ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ sde->enabled = false; ++ put_sde(sde, cs); ++ return SDEI_SUCCESS; ++} ++ ++static int64_t sdei_event_context(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDECpu *sde_cpu = get_sde_cpu(s, cs); ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ uint32_t param_id = args[1]; ++ int critical; ++ QemuSDECpuCtx *ctx; ++ ++ if (param_id >= SDEI_PARAM_MAX) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ if (!sde_cpu) { ++ return SDEI_DENIED; ++ } ++ ++ if (sde_cpu->critical_running_event >= 0) { ++ critical = 1; ++ } else if (sde_cpu->normal_running_event >= 0) { ++ critical = 0; ++ } else { ++ return SDEI_DENIED; ++ } ++ ++ ctx = &sde_cpu->ctx[critical]; ++ return ctx->xregs[param_id]; ++} ++ ++static int64_t sdei_event_complete(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDE *sde; ++ QemuSDECpu *cpu = get_sde_cpu(s, cs); ++ int32_t event; ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ bool is_critical; ++ ++ if (!cpu) { ++ return SDEI_DENIED; ++ } ++ ++ event = get_running_sde(s, cs); ++ if (event < 0) { ++ return SDEI_DENIED; ++ } ++ ++ if (!is_valid_event_number(event)) { ++ error_report("BUG: running event number 0x%x is invalid!", ++ event); ++ return SDEI_DENIED; ++ } ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id != event) { ++ error_report("BUG: sde event id 0x%x != running event 0x%x!", ++ sde->event_id, event); ++ put_sde(sde, cs); ++ return SDEI_DENIED; ++ } ++ ++ sde->running = false; ++ is_critical = sde->prop->is_critical; ++ if (sde->unregister_pending) { ++ atomic_dec(&sde->prop->refcount); ++ sde->event_id = SDEI_INVALID_EVENT_ID; ++ sde->unregister_pending = false; ++ } ++ put_sde(sde, cs); ++ ++ sde_restore_cpu_ctx(s, cs, is_critical); ++ ++ kvm_arch_put_registers(cs, 1); ++ override_return_value(cs, args); ++ if (cpu->critical_running_event >= 0) { ++ cpu->critical_running_event = SDEI_INVALID_EVENT_ID; ++ } else { ++ cpu->normal_running_event = SDEI_INVALID_EVENT_ID; ++ } ++ ++ dispatch_cpu(s, cs, true); ++ if (cpu->critical_running_event < 0 && cpu->normal_running_event < 0) { ++ dispatch_cpu(s, cs, false); ++ } ++ return args[0]; ++} ++ ++static int64_t sdei_event_complete_and_resume(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDE *sde; ++ QemuSDECpu *cpu = get_sde_cpu(s, cs); ++ int32_t event; ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ bool is_critical; ++ uint64_t resume_addr = args[1]; ++ ++ if (!cpu) { ++ return SDEI_DENIED; ++ } ++ ++ event = get_running_sde(s, cs); ++ if (event < 0) { ++ return SDEI_DENIED; ++ } ++ ++ if (!is_valid_event_number(event)) { ++ error_report("BUG: running event number 0x%x is invalid!", ++ event); ++ return SDEI_DENIED; ++ } ++ ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id != event) { ++ error_report("BUG: sde event id 0x%x != running event 0x%x!", ++ sde->event_id, event); ++ put_sde(sde, cs); ++ return SDEI_DENIED; ++ } ++ ++ sde->running = false; ++ is_critical = sde->prop->is_critical; ++ ++ if (sde->unregister_pending) { ++ atomic_dec(&sde->prop->refcount); ++ sde->event_id = SDEI_INVALID_EVENT_ID; ++ sde->unregister_pending = false; ++ } ++ put_sde(sde, cs); ++ ++ sde_restore_cpu_ctx_for_resume(s, cs, is_critical, resume_addr); ++ kvm_arch_put_registers(cs, 1); ++ ++ override_return_value(cs, args); ++ if (cpu->critical_running_event >= 0) { ++ cpu->critical_running_event = SDEI_INVALID_EVENT_ID; ++ } else { ++ cpu->normal_running_event = SDEI_INVALID_EVENT_ID; ++ } ++ ++ dispatch_cpu(s, cs, true); ++ if (cpu->critical_running_event < 0 && cpu->normal_running_event < 0) { ++ dispatch_cpu(s, cs, false); ++ } ++ return args[0]; ++} ++ ++static int64_t sdei_event_unregister(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ int32_t event = args[1]; ++ ++ return unregister_single_sde(s, event, cs, false); ++} ++ ++static int64_t sdei_event_status(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDE *sde; ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ int32_t event = args[1]; ++ int64_t status = 0; ++ ++ if (!is_valid_event(s, event)) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id == SDEI_INVALID_EVENT_ID) { ++ put_sde(sde, cs); ++ return status; ++ } ++ ++ status |= SDEI_EVENT_STATUS_REGISTERED; ++ if (sde->enabled) { ++ status |= SDEI_EVENT_STATUS_ENABLED; ++ } ++ if (sde->running) { ++ status |= SDEI_EVENT_STATUS_RUNNING; ++ } ++ put_sde(sde, cs); ++ return status; ++} ++ ++enum { ++ SDEI_EV_GET_INFO_ARG_EVENT = 1, ++ SDEI_EV_GET_INFO_ARG_INFO, ++} SDEIEventGetInfoArgs; ++ ++static int64_t sdei_event_get_info(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDEProp *prop; ++ QemuSDE *sde; ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ int32_t event = args[SDEI_EV_GET_INFO_ARG_EVENT]; ++ uint32_t info = args[SDEI_EV_GET_INFO_ARG_INFO]; ++ int64_t ret = SDEI_INVALID_PARAMETERS; ++ ++ if (info > SDEI_EVENT_INFO_EV_ROUTING_AFF) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ prop = get_sde_prop(s, event); ++ if (!prop) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ switch (info) { ++ case SDEI_EVENT_INFO_EV_TYPE: ++ ret = prop->is_shared; ++ break; ++ case SDEI_EVENT_INFO_EV_SIGNALED: ++ ret = (event == SDEI_STD_EVT_SOFTWARE_SIGNAL) ? 1 : 0; ++ break; ++ case SDEI_EVENT_INFO_EV_PRIORITY: ++ ret = prop->is_critical; ++ break; ++ case SDEI_EVENT_INFO_EV_ROUTING_MODE: ++ case SDEI_EVENT_INFO_EV_ROUTING_AFF: ++ if (!prop->is_shared) { ++ break; ++ } ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id == SDEI_INVALID_EVENT_ID) { ++ put_sde(sde, cs); ++ ret = SDEI_DENIED; ++ break; ++ } ++ if (info == SDEI_EVENT_INFO_EV_ROUTING_MODE) { ++ ret = sde->routing_mode; ++ } else if (sde->routing_mode == SDEI_EVENT_REGISTER_RM_PE) { ++ ret = ARM_CPU(sde->target_cpu)->mp_affinity; ++ } ++ put_sde(sde, cs); ++ break; ++ default: ++ ret = SDEI_NOT_SUPPORTED; ++ } ++ put_sde_prop(prop); ++ return ret; ++} ++ ++enum { ++ SDEI_EV_ROUTING_SET_ARG_EVENT = 1, ++ SDEI_EV_ROUTING_SET_ARG_MODE, ++ SDEI_EV_ROUTING_SET_ARG_AFFINITY, ++} SDEIEventRoutingSetArgs; ++ ++static int64_t sdei_event_routing_set(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDE *sde; ++ CPUState *target = cs; ++ uint64_t *args = (uint64_t *)run->hypercall.args; ++ int32_t event = args[SDEI_EV_ROUTING_SET_ARG_EVENT]; ++ uint64_t mode = args[SDEI_EV_ROUTING_SET_ARG_MODE]; ++ uint64_t affinity = args[SDEI_EV_ROUTING_SET_ARG_AFFINITY]; ++ ++ if (mode & ~1ULL) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ if (mode == SDEI_EVENT_REGISTER_RM_PE) { ++ target = arm_get_cpu_by_id(affinity); ++ if (!target) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ } ++ ++ if (!is_valid_event(s, event) || !SDEI_IS_SHARED_EVENT(event)) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id == SDEI_INVALID_EVENT_ID) { ++ put_sde(sde, cs); ++ return SDEI_DENIED; ++ } ++ if (sde->enabled || sde->running || ++ sde->pending || sde->unregister_pending) { ++ put_sde(sde, cs); ++ return SDEI_DENIED; ++ } ++ ++ sde->target_cpu = target; ++ sde->routing_mode = mode; ++ put_sde(sde, cs); ++ ++ return SDEI_SUCCESS; ++} ++ ++static int64_t sdei_event_pe_mask(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDECpu *sde_cpu; ++ ++ sde_cpu = get_sde_cpu(s, cs); ++ if (!sde_cpu) { ++ return SDEI_DENIED; ++ } ++ ++ if (sde_cpu->masked) { ++ return 0; ++ } ++ sde_cpu->masked = true; ++ return 1; ++} ++ ++static int64_t sdei_event_pe_unmask(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDECpu *sde_cpu; ++ ++ sde_cpu = get_sde_cpu(s, cs); ++ if (!sde_cpu) { ++ return SDEI_DENIED; ++ } ++ ++ sde_cpu->masked = false; ++ dispatch_cpu(s, cs, true); ++ dispatch_cpu(s, cs, false); ++ return SDEI_SUCCESS; ++} ++ ++static int64_t sdei_event_interrupt_bind(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ uint32_t intid = args[1]; ++ ++ if (intid < GIC_NR_SGIS || intid >= GIC_MAXIRQ) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ return sdei_alloc_event_num(s, false, intid >= GIC_INTERNAL, intid); ++} ++ ++static int64_t sdei_event_interrupt_release(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDEProp *prop; ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ int32_t event = args[1]; ++ int32_t ret; ++ ++ qemu_mutex_lock(&s->sdei_interrupt_bind_lock); ++ prop = get_sde_prop(s, event); ++ if (!prop) { ++ qemu_mutex_unlock(&s->sdei_interrupt_bind_lock); ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ ret = sdei_free_event_num_locked(s, prop); ++ put_sde_prop(prop); ++ qemu_mutex_unlock(&s->sdei_interrupt_bind_lock); ++ return ret; ++} ++ ++enum { ++ SDEI_EV_SIGNAL_ARG_EVENT = 1, ++ SDEI_EV_SIGNAL_ARG_TARGET, ++} SDEIEventSignalArgs; ++ ++static int64_t sdei_event_signal(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ QemuSDE *sde; ++ CPUState *target_cpu; ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ int32_t event = args[SDEI_EV_SIGNAL_ARG_EVENT]; ++ ++ if (event != SDEI_STD_EVT_SOFTWARE_SIGNAL) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ target_cpu = arm_get_cpu_by_id(args[SDEI_EV_SIGNAL_ARG_TARGET]); ++ if (!target_cpu) { ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ sde = get_sde_no_check(s, event, target_cpu); ++ if (sde->event_id == SDEI_INVALID_EVENT_ID) { ++ put_sde(sde, cs); ++ return SDEI_INVALID_PARAMETERS; ++ } ++ ++ sde->pending = true; ++ dispatch_single(s, sde, target_cpu); ++ return SDEI_SUCCESS; ++} ++ ++#define SDEI_FEATURES_SHARED_SLOTS_SHIFT 16 ++static int64_t sdei_features(QemuSDEState *s, CPUState *cs, struct kvm_run *run) ++{ ++ uint64_t *args = (uint64_t *)(run->hypercall.args); ++ uint32_t feature = args[1]; ++ ++ switch (feature) { ++ case SDEI_FEATURE_BIND_SLOTS: ++ return ((SHARED_SLOT_COUNT - PLAT_SHARED_SLOT_COUNT) << ++ SDEI_FEATURES_SHARED_SLOTS_SHIFT) | ++ (PRIVATE_SLOT_COUNT - PLAT_PRIVATE_SLOT_COUNT); ++ default: ++ return SDEI_INVALID_PARAMETERS; ++ } ++} ++ ++static int64_t sdei_private_reset(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ return sdei_private_reset_common(s, cs, false); ++} ++ ++static int64_t sdei_shared_reset(QemuSDEState *s, CPUState *cs, ++ struct kvm_run *run) ++{ ++ return sdei_shared_reset_common(s, cs, false); ++} ++ ++static sdei_single_function sdei_functions[] = { ++ sdei_version, ++ sdei_event_register, ++ sdei_event_enable, ++ sdei_event_disable, ++ sdei_event_context, ++ sdei_event_complete, ++ sdei_event_complete_and_resume, ++ sdei_event_unregister, ++ sdei_event_status, ++ sdei_event_get_info, ++ sdei_event_routing_set, ++ sdei_event_pe_mask, ++ sdei_event_pe_unmask, ++ sdei_event_interrupt_bind, ++ sdei_event_interrupt_release, ++ sdei_event_signal, ++ sdei_features, ++ sdei_private_reset, ++ sdei_shared_reset, ++}; ++ ++void sdei_handle_request(CPUState *cs, struct kvm_run *run) ++{ ++ uint32_t func_id = run->hypercall.args[0]; ++ ++ if (!sde_state) { ++ run->hypercall.args[0] = SDEI_NOT_SUPPORTED; ++ return; ++ } ++ ++ if (func_id < SDEI_1_0_FN_BASE || func_id > SDEI_MAX_REQ) { ++ error_report("Invalid SDEI function ID: 0x%x", func_id); ++ run->hypercall.args[0] = SDEI_INVALID_PARAMETERS; ++ return; ++ } ++ ++ func_id -= SDEI_1_0_FN_BASE; ++ if (func_id < ARRAY_SIZE(sdei_functions) && sdei_functions[func_id]) { ++ run->hypercall.args[0] = sdei_functions[func_id](sde_state, cs, run); ++ } else { ++ run->hypercall.args[0] = SDEI_NOT_SUPPORTED; ++ } ++} ++ + static void sde_array_init(QemuSDE **array, int count) + { + int i; +diff --git a/target/arm/sdei.h b/target/arm/sdei.h +new file mode 100644 +index 00000000..cfcf9ac0 +--- /dev/null ++++ b/target/arm/sdei.h +@@ -0,0 +1,34 @@ ++/* ++ * ARM SDEI emulation external interfaces ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. ++ * ++ * Authors: ++ * Heyi Guo ++ * Jingyi Wang ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2 or later, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#ifndef QEMU_SDEI_H ++#define QEMU_SDEI_H ++ ++#include ++#include ++#include "qom/cpu.h" ++ ++#define SDEI_MAX_REQ SDEI_1_0_FN(0x12) ++ ++void sdei_handle_request(CPUState *cs, struct kvm_run *run); ++ ++#endif +-- +2.23.0 + diff --git a/arm-sdei-add-support-to-register-interrupt-bind-noti.patch b/arm-sdei-add-support-to-register-interrupt-bind-noti.patch new file mode 100644 index 0000000000000000000000000000000000000000..480e3bb0bda42f54334242387c0cf43ab80fee5d --- /dev/null +++ b/arm-sdei-add-support-to-register-interrupt-bind-noti.patch @@ -0,0 +1,119 @@ +From 422b3bf8e3b745ea67aa0adc8fb92e707b4e7d76 Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:52 +0800 +Subject: [PATCH 10/15] arm/sdei: add support to register interrupt bind + notifier + +Other qemu modules related with the interrupt bind operation may want +to be notified when guest requests to bind interrupt to sdei event, so +we add register and unregister interfaces. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + target/arm/sdei.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ + target/arm/sdei.h | 17 +++++++++++++++++ + 2 files changed, 65 insertions(+) + +diff --git a/target/arm/sdei.c b/target/arm/sdei.c +index 8bbff76c..56c496b7 100644 +--- a/target/arm/sdei.c ++++ b/target/arm/sdei.c +@@ -48,6 +48,52 @@ + + static QemuSDEState *sde_state; + ++typedef struct QemuSDEIBindNotifyEntry { ++ QTAILQ_ENTRY(QemuSDEIBindNotifyEntry) entry; ++ QemuSDEIBindNotify *func; ++ void *opaque; ++ int irq; ++} QemuSDEIBindNotifyEntry; ++ ++static QTAILQ_HEAD(, QemuSDEIBindNotifyEntry) bind_notifiers = ++ QTAILQ_HEAD_INITIALIZER(bind_notifiers); ++ ++void qemu_register_sdei_bind_notifier(QemuSDEIBindNotify *func, ++ void *opaque, int irq) ++{ ++ QemuSDEIBindNotifyEntry *be = g_new0(QemuSDEIBindNotifyEntry, 1); ++ ++ be->func = func; ++ be->opaque = opaque; ++ be->irq = irq; ++ QTAILQ_INSERT_TAIL(&bind_notifiers, be, entry); ++} ++ ++void qemu_unregister_sdei_bind_notifier(QemuSDEIBindNotify *func, ++ void *opaque, int irq) ++{ ++ QemuSDEIBindNotifyEntry *be; ++ ++ QTAILQ_FOREACH(be, &bind_notifiers, entry) { ++ if (be->func == func && be->opaque == opaque && be->irq == irq) { ++ QTAILQ_REMOVE(&bind_notifiers, be, entry); ++ g_free(be); ++ return; ++ } ++ } ++} ++ ++static void sdei_notify_bind(int irq, int32_t event, bool bind) ++{ ++ QemuSDEIBindNotifyEntry *be, *nbe; ++ ++ QTAILQ_FOREACH_SAFE(be, &bind_notifiers, entry, nbe) { ++ if (be->irq == irq) { ++ be->func(be->opaque, irq, event, bind); ++ } ++ } ++} ++ + static void qemu_sde_prop_init(QemuSDEState *s) + { + QemuSDEProp *sde_props = s->sde_props_state; +@@ -525,6 +571,7 @@ static int32_t sdei_alloc_event_num(QemuSDEState *s, bool is_critical, + sde_props[index].interrupt = intid; + sde_props[index].is_shared = is_shared; + sde_props[index].is_critical = is_critical; ++ sdei_notify_bind(intid, event, true); + override_qemu_irq(s, event, intid); + s->irq_map[intid] = event; + qemu_mutex_unlock(&sde_props[index].lock); +@@ -543,6 +590,7 @@ static int32_t sdei_free_event_num_locked(QemuSDEState *s, QemuSDEProp *prop) + return SDEI_DENIED; + } + ++ sdei_notify_bind(prop->interrupt, prop->event_id, false); + restore_qemu_irq(s, prop->event_id, prop->interrupt); + s->irq_map[prop->interrupt] = SDEI_INVALID_EVENT_ID; + prop->event_id = SDEI_INVALID_EVENT_ID; +diff --git a/target/arm/sdei.h b/target/arm/sdei.h +index 02e23887..49804759 100644 +--- a/target/arm/sdei.h ++++ b/target/arm/sdei.h +@@ -38,4 +38,21 @@ void sdei_handle_request(CPUState *cs, struct kvm_run *run); + */ + bool trigger_sdei_by_irq(int cpu, int irq); + ++/* ++ * Notify callback prototype; the argument "bind" tells whether it is a bind ++ * operation or unbind one. ++ */ ++typedef void QemuSDEIBindNotify(void *opaque, int irq, ++ int32_t event, bool bind); ++/* ++ * Register a notify callback for a specific interrupt bind operation; the ++ * client will be both notified by bind and unbind operation. ++ */ ++void qemu_register_sdei_bind_notifier(QemuSDEIBindNotify *func, ++ void *opaque, int irq); ++/* ++ * Unregister a notify callback for a specific interrupt bind operation. ++ */ ++void qemu_unregister_sdei_bind_notifier(QemuSDEIBindNotify *func, ++ void *opaque, int irq); + #endif +-- +2.23.0 + diff --git a/arm-sdei-add-support-to-trigger-event-by-GIC-interru.patch b/arm-sdei-add-support-to-trigger-event-by-GIC-interru.patch new file mode 100644 index 0000000000000000000000000000000000000000..a3cf28f6c14e2af082f0239a0edb6b9ebb2e6464 --- /dev/null +++ b/arm-sdei-add-support-to-trigger-event-by-GIC-interru.patch @@ -0,0 +1,90 @@ +From 193690ce3d10d6f02060b8a47e52f0ebcf1affac Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:49 +0800 +Subject: [PATCH 07/15] arm/sdei: add support to trigger event by GIC interrupt + ID + +Add an external interface to trigger an SDEI event bound to an +interrupt by providing GIC interrupt ID. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + target/arm/sdei.c | 37 +++++++++++++++++++++++++++++++++++++ + target/arm/sdei.h | 7 +++++++ + 2 files changed, 44 insertions(+) + +diff --git a/target/arm/sdei.c b/target/arm/sdei.c +index 8110fdea..9b727883 100644 +--- a/target/arm/sdei.c ++++ b/target/arm/sdei.c +@@ -472,6 +472,28 @@ static int64_t sdei_version(QemuSDEState *s, CPUState *cs, struct kvm_run *run) + (0ULL << SDEI_VERSION_MINOR_SHIFT); + } + ++static bool inject_event(QemuSDEState *s, CPUState *cs, int32_t event, int irq) ++{ ++ QemuSDE *sde; ++ ++ if (event < 0) { ++ return false; ++ } ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->event_id == SDEI_INVALID_EVENT_ID) { ++ put_sde(sde, cs); ++ return false; ++ } ++ if (irq > 0 && sde->prop->interrupt != irq) { ++ /* Someone unbinds the interrupt! */ ++ put_sde(sde, cs); ++ return false; ++ } ++ sde->pending = true; ++ dispatch_single(s, sde, cs); ++ return true; ++} ++ + static int64_t unregister_single_sde(QemuSDEState *s, int32_t event, + CPUState *cs, bool force) + { +@@ -1124,6 +1146,21 @@ void sdei_handle_request(CPUState *cs, struct kvm_run *run) + } + } + ++bool trigger_sdei_by_irq(int cpu, int irq) ++{ ++ QemuSDEState *s = sde_state; ++ ++ if (!s || irq >= ARRAY_SIZE(s->irq_map)) { ++ return false; ++ } ++ ++ if (s->irq_map[irq] == SDEI_INVALID_EVENT_ID) { ++ return false; ++ } ++ ++ return inject_event(s, qemu_get_cpu(cpu), s->irq_map[irq], irq); ++} ++ + static void sde_array_init(QemuSDE **array, int count) + { + int i; +diff --git a/target/arm/sdei.h b/target/arm/sdei.h +index cfcf9ac0..02e23887 100644 +--- a/target/arm/sdei.h ++++ b/target/arm/sdei.h +@@ -31,4 +31,11 @@ + + void sdei_handle_request(CPUState *cs, struct kvm_run *run); + ++/* ++ * Trigger an SDEI event bound to an interrupt. ++ * Return true if event has been triggered successfully. ++ * Return false if event has not been triggered for some reason. ++ */ ++bool trigger_sdei_by_irq(int cpu, int irq); ++ + #endif +-- +2.23.0 + diff --git a/arm-sdei-add-system-reset-callback.patch b/arm-sdei-add-system-reset-callback.patch new file mode 100644 index 0000000000000000000000000000000000000000..70fbcf4053535d57c0b5d53eacb545f47eea5434 --- /dev/null +++ b/arm-sdei-add-system-reset-callback.patch @@ -0,0 +1,57 @@ +From d0a34853fb77882df0f04b40960b514ebdbf5e65 Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:48 +0800 +Subject: [PATCH 06/15] arm/sdei: add system reset callback + +For this is a logical device which is not attached to system bus, we +cannot use DeviceClass->reset interface directly. Instead we register +our own reset callback to reset SDEI services when system resets. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + target/arm/sdei.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/target/arm/sdei.c b/target/arm/sdei.c +index 1fb5bcf3..8110fdea 100644 +--- a/target/arm/sdei.c ++++ b/target/arm/sdei.c +@@ -1167,6 +1167,26 @@ static void qemu_sde_init(QemuSDEState *s) + qemu_private_sde_init(s); + } + ++static void qemu_sde_reset(void *opaque) ++{ ++ int64_t ret = 0; ++ CPUState *cs; ++ QemuSDEState *s = opaque; ++ ++ CPU_FOREACH(cs) { ++ QemuSDECpu *sde_cpu = get_sde_cpu(s, cs); ++ ret |= sdei_private_reset_common(s, cs, true); ++ sde_cpu->masked = true; ++ sde_cpu->critical_running_event = SDEI_INVALID_EVENT_ID; ++ sde_cpu->normal_running_event = SDEI_INVALID_EVENT_ID; ++ } ++ ++ ret |= sdei_shared_reset_common(s, first_cpu, true); ++ if (ret) { ++ error_report("SDEI system reset failed: 0x%lx", ret); ++ } ++} ++ + static void sde_array_save(QemuSDE **array, int count) + { + int i; +@@ -1319,6 +1339,7 @@ static void sdei_initfn(Object *obj) + sde_state = s; + + qemu_sde_init(s); ++ qemu_register_reset(qemu_sde_reset, s); + } + + static void qemu_sde_class_init(ObjectClass *klass, void *data) +-- +2.23.0 + diff --git a/arm-sdei-add-virtual-device-framework.patch b/arm-sdei-add-virtual-device-framework.patch new file mode 100644 index 0000000000000000000000000000000000000000..12418ea260d04aeb1870fe8dafab14443a492d3d --- /dev/null +++ b/arm-sdei-add-virtual-device-framework.patch @@ -0,0 +1,518 @@ +From 24c5fd09ed71baf07e77c924aafcf5078a35579a Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:45 +0800 +Subject: [PATCH 03/15] arm/sdei: add virtual device framework + +SDEI is useful to emulate NMI on arm64 platforms. To support SDEI in +virtual machine with KVM enabled, we choose to implement SDEI +interfaces in qemu. It is targeted for KVM mode only, for the full +user space emulation can also emulate secure world and have ARM +Trusted Firmware to run on emulated EL3. + +- We create a logical SDEI device to hold the states of SDEI services, + to support VM migration. +- Only one SDEI virtual device is allowed in the whole VM to provide + SDEI services. +- We create struct QemuSDE to hold states of each SDEI event, and + private events with the same ID on different CPUs have their own + QemuSDE instance. +- We create struct QemuSDEProp to hold properties of each SDEI event, + so all private instances with the same ID will pointed to the same + QemuSDEProp. +- We create struct QemuSDECpu to hold CPU/PE states, including the + interrupted CPU context. +- Slot numbers for private and shared event are fixed, for guests + cannot request more interrupt binds than BIND_SLOTS in SDEI_FEATURES + call. +- The first PRIVATE_SLOT_COUNT slots in property array are for private + events, and the next SHARED_SLOT_COUNT slots are for shared events. +- We use property slot index as lower bit for each allocated event + number, so that we can get property easily from valid input event + number, as well as the QemuSDE instance. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + target/arm/sdei.c | 344 ++++++++++++++++++++++++++++++++++++++++++ + target/arm/sdei_int.h | 118 +++++++++++++++ + 2 files changed, 462 insertions(+) + create mode 100644 target/arm/sdei.c + create mode 100644 target/arm/sdei_int.h + +diff --git a/target/arm/sdei.c b/target/arm/sdei.c +new file mode 100644 +index 00000000..6ebcc096 +--- /dev/null ++++ b/target/arm/sdei.c +@@ -0,0 +1,344 @@ ++/* ++ * ARM SDEI emulation for ARM64 virtual machine with KVM ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. ++ * ++ * Authors: ++ * Heyi Guo ++ * Jingyi Wang ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2 or later, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include "qemu/osdep.h" ++#include "qapi/error.h" ++#include "arm-powerctl.h" ++#include "qemu/timer.h" ++#include "sysemu/kvm.h" ++#include "sysemu/kvm_int.h" ++#include "sysemu/sysemu.h" ++#include "sysemu/reset.h" ++#include "qemu/error-report.h" ++#include "sdei_int.h" ++#include "internals.h" ++#include "hw/boards.h" ++#include "hw/intc/arm_gicv3.h" ++#include "hw/intc/arm_gic.h" ++#include "hw/irq.h" ++#include "hw/sysbus.h" ++#include "migration/vmstate.h" ++#include "qom/object.h" ++ ++#define TYPE_ARM_SDEI "arm_sdei" ++#define QEMU_SDEI(obj) OBJECT_CHECK(QemuSDEState, (obj), TYPE_ARM_SDEI) ++ ++static QemuSDEState *sde_state; ++ ++static void qemu_sde_prop_init(QemuSDEState *s) ++{ ++ QemuSDEProp *sde_props = s->sde_props_state; ++ int i; ++ for (i = 0; i < ARRAY_SIZE(s->sde_props_state); i++) { ++ sde_props[i].event_id = SDEI_INVALID_EVENT_ID; ++ sde_props[i].interrupt = SDEI_INVALID_INTERRUPT; ++ sde_props[i].sde_index = i >= PRIVATE_SLOT_COUNT ? ++ i - PRIVATE_SLOT_COUNT : i; ++ ++ qemu_mutex_init(&(sde_props[i].lock)); ++ sde_props[i].refcount = 0; ++ } ++ sde_props[0].event_id = SDEI_STD_EVT_SOFTWARE_SIGNAL; ++ sde_props[0].interrupt = SDEI_INVALID_INTERRUPT; ++ sde_props[0].is_shared = false; ++ sde_props[0].is_critical = false; ++ ++ for (i = 0; i < ARRAY_SIZE(s->irq_map); i++) { ++ s->irq_map[i] = SDEI_INVALID_EVENT_ID; ++ } ++ ++ qemu_mutex_init(&s->sdei_interrupt_bind_lock); ++} ++ ++static void qemu_sde_cpu_init(QemuSDEState *s) ++{ ++ int i; ++ QemuSDECpu *sde_cpus; ++ ++ s->sdei_max_cpus = current_machine->smp.max_cpus; ++ s->sde_cpus = g_new0(QemuSDECpu, s->sdei_max_cpus); ++ sde_cpus = s->sde_cpus; ++ for (i = 0; i < s->sdei_max_cpus; i++) { ++ sde_cpus[i].masked = true; ++ sde_cpus[i].critical_running_event = SDEI_INVALID_EVENT_ID; ++ sde_cpus[i].normal_running_event = SDEI_INVALID_EVENT_ID; ++ } ++} ++ ++static bool is_valid_event_number(int32_t event) ++{ ++ int32_t slot_id; ++ ++ if (event < 0 || (event & 0x3F000000)) { ++ return false; ++ } ++ ++ slot_id = SDEI_EVENT_TO_SLOT(event); ++ if (slot_id >= PRIVATE_SLOT_COUNT + SHARED_SLOT_COUNT) { ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool is_valid_event(QemuSDEState *s, int32_t event) ++{ ++ if (!is_valid_event_number(event)) { ++ return false; ++ } ++ ++ if (s->sde_props_state[SDEI_EVENT_TO_SLOT(event)].event_id != event) { ++ return false; ++ } ++ ++ return true; ++} ++ ++static QemuSDEProp *get_sde_prop_no_lock(QemuSDEState *s, int32_t event) ++{ ++ if (!is_valid_event(s, event)) { ++ return NULL; ++ } ++ ++ return &s->sde_props_state[SDEI_EVENT_TO_SLOT(event)]; ++} ++ ++static void sde_array_init(QemuSDE **array, int count) ++{ ++ int i; ++ ++ for (i = 0; i < count; i++) { ++ QemuSDE *sde; ++ sde = array[i]; ++ if (!sde) { ++ sde = g_new0(QemuSDE, 1); ++ } ++ sde->event_id = SDEI_INVALID_EVENT_ID; ++ sde->enabled = false; ++ sde->running = false; ++ sde->pending = false; ++ sde->unregister_pending = false; ++ qemu_mutex_init(&sde->lock); ++ array[i] = sde; ++ } ++} ++ ++static void qemu_shared_sde_init(QemuSDEState *s) ++{ ++ sde_array_init(s->shared_sde_array, SHARED_SLOT_COUNT); ++} ++ ++static void qemu_private_sde_init(QemuSDEState *s) ++{ ++ int i; ++ ++ for (i = 0; i < s->sdei_max_cpus; i++) { ++ sde_array_init(s->sde_cpus[i].private_sde_array, PRIVATE_SLOT_COUNT); ++ } ++} ++ ++static void qemu_sde_init(QemuSDEState *s) ++{ ++ qemu_sde_prop_init(s); ++ qemu_sde_cpu_init(s); ++ ++ qemu_shared_sde_init(s); ++ qemu_private_sde_init(s); ++} ++ ++static void sde_array_save(QemuSDE **array, int count) ++{ ++ int i; ++ ++ for (i = 0; i < count; i++) { ++ QemuSDE *sde = array[i]; ++ if (sde->event_id != SDEI_INVALID_EVENT_ID) { ++ sde->event_id = sde->prop->event_id; ++ sde->cpu_affinity = ARM_CPU(sde->target_cpu)->mp_affinity; ++ } ++ } ++} ++ ++static int qemu_sdei_pre_save(void *opaque) ++{ ++ QemuSDEState *s = opaque; ++ int i; ++ ++ for (i = 0; i < s->sdei_max_cpus; i++) { ++ sde_array_save(s->sde_cpus[i].private_sde_array, PRIVATE_SLOT_COUNT); ++ } ++ ++ sde_array_save(s->shared_sde_array, SHARED_SLOT_COUNT); ++ ++ return 0; ++} ++ ++ ++static int qemu_sdei_post_load(void *opaque, int version_id) ++{ ++ QemuSDEState *s = opaque; ++ QemuSDEProp *sde_props = s->sde_props_state; ++ QemuSDE **array; ++ int i, j; ++ ++ for (i = 0; i < s->sdei_max_cpus; i++) { ++ array = s->sde_cpus[i].private_sde_array; ++ for (j = 0; j < PRIVATE_SLOT_COUNT; j++) { ++ QemuSDE *sde = array[j]; ++ if (sde->event_id != SDEI_INVALID_EVENT_ID) { ++ sde->prop = get_sde_prop_no_lock(s, sde->event_id); ++ sde->target_cpu = arm_get_cpu_by_id(sde->cpu_affinity); ++ } ++ } ++ } ++ ++ array = s->shared_sde_array; ++ for (j = 0; j < SHARED_SLOT_COUNT; j++) { ++ QemuSDE *sde = array[j]; ++ if (sde->event_id != SDEI_INVALID_EVENT_ID) { ++ sde->prop = get_sde_prop_no_lock(s, sde->event_id); ++ sde->target_cpu = arm_get_cpu_by_id(sde->cpu_affinity); ++ } ++ } ++ ++ for (i = 0; i < PRIVATE_SLOT_COUNT + SHARED_SLOT_COUNT; i++) { ++ int intid = sde_props[i].interrupt; ++ ++ if (intid >= 0 && intid < GIC_MAXIRQ) { ++ s->irq_map[intid] = sde_props[i].event_id; ++ } ++ } ++ ++ return 0; ++} ++ ++static const VMStateDescription vmstate_sdes = { ++ .name = "qemu_sdei/sdes", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .fields = (VMStateField[]) { ++ VMSTATE_BOOL(enabled, QemuSDE), ++ VMSTATE_BOOL(running, QemuSDE), ++ VMSTATE_BOOL(pending, QemuSDE), ++ VMSTATE_BOOL(unregister_pending, QemuSDE), ++ VMSTATE_UINT64(ep_address, QemuSDE), ++ VMSTATE_UINT64(ep_argument, QemuSDE), ++ VMSTATE_UINT64(routing_mode, QemuSDE), ++ VMSTATE_INT32(event_id, QemuSDE), ++ VMSTATE_UINT64(cpu_affinity, QemuSDE), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++static const VMStateDescription vmstate_sde_props = { ++ .name = "qemu_sdei/sde_props", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .fields = (VMStateField[]) { ++ VMSTATE_INT32(event_id, QemuSDEProp), ++ VMSTATE_INT32(interrupt, QemuSDEProp), ++ VMSTATE_BOOL(is_shared, QemuSDEProp), ++ VMSTATE_BOOL(is_critical, QemuSDEProp), ++ VMSTATE_INT32(sde_index, QemuSDEProp), ++ VMSTATE_INT32(refcount, QemuSDEProp), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++static const VMStateDescription vmstate_sde_cpu = { ++ .name = "qemu_sdei/sde_cpu", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .fields = (VMStateField[]) { ++ VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(private_sde_array, QemuSDECpu, ++ PRIVATE_SLOT_COUNT, 1, ++ vmstate_sdes, QemuSDE), ++ VMSTATE_UINT64_ARRAY(ctx[0].xregs, QemuSDECpu, SAVED_GP_NUM), ++ VMSTATE_UINT64_ARRAY(ctx[1].xregs, QemuSDECpu, SAVED_GP_NUM), ++ VMSTATE_UINT64(ctx[0].pc, QemuSDECpu), ++ VMSTATE_UINT64(ctx[1].pc, QemuSDECpu), ++ VMSTATE_UINT32(ctx[0].pstate, QemuSDECpu), ++ VMSTATE_UINT32(ctx[1].pstate, QemuSDECpu), ++ VMSTATE_INT32(critical_running_event, QemuSDECpu), ++ VMSTATE_INT32(normal_running_event, QemuSDECpu), ++ VMSTATE_BOOL(masked, QemuSDECpu), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++static const VMStateDescription vmstate_sde_state = { ++ .name = "qemu_sdei", ++ .version_id = 1, ++ .minimum_version_id = 1, ++ .pre_save = qemu_sdei_pre_save, ++ .post_load = qemu_sdei_post_load, ++ .fields = (VMStateField[]) { ++ VMSTATE_STRUCT_ARRAY(sde_props_state, QemuSDEState, ++ PRIVATE_SLOT_COUNT + SHARED_SLOT_COUNT, 1, ++ vmstate_sde_props, QemuSDEProp), ++ VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(shared_sde_array, QemuSDEState, ++ SHARED_SLOT_COUNT, 1, ++ vmstate_sdes, QemuSDE), ++ VMSTATE_STRUCT_VARRAY_POINTER_INT32(sde_cpus, QemuSDEState, ++ sdei_max_cpus, ++ vmstate_sde_cpu, QemuSDECpu), ++ VMSTATE_END_OF_LIST() ++ } ++}; ++ ++ ++static void sdei_initfn(Object *obj) ++{ ++ QemuSDEState *s = QEMU_SDEI(obj); ++ ++ if (sde_state) { ++ error_report("Only one SDEI dispatcher is allowed!"); ++ abort(); ++ } ++ sde_state = s; ++ ++ qemu_sde_init(s); ++} ++ ++static void qemu_sde_class_init(ObjectClass *klass, void *data) ++{ ++ DeviceClass *dc = DEVICE_CLASS(klass); ++ ++ dc->desc = "SDEI_QEMU"; ++ dc->vmsd = &vmstate_sde_state; ++ dc->user_creatable = true; ++} ++ ++static const TypeInfo sde_qemu_info = { ++ .name = TYPE_ARM_SDEI, ++ .parent = TYPE_DEVICE, ++ .instance_size = sizeof(QemuSDEState), ++ .instance_init = sdei_initfn, ++ .class_init = qemu_sde_class_init, ++}; ++ ++static void register_types(void) ++{ ++ type_register_static(&sde_qemu_info); ++} ++ ++type_init(register_types); +diff --git a/target/arm/sdei_int.h b/target/arm/sdei_int.h +new file mode 100644 +index 00000000..d3fd7cbc +--- /dev/null ++++ b/target/arm/sdei_int.h +@@ -0,0 +1,118 @@ ++/* ++ * ARM SDEI emulation internal interfaces ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved. ++ * ++ * Authors: ++ * Heyi Guo ++ * Jingyi Wang ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2 or later, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#ifndef QEMU_SDEI_INT_H ++#define QEMU_SDEI_INT_H ++ ++#include ++#include ++#include ++#include "hw/intc/arm_gic_common.h" ++#include "qemu/thread.h" ++ ++#define SDEI_STD_EVT_SOFTWARE_SIGNAL 0 ++#define SDEI_FEATURE_BIND_SLOTS 0 ++#define SDEI_PARAM_MAX 18 ++ ++#define PRIVATE_SLOT_COUNT 16 ++#define PLAT_PRIVATE_SLOT_COUNT 8 ++#define SHARED_SLOT_COUNT 32 ++#define PLAT_SHARED_SLOT_COUNT 16 ++#define SDEI_INVALID_INTERRUPT -1 ++#define SDEI_INVALID_EVENT_ID -1 ++ ++#define SDEI_EVENT_TO_SLOT(event) ((event) & 0xFFFFFF) ++#define SDEI_IS_SHARED_EVENT(event) \ ++ (SDEI_EVENT_TO_SLOT(event) >= PRIVATE_SLOT_COUNT) ++ ++typedef enum { ++ SDEI_PRIO_NORMAL = 0, ++ SDEI_PRIO_CRITICAL = 1, ++} QemuSDEIPriority; ++ ++typedef struct QemuSDEProp { ++ QemuMutex lock; ++ int32_t event_id; ++ int interrupt; ++ bool is_shared; ++ bool is_critical; ++ /* This is the internal index for private or shared SDE */ ++ int sde_index; ++ int refcount; ++} QemuSDEProp; ++ ++typedef struct QemuSDE { ++ QemuSDEProp *prop; ++ CPUState *target_cpu; ++ QemuMutex lock; ++ bool enabled; ++ bool running; ++ bool pending; ++ bool unregister_pending; ++ uint64_t ep_address; ++ uint64_t ep_argument; ++ uint64_t routing_mode; ++ int32_t event_id; ++ /* ++ * For it is not easy to save the pointer target_cpu during migration, we ++ * add below field to save the corresponding numerical values. ++ */ ++ uint64_t cpu_affinity; ++} QemuSDE; ++ ++/* ++ * GP registers x0~x17 may be modified by client, so they must be saved by ++ * dispatcher. ++ */ ++#define SAVED_GP_NUM 18 ++ ++typedef struct QemuSDECpuCtx { ++ uint64_t xregs[SAVED_GP_NUM]; ++ uint64_t pc; ++ uint32_t pstate; ++} QemuSDECpuCtx; ++ ++typedef enum { ++ SDEI_EVENT_PRIO_NORMAL = 0, ++ SDEI_EVENT_PRIO_CRITICAL, ++ SDEI_EVENT_PRIO_COUNT, ++} SdeiEventPriority; ++ ++typedef struct QemuSDECpu { ++ QemuSDE *private_sde_array[PRIVATE_SLOT_COUNT]; ++ QemuSDECpuCtx ctx[SDEI_EVENT_PRIO_COUNT]; ++ bool masked; ++ int32_t critical_running_event; ++ int32_t normal_running_event; ++} QemuSDECpu; ++ ++typedef struct QemuSDEState { ++ DeviceState parent_obj; ++ QemuSDEProp sde_props_state[PRIVATE_SLOT_COUNT + SHARED_SLOT_COUNT]; ++ QemuSDECpu *sde_cpus; ++ int sdei_max_cpus; ++ QemuSDE *shared_sde_array[SHARED_SLOT_COUNT]; ++ int32_t irq_map[GIC_MAXIRQ]; ++ QemuMutex sdei_interrupt_bind_lock; ++} QemuSDEState; ++ ++#endif +-- +2.23.0 + diff --git a/arm-sdei-override-qemu_irq-handler-when-binding-inte.patch b/arm-sdei-override-qemu_irq-handler-when-binding-inte.patch new file mode 100644 index 0000000000000000000000000000000000000000..569f55fb1135ef709b17e5ded3c4ff54975e06b2 --- /dev/null +++ b/arm-sdei-override-qemu_irq-handler-when-binding-inte.patch @@ -0,0 +1,240 @@ +From 566eec6667956e1b238dfa0b40102a9c15dbc6c5 Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:51 +0800 +Subject: [PATCH 09/15] arm/sdei: override qemu_irq handler when binding + interrupt + +Override qemu_irq handler to support trigger SDEI event transparently +after guest binds interrupt to SDEI event. We don't have good way to +get GIC device and to guarantee SDEI device is initialized after GIC, +so we search GIC in system bus when the first SDEI request happens or +in VMSTATE post_load(). + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + target/arm/sdei.c | 130 +++++++++++++++++++++++++++++++++++++++++- + target/arm/sdei_int.h | 3 + + 2 files changed, 132 insertions(+), 1 deletion(-) + +diff --git a/target/arm/sdei.c b/target/arm/sdei.c +index 9b727883..8bbff76c 100644 +--- a/target/arm/sdei.c ++++ b/target/arm/sdei.c +@@ -88,6 +88,24 @@ static void qemu_sde_cpu_init(QemuSDEState *s) + } + } + ++static int gic_int_to_irq(int num_irq, int intid, int cpu) ++{ ++ if (intid >= GIC_INTERNAL) { ++ return intid - GIC_INTERNAL; ++ } ++ return num_irq - GIC_INTERNAL + cpu * GIC_INTERNAL + intid; ++} ++ ++static int irq_to_gic_int(int num_irq, int irq, int *cpu) ++{ ++ if (irq < num_irq - GIC_INTERNAL) { ++ return irq + GIC_INTERNAL; ++ } ++ irq -= num_irq - GIC_INTERNAL; ++ *cpu = irq / GIC_INTERNAL; ++ return irq % GIC_INTERNAL; ++} ++ + static inline QemuSDECpu *get_sde_cpu(QemuSDEState *s, CPUState *cs) + { + if (cs->cpu_index >= s->sdei_max_cpus) { +@@ -406,6 +424,74 @@ static void dispatch_cpu(QemuSDEState *s, CPUState *cs, bool is_critical) + } + } + ++static void qemu_sdei_irq_handler(void *opaque, int irq, int level) ++{ ++ int cpu = 0; ++ ++ irq = irq_to_gic_int(sde_state->num_irq, irq, &cpu); ++ trigger_sdei_by_irq(cpu, irq); ++} ++ ++static void override_qemu_irq(QemuSDEState *s, int32_t event, uint32_t intid) ++{ ++ qemu_irq irq; ++ QemuSDE *sde; ++ CPUState *cs; ++ ++ /* SPI */ ++ if (intid >= GIC_INTERNAL) { ++ cs = first_cpu; ++ irq = qdev_get_gpio_in(s->gic_dev, ++ gic_int_to_irq(s->num_irq, intid, 0)); ++ if (irq) { ++ qemu_irq_intercept_in(&irq, qemu_sdei_irq_handler, 1); ++ } ++ sde = get_sde_no_check(s, event, cs); ++ sde->irq = irq; ++ put_sde(sde, cs); ++ return; ++ } ++ /* PPI */ ++ CPU_FOREACH(cs) { ++ irq = qdev_get_gpio_in( ++ s->gic_dev, ++ gic_int_to_irq(s->num_irq, intid, cs->cpu_index)); ++ if (irq) { ++ qemu_irq_intercept_in(&irq, qemu_sdei_irq_handler, 1); ++ } ++ sde = get_sde_no_check(s, event, cs); ++ sde->irq = irq; ++ put_sde(sde, cs); ++ } ++} ++ ++static void restore_qemu_irq(QemuSDEState *s, int32_t event, uint32_t intid) ++{ ++ QemuSDE *sde; ++ CPUState *cs; ++ ++ /* SPI */ ++ if (intid >= GIC_INTERNAL) { ++ cs = first_cpu; ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->irq) { ++ qemu_irq_remove_intercept(&sde->irq, 1); ++ sde->irq = NULL; ++ } ++ put_sde(sde, cs); ++ return; ++ } ++ /* PPI */ ++ CPU_FOREACH(cs) { ++ sde = get_sde_no_check(s, event, cs); ++ if (sde->irq) { ++ qemu_irq_remove_intercept(&sde->irq, 1); ++ sde->irq = NULL; ++ } ++ put_sde(sde, cs); ++ } ++} ++ + static int32_t sdei_alloc_event_num(QemuSDEState *s, bool is_critical, + bool is_shared, int intid) + { +@@ -439,6 +525,7 @@ static int32_t sdei_alloc_event_num(QemuSDEState *s, bool is_critical, + sde_props[index].interrupt = intid; + sde_props[index].is_shared = is_shared; + sde_props[index].is_critical = is_critical; ++ override_qemu_irq(s, event, intid); + s->irq_map[intid] = event; + qemu_mutex_unlock(&sde_props[index].lock); + qemu_mutex_unlock(&s->sdei_interrupt_bind_lock); +@@ -456,6 +543,7 @@ static int32_t sdei_free_event_num_locked(QemuSDEState *s, QemuSDEProp *prop) + return SDEI_DENIED; + } + ++ restore_qemu_irq(s, prop->event_id, prop->interrupt); + s->irq_map[prop->interrupt] = SDEI_INVALID_EVENT_ID; + prop->event_id = SDEI_INVALID_EVENT_ID; + prop->interrupt = SDEI_INVALID_INTERRUPT; +@@ -1007,13 +1095,33 @@ static int64_t sdei_event_pe_unmask(QemuSDEState *s, CPUState *cs, + return SDEI_SUCCESS; + } + ++static int dev_walkerfn(DeviceState *dev, void *opaque) ++{ ++ QemuSDEState *s = opaque; ++ ++ if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_GICV3_COMMON)) { ++ GICv3State *gic = ARM_GICV3_COMMON(dev); ++ s->num_irq = gic->num_irq; ++ s->gic_dev = dev; ++ return -1; ++ } ++ ++ if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_GIC_COMMON)) { ++ GICState *gic = ARM_GIC_COMMON(dev); ++ s->num_irq = gic->num_irq; ++ s->gic_dev = dev; ++ return -1; ++ } ++ return 0; ++} ++ + static int64_t sdei_event_interrupt_bind(QemuSDEState *s, CPUState *cs, + struct kvm_run *run) + { + uint64_t *args = (uint64_t *)(run->hypercall.args); + uint32_t intid = args[1]; + +- if (intid < GIC_NR_SGIS || intid >= GIC_MAXIRQ) { ++ if (intid < GIC_NR_SGIS || intid >= s->num_irq) { + return SDEI_INVALID_PARAMETERS; + } + return sdei_alloc_event_num(s, false, intid >= GIC_INTERNAL, intid); +@@ -1132,6 +1240,17 @@ void sdei_handle_request(CPUState *cs, struct kvm_run *run) + return; + } + ++ if (!sde_state->gic_dev) { ++ /* Search for ARM GIC device */ ++ qbus_walk_children(sysbus_get_default(), dev_walkerfn, ++ NULL, NULL, NULL, sde_state); ++ if (!sde_state->gic_dev) { ++ error_report("Cannot find ARM GIC device!"); ++ run->hypercall.args[0] = SDEI_NOT_SUPPORTED; ++ return; ++ } ++ } ++ + if (func_id < SDEI_1_0_FN_BASE || func_id > SDEI_MAX_REQ) { + error_report("Invalid SDEI function ID: 0x%x", func_id); + run->hypercall.args[0] = SDEI_INVALID_PARAMETERS; +@@ -1279,11 +1398,20 @@ static int qemu_sdei_post_load(void *opaque, int version_id) + } + } + ++ /* Search for ARM GIC device */ ++ qbus_walk_children(sysbus_get_default(), dev_walkerfn, ++ NULL, NULL, NULL, s); ++ if (!s->gic_dev) { ++ error_report("Cannot find ARM GIC device!"); ++ return 0; ++ } ++ + for (i = 0; i < PRIVATE_SLOT_COUNT + SHARED_SLOT_COUNT; i++) { + int intid = sde_props[i].interrupt; + + if (intid >= 0 && intid < GIC_MAXIRQ) { + s->irq_map[intid] = sde_props[i].event_id; ++ override_qemu_irq(s, sde_props[i].event_id, intid); + } + } + +diff --git a/target/arm/sdei_int.h b/target/arm/sdei_int.h +index d3fd7cbc..a251b04a 100644 +--- a/target/arm/sdei_int.h ++++ b/target/arm/sdei_int.h +@@ -63,6 +63,7 @@ typedef struct QemuSDEProp { + typedef struct QemuSDE { + QemuSDEProp *prop; + CPUState *target_cpu; ++ qemu_irq irq; + QemuMutex lock; + bool enabled; + bool running; +@@ -107,9 +108,11 @@ typedef struct QemuSDECpu { + + typedef struct QemuSDEState { + DeviceState parent_obj; ++ DeviceState *gic_dev; + QemuSDEProp sde_props_state[PRIVATE_SLOT_COUNT + SHARED_SLOT_COUNT]; + QemuSDECpu *sde_cpus; + int sdei_max_cpus; ++ int num_irq; + QemuSDE *shared_sde_array[SHARED_SLOT_COUNT]; + int32_t irq_map[GIC_MAXIRQ]; + QemuMutex sdei_interrupt_bind_lock; +-- +2.23.0 + diff --git a/core-irq-add-qemu_irq_remove_intercept-interface.patch b/core-irq-add-qemu_irq_remove_intercept-interface.patch new file mode 100644 index 0000000000000000000000000000000000000000..c5863a73e808c12cddd67a625b5f70c67abe75c4 --- /dev/null +++ b/core-irq-add-qemu_irq_remove_intercept-interface.patch @@ -0,0 +1,65 @@ +From 0311642287a6f9156a558c6a956eecad1d311c73 Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:50 +0800 +Subject: [PATCH 08/15] core/irq: add qemu_irq_remove_intercept interface + +We use qemu_irq as the bridge for other qemu modules to switch from +irq injection to SDEI event trigger after VM binds the interrupt to +SDEI event. We use qemu_irq_intercept_in() to override qemu_irq +handler with SDEI event trigger, so we also need a corresponding +interface to restore the handler to default one (i.e. ARM GIC). + +qemu_irq_remove_intercept() is the new interface to do the above +job. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + hw/core/irq.c | 11 +++++++++++ + include/hw/irq.h | 8 ++++++-- + 2 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/hw/core/irq.c b/hw/core/irq.c +index 7cc0295d..114bce6c 100644 +--- a/hw/core/irq.c ++++ b/hw/core/irq.c +@@ -145,6 +145,17 @@ void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n) + } + } + ++void qemu_irq_remove_intercept(qemu_irq *gpio_in, int n) ++{ ++ int i; ++ qemu_irq *old_irqs = gpio_in[0]->opaque; ++ for (i = 0; i < n; i++) { ++ gpio_in[i]->handler = old_irqs[i]->handler; ++ gpio_in[i]->opaque = old_irqs[i]->opaque; ++ } ++ qemu_free_irqs(old_irqs, n); ++} ++ + static const TypeInfo irq_type_info = { + .name = TYPE_IRQ, + .parent = TYPE_OBJECT, +diff --git a/include/hw/irq.h b/include/hw/irq.h +index 7a40e3ed..3f4c2284 100644 +--- a/include/hw/irq.h ++++ b/include/hw/irq.h +@@ -60,8 +60,12 @@ qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2); + */ + qemu_irq *qemu_irq_proxy(qemu_irq **target, int n); + +-/* For internal use in qtest. Similar to qemu_irq_split, but operating +- on an existing vector of qemu_irq. */ ++/* ++ * Similar to qemu_irq_split, but operating on an existing vector of qemu_irq. ++ */ + void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n); + ++/* Restore the irq handler intercepted by qemu_irq_intercept_in() */ ++void qemu_irq_remove_intercept(qemu_irq *gpio_in, int n); ++ + #endif +-- +2.23.0 + diff --git a/linux-headers-kvm.h-add-capability-to-forward-hyperc.patch b/linux-headers-kvm.h-add-capability-to-forward-hyperc.patch new file mode 100644 index 0000000000000000000000000000000000000000..371334f77ba4d2950874e78145b4e1470d16bb28 --- /dev/null +++ b/linux-headers-kvm.h-add-capability-to-forward-hyperc.patch @@ -0,0 +1,93 @@ +From b12ec4eeea983fc62a70b43c967b72e20cecb862 Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:53 +0800 +Subject: [PATCH 11/15] linux-headers/kvm.h: add capability to forward + hypercall + +To keep backward compatibility, we add new KVM capability +"KVM_CAP_FORWARD_HYPERCALL" to probe whether KVM supports forwarding +hypercall to userspace. + +The capability should be enabled explicitly, for we don't want user +space application to deal with unexpected hypercall exits. After +enabling this cap, all HVC calls unhandled by kvm will be forwarded to +user space. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + linux-headers/linux/kvm.h | 2 ++ + target/arm/sdei.c | 16 ++++++++++++++++ + target/arm/sdei.h | 2 ++ + 3 files changed, 20 insertions(+) + +diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h +index 744e888e..18e30229 100644 +--- a/linux-headers/linux/kvm.h ++++ b/linux-headers/linux/kvm.h +@@ -995,6 +995,8 @@ struct kvm_ppc_resize_hpt { + #define KVM_CAP_ARM_PTRAUTH_ADDRESS 171 + #define KVM_CAP_ARM_PTRAUTH_GENERIC 172 + ++#define KVM_CAP_FORWARD_HYPERCALL 506 ++ + #ifdef KVM_CAP_IRQ_ROUTING + + struct kvm_irq_routing_irqchip { +diff --git a/target/arm/sdei.c b/target/arm/sdei.c +index 56c496b7..16e22027 100644 +--- a/target/arm/sdei.c ++++ b/target/arm/sdei.c +@@ -46,6 +46,7 @@ + #define SMCCC_RETURN_REG_COUNT 4 + #define PSTATE_M_EL_SHIFT 2 + ++bool sdei_enabled; + static QemuSDEState *sde_state; + + typedef struct QemuSDEIBindNotifyEntry { +@@ -1544,6 +1545,7 @@ static const VMStateDescription vmstate_sde_state = { + static void sdei_initfn(Object *obj) + { + QemuSDEState *s = QEMU_SDEI(obj); ++ KVMState *kvm = KVM_STATE(current_machine->accelerator); + + if (sde_state) { + error_report("Only one SDEI dispatcher is allowed!"); +@@ -1553,6 +1555,20 @@ static void sdei_initfn(Object *obj) + + qemu_sde_init(s); + qemu_register_reset(qemu_sde_reset, s); ++ ++ if (kvm_check_extension(kvm, KVM_CAP_FORWARD_HYPERCALL)) { ++ int ret; ++ ret = kvm_vm_enable_cap(kvm, KVM_CAP_FORWARD_HYPERCALL, 0, 0); ++ if (ret < 0) { ++ error_report("Enable hypercall forwarding failed: %s", ++ strerror(-ret)); ++ abort(); ++ } ++ sdei_enabled = true; ++ info_report("qemu sdei enabled"); ++ } else { ++ info_report("KVM does not support forwarding hypercall."); ++ } + } + + static void qemu_sde_class_init(ObjectClass *klass, void *data) +diff --git a/target/arm/sdei.h b/target/arm/sdei.h +index 49804759..9bb9b977 100644 +--- a/target/arm/sdei.h ++++ b/target/arm/sdei.h +@@ -29,6 +29,8 @@ + + #define SDEI_MAX_REQ SDEI_1_0_FN(0x12) + ++extern bool sdei_enabled; ++ + void sdei_handle_request(CPUState *cs, struct kvm_run *run); + + /* +-- +2.23.0 + diff --git a/qemu.spec b/qemu.spec index ebb7e27effa027df27bae44d961cbfca2e678cd5..c88e56b53e90655ad2597c93be59415755745a5a 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1,6 +1,6 @@ Name: qemu Version: 4.1.0 -Release: 11 +Release: 12 Epoch: 2 Summary: QEMU is a generic and open source machine emulator and virtualizer License: GPLv2 and BSD and MIT and CC-BY @@ -173,6 +173,21 @@ Patch0160: ARM64-record-vtimer-tick-when-cpu-is-stopped.patch Patch0161: hw-arm-virt-add-missing-compat-for-kvm-no-adjvtime.patch Patch0162: migration-Compat-virtual-timer-adjust-for-v4.0.1-and.patch Patch0163: vtimer-Drop-vtimer-virtual-timer-adjust.patch +Patch0164: update-linux-headers.sh-import-linux-arm_sdei.h-to-s.patch +Patch0165: standard-headers-import-arm_sdei.h.patch +Patch0166: arm-sdei-add-virtual-device-framework.patch +Patch0167: arm-add-CONFIG_SDEI-build-flag.patch +Patch0168: arm-sdei-add-support-to-handle-SDEI-requests-from-gu.patch +Patch0169: arm-sdei-add-system-reset-callback.patch +Patch0170: arm-sdei-add-support-to-trigger-event-by-GIC-interru.patch +Patch0171: core-irq-add-qemu_irq_remove_intercept-interface.patch +Patch0172: arm-sdei-override-qemu_irq-handler-when-binding-inte.patch +Patch0173: arm-sdei-add-support-to-register-interrupt-bind-noti.patch +Patch0174: linux-headers-kvm.h-add-capability-to-forward-hyperc.patch +Patch0175: arm-sdei-add-stub-to-fix-build-failure-when-SDEI-is-.patch +Patch0176: arm-kvm-handle-guest-exit-of-hypercall.patch +Patch0177: virt-acpi-add-SDEI-table-if-SDEI-is-enabled.patch +Patch0178: arm-sdei-Add-SDEI-watchdog.patch BuildRequires: flex BuildRequires: bison @@ -518,6 +533,23 @@ getent passwd qemu >/dev/null || \ %endif %changelog +* Thu May 28 2020 Huawei Technologies Co., Ltd. +- update-linux-headers.sh: import linux/arm_sdei.h to standard-headers +- standard-headers: import arm_sdei.h +- arm/sdei: add virtual device framework +- arm: add CONFIG_SDEI build flag +- arm/sdei: add support to handle SDEI requests from guest +- arm/sdei: add system reset callback +- arm/sdei: add support to trigger event by GIC interrupt ID +- core/irq: add qemu_irq_remove_intercept interface +- arm/sdei: override qemu_irq handler when binding interrupt +- arm/sdei: add support to register interrupt bind notifier +- linux-headers/kvm.h: add capability to forward hypercall +- arm/sdei: add stub to fix build failure when SDEI is not enabled +- arm/kvm: handle guest exit of hypercall +- virt/acpi: add SDEI table if SDEI is enabled +- arm/sdei: Add SDEI watchdog + * Wed May 27 2020 Huawei Technologies Co., Ltd. - Revert: "vtimer: compat cross version migration from v4.0.1" - ARM64: record vtimer tick when cpu is stopped diff --git a/standard-headers-import-arm_sdei.h.patch b/standard-headers-import-arm_sdei.h.patch new file mode 100644 index 0000000000000000000000000000000000000000..0a72515b78614f14c7a9ac60525ad93386f32fea --- /dev/null +++ b/standard-headers-import-arm_sdei.h.patch @@ -0,0 +1,98 @@ +From cea4c51ba31bacf7ee8900763260abf09efc6e1e Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:44 +0800 +Subject: [PATCH 02/15] standard-headers: import arm_sdei.h + +Import Linux header file include/uapi/linux/arm_sdei.h from kernel v5.4-rc5. + +This is to prepare for qemu SDEI emulation. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + include/standard-headers/linux/arm_sdei.h | 73 +++++++++++++++++++++++ + 1 file changed, 73 insertions(+) + create mode 100644 include/standard-headers/linux/arm_sdei.h + +diff --git a/include/standard-headers/linux/arm_sdei.h b/include/standard-headers/linux/arm_sdei.h +new file mode 100644 +index 00000000..568d9719 +--- /dev/null ++++ b/include/standard-headers/linux/arm_sdei.h +@@ -0,0 +1,73 @@ ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ ++/* Copyright (C) 2017 Arm Ltd. */ ++#ifndef _LINUX_ARM_SDEI_H ++#define _LINUX_ARM_SDEI_H ++ ++#define SDEI_1_0_FN_BASE 0xC4000020 ++#define SDEI_1_0_MASK 0xFFFFFFE0 ++#define SDEI_1_0_FN(n) (SDEI_1_0_FN_BASE + (n)) ++ ++#define SDEI_1_0_FN_SDEI_VERSION SDEI_1_0_FN(0x00) ++#define SDEI_1_0_FN_SDEI_EVENT_REGISTER SDEI_1_0_FN(0x01) ++#define SDEI_1_0_FN_SDEI_EVENT_ENABLE SDEI_1_0_FN(0x02) ++#define SDEI_1_0_FN_SDEI_EVENT_DISABLE SDEI_1_0_FN(0x03) ++#define SDEI_1_0_FN_SDEI_EVENT_CONTEXT SDEI_1_0_FN(0x04) ++#define SDEI_1_0_FN_SDEI_EVENT_COMPLETE SDEI_1_0_FN(0x05) ++#define SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME SDEI_1_0_FN(0x06) ++#define SDEI_1_0_FN_SDEI_EVENT_UNREGISTER SDEI_1_0_FN(0x07) ++#define SDEI_1_0_FN_SDEI_EVENT_STATUS SDEI_1_0_FN(0x08) ++#define SDEI_1_0_FN_SDEI_EVENT_GET_INFO SDEI_1_0_FN(0x09) ++#define SDEI_1_0_FN_SDEI_EVENT_ROUTING_SET SDEI_1_0_FN(0x0A) ++#define SDEI_1_0_FN_SDEI_PE_MASK SDEI_1_0_FN(0x0B) ++#define SDEI_1_0_FN_SDEI_PE_UNMASK SDEI_1_0_FN(0x0C) ++#define SDEI_1_0_FN_SDEI_INTERRUPT_BIND SDEI_1_0_FN(0x0D) ++#define SDEI_1_0_FN_SDEI_INTERRUPT_RELEASE SDEI_1_0_FN(0x0E) ++#define SDEI_1_0_FN_SDEI_PRIVATE_RESET SDEI_1_0_FN(0x11) ++#define SDEI_1_0_FN_SDEI_SHARED_RESET SDEI_1_0_FN(0x12) ++ ++#define SDEI_VERSION_MAJOR_SHIFT 48 ++#define SDEI_VERSION_MAJOR_MASK 0x7fff ++#define SDEI_VERSION_MINOR_SHIFT 32 ++#define SDEI_VERSION_MINOR_MASK 0xffff ++#define SDEI_VERSION_VENDOR_SHIFT 0 ++#define SDEI_VERSION_VENDOR_MASK 0xffffffff ++ ++#define SDEI_VERSION_MAJOR(x) (x>>SDEI_VERSION_MAJOR_SHIFT & SDEI_VERSION_MAJOR_MASK) ++#define SDEI_VERSION_MINOR(x) (x>>SDEI_VERSION_MINOR_SHIFT & SDEI_VERSION_MINOR_MASK) ++#define SDEI_VERSION_VENDOR(x) (x>>SDEI_VERSION_VENDOR_SHIFT & SDEI_VERSION_VENDOR_MASK) ++ ++/* SDEI return values */ ++#define SDEI_SUCCESS 0 ++#define SDEI_NOT_SUPPORTED -1 ++#define SDEI_INVALID_PARAMETERS -2 ++#define SDEI_DENIED -3 ++#define SDEI_PENDING -5 ++#define SDEI_OUT_OF_RESOURCE -10 ++ ++/* EVENT_REGISTER flags */ ++#define SDEI_EVENT_REGISTER_RM_ANY 0 ++#define SDEI_EVENT_REGISTER_RM_PE 1 ++ ++/* EVENT_STATUS return value bits */ ++#define SDEI_EVENT_STATUS_RUNNING 2 ++#define SDEI_EVENT_STATUS_ENABLED 1 ++#define SDEI_EVENT_STATUS_REGISTERED 0 ++ ++/* EVENT_COMPLETE status values */ ++#define SDEI_EV_HANDLED 0 ++#define SDEI_EV_FAILED 1 ++ ++/* GET_INFO values */ ++#define SDEI_EVENT_INFO_EV_TYPE 0 ++#define SDEI_EVENT_INFO_EV_SIGNALED 1 ++#define SDEI_EVENT_INFO_EV_PRIORITY 2 ++#define SDEI_EVENT_INFO_EV_ROUTING_MODE 3 ++#define SDEI_EVENT_INFO_EV_ROUTING_AFF 4 ++ ++/* and their results */ ++#define SDEI_EVENT_TYPE_PRIVATE 0 ++#define SDEI_EVENT_TYPE_SHARED 1 ++#define SDEI_EVENT_PRIORITY_NORMAL 0 ++#define SDEI_EVENT_PRIORITY_CRITICAL 1 ++ ++#endif /* _LINUX_ARM_SDEI_H */ +-- +2.23.0 + diff --git a/update-linux-headers.sh-import-linux-arm_sdei.h-to-s.patch b/update-linux-headers.sh-import-linux-arm_sdei.h-to-s.patch new file mode 100644 index 0000000000000000000000000000000000000000..03de35a27e1ad20eb0022b6f7908cba90f6e6947 --- /dev/null +++ b/update-linux-headers.sh-import-linux-arm_sdei.h-to-s.patch @@ -0,0 +1,29 @@ +From 5696b45e0a2d7b53ff43947da006d5b82bd1564f Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:43 +0800 +Subject: [PATCH 01/15] update-linux-headers.sh: import linux/arm_sdei.h to + standard-headers + +This is to prepare for qemu SDEI emulation. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + scripts/update-linux-headers.sh | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh +index f76d7736..84d15c18 100755 +--- a/scripts/update-linux-headers.sh ++++ b/scripts/update-linux-headers.sh +@@ -191,6 +191,7 @@ for i in "$tmpdir"/include/linux/*virtio*.h \ + "$tmpdir/include/linux/pci_regs.h" \ + "$tmpdir/include/linux/ethtool.h" "$tmpdir/include/linux/kernel.h" \ + "$tmpdir/include/linux/vhost_types.h" \ ++ "$tmpdir/include/linux/arm_sdei.h" \ + "$tmpdir/include/linux/sysinfo.h"; do + cp_portable "$i" "$output/include/standard-headers/linux" + done +-- +2.23.0 + diff --git a/virt-acpi-add-SDEI-table-if-SDEI-is-enabled.patch b/virt-acpi-add-SDEI-table-if-SDEI-is-enabled.patch new file mode 100644 index 0000000000000000000000000000000000000000..296c28123bdefb8e1d757633ef299ebd422699eb --- /dev/null +++ b/virt-acpi-add-SDEI-table-if-SDEI-is-enabled.patch @@ -0,0 +1,68 @@ +From d503c6787c3404838d9bdc2b355fa72a0d7a9ab0 Mon Sep 17 00:00:00 2001 +From: Heyi Guo +Date: Tue, 5 Nov 2019 17:10:56 +0800 +Subject: [PATCH 14/15] virt/acpi: add SDEI table if SDEI is enabled + +Add SDEI table if SDEI is enabled, so that guest OS can get aware and +utilize the interfaces. + +Signed-off-by: Heyi Guo +Signed-off-by: Jingyi Wang +--- + hw/arm/virt-acpi-build.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c +index 2cfac7b8..25873ebe 100644 +--- a/hw/arm/virt-acpi-build.c ++++ b/hw/arm/virt-acpi-build.c +@@ -32,6 +32,7 @@ + #include "trace.h" + #include "qom/cpu.h" + #include "target/arm/cpu.h" ++#include "target/arm/sdei.h" + #include "hw/acpi/acpi-defs.h" + #include "hw/acpi/acpi.h" + #include "hw/nvram/fw_cfg.h" +@@ -555,6 +556,26 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) + "IORT", table_data->len - iort_start, 0, NULL, NULL); + } + ++/* ++ * ACPI spec 6.2 Software Delegated Exception Interface (SDEI). ++ * (Revision 1.0) ++ * "SDEI" was reserved in ACPI 6.2. See "Links to ACPI-Related Documents" ++ * (http://uefi.org/acpi) under the heading "Software ++ * Delegated Exceptions Interface." The definition is under ++ * "10 Appendix C: ACPI table definitions for SDEI" in the linked document. ++ * ++ * This is a dummy table to expose platform SDEI capbility to OS. ++ */ ++static void ++build_sdei(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) ++{ ++ int sdei_start = table_data->len; ++ ++ (void)acpi_data_push(table_data, sizeof(AcpiTableHeader)); ++ build_header(linker, table_data, (void *)(table_data->data + sdei_start), ++ "SDEI", table_data->len - sdei_start, 1, NULL, NULL); ++} ++ + static void + build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) + { +@@ -953,6 +974,11 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) + acpi_add_table(table_offsets, tables_blob); + build_spcr(tables_blob, tables->linker, vms); + ++ if (sdei_enabled) { ++ acpi_add_table(table_offsets, tables_blob); ++ build_sdei(tables_blob, tables->linker, vms); ++ } ++ + if (nb_numa_nodes > 0) { + acpi_add_table(table_offsets, tables_blob); + build_srat(tables_blob, tables->linker, vms); +-- +2.23.0 +