target/arm/ptw.c: Add Granule Bypass Windows
Signed-off-by: Jim MacArthur <jim.macarthur@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20260618-jmac-gpc3b-v3-3-353e546067e7@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Jim MacArthur committed
Jun 18, 2026 at 17:33 UTC
f6bfcaaa506d6ac8ed4ab4a36539899ddbbc3d46
1 file changed
+77
target/arm/ptw.c
+77
@@ -343,11 +343,21 @@ bool arm_granule_protection_check(ARMGranuleProtectionConfig config,
343
.space = ARMSS_Root,
344
};
345
const uint64_t gpccr = config.gpccr;
346
+ const uint64_t gpcbw = config.gpcbw;
347
unsigned pps, pgs, l0gptsz, level = 0;
348
uint64_t tableaddr, pps_mask, align, entry, index;
349
MemTxResult result;
350
int gpi;
351
352
+ const uint64_t BW_ADDR_SHIFT = 30;
353
+ const uint64_t BW_SIZE_SHIFT = 30;
354
+ const uint64_t BW_STRIDE_SHIFT = 40;
355
+
356
+ uint64_t bw_size_field = FIELD_EX64(gpcbw, GPCBW, BWSIZE);
357
+ uint64_t bw_stride_field = FIELD_EX64(gpcbw, GPCBW, BWSTRIDE);
358
+ uint64_t bw_addr = FIELD_EX64(gpcbw, GPCBW, BWADDR) << BW_ADDR_SHIFT;
359
+ uint64_t bw_mask = 0;
360
+
361
/*
362
* We assume Granule Protection Check is enabled when
363
* calling this function (GPCCR.GPC == 1).
@@ -399,6 +409,58 @@ bool arm_granule_protection_check(ARMGranuleProtectionConfig config,
409
goto fault_walk;
410
}
411
412
+ /* At this point, GPCCR_EL3 is valid */
413
+
414
+ /*
415
+ * GPC Priority 1 (R_GMGRR):
416
+ * If GPCCR_EL3.GPCBW is 1 and the configuration GPCBW
417
+ * is invalid, the access fails as GPT walk fault at level 0.
418
+ */
419
+ if (FIELD_EX64(gpccr, GPCCR, GPCBW)) {
420
+ uint64_t bw_size = 0;
421
+ uint64_t bw_stride = 0;
422
+
423
+ /* BWSIZE, BWSTRIDE have a limited number of acceptable values. */
424
+ switch (bw_size_field) {
425
+ case 0b000:
426
+ case 0b001:
427
+ case 0b010:
428
+ case 0b100:
429
+ case 0b110:
430
+ bw_size = 1ULL << (bw_size_field + BW_SIZE_SHIFT);
431
+ break;
432
+ default: /* Reserved value */
433
+ goto fault_walk;
434
+ }
435
+ switch (bw_stride_field) {
436
+ case 0b00000:
437
+ case 0b00010:
438
+ case 0b00100:
439
+ case 0b00110:
440
+ case 0b00111:
441
+ case 0b01000:
442
+ case 0b01001:
443
+ case 0b01010:
444
+ case 0b10000:
445
+ bw_stride = 1ULL << (bw_stride_field + BW_STRIDE_SHIFT);
446
+ break;
447
+ default: /* Reserved value */
448
+ goto fault_walk;
449
+ }
450
+ /*
451
+ * GPCBW is invalid if the base address is:
452
+ * not aligned to the size programmed in BWSIZE, or
453
+ * greater than or equal to the stride value configured by BWSTRIDE.
454
+ * We can make bw_mask which marks exactly which bits in bw_addr may
455
+ * be set (gpcbwu:gpcbwl).
456
+ */
457
+ bw_mask = bw_stride - bw_size;
458
+
459
+ if (bw_addr & ~bw_mask) {
460
+ goto fault_walk;
461
+ }
462
+ }
463
+
464
/* Note this field is read-only and fixed at reset. */
465
l0gptsz = 30 + FIELD_EX64(gpccr, GPCCR, L0GPTSZ);
466
@@ -433,6 +495,20 @@ bool arm_granule_protection_check(ARMGranuleProtectionConfig config,
495
goto fault_fail;
496
}
497
498
+ /*
499
+ * Bypass window check.
500
+ * I_JJLRM: Granule Protection Table (GPT) lookups can be skipped
501
+ * in portions of the memory map by using GPC bypass windows.
502
+ * I_XNHTX: The GPC bypass window check (...) is performed
503
+ * immediately after priority 3.
504
+ * bw_mask from earlier makes this check for us.
505
+ */
506
+ if (FIELD_EX64(gpccr, GPCCR, GPCBW)) {
507
+ if ((paddress & bw_mask) == bw_addr) {
508
+ return true;
509
+ }
510
+ }
511
+
512
/* GPC Priority 4: the base address of GPTBR_EL3 exceeds PPS. */
513
tableaddr = config.gptbr << 12;
514
if (tableaddr & ~pps_mask) {
@@ -3879,6 +3955,7 @@ static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw,
3955
};
3956
struct ARMGranuleProtectionConfig config = {
3957
.gpccr = env->cp15.gpccr_el3,
3958
+ .gpcbw = env->cp15.gpcbw_el3,
3959
.gptbr = env->cp15.gptbr_el3,
3960
.parange = FIELD_EX64_IDREG(&cpu->isar, ID_AA64MMFR0, PARANGE),
3961
.support_sel2 = cpu_isar_feature(aa64_sel2, cpu),