[PATCH v9 3/6] x86/tdx: Add TDX Guest event notify interrupt support

Kuppuswamy Sathyanarayanan
2022-07-28T03:44:42Z

Host-guest event notification via configured interrupt vector is useful
in cases where a guest makes an asynchronous request and needs a
callback from the host to indicate the completion or to let the host
notify the guest about events like device removal. One usage example is,
callback requirement of GetQuote asynchronous hypercall.

In TDX guest, SetupEventNotifyInterrupt hypercall can be used by the
guest to specify which interrupt vector to use as an event-notify
vector to the VMM. Details about the SetupEventNotifyInterrupt
hypercall can be found in TDX Guest-Host Communication Interface
(GHCI) Specification, sec 3.5 "VP.VMCALL<SetupEventNotifyInterrupt>".
Add a tdx_hcall_set_notify_intr() helper function to implement the
SetupEventNotifyInterrupt hypercall.

As per design VMM will post the event completion IRQ using the same CPU
in which SetupEventNotifyInterrupt hypercall request is received. So
allocate an IRQ vector from "x86_vector_domain", and set the CPU
affinity of the IRQ vector to the current CPU.

Please note that this patch only reserves the IRQ number. It is
expected that the user of event notify IRQ (like GetQuote handler) will
directly register the handler for "tdx_notify_irq" IRQ by using
request_irq() with IRQF_SHARED flag set. Using IRQF_SHARED will enable
multiple users to use the same IRQ for event notification.

Reviewed-by: Tony Luck <tony.luck@intel.com>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Wander Lairson Costa <wander@redhat.com>
Signed-off-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
---
arch/x86/coco/tdx/tdx.c | 84 ++++++++++++++++++++++++++++++++++++++
arch/x86/include/asm/tdx.h | 2 +
2 files changed, 86 insertions(+)

diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
index 205f98f42da8..3563b208979c 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -8,12 +8,16 @@
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/numa.h>
#include <asm/coco.h>
#include <asm/tdx.h>
#include <asm/vmx.h>
#include <asm/insn.h>
#include <asm/insn-eval.h>
#include <asm/pgtable.h>
+#include <asm/irqdomain.h>

#include "tdx.h"

@@ -24,6 +28,7 @@

/* TDX hypercall Leaf IDs */
#define TDVMCALL_MAP_GPA 0x10001
+#define TDVMCALL_SETUP_NOTIFY_INTR 0x10004

/* MMIO direction */
#define EPT_READ 0
@@ -42,6 +47,7 @@
#define DRIVER_NAME "tdx-guest"

static struct miscdevice tdx_misc_dev;
+int tdx_notify_irq = -1;

/*
* Wrapper for standard use of __tdx_hypercall with no output aside from
@@ -107,6 +113,31 @@ static inline void tdx_module_call(u64 fn, u64 rcx, u64 rdx, u64 r8, u64 r9,
panic("TDCALL %lld failed (Buggy TDX module!)\n", fn);
}

+/*
+ * tdx_hcall_set_notify_intr() - Setup Event Notify Interrupt Vector.
+ *
+ * @vector: Vector address to be used for notification.
+ *
+ * return 0 on success or failure error number.
+ */
+static long tdx_hcall_set_notify_intr(u8 vector)
+{
+ /* Minimum vector value allowed is 32 */
+ if (vector < 32)
+ return -EINVAL;
+
+ /*
+ * Register callback vector address with VMM. More details
+ * about the ABI can be found in TDX Guest-Host-Communication
+ * Interface (GHCI), sec titled
+ * "TDG.VP.VMCALL<SetupEventNotifyInterrupt>".
+ */
+ if (_tdx_hypercall(TDVMCALL_SETUP_NOTIFY_INTR, vector, 0, 0, 0))
+ return -EIO;
+
+ return 0;
+}
+
static u64 get_cc_mask(void)
{
struct tdx_module_output out;
@@ -830,3 +861,56 @@ static int __init tdx_guest_init(void)
return 0;
}
device_initcall(tdx_guest_init)
+
+/* Reserve an IRQ from x86_vector_domain for TD event notification */
+static int __init tdx_arch_init(void)
+{
+ struct irq_alloc_info info;
+ struct irq_cfg *cfg;
+ int cpu;
+
+ if (!cpu_feature_enabled(X86_FEATURE_TDX_GUEST))
+ return 0;
+
+ /* Make sure x86 vector domain is initialized */
+ if (!x86_vector_domain) {
+ pr_err("x86 vector domain is NULL\n");
+ return 0;
+ }
+
+ init_irq_alloc_info(&info, NULL);
+
+ /*
+ * Event notification vector will be delivered to the CPU
+ * in which TDVMCALL_SETUP_NOTIFY_INTR hypercall is requested.
+ * So set the IRQ affinity to the current CPU.
+ */
+ cpu = get_cpu();
+
+ info.mask = cpumask_of(cpu);
+
+ tdx_notify_irq = irq_domain_alloc_irqs(x86_vector_domain, 1,
+ NUMA_NO_NODE, &info);
+
+ if (tdx_notify_irq < 0) {
+ pr_err("Event notification IRQ allocation failed %d\n",
+ tdx_notify_irq);
+ goto init_failed;
+ }
+
+ irq_set_handler(tdx_notify_irq, handle_edge_irq);
+
+ cfg = irq_cfg(tdx_notify_irq);
+ if (!cfg) {
+ pr_err("Event notification IRQ config not found\n");
+ goto init_failed;
+ }
+
+ if (tdx_hcall_set_notify_intr(cfg->vector))
+ pr_err("Setting event notification interrupt failed\n");
+
+init_failed:
+ put_cpu();
+ return 0;
+}
+arch_initcall(tdx_arch_init);
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index 020c81a7c729..8f933e67547f 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -67,6 +67,8 @@ void tdx_safe_halt(void);

bool tdx_early_handle_ve(struct pt_regs *regs);

+extern int tdx_notify_irq;
+
#else

static inline void tdx_early_init(void) { };
--
2.25.1