test(VHE): Add tests for FFA_MEM_PERM_GET/SET
This patch adds tests for the aforementioned ABIs. It also updates the
EL0 entry function to account for the fact that hafnium marks all of EL0
partition memory as RX during load/init. The partition must use its own
methods to determine layout and use the ABI's to set permissions for the
image appropriately.
Change-Id: If039224969a0dde3b2af78cde3c13e89ee828f4a
Signed-off-by: Raghu Krishnamurthy <raghu.ncstate@gmail.com>
diff --git a/src/arch/aarch64/hftest/el0_entry.S b/src/arch/aarch64/hftest/el0_entry.S
index 7ac250a..85970bc 100644
--- a/src/arch/aarch64/hftest/el0_entry.S
+++ b/src/arch/aarch64/hftest/el0_entry.S
@@ -6,6 +6,113 @@
* https://opensource.org/licenses/BSD-3-Clause.
*/
+.macro ffa_mem_perm_set start:req end:req perm:req
+ adrp x29, \start
+ add x29, x29, :lo12: \start
+
+ adrp x30, \end
+ add x30, x30, :lo12:\end
+
+ /* x30 = end - begin */
+ sub x30, x30, x29
+ /* x28 = x30 >> 12 (number of pages) */
+ mov x28, #12
+ lsrv x28, x30, x28
+
+ /* 0x84000089 is function identifier for FFA_MEM_PERM_SET_32 */
+ mov w0, #0x89
+ movk w0, #0x8400, lsl #16
+ mov x1, x29
+ mov x2, x28
+ mov w3, #\perm
+
+ svc #0
+
+ /* 0x84000061 is function identifier for FFA_SUCCESS_32 */
+ mov w1, #0x61
+ movk w1, #0x8400, lsl #16
+ cmp w1, w0
+ b.ne .
+.endm
+
+.section .init.entry, "ax"
+.global entry
+entry:
+ /* Linux aarch64 image header. */
+ b 0f
+ .word 0
+ .quad 0x1000 /* text_offset */
+ .quad image_size /* image_size */
+ .quad 0 /* flags */
+ .quad 0 /* res2 */
+ .quad 0 /* res3 */
+ .quad 0 /* res4 */
+ .word 0x644d5241 /* magic */
+ .word 0
+
+0:
+ /* Save data (fdt pointer or mem size) passed by hypervisor. */
+ mov x10, x0
+
+ /* Set everything other than text as RW, so that relocations can succeed. */
+ ffa_mem_perm_set rodata_begin image_end 5
+
+ /*
+ * Calculate the difference between the actual load address and the
+ * preferred one. We'll use this to relocate.
+ */
+ adrp x25, entry
+ add x25, x25, :lo12:entry
+
+ ldr w29, =ORIGIN_ADDRESS
+
+ sub x25, x25, x29
+
+ /* Find where the relocations begin and end. */
+ adrp x29, rela_begin
+ add x29, x29, :lo12:rela_begin
+
+ adrp x30, rela_end
+ add x30, x30, :lo12:rela_end
+
+ /* Iterate over all relocations. */
+1: cmp x29, x30
+ b.eq 2f
+
+ ldp x26, x27, [x29], #16
+ ldr x28, [x29], #8
+
+ cmp w27, #1027 /* R_AARCH64_RELATIVE */
+ b.ne .
+
+ add x28, x28, x25
+ str x28, [x26, x25]
+ b 1b
+
+ /* Set everything between ro data and data begin as RO */
+2: ffa_mem_perm_set rodata_begin data_begin 7
+
+ /* set everthing else as RW */
+ ffa_mem_perm_set data_begin image_end 5
+
+ /* Zero out the bss section. */
+ adrp x29, bss_begin
+ add x29, x29, :lo12:bss_begin
+
+ adrp x30, bss_end
+ add x30, x30, :lo12:bss_end
+
+3: cmp x29, x30
+ b.hs 4f
+
+ stp xzr, xzr, [x29], #16
+ b 3b
+
+4: mov x0, x10
+
+ /* Branch to the entry point for the specific image. */
+ b image_entry
+
.section .init.image_entry, "ax"
.global image_entry
image_entry:
diff --git a/test/vmapi/el0_partitions/boot.c b/test/vmapi/el0_partitions/boot.c
index 1720703..21c562c 100644
--- a/test/vmapi/el0_partitions/boot.c
+++ b/test/vmapi/el0_partitions/boot.c
@@ -61,3 +61,48 @@
run_res = ffa_run(SERVICE_VM1, 0);
EXPECT_FFA_ERROR(run_res, FFA_ABORTED);
}
+
+TEST(mem_permission, ffa_mem_get_test)
+{
+ struct ffa_value res;
+ struct mailbox_buffers mb = set_up_mailbox();
+ SERVICE_SELECT(SERVICE_VM1, "ffa_mem_perm_get", mb.send);
+
+ /* Let the secondary get started and wait for a message. */
+ res = ffa_run(SERVICE_VM1, 0);
+ EXPECT_EQ(res.func, FFA_MSG_WAIT_32);
+ EXPECT_EQ(res.arg2, FFA_SLEEP_INDEFINITE);
+
+ /*
+ * Send direct message to tell service VM to do FFA_MEM_PERM_GET tests.
+ */
+ res = ffa_msg_send_direct_req(HF_PRIMARY_VM_ID, SERVICE_VM1, 1, 0, 0, 0,
+ 0);
+ EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
+
+ /* Check that VM's cannot use this ABI */
+ res = ffa_mem_perm_get(0xDEADBEEF);
+ EXPECT_EQ(res.func, FFA_ERROR_32);
+ EXPECT_EQ(ffa_error_code(res), FFA_DENIED);
+}
+
+TEST(mem_permission, ffa_mem_set_test)
+{
+ struct ffa_value res;
+ struct mailbox_buffers mb = set_up_mailbox();
+ SERVICE_SELECT(SERVICE_VM1, "ffa_mem_perm_set", mb.send);
+
+ /* Let the secondary get started and wait for a message. */
+ res = ffa_run(SERVICE_VM1, 0);
+ EXPECT_EQ(res.func, FFA_MSG_WAIT_32);
+ EXPECT_EQ(res.arg2, FFA_SLEEP_INDEFINITE);
+
+ res = ffa_msg_send_direct_req(HF_PRIMARY_VM_ID, SERVICE_VM1, 1, 0, 0, 0,
+ 0);
+ EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
+
+ /* Check that VM's cannot use this ABI */
+ res = ffa_mem_perm_set(0xDEADBEEF, 0x1000, 0xf);
+ EXPECT_EQ(res.func, FFA_ERROR_32);
+ EXPECT_EQ(ffa_error_code(res), FFA_DENIED);
+}
diff --git a/test/vmapi/el0_partitions/secure_partitions/BUILD.gn b/test/vmapi/el0_partitions/secure_partitions/BUILD.gn
index 60efd74..9475993 100644
--- a/test/vmapi/el0_partitions/secure_partitions/BUILD.gn
+++ b/test/vmapi/el0_partitions/secure_partitions/BUILD.gn
@@ -49,7 +49,6 @@
"../services:el0_dlog",
"../services:el0_fdt_handler",
"//src:panic",
- "//src/arch/${plat_arch}:entry",
"//src/arch/${plat_arch}/hftest:el0_entry",
]
}
diff --git a/test/vmapi/el0_partitions/services/BUILD.gn b/test/vmapi/el0_partitions/services/BUILD.gn
index e84dcdb..c3ad2ff 100644
--- a/test/vmapi/el0_partitions/services/BUILD.gn
+++ b/test/vmapi/el0_partitions/services/BUILD.gn
@@ -50,7 +50,6 @@
":el0_dlog",
":el0_fdt_handler",
"//src:panic",
- "//src/arch/${plat_arch}:entry",
"//src/arch/${plat_arch}/hftest:el0_entry",
]
}
diff --git a/test/vmapi/el0_partitions/services/boot.c b/test/vmapi/el0_partitions/services/boot.c
index 8fc0070..6fcd9d8 100644
--- a/test/vmapi/el0_partitions/services/boot.c
+++ b/test/vmapi/el0_partitions/services/boot.c
@@ -14,7 +14,7 @@
#include "vmapi/hf/call.h"
#include "test/hftest.h"
-
+#include "test/vmapi/ffa.h"
/*
* This must match the size specified for services1 in
* //test/vmapi/el0_partitions/manifest.dts.
@@ -22,6 +22,12 @@
#define SECONDARY_MEMORY_SIZE 1048576
extern uint8_t volatile text_begin[];
+extern uint8_t volatile text_end[];
+extern uint8_t volatile rodata_begin[];
+extern uint8_t volatile rodata_end[];
+extern uint8_t volatile data_begin[];
+extern uint8_t volatile data_end[];
+extern uint8_t volatile image_end[];
TEST_SERVICE(boot_memory)
{
@@ -66,3 +72,143 @@
dlog("Managed to read memory above limit");
ffa_yield();
}
+
+TEST_SERVICE(ffa_mem_perm_get)
+{
+ struct ffa_value ret = ffa_msg_wait();
+ uint64_t num_pages = 0;
+ uint64_t base_va = 0;
+ /* Hafnium may use rx/tx buffers so one page may be marked as RO */
+ uint32_t num_ro_pages_in_data = 0;
+
+ EXPECT_EQ(ret.func, FFA_MSG_SEND_DIRECT_REQ_32);
+
+ num_pages = align_up((text_end - text_begin), PAGE_SIZE) / PAGE_SIZE;
+ base_va = (uint64_t)align_down(text_begin, PAGE_SIZE);
+
+ while (num_pages) {
+ struct ffa_value res = ffa_mem_perm_get(base_va);
+ EXPECT_EQ(res.func, FFA_SUCCESS_32);
+ EXPECT_EQ(res.arg2, FFA_MEM_PERM_RX);
+ base_va += PAGE_SIZE;
+ num_pages--;
+ }
+
+ num_pages =
+ align_up((rodata_end - rodata_begin), PAGE_SIZE) / PAGE_SIZE;
+ base_va = (uint64_t)align_down(rodata_begin, PAGE_SIZE);
+
+ while (num_pages) {
+ struct ffa_value res = ffa_mem_perm_get(base_va);
+ EXPECT_EQ(res.func, FFA_SUCCESS_32);
+ EXPECT_EQ(res.arg2, FFA_MEM_PERM_RO);
+ base_va += PAGE_SIZE;
+ num_pages--;
+ }
+
+ num_pages = align_up((data_end - data_begin), PAGE_SIZE) / PAGE_SIZE;
+ base_va = (uint64_t)align_down(data_begin, PAGE_SIZE);
+
+ while (num_pages) {
+ struct ffa_value res = ffa_mem_perm_get(base_va);
+ EXPECT_EQ(res.func, FFA_SUCCESS_32);
+ if (res.arg2 == FFA_MEM_PERM_RO) {
+ /*
+ * Hafnium may use rx/tx buffers so one page may be
+ * marked as RO
+ */
+ num_ro_pages_in_data++;
+ } else {
+ EXPECT_EQ(res.arg2, FFA_MEM_PERM_RW);
+ }
+ base_va += PAGE_SIZE;
+ num_pages--;
+ }
+ EXPECT_EQ(num_ro_pages_in_data, 1);
+
+ num_pages = align_up((image_end - data_end), PAGE_SIZE) / PAGE_SIZE;
+ base_va = (uint64_t)align_down(data_end, PAGE_SIZE);
+
+ while (num_pages) {
+ struct ffa_value res = ffa_mem_perm_get(base_va);
+ EXPECT_EQ(res.func, FFA_SUCCESS_32);
+ EXPECT_EQ(res.arg2, FFA_MEM_PERM_RW);
+ base_va += PAGE_SIZE;
+ num_pages--;
+ }
+
+ /* Check that permissions on an invalid address returns error */
+ struct ffa_value res = ffa_mem_perm_get(0xDEADBEEF);
+ EXPECT_EQ(res.func, FFA_ERROR_32);
+ EXPECT_EQ(ffa_error_code(res), FFA_INVALID_PARAMETERS);
+
+ res = ffa_mem_perm_get(0x0);
+ EXPECT_EQ(res.func, FFA_ERROR_32);
+ EXPECT_EQ(ffa_error_code(res), FFA_INVALID_PARAMETERS);
+
+ ffa_msg_send_direct_resp(ffa_receiver(ret), ffa_sender(ret), 0, 0, 0, 0,
+ 0);
+}
+
+TEST_SERVICE(ffa_mem_perm_set)
+{
+ struct ffa_value ret = ffa_msg_wait();
+
+ uint64_t num_pages = 0;
+ uint64_t base_va = 0;
+
+ EXPECT_EQ(ret.func, FFA_MSG_SEND_DIRECT_REQ_32);
+
+ num_pages = 1;
+ base_va = (uint64_t)align_down(data_begin, PAGE_SIZE);
+ struct ffa_value res = ffa_mem_perm_get(base_va);
+ EXPECT_EQ(res.func, FFA_SUCCESS_32);
+ EXPECT_EQ(res.arg2, FFA_MEM_PERM_RW);
+
+ res = ffa_mem_perm_set(base_va, num_pages, FFA_MEM_PERM_RO);
+ EXPECT_EQ(res.func, FFA_SUCCESS_32);
+
+ res = ffa_mem_perm_get(base_va);
+ EXPECT_EQ(res.func, FFA_SUCCESS_32);
+ EXPECT_EQ(res.arg2, FFA_MEM_PERM_RO);
+
+ res = ffa_mem_perm_set(base_va, num_pages, FFA_MEM_PERM_RW);
+ EXPECT_EQ(res.func, FFA_SUCCESS_32);
+
+ res = ffa_mem_perm_get(base_va);
+ EXPECT_EQ(res.func, FFA_SUCCESS_32);
+ EXPECT_EQ(res.arg2, FFA_MEM_PERM_RW);
+
+ /* ensure permission for invalid pages cannot be changed */
+ res = ffa_mem_perm_set(0xDEADBEEF, 0x1000, FFA_MEM_PERM_RX);
+ EXPECT_EQ(res.func, FFA_ERROR_32);
+ EXPECT_EQ(ffa_error_code(res), FFA_INVALID_PARAMETERS);
+
+ res = ffa_mem_perm_set(0x0, 0x1000, FFA_MEM_PERM_RX);
+ EXPECT_EQ(res.func, FFA_ERROR_32);
+ EXPECT_EQ(ffa_error_code(res), FFA_INVALID_PARAMETERS);
+
+ /* Ensure permissions cannot be changed for an unaligned, but valid
+ * address */
+ base_va = (uint64_t)align_down(data_begin, PAGE_SIZE);
+ res = ffa_mem_perm_set((base_va + 1), 1, FFA_MEM_PERM_RX);
+ EXPECT_EQ(res.func, FFA_ERROR_32);
+ EXPECT_EQ(ffa_error_code(res), FFA_INVALID_PARAMETERS);
+
+ /*
+ * Ensure permissions cannot be changed for valid address that crosses
+ * boundary into invalid address */
+ base_va = (uint64_t)align_down(data_begin, PAGE_SIZE);
+ res = ffa_mem_perm_set(base_va, 256, FFA_MEM_PERM_RX);
+ EXPECT_EQ(res.func, FFA_ERROR_32);
+ EXPECT_EQ(ffa_error_code(res), FFA_INVALID_PARAMETERS);
+
+ /* Esnure permissions cannot be changed using invalid attributes */
+ base_va = (uint64_t)align_down(data_begin, PAGE_SIZE);
+ res = ffa_mem_perm_set(base_va, 1, 0x1);
+ EXPECT_EQ(res.func, FFA_ERROR_32);
+ EXPECT_EQ(ffa_error_code(res), FFA_INVALID_PARAMETERS);
+
+ ffa_msg_send_direct_resp(ffa_receiver(ret), ffa_sender(ret), 0, 0, 0, 0,
+ 0);
+}