tests/tcg/aarch64/system/gpc-test.c: Basic test for granule protection check
* Sets up granule protection tables * Enables GPC and bypass windows * Performs memory accesses in the protected region to check for allowed and disallowed reads. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org> Message-id: 20260618-jmac-gpc3b-v3-6-353e546067e7@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Jim MacArthur committed
Jun 18, 2026 at 17:33 UTC
60581a7743ebcd8ccbcaa0948b454cf9025e16b5
2 files changed
+175
-1
tests/tcg/aarch64/Makefile.softmmu-target
+10
-1
@@ -53,7 +53,10 @@ memory-sve: memory.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS)
53
54
memory-sve: CFLAGS+=-DCHECK_UNALIGNED=1 -march=armv8.1-a+sve -O3
55
56
-TESTS+=memory-sve
56
+gpc-test: gpc-test.c $(LINK_SCRIPT) vector_log_boot.o $(MINILIB_OBJS)
57
+ $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) vector_log_boot.o
58
+
59
+TESTS+=memory-sve gpc-test
60
61
# Running
62
QEMU_BASE_MACHINE=-M virt -cpu max -display none
@@ -74,6 +77,12 @@ QEMU_EL2_MACHINE=-machine virt,virtualization=on,gic-version=2 -cpu cortex-a57 -
77
QEMU_EL2_BASE_ARGS=-semihosting-config enable=on,target=native,chardev=output,arg="2"
78
run-vtimer: QEMU_OPTS=$(QEMU_EL2_MACHINE) $(QEMU_EL2_BASE_ARGS) -kernel
79
80
+# gpc tests need EL3 and RME
81
+QEMU_EL3_MACHINE=-machine virt,virtualization=on,secure=on,gic-version=3 -cpu max,x-rme=on
82
+QEMU_EL3_BASE_ARGS=-semihosting-config enable=on,target=native,chardev=output,arg="3"
83
+run-gpc-test: QEMU_OPTS=$(QEMU_EL3_MACHINE) $(QEMU_EL3_BASE_ARGS) -kernel
84
+run-gpc3-test: QEMU_OPTS=$(QEMU_EL3_MACHINE) $(QEMU_EL3_BASE_ARGS) -kernel
85
+
86
# Simple Record/Replay Test
87
.PHONY: memory-record
88
run-memory-record: memory-record memory
tests/tcg/aarch64/system/gpc-test.c
new
+165
@@ -0,0 +1,165 @@
1
+/* SPDX-License-Identifier: GPL-2.0-or-later */
2
+/*
3
+ *
4
+ *
5
+ * Copyright (c) 2026 Linaro Ltd
6
+ *
7
+ */
8
+
9
+#include <stdbool.h>
10
+#include <stdint.h>
11
+#include <minilib.h>
12
+#include "boot.h"
13
+
14
+#define ID_AA64PFR0_EL1 "S3_0_C0_C4_0"
15
+
16
+#define GPTBR_EL3 "S3_6_C2_C1_4"
17
+#define GPCBW_EL3 "S3_6_C2_C1_5"
18
+#define GPCCR_EL3 "S3_6_C2_C1_6"
19
+#define VBAR_EL3 "S3_6_C12_C0_0"
20
+
21
+#define get_sys_reg(register_name, dest) \
22
+ asm("mrs %[reg], " register_name "\n\t" : [reg] "=r" (dest))
23
+#define set_sys_reg(register_name, value) \
24
+ asm("msr " register_name ", %[reg]\n\r" : : [reg] "r" (value))
25
+
26
+const uint32_t gpc_granule_size = 4096;
27
+const uint32_t gpis_per_64_bits = 16;
28
+
29
+int main(uint64_t sp)
30
+{
31
+ uint64_t out;
32
+ uint64_t pfr0;
33
+ uint64_t gpt_base;
34
+ uint64_t rme_status;
35
+ uint64_t currentel_raw;
36
+ uint64_t currentel;
37
+ uint64_t gpcbw;
38
+ uint64_t gpt_table0_addr = (uint64_t) realms_gpt0;
39
+ uint64_t gpt_table1_addr = (uint64_t) realms_gpt1;
40
+
41
+ /* Mask is FNG1, FNG0, and A2 */
42
+ const uint64_t feature_mask = (1ULL << 18 | 1ULL << 17 | 1ULL << 16);
43
+ const uint64_t in = feature_mask;
44
+
45
+ get_sys_reg("CurrentEL", currentel_raw);
46
+ currentel = (currentel_raw >> 2) & 0x3;
47
+
48
+ if (currentel < 3) {
49
+ ml_printf("FAIL: Test must be run at EL3 (it is %d)\n", currentel);
50
+ return 1;
51
+ }
52
+
53
+ get_sys_reg(ID_AA64PFR0_EL1, pfr0);
54
+
55
+ /* rme_status is 1 for RME, 2 for RME + GPC2, 3 for RME+GPC3 */
56
+ rme_status = (pfr0 >> 52) & 0xF;
57
+ if (rme_status < 2) {
58
+ ml_printf("SKIP: System does not support RME (RME=%ld)\n", rme_status);
59
+ return 0;
60
+ }
61
+
62
+ /* Configure the level 0 table for the first 4GB of memory */
63
+ realms_gpt0[0] = gpt_table1_addr | 0x3; /* Covers GB 0; table descriptor */
64
+ realms_gpt0[1] = 0xf1; /* Covers GB 1; full access */
65
+ realms_gpt0[2] = 0xf1; /* Covers GB 2; full access */
66
+ realms_gpt0[3] = 0xf1; /* Covers GB 3; full access */
67
+
68
+ /* Pick an artibtrary location to read inside the first 1GB. */
69
+ uint64_t fault_location = 0x10202008;
70
+ uint32_t gpi_index = fault_location / gpc_granule_size;
71
+ realms_gpt1[gpi_index / gpis_per_64_bits] = 0;
72
+
73
+ gpt_base = gpt_table0_addr >> 12;
74
+ set_sys_reg(GPTBR_EL3, gpt_base);
75
+
76
+ /*
77
+ * Default values:
78
+ * PPS=0: GPC table 0 protects 4GB.
79
+ * RLPAD=0: Realm physical address spaces are normal
80
+ * NSPAD=0: Non-secure physical address spaces are normal
81
+ * SPAD=0: Secure physical address spaces are normal
82
+ * IRGN=0: Inner non-cacheable
83
+ * ORGN=0: Outer non-cacheable
84
+ * PGS=0: Physical granule size is 4KB.
85
+ * GPCP=0: All GPC faults reported
86
+ * TBGPCP=0: Trace buffer rejects trace
87
+ * L0GPTSZ=0: Each entry in table 0 protects 1GB.
88
+ * APPSAA=0: Accesses above 4GB must be to Non-secure PAs
89
+ * GPCBW=0: Bypass windows disabled.
90
+ * NA6, NA7, NSP, SA, NSO are all reserved values for GPI.
91
+ */
92
+ uint64_t gpccr = 0;
93
+
94
+ /* Switch on granule protection check */
95
+ gpccr |= 1 << 16; /* GPC enabled. */
96
+ gpccr |= 0b10 << 12; /* SH = Outer shareable */
97
+ set_sys_reg(GPCCR_EL3, gpccr);
98
+
99
+ /* Access some memory outside the GPC forbidden region */
100
+ uint64_t x = *(unsigned int *) (fault_location + 4096 * 16);
101
+ ml_printf("Fault address: %lx\n", exception_fault_address);
102
+ if (exception_fault_address != 0) {
103
+ ml_printf("FAIL: Memory access was blocked by GPC, "
104
+ "and should not have been\n");
105
+ return 1;
106
+ }
107
+
108
+ /* Access the GPC forbidden region */
109
+ x = *(unsigned int *) fault_location;
110
+
111
+ ml_printf("Fault address: %lx\n", exception_fault_address);
112
+ if (exception_fault_address != fault_location) {
113
+ ml_printf("FAIL: Memory access was not blocked by GPC, "
114
+ "and should have been\n");
115
+ return 1;
116
+ }
117
+
118
+ rme_status = (pfr0 >> 52) & 0xF;
119
+ if (rme_status < 3) {
120
+ ml_printf("SKIP: System does not support GPC3 (RME=%ld)\n", rme_status);
121
+ return 0;
122
+ }
123
+
124
+ /* Clear the exception record */
125
+ exception_fault_address = 0;
126
+
127
+ /* Enable bypass windows */
128
+ gpccr |= 1 << 29; /* GPC Bypass windows enabled */
129
+ set_sys_reg(GPCCR_EL3, gpccr);
130
+
131
+ gpcbw = 0; /* Base 0GB, Size 1GB, Stride 1TB */
132
+ set_sys_reg(GPCBW_EL3, gpcbw);
133
+ ml_printf("GPCBW configured\n");
134
+
135
+ /* Access the GPC forbidden region again */
136
+ x = *(unsigned int *) fault_location;
137
+
138
+ ml_printf("Fault address: %lx\n", exception_fault_address);
139
+ if (exception_fault_address != 0) {
140
+ ml_printf("FAIL: Memory access was blocked by GPC, "
141
+ "and should have been allowed by bypass window. code=%lx\n",
142
+ exception_type_code);
143
+ return 1;
144
+ }
145
+
146
+ /* Clear the exception record */
147
+ exception_fault_address = 0;
148
+ /* Reconfigure GPCBW to 1GB start */
149
+ gpcbw = 1; /* Base 1GB, Size 1GB, Stride 1TB */
150
+ set_sys_reg(GPCBW_EL3, gpcbw);
151
+ ml_printf("GPCBW reconfigured for 1GB start\n");
152
+
153
+ /* Access the GPC forbidden region again */
154
+ x = *(unsigned int *) fault_location;
155
+
156
+ ml_printf("Fault address: %lx\n", exception_fault_address);
157
+ if (exception_fault_address != fault_location) {
158
+ ml_printf("FAIL: Memory access was allowed by GPC, "
159
+ "and should not have been allowed by bypass window. code=%lx\n",
160
+ exception_type_code);
161
+ return 1;
162
+ }
163
+
164
+ return 0;
165
+}