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