Add APIs to yield and get the ID of the VM.

Change-Id: I2e6340d200cfddc7ca9877e91d785c438620b6c6
diff --git a/inc/hf/api.h b/inc/hf/api.h
index 94e3327..b4bbbf7 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -23,6 +23,7 @@
 #include "vmapi/hf/call.h"
 
 void api_init(struct mpool *ppool);
+int64_t api_vm_get_id(const struct vcpu *current);
 int64_t api_vm_get_count(void);
 int64_t api_vcpu_get_count(uint32_t vm_id, const struct vcpu *current);
 struct hf_vcpu_run_return api_vcpu_run(uint32_t vm_id, uint32_t vcpu_idx,
diff --git a/inc/vmapi/hf/call.h b/inc/vmapi/hf/call.h
index 37428f3..a7c58a2 100644
--- a/inc/vmapi/hf/call.h
+++ b/inc/vmapi/hf/call.h
@@ -23,16 +23,18 @@
 /* clang-format off */
 
 /* TODO: Define constants below according to spec. */
-#define HF_VCPU_RUN                      0xff00
+#define HF_VM_GET_ID                     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
+#define HF_VCPU_RUN                      0xff03
+#define HF_VCPU_YIELD                    0xff04
+#define HF_VM_CONFIGURE                  0xff05
+#define HF_MAILBOX_SEND                  0xff06
+#define HF_MAILBOX_RECEIVE               0xff07
+#define HF_MAILBOX_CLEAR                 0xff08
+#define HF_ENABLE_INTERRUPT              0xff09
+#define HF_GET_AND_ACKNOWLEDGE_INTERRUPT 0xff0a
+#define HF_INJECT_INTERRUPT              0xff0b
 
 /** The amount of data that can be sent to a mailbox. */
 #define HF_MAILBOX_SIZE 4096
@@ -46,15 +48,11 @@
 int64_t hf_call(size_t arg0, size_t arg1, size_t arg2, size_t arg3);
 
 /**
- * Runs the given vcpu of the given vm.
- *
- * Returns an hf_vcpu_run_return struct telling the scheduler what to do next.
+ * Returns the VM's own ID.
  */
-static inline struct hf_vcpu_run_return hf_vcpu_run(uint32_t vm_id,
-						    uint32_t vcpu_idx)
+static inline uint32_t hf_vm_get_id()
 {
-	return hf_vcpu_run_return_decode(
-		hf_call(HF_VCPU_RUN, vm_id, vcpu_idx, 0));
+	return hf_call(HF_VM_GET_ID, 0, 0, 0);
 }
 
 /**
@@ -74,6 +72,26 @@
 }
 
 /**
+ * Runs the given vcpu of the given vm.
+ *
+ * Returns an hf_vcpu_run_return struct telling the scheduler what to do next.
+ */
+static inline struct hf_vcpu_run_return hf_vcpu_run(uint32_t vm_id,
+						    uint32_t vcpu_idx)
+{
+	return hf_vcpu_run_return_decode(
+		hf_call(HF_VCPU_RUN, vm_id, vcpu_idx, 0));
+}
+
+/**
+ * Hints that the vcpu is willing to yield its current use of the physical CPU.
+ */
+static inline void hf_vcpu_yield(void)
+{
+	hf_call(HF_VCPU_YIELD, 0, 0, 0);
+}
+
+/**
  * Configures the pages to send/receive data through. The pages must not be
  * shared.
  *
@@ -88,11 +106,11 @@
  * Copies data from the sender's send buffer to the recipient's receive buffer.
  *
  * Returns -1 on failure, and on success either:
- * - 0, if the caller is a secondary VM
- * - the ID of the vCPU to run to receive the message, if the caller is the
- * primary VM.
- * - HF_INVALID_VCPU if the caller is the primary VM and no vCPUs on the target
- * VM are currently waiting to receive a message.
+ *  - 0, if the caller is a secondary VM
+ *  - the ID of the vCPU to run to receive the message, if the caller is the
+ *    primary VM.
+ *  - HF_INVALID_VCPU if the caller is the primary VM and no vCPUs on the target
+ *    VM are currently waiting to receive a message.
  */
 static inline int64_t hf_mailbox_send(uint32_t vm_id, size_t size)
 {
diff --git a/src/api.c b/src/api.c
index 6be8702..7e6e4e6 100644
--- a/src/api.c
+++ b/src/api.c
@@ -101,6 +101,14 @@
 }
 
 /**
+ * Returns the ID of the VM.
+ */
+int64_t api_vm_get_id(const struct vcpu *current)
+{
+	return current->vm->id;
+}
+
+/**
  * Returns the number of VMs configured to run.
  */
 int64_t api_vm_get_count(void)
diff --git a/src/arch/aarch64/handler.c b/src/arch/aarch64/handler.c
index c15b550..990b212 100644
--- a/src/arch/aarch64/handler.c
+++ b/src/arch/aarch64/handler.c
@@ -217,6 +217,10 @@
 	}
 
 	switch ((uint32_t)arg0 & ~PSCI_CONVENTION_MASK) {
+	case HF_VM_GET_ID:
+		ret.user_ret = api_vm_get_id(current());
+		break;
+
 	case HF_VM_GET_COUNT:
 		ret.user_ret = api_vm_get_count();
 		break;
@@ -230,6 +234,11 @@
 			api_vcpu_run(arg1, arg2, current(), &ret.new));
 		break;
 
