From 9960f468bd7298a0431ae04a73e1f87cd38943a1 Mon Sep 17 00:00:00 2001 From: Wojciech Sipak Date: Wed, 8 Jan 2025 17:32:42 +0100 Subject: [PATCH] wip simplify random test for pmp --- testbench/tests/pmp_random/main.c | 527 ++++++++---------- .../pmp_random/{pmp.ld => pmp_random.ld} | 0 2 files changed, 244 insertions(+), 283 deletions(-) rename testbench/tests/pmp_random/{pmp.ld => pmp_random.ld} (100%) diff --git a/testbench/tests/pmp_random/main.c b/testbench/tests/pmp_random/main.c index 9313f01515f..327c1478238 100644 --- a/testbench/tests/pmp_random/main.c +++ b/testbench/tests/pmp_random/main.c @@ -93,16 +93,11 @@ void __attribute__((noinline)) test_hello () { printf(" hello\n"); } -int __attribute__((noinline)) test_read (const uint32_t* pattern) { +int __attribute__((noinline)) test_read (const uint32_t* pattern, uint32_t* source, size_t size) { did_execute = 1; - printf(" reading from .area...\n"); + printf(" reading from 0x%x...\n", source); - uint32_t arr[16]; - for (size_t i=0; i<16; ++i) { - arr[i] = test_area[i]; - } - - if (memcmp(arr, pattern, sizeof(arr))) { + if (memcmp(source, pattern, size)) { printf(" data mismatch\n"); return -1; } @@ -113,13 +108,11 @@ int __attribute__((noinline)) test_read (const uint32_t* pattern) { return 0; } -void __attribute__((noinline)) test_write (const uint32_t* pattern) { +void __attribute__((noinline)) test_write (const uint32_t* pattern, uint32_t* target, size_t size) { did_execute = 1; - printf(" writing to .area...\n"); + printf(" writing to 0x%x...\n", target); - for (size_t i=0; i<16; ++i) { - test_area[i] = pattern[i]; - } + memcpy((void*)target, pattern, size); } void __attribute__((noinline, section(".area.code"))) test_exec () { @@ -150,9 +143,36 @@ int trap_handler (const struct fault* fault) { #define ADDR2PMP(x) ((((uint32_t)(x)) & 3) ? ((((uint32_t)(x)) >> 2) + 1) : \ (((uint32_t)(x)) >> 2)) -uint32_t legalize_address(uint32_t address, uint32_t cfg) { +enum RegionType { + OFF = 0, + TOR = 1, + NA4 = 2, + NAPOT = 3 +}; + + +uint32_t legalize_address(uint32_t address, enum RegionType region_type) { uint32_t area_end = ((uint32_t)&_area) + 0x40; + switch (region_type) { + case OFF: + return 0; + case TOR: + return 0; + case NA4: + // Do not use two most significant bits, VeeR ties them to 0 anyway. + address &= 0x3fffffff; + break; + case NAPOT: + // Do not use large regions. + while ((address & 0xf) == 0xf) address >>= 1; + // Do not use two most significant bits, VeeR ties them to 0 anyway. + address &= 0x3fffffff; + break; + default: + break; + } + // if ((cfg & PMP_NA4) == PMP_NA4) { // if ((address < (uint32_t)&_stack_hi && address > (uint32_t)&_stack_lo) // |(address < (uint32_t)&_text && address > (uint32_t)&_text_end) @@ -163,10 +183,10 @@ uint32_t legalize_address(uint32_t address, uint32_t cfg) { // } // } else if ((cfg & PMP_NAPOT) == PMP_NAPOT) { // } - address >>= 8; - address &= ~(1<<3); - address |= 3; - return (address); + // address >>= 8; + // address &= ~(1<<3); + // address |= 3; + return address; } uint32_t legalize_config(uint32_t config) { @@ -175,48 +195,64 @@ uint32_t legalize_config(uint32_t config) { * test. */ // temporarily enforce NA4 - if ((config & PMP_NAPOT) == PMP_NAPOT) { - config &= ~(PMP_NAPOT); - config |= PMP_NA4; - } - while((config & (PMP_NAPOT | PMP_NA4 | PMP_TOR) == 0) && config != 0) { - // if not enabled, shift right until it's a valid enabled region. - // if it's 0, give up. - config >>= 1; - } + config &= ~(PMP_NAPOT); + config |= PMP_NA4; + + // if ((config & PMP_NAPOT) == PMP_NAPOT) { + // config &= ~(PMP_NAPOT); + // config |= PMP_NA4; + // } + // while((config & (PMP_NAPOT | PMP_NA4 | PMP_TOR) == 0) && config != 0) { + // // if not enabled, shift right until it's a valid enabled region. + // // if it's 0, give up. + // config >>= 1; + // } config &= ~PMP_LOCK; -#if !(RV_SMEPMP) - -#endif return config; } -// Set mstatus MPRV -#define set_mprv(x) { \ - uint32_t mstatus; \ - CSRR_READ(mstatus, CSR_MSTATUS); \ - if (x) mstatus |= (1 << 17); \ - else mstatus &= ~(1 << 17); \ - CSRR_WRITE(mstatus, CSR_MSTATUS); \ -} - -// Set mstatus MPP to 00 or 11 -#define set_mpp(x) { \ - uint32_t mstatus; \ - CSRR_READ(mstatus, CSR_MSTATUS); \ - if (x) mstatus |= (3 << 11); \ - else mstatus &= ~(3 << 11); \ - CSRR_WRITE(mstatus, CSR_MSTATUS); \ -} - #define TST_R 1 #define TST_W 2 #define TST_X 4 #define TST_M 16 -#define TST_MPRV 32 -#define TST_MPP 64 -// ============================================================================ +#define trailing_ones(x) __builtin_ctz(~x & (x + 1)) + +uint32_t generate_napot_mask(uint32_t value) { + // Find the position of the first zero + uint32_t pos = trailing_ones(value); + // Create a mask with 'pos' number of ones + uint32_t mask = (1U << pos) - 1; + return mask; +} + + +int get_effective_range(uint32_t *address, enum RegionType region_type, uint32_t **addr_hi, uint32_t **addr_lo){ + *address = legalize_address(*address, region_type); + + switch (region_type) { + case OFF: + *addr_lo = 0; + *addr_hi = 0; + break; + case TOR: + *addr_lo = 0; + *addr_hi = *address << 2; + break; + case NA4: + *addr_lo = *address << 2; + *addr_hi = *addr_lo + 1; + break; + case NAPOT: + uint32_t mask = generate_napot_mask(*address); + *addr_lo = ((uint32_t)address & ~mask) << 2; + *addr_hi = *addr_lo + (1 << trailing_ones(*address)); + break; + default: + break; + } + return 0; +} int main () { printf("Hello VeeR (M mode)\n"); @@ -243,247 +279,172 @@ int main () { int failed = 0; pmp_clear(); - - // ....................................................................... - // Test PMP operation for all possible RWX combinations in U and M mode. - - // Generate test cases. A test case is encoded on a byte: - // bit 0: R - // bit 1: W - // bit 2: X - // bit 4: 0-user, 1-machine - // bit 5: mstatus.MPRV - // bit 6: mstatus.MPP (0 for 00, 1 for 11) - uint8_t test_cases [32]; - uint32_t test_count = 0; - - // Test cases for all RWX combinations in machine mode - for (size_t i=0; i<8; ++i) { - uint32_t r = (i & 1) ? PMP_R : 0; - uint32_t w = (i & 2) ? PMP_W : 0; - uint32_t x = (i & 4) ? PMP_X : 0; - -#if RV_SMEPMP - // Skip -W- and -WX combinations - if (!r && w && !x) continue; - if (!r && w && x) continue; -#endif - test_cases[test_count++] = TST_M | i; - } - // ................................ // Do the tests // use regions 6..15 for random data int region_for_rand_data = 6; - for (size_t i=0; i> 3) & 0x3); + + /* calculate effective range using type and addr */ + get_effective_range(&address, region_type, &addr_hi, &addr_lo); + region_size = addr_hi - addr_lo; + + // Effective mode + uint32_t r_eff = 1; + uint32_t w_eff = 1; + uint32_t x_eff = x; + + char pstr[4] = { + r ? 'R' : '-', + w ? 'W' : '-', + x ? 'X' : '-', + 0x00 + }; + + printf("%02d - Machine mode: test %s in region(%d): 0x%x - 0x%x\n", tid++, pstr, region_type, addr_lo, addr_hi); + + // Write data to the tested region before configuring PMP. + const uint32_t* pattern = (i & 1) ? test_pattern_b : test_pattern_a; + const uint32_t* other = (i & 1) ? test_pattern_a : test_pattern_b; + + size_t test_size = (region_size > sizeof(other)) ? sizeof(other) : region_size; + + memcpy((void*)addr_lo, other, test_size); + + // Disable the region previously used for random data. + random_entry.cfg = PMP_OFF; + random_entry.addr = 0; + pmp_entry_write(region_for_rand_data, &random_entry); + + // Use the next region for random data (from the range 6..15) + region_for_rand_data = (region_for_rand_data < 15) ? region_for_rand_data+1 : 6; + + printf(" using random data (%d)...\n", rand_test_i); + random_entry.cfg = config; + random_entry.addr = address; + pmp_entry_write(region_for_rand_data, &random_entry); + + // Check + struct pmp_entry_s readback; + pmp_entry_read(5, &readback); + + // An illegal PMP region configuration has been written and readback + // this is an error + // + // -W- and -WX combinations are reserved except for when Smepmp is + // present and mseccfg.MML=1. This test does not enable the latter + // so the combinations are not legal. + if (!pmp_is_cfg_legal(readback.cfg)) { + printf(" error, an illegal PMP configuration accepted by the core\n", readback.cfg); + failed++; + continue; } - } - // ....................................................................... - // Test PMP region lock feature. - - // Since unlocking a region requires core reset this test does only X - // permission check. Testing different possibilities would require either - // a set of tests of a form of persistent storage in the testbench / SoC - // RTL. - - printf("%02d - Testing execution from a locked region in U and M mode\n", tid++); - - // Prevent both U and M modes form executing from .area1 - entry.addr = ADDR2PMP(&_area); - entry.addr = (entry.addr & 0xFFFFFC00) | 0x000001FF; // NAPOT, 2^12 - entry.cfg = PMP_NAPOT | PMP_R | PMP_W | PMP_LOCK; - pmp_entry_write(5, &entry); - - // Execute from M - printf(" testing from M mode...\n"); - TRY { - test_exec(); - printf(" fail\n"); - failed++; - } - CATCH { - printf(" pass\n"); - } - END_TRY; - - // Check if the region can be un-locked - printf(" attempting to unlock region...\n"); - - entry.addr = ADDR2PMP(&_area); - entry.addr = (entry.addr & 0xFFFFFC00) | 0x000001FF; // NAPOT, 2^12 - entry.cfg = PMP_NAPOT | PMP_R | PMP_W | PMP_X; - pmp_entry_write(5, &entry); - - // Execute from M - printf(" testing from M mode...\n"); - TRY { - test_exec(); - printf(" fail\n"); - failed++; - } - CATCH { - printf(" pass\n"); + // R,W and X fields are WARL which means that the readback does not + // need to match what was written. In such a case skip the test but + // not mark it as an error. + if (readback.cfg != entry.cfg) { + continue; + } + + int exc; + int cmp; + int any_fail = 0; + + // Test writing. Write pattern from user mode and check if it was + // successfully written. + printf(" testing W...\n"); + did_execute = 0; + exc = 0; + TRY { test_write(pattern, addr_lo, test_size); } + CATCH { exc = 1; } + END_TRY; + + cmp = memcmp((void*)addr_lo, pattern, test_size); + if (cmp) { + printf(" data mismatch\n"); + } else { + printf(" data match\n"); + } + + if (did_execute && ((!w_eff && exc && cmp) || (w_eff && !exc && !cmp))) { + printf(" pass\n"); + } else { + printf(" fail\n"); + any_fail = 1; + printf(" random data used:\n addr: 0x%x,\n cfg: 0x%x\n", rand_address[rand_test_i], rand_config[rand_test_i]); + return 0; + } + + // Test reading. Read area from user mode and compare against the + // pattern + printf(" testing R...\n"); + + // Write pattern + if (!w_eff) { + memcpy((void*)addr_lo, pattern, test_size); + } + + did_execute = 0; + exc = 0; + TRY { cmp = test_read(pattern, addr_lo, test_size); } + CATCH { exc = 1; } + END_TRY; + + if (did_execute && ((!r_eff && exc) || (r_eff && !exc && !cmp))) { + printf(" pass\n"); + } else { + printf(" fail\n"); + any_fail = 1; + } + + // Call a function placed in the designated area + // printf(" testing X...\n"); + // TRY { + // test_exec(); + // if (x_eff) { + // printf(" pass\n"); + // } else { + // printf(" fail\n"); + // any_fail = 1; + // } + // } + // CATCH { + // if (x_eff) { + // printf(" fail\n"); + // any_fail = 1; + // } else { + // printf(" pass\n"); + // } + // } + // END_TRY; + + // Count fails + failed += any_fail; } - END_TRY; // ....................................................................... diff --git a/testbench/tests/pmp_random/pmp.ld b/testbench/tests/pmp_random/pmp_random.ld similarity index 100% rename from testbench/tests/pmp_random/pmp.ld rename to testbench/tests/pmp_random/pmp_random.ld