Implement API to inject virtual interrupts into VMs.

This doesn't yet work properly in multiple-PE configurations, and doesn't have
any concept of priorities.

Bug: 117270899
Change-Id: Id62c59d78d0604b934aeca75ea459248db660488
diff --git a/inc/hf/api.h b/inc/hf/api.h
index 5359f86..692fbac 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -38,3 +38,9 @@
 
 struct vcpu *api_wait_for_interrupt(struct vcpu *current);
 struct vcpu *api_yield(struct vcpu *current);
+
+int64_t api_enable_interrupt(uint32_t intid, bool enable, struct vcpu *current);
+uint32_t api_get_and_acknowledge_interrupt(struct vcpu *current);
+int64_t api_inject_interrupt(uint32_t target_vm_id, uint32_t target_vcpu_idx,
+			     uint32_t intid, struct vcpu *current,
+			     struct vcpu **next);
diff --git a/inc/hf/cpu.h b/inc/hf/cpu.h
index 60261fe..d2029dd 100644
--- a/inc/hf/cpu.h
+++ b/inc/hf/cpu.h
@@ -25,6 +25,11 @@
 #include "hf/addr.h"
 #include "hf/spinlock.h"
 
+#include "vmapi/hf/types.h"
+
+/** The number of bits in each element of the interrupt bitfields. */
+#define INTERRUPT_REGISTER_BITS 32
+
 enum vcpu_state {
 	/** The vcpu is switched off. */
 	vcpu_state_off,
@@ -42,6 +47,13 @@
 	vcpu_state_blocked_interrupt,
 };
 
+struct interrupts {
+	/** Bitfield keeping track of which interrupts are enabled. */
+	uint32_t interrupt_enabled[HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS];
+	/** Bitfield keeping track of which interrupts are pending. */
+	uint32_t interrupt_pending[HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS];
+};
+
 struct vcpu {
 	struct spinlock lock;
 	enum vcpu_state state;
@@ -49,6 +61,7 @@
 	struct vm *vm;
 	struct vcpu *mailbox_next;
 	struct arch_regs regs;
+	struct interrupts interrupts;
 };
 
 /* TODO: Update alignment such that cpus are in different cache lines. */
diff --git a/inc/hf/std.h b/inc/hf/std.h
index 55922fa..39d3d08 100644
--- a/inc/hf/std.h
+++ b/inc/hf/std.h
@@ -27,6 +27,8 @@
 size_t strlen(const char *str);
 int strcmp(const char *a, const char *b);
 
+#define ctz(x) __builtin_ctz(x)
+
 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 
 #define be16toh(v) __builtin_bswap16(v)
diff --git a/inc/vmapi/hf/call.h b/inc/vmapi/hf/call.h
index 102fd6c..7de8160 100644
--- a/inc/vmapi/hf/call.h
+++ b/inc/vmapi/hf/call.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include "hf/arch/cpu.h"
+
 #include "hf/abi.h"
 #include "hf/types.h"
 
@@ -23,13 +25,16 @@
 /* clang-format off */
 
 /* TODO: Define constants below according to spec. */
-#define HF_VCPU_RUN         0xff00
-#define HF_VM_GET_COUNT     0xff01
-#define HF_VCPU_GET_COUNT   0xff02
-#define HF_VM_CONFIGURE     0xff03
-#define HF_MAILBOX_SEND     0xff04
-#define HF_MAILBOX_RECEIVE  0xff05
-#define HF_MAILBOX_CLEAR    0xff06
+#define HF_VCPU_RUN                      0xff00
+#define HF_VM_GET_COUNT                  0xff01
+#define HF_VCPU_GET_COUNT                0xff02
+#define HF_VM_CONFIGURE                  0xff03
+#define HF_MAILBOX_SEND                  0xff04
+#define HF_MAILBOX_RECEIVE               0xff05
+#define HF_MAILBOX_CLEAR                 0xff06
+#define HF_ENABLE_INTERRUPT              0xff07
+#define HF_GET_AND_ACKNOWLEDGE_INTERRUPT 0xff08
+#define HF_INJECT_INTERRUPT              0xff09
 
 /** The amount of data that can be sent to a mailbox. */
 #define HF_MAILBOX_SIZE 4096
@@ -118,3 +123,39 @@
 {
 	return hf_call(HF_MAILBOX_CLEAR, 0, 0, 0);
 }
+
+/**
+ * Enables or disables a given interrupt ID.
+ *
+ * Returns 0 on success, or -1 if the intid is invalid.
+ */
+static inline uint64_t hf_enable_interrupt(uint32_t intid, bool enable)
+{
+	return hf_call(HF_ENABLE_INTERRUPT, intid, enable, 0);
+}
+
+/**
+ * Gets the ID of the pending interrupt (if any) and acknowledge it.
+ *
+ * Returns HF_INVALID_INTID if there are no pending interrupts.
+ */
+static inline uint32_t hf_get_and_acknowledge_interrupt()
+{
+	return hf_call(HF_GET_AND_ACKNOWLEDGE_INTERRUPT, 0, 0, 0);
+}
+
+/**
+ * Injects a virtual interrupt of the given ID into the given target vCPU.
+ * This doesn't cause the vCPU to actually be run immediately; it will be taken
+ * when the vCPU is next run, which is up to the scheduler.
+ *
+ * Returns 0 on success, or -1 if the target VM or vCPU doesn't exist or
+ * the interrupt ID is invalid.
+ */
+static inline int64_t hf_inject_interrupt(uint32_t target_vm_id,
+					  uint32_t target_vcpu_idx,
+					  uint32_t intid)
+{
+	return hf_call(HF_INJECT_INTERRUPT, target_vm_id, target_vcpu_idx,
+		       intid);
+}
diff --git a/inc/vmapi/hf/types.h b/inc/vmapi/hf/types.h
index 45aa257..697a990 100644
--- a/inc/vmapi/hf/types.h
+++ b/inc/vmapi/hf/types.h
@@ -39,3 +39,9 @@
 /* Invalid values for fields to indicate absence or errors. */
 #define HF_INVALID_VM_ID 0xffffffff
 #define HF_INVALID_VCPU 0xffff
+
+/** The number of virtual interrupt IDs which are supported. */
+#define HF_NUM_INTIDS 64
+
+/** Interrupt ID returned when there is no interrupt pending. */
+#define HF_INVALID_INTID 0xffffffff