+	case HF_VCPU_YIELD:
+		ret.user_ret = 0;
+		ret.new = api_yield(current());
+		break;
+
 	case HF_VM_CONFIGURE:
 		ret.user_ret = api_vm_configure(ipa_init(arg1), ipa_init(arg2),
 						current());
diff --git a/test/vmapi/primary_only.c b/test/vmapi/primary_only.c
index ba9e6d7..5b3d0f0 100644
--- a/test/vmapi/primary_only.c
+++ b/test/vmapi/primary_only.c
@@ -24,32 +24,62 @@
 
 #include "hftest.h"
 
+/**
+ * Confirms the primary VM has the primary ID.
+ */
+TEST(hf_vm_get_id, primary_has_primary_id)
+{
+	EXPECT_EQ(hf_vm_get_id(), HF_PRIMARY_VM_ID);
+}
+
+/**
+ * Confirm there is only the primary VM.
+ */
 TEST(hf_vm_get_count, no_secondary_vms)
 {
 	EXPECT_EQ(hf_vm_get_count(), 1);
 }
 
+/**
+ * Confirm the primary has at least one vcpu.
+ */
 TEST(hf_vcpu_get_count, primary_has_at_least_one)
 {
 	EXPECT_GE(hf_vcpu_get_count(0), 0);
 }
 
+/**
+ * Confirm an error is returned when getting the vcpu count of a non-existant
+ * VM.
+ */
 TEST(hf_vcpu_get_count, no_secondary_vms)
 {
 	EXPECT_EQ(hf_vcpu_get_count(1), -1);
 }
 
+/**
+ * Confirm an error is returned when getting the vcpu count of a VM with an ID
+ * that is likely to be far outside the resource limit.
+ */
 TEST(hf_vcpu_get_count, large_invalid_vm_index)
 {
 	EXPECT_EQ(hf_vcpu_get_count(0xffffffff), -1);
 }
 
+/**
+ * Confirm it is a no-op with a valid return code when running a vcpu from the
+ * primary VM.
+ */
 TEST(hf_vcpu_run, cannot_run_primary)
 {
 	struct hf_vcpu_run_return res = hf_vcpu_run(HF_PRIMARY_VM_ID, 0);
 	EXPECT_EQ(res.code, HF_VCPU_RUN_WAIT_FOR_INTERRUPT);
 }
 
+/**
+ * Confirm it is a no-op with a valid return code when running a vcpu from a
+ * non-existant secondary VM.
+ */
 TEST(hf_vcpu_run, cannot_run_absent_secondary)
 {
 	struct hf_vcpu_run_return res = hf_vcpu_run(1, 0);
@@ -57,6 +87,14 @@
 }
 
 /**
+ * Confirm that yielding from the primary will still allow the test to complete.
+ */
+TEST(hf_vcpu_yield, yield_is_noop_for_primary)
+{
+	hf_vcpu_yield();
+}
+
+/**
  * Releases the lock passed in.
  */
 static void vm_cpu_entry(uintptr_t arg)
@@ -67,6 +105,9 @@
 	sl_unlock(lock);
 }
 
+/**
+ * Confirm a new cpu can be started to execute in parallel.
+ */
 TEST(cpus, start)
 {
 	struct spinlock lock = SPINLOCK_INIT;
diff --git a/test/vmapi/primary_with_secondaries.c b/test/vmapi/primary_with_secondaries.c
index b13ac6e..10b06d0 100644
--- a/test/vmapi/primary_with_secondaries.c
+++ b/test/vmapi/primary_with_secondaries.c
@@ -82,6 +82,14 @@
 }
 
 /**
+ * Confirms the primary VM has the primary ID.
+ */
+TEST(hf_vm_get_id, primary_has_primary_id)
+{
+	EXPECT_EQ(hf_vm_get_id(), HF_PRIMARY_VM_ID);
+}
+
+/**
  * Confirm there are 4 secondary VMs as well as this primary VM.
  */
 TEST(hf_vm_get_count, four_secondary_vms)