Tell TF-A in EL3 about TEE message buffers.

This must be enabled by a flag in the manifest, as it won't work without
the matching support in TF-A, e.g. in the case of running tests on QEMU
without TF-A.

Bug: 132429380
Change-Id: I0a0848c7b1afb416d98c0afeabcc4601abf9d193
diff --git a/docs/Manifest.md b/docs/Manifest.md
index 0d9d16e..458b130 100644
--- a/docs/Manifest.md
+++ b/docs/Manifest.md
@@ -13,6 +13,8 @@
 	hypervisor {
 		compatible = "hafnium,hafnium";
 
+		spci_tee;
+
 		vm1 {
 			debug_name = "name";
 			kernel_filename = "vmlinuz";
@@ -38,7 +40,8 @@
 of memory, 4 CPUs and, by omitting the `kernel_filename` property, a kernel
 preloaded into memory. The primary VM is given all remaining memory, the same
 number of CPUs as the hardware, a kernel image called `vmlinuz` and a ramdisk
-`initrd.img`. Secondaries cannot have a ramdisk.
+`initrd.img`. Secondaries cannot have a ramdisk. SPCI memory sharing with the
+TEE is enabled.
 
 ```
 /dts-v1/;
@@ -47,6 +50,8 @@
 	hypervisor {
 		compatible = "hafnium,hafnium";
 
+		spci_tee;
+
 		vm1 {
 			debug_name = "primary VM";
 			kernel_filename = "vmlinuz";
diff --git a/inc/hf/arch/tee.h b/inc/hf/arch/tee.h
index 709ad37..f37fe0b 100644
--- a/inc/hf/arch/tee.h
+++ b/inc/hf/arch/tee.h
@@ -18,4 +18,5 @@
 
 #include "hf/spci.h"
 
+void arch_tee_init(void);
 struct spci_value arch_tee_call(struct spci_value args);
diff --git a/inc/hf/manifest.h b/inc/hf/manifest.h
index f1aebaf..4c3c5dc 100644
--- a/inc/hf/manifest.h
+++ b/inc/hf/manifest.h
@@ -50,6 +50,7 @@
  * Hafnium manifest parsed from FDT.
  */
 struct manifest {
+	bool spci_tee_enabled;
 	spci_vm_count_t vm_count;
 	struct manifest_vm vm[MAX_VMS];
 };
diff --git a/kokoro/test.sh b/kokoro/test.sh
index 300a2b1..803add7 100755
--- a/kokoro/test.sh
+++ b/kokoro/test.sh
@@ -101,6 +101,10 @@
     HFTEST_CPU+=(--log "$LOG_DIR_BASE")
   fi
   "${HFTEST_CPU[@]}" arch_test
+  if [ $USE_TFA == true || $USE_FVP == true ]
+  then
+    "${HFTEST_CPU[@]}" aarch64_test
+  fi
   "${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/arch/aarch64/aarch64_test
   "${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/arch/aarch64/gicv3/gicv3_test
   "${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/primary_only/primary_only_test
diff --git a/src/BUILD.gn b/src/BUILD.gn
index 3220b4e..c9ecd1a 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -40,6 +40,7 @@
   deps = [
     ":src_testable",
     "//project/${project}/${plat_name}",
+    "//src/arch/${plat_arch}/hypervisor:tee",
     plat_boot_flow,
     plat_console,
     plat_iommu,
@@ -58,7 +59,6 @@
     "panic.c",
     "spci_memory.c",
     "vcpu.c",
-    "vm.c",
   ]
 
   deps = [
@@ -69,8 +69,9 @@
     ":memiter",
     ":mm",
     ":std",
-    ":string",
+    ":vm",
     "//src/arch/${plat_arch}/hypervisor",
+    "//src/arch/${plat_arch}/hypervisor:tee",
     "//vmlib",
     plat_boot_flow,
     plat_console,
@@ -91,6 +92,12 @@
   ]
 }
 
+source_set("vm") {
+  sources = [
+    "vm.c",
+  ]
+}
+
 # Standard library functions.
 source_set("std") {
   sources = [
diff --git a/src/arch/aarch64/hypervisor/BUILD.gn b/src/arch/aarch64/hypervisor/BUILD.gn
index d5db714..67e68bd 100644
--- a/src/arch/aarch64/hypervisor/BUILD.gn
+++ b/src/arch/aarch64/hypervisor/BUILD.gn
@@ -22,6 +22,16 @@
   path = "hf/arch/offsets.h"
 }
 
+source_set("tee") {
+  public_configs = [ "//src/arch/aarch64:config" ]
+  sources = [
+    "tee.c",
+  ]
+  deps = [
+    "//src:vm",
+  ]
+}
+
 # Hypervisor specific code.
 source_set("hypervisor") {
   public_configs = [ "//src/arch/aarch64:config" ]
@@ -38,7 +48,6 @@
     "handler.c",
     "perfmon.c",
     "psci_handler.c",
-    "tee.c",
     "vm.c",
   ]
 
diff --git a/src/arch/aarch64/hypervisor/tee.c b/src/arch/aarch64/hypervisor/tee.c
index de6b094..c3885ac 100644
--- a/src/arch/aarch64/hypervisor/tee.c
+++ b/src/arch/aarch64/hypervisor/tee.c
@@ -16,10 +16,49 @@
 
 #include "hf/arch/tee.h"
 
+#include "hf/check.h"
+#include "hf/dlog.h"
+#include "hf/panic.h"
 #include "hf/spci.h"
+#include "hf/vm.h"
 
 #include "smc.h"
 
+void arch_tee_init(void)
+{
+	struct vm *tee_vm = vm_find(HF_TEE_VM_ID);
+	struct spci_value ret;
+	uint32_t func;
+
+	CHECK(tee_vm != NULL);
+	/*
+	 * Note that send and recv are swapped around, as the send buffer from
+	 * Hafnium's perspective is the recv buffer from the EL3 dispatcher's
+	 * perspective and vice-versa.
+	 */
+	dlog_verbose("Setting up buffers for TEE.\n");
+	ret = arch_tee_call((struct spci_value){
+		.func = SPCI_RXTX_MAP_64,
+		.arg1 = pa_addr(pa_from_va(va_from_ptr(tee_vm->mailbox.recv))),
+		.arg2 = pa_addr(pa_from_va(va_from_ptr(tee_vm->mailbox.send))),
+		.arg3 = HF_MAILBOX_SIZE / SPCI_PAGE_SIZE});
+	func = ret.func & ~SMCCC_CONVENTION_MASK;
+	if (ret.func == SMCCC_ERROR_UNKNOWN) {
+		dlog_error(
+			"Unknown function setting up TEE message buffers. "
+			"Memory sharing with TEE will not work.\n");
+		return;
+	}
+	if (func == SPCI_ERROR_32) {
+		panic("Error %d setting up TEE message buffers.", ret.arg2);
+	} else if (func != SPCI_SUCCESS_32) {
+		panic("Unexpected function %#x returned setting up TEE message "
+		      "buffers.",
+		      ret.func);
+	}
+	dlog_verbose("TEE finished setting up buffers.\n");
+}
+
 struct spci_value arch_tee_call(struct spci_value args)
 {
 	return smc_forward(args.func, args.arg1, args.arg2, args.arg3,
diff --git a/src/arch/fake/hypervisor/BUILD.gn b/src/arch/fake/hypervisor/BUILD.gn
index e7e970f..1afcca6 100644
--- a/src/arch/fake/hypervisor/BUILD.gn
+++ b/src/arch/fake/hypervisor/BUILD.gn
@@ -15,9 +15,14 @@
 source_set("hypervisor") {
   sources = [
     "cpu.c",
-    "tee.c",
   ]
   deps = [
     "//src/arch/fake",
   ]
 }
+
+source_set("tee") {
+  sources = [
+    "tee.c",
+  ]
+}
diff --git a/src/init.c b/src/init.c
index 2f241b8..d224d49 100644
--- a/src/init.c
+++ b/src/init.c
@@ -19,6 +19,8 @@
 #include <stdalign.h>
 #include <stddef.h>
 
+#include "hf/arch/tee.h"
+
 #include "hf/api.h"
 #include "hf/boot_flow.h"
 #include "hf/boot_params.h"
@@ -158,5 +160,10 @@
 	/* Enable TLB invalidation for VM page table updates. */
 	mm_vm_enable_invalidation();
 
+	if (manifest.spci_tee_enabled) {
+		/* Set up message buffers for TEE dispatcher. */
+		arch_tee_init();
+	}
+
 	dlog_info("Hafnium initialisation completed\n");
 }
diff --git a/src/manifest.c b/src/manifest.c
index 438b5d0..9b80106 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -280,6 +280,8 @@
 		return MANIFEST_ERROR_NOT_COMPATIBLE;
 	}
 
+	TRY(read_bool(&hyp_node, "spci_tee", &manifest->spci_tee_enabled));
+
 	/* Iterate over reserved VM IDs and check no such nodes exist. */
 	for (i = 0; i < HF_VM_ID_OFFSET; i++) {
 		spci_vm_id_t vm_id = (spci_vm_id_t)i;
diff --git a/test/arch/BUILD.gn b/test/arch/BUILD.gn
index c770dee..a2534c5 100644
--- a/test/arch/BUILD.gn
+++ b/test/arch/BUILD.gn
@@ -20,6 +20,7 @@
 
   deps = [
     ":arch_test",
+    "${plat_arch}",
   ]
 }
 
diff --git a/test/arch/aarch64/BUILD.gn b/test/arch/aarch64/BUILD.gn
new file mode 100644
index 0000000..c030308
--- /dev/null
+++ b/test/arch/aarch64/BUILD.gn
@@ -0,0 +1,38 @@
+# Copyright 2020 The Hafnium Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import("//build/image/image.gni")
+import("//build/toolchain/platform.gni")
+
+group("aarch64") {
+  testonly = true
+
+  deps = [
+    ":aarch64_test",
+  ]
+}
+
+hypervisor("aarch64_test") {
+  testonly = true
+
+  sources = [
+    "tee_test.c",
+  ]
+
+  deps = [
+    "//src:vm",
+    "//src/arch/${plat_arch}/hypervisor:tee",
+    "//test/hftest:hftest_hypervisor",
+  ]
+}
diff --git a/test/arch/aarch64/tee_test.c b/test/arch/aarch64/tee_test.c
new file mode 100644
index 0000000..1b07a3c
--- /dev/null
+++ b/test/arch/aarch64/tee_test.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hf/arch/tee.h"
+
+#include <stdint.h>
+
+#include "hf/addr.h"
+#include "hf/spci.h"
+#include "hf/types.h"
+
+#include "smc.h"
+#include "test/hftest.h"
+
+alignas(SPCI_PAGE_SIZE) static uint8_t tee_send_buffer[HF_MAILBOX_SIZE];
+alignas(SPCI_PAGE_SIZE) static uint8_t tee_recv_buffer[HF_MAILBOX_SIZE];
+
+/**
+ * Make sure SPCI_RXTX_MAP to EL3 works.
+ */
+TEST(arch_tee, init)
+{
+	struct spci_value ret = arch_tee_call((struct spci_value){
+		.func = SPCI_RXTX_MAP_64,
+		.arg1 = pa_addr(pa_from_va(va_from_ptr(tee_recv_buffer))),
+		.arg2 = pa_addr(pa_from_va(va_from_ptr(tee_send_buffer))),
+		.arg3 = HF_MAILBOX_SIZE / SPCI_PAGE_SIZE});
+	uint32_t func = ret.func & ~SMCCC_CONVENTION_MASK;
+
+	/*
+	 * TODO(qwandor): Remove this UNKNOWN check once we have a build of TF-A
+	 * which supports SPCI memory sharing.
+	 */
+	if (ret.func != SMCCC_ERROR_UNKNOWN) {
+		ASSERT_EQ(func, SPCI_SUCCESS_32);
+	}
+}