@samitouri / QOSamiQemu / commits / 60581a7743

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 +}