|  | 
|  | 1 | +/* | 
|  | 2 | + * Copyright (c) 2025 Google LLC | 
|  | 3 | + * | 
|  | 4 | + * SPDX-License-Identifier: Apache-2.0 | 
|  | 5 | + */ | 
|  | 6 | + | 
|  | 7 | +#include <kernel_internal.h> | 
|  | 8 | +#include <zephyr/mem_mgmt/mem_attr.h> | 
|  | 9 | +#include <zephyr/tc_util.h> | 
|  | 10 | +#include <zephyr/ztest.h> | 
|  | 11 | + | 
|  | 12 | +void z_riscv_pmp_read_config(unsigned long *pmp_cfg, size_t pmp_cfg_size); | 
|  | 13 | +void z_riscv_pmp_read_addr(unsigned long *pmp_addr, size_t pmp_addr_size); | 
|  | 14 | +void pmp_decode_region(uint8_t cfg_byte, unsigned long *pmp_addr, unsigned int index, | 
|  | 15 | +		       unsigned long *start, unsigned long *end); | 
|  | 16 | + | 
|  | 17 | +/* Checks if the Machine Privilege Register Virtualization (MPRV) bit in mstatus is 1 (enabled). */ | 
|  | 18 | +static bool riscv_mprv_is_enabled(void) | 
|  | 19 | +{ | 
|  | 20 | +	return csr_read(mstatus) & MSTATUS_MPRV; | 
|  | 21 | +} | 
|  | 22 | + | 
|  | 23 | +/* Checks if the Machine Previous Privilege (MPP) field in mstatus is set to M-Mode (0b11). */ | 
|  | 24 | +static bool riscv_mpp_is_m_mode(void) | 
|  | 25 | +{ | 
|  | 26 | +	return (csr_read(mstatus) & MSTATUS_MPP) == MSTATUS_MPP; | 
|  | 27 | +} | 
|  | 28 | + | 
|  | 29 | +/* Helper structure to define the expected PMP regions derived from the Device Tree. */ | 
|  | 30 | +struct expected_region { | 
|  | 31 | +	uintptr_t base; | 
|  | 32 | +	size_t size; | 
|  | 33 | +	uint8_t perm; | 
|  | 34 | +	bool found; | 
|  | 35 | +}; | 
|  | 36 | + | 
|  | 37 | +/* | 
|  | 38 | + * Extract base address, size, and permission for the memory regions | 
|  | 39 | + * defined in the Device Tree under the 'memattr' nodes. | 
|  | 40 | + */ | 
|  | 41 | +static struct expected_region dt_regions[] = { | 
|  | 42 | +	{.base = DT_REG_ADDR(DT_NODELABEL(memattr_region1)), | 
|  | 43 | +	 .size = DT_REG_SIZE(DT_NODELABEL(memattr_region1)), | 
|  | 44 | +	 .perm = DT_MEM_RISCV_TO_PMP_PERM( | 
|  | 45 | +		 DT_PROP(DT_NODELABEL(memattr_region1), zephyr_memory_attr)), | 
|  | 46 | +	 .found = false}, | 
|  | 47 | +	{.base = DT_REG_ADDR(DT_NODELABEL(memattr_region2)), | 
|  | 48 | +	 .size = DT_REG_SIZE(DT_NODELABEL(memattr_region2)), | 
|  | 49 | +	 .perm = DT_MEM_RISCV_TO_PMP_PERM( | 
|  | 50 | +		 DT_PROP(DT_NODELABEL(memattr_region2), zephyr_memory_attr)), | 
|  | 51 | +	 .found = false}}; | 
|  | 52 | + | 
|  | 53 | +ZTEST(riscv_pmp_memattr_entries, test_pmp_devicetree_memattr_config) | 
|  | 54 | +{ | 
|  | 55 | +	const size_t num_pmpcfg_regs = CONFIG_PMP_SLOTS / sizeof(unsigned long); | 
|  | 56 | +	const size_t num_pmpaddr_regs = CONFIG_PMP_SLOTS; | 
|  | 57 | + | 
|  | 58 | +	unsigned long current_pmpcfg_regs[num_pmpcfg_regs]; | 
|  | 59 | +	unsigned long current_pmpaddr_regs[num_pmpaddr_regs]; | 
|  | 60 | + | 
|  | 61 | +	/* Read the current PMP configuration from the control registers */ | 
|  | 62 | +	z_riscv_pmp_read_config(current_pmpcfg_regs, num_pmpcfg_regs); | 
|  | 63 | +	z_riscv_pmp_read_addr(current_pmpaddr_regs, num_pmpaddr_regs); | 
|  | 64 | + | 
|  | 65 | +	const uint8_t *const current_pmp_cfg_entries = (const uint8_t *)current_pmpcfg_regs; | 
|  | 66 | + | 
|  | 67 | +	for (unsigned int index = 0; index < CONFIG_PMP_SLOTS; ++index) { | 
|  | 68 | +		unsigned long start, end; | 
|  | 69 | +		uint8_t cfg_byte = current_pmp_cfg_entries[index]; | 
|  | 70 | + | 
|  | 71 | +		/* Decode the configured PMP region (start and end addresses) */ | 
|  | 72 | +		pmp_decode_region(cfg_byte, current_pmpaddr_regs, index, &start, &end); | 
|  | 73 | + | 
|  | 74 | +		/* Compare the decoded region against the list of expected DT regions */ | 
|  | 75 | +		for (size_t i = 0; i < ARRAY_SIZE(dt_regions); ++i) { | 
|  | 76 | +			if ((start == dt_regions[i].base) && | 
|  | 77 | +			    (end == dt_regions[i].base + dt_regions[i].size - 1) && | 
|  | 78 | +			    ((cfg_byte & 0x07) == dt_regions[i].perm)) { | 
|  | 79 | + | 
|  | 80 | +				dt_regions[i].found = true; | 
|  | 81 | +				break; | 
|  | 82 | +			} | 
|  | 83 | +		} | 
|  | 84 | +	} | 
|  | 85 | + | 
|  | 86 | +	for (size_t i = 0; i < ARRAY_SIZE(dt_regions); i++) { | 
|  | 87 | +		zassert_true(dt_regions[i].found, | 
|  | 88 | +			     "PMP entry for DT region %zu (base 0x%lx, size 0x%zx, perm 0x%x) not " | 
|  | 89 | +			     "found.", | 
|  | 90 | +			     i + 1, dt_regions[i].base, dt_regions[i].size, dt_regions[i].perm); | 
|  | 91 | +	} | 
|  | 92 | +} | 
|  | 93 | + | 
|  | 94 | +ZTEST(riscv_pmp_memattr_entries, test_riscv_mprv_mpp_config) | 
|  | 95 | +{ | 
|  | 96 | +	zassert_true(riscv_mprv_is_enabled(), | 
|  | 97 | +		     "MPRV should be enabled (1) to use the privilege specified by the MPP field."); | 
|  | 98 | + | 
|  | 99 | +	zassert_false(riscv_mpp_is_m_mode(), | 
|  | 100 | +		      "MPP should be set to 0x00 (U-Mode) before execution."); | 
|  | 101 | +} | 
|  | 102 | + | 
|  | 103 | +ZTEST(riscv_pmp_memattr_entries, test_dt_pmp_perm_conversion) | 
|  | 104 | +{ | 
|  | 105 | +	uint8_t result; | 
|  | 106 | + | 
|  | 107 | +	result = DT_MEM_RISCV_TO_PMP_PERM(0); | 
|  | 108 | +	zassert_equal(result, 0, "Expected 0, got 0x%x", result); | 
|  | 109 | + | 
|  | 110 | +	result = DT_MEM_RISCV_TO_PMP_PERM(DT_MEM_RISCV_TYPE_IO_R); | 
|  | 111 | +	zassert_equal(result, PMP_R, "Expected PMP_R (0x%x), got 0x%x", PMP_R, result); | 
|  | 112 | + | 
|  | 113 | +	result = DT_MEM_RISCV_TO_PMP_PERM(DT_MEM_RISCV_TYPE_IO_W); | 
|  | 114 | +	zassert_equal(result, PMP_W, "Expected PMP_W (0x%x), got 0x%x", PMP_W, result); | 
|  | 115 | + | 
|  | 116 | +	result = DT_MEM_RISCV_TO_PMP_PERM(DT_MEM_RISCV_TYPE_IO_X); | 
|  | 117 | +	zassert_equal(result, PMP_X, "Expected PMP_X (0x%x), got 0x%x", PMP_X, result); | 
|  | 118 | + | 
|  | 119 | +	result = DT_MEM_RISCV_TO_PMP_PERM(DT_MEM_RISCV_TYPE_IO_R | DT_MEM_RISCV_TYPE_IO_W); | 
|  | 120 | +	zassert_equal(result, PMP_R | PMP_W, "Expected R|W (0x%x), got 0x%x", PMP_R | PMP_W, | 
|  | 121 | +		      result); | 
|  | 122 | + | 
|  | 123 | +	result = DT_MEM_RISCV_TO_PMP_PERM(DT_MEM_RISCV_TYPE_IO_R | DT_MEM_RISCV_TYPE_IO_W | | 
|  | 124 | +					  DT_MEM_RISCV_TYPE_IO_X); | 
|  | 125 | +	zassert_equal(result, PMP_R | PMP_W | PMP_X, "Expected R|W|X (0x%x), got 0x%x", | 
|  | 126 | +		      PMP_R | PMP_W | PMP_X, result); | 
|  | 127 | +} | 
|  | 128 | + | 
|  | 129 | +ZTEST_SUITE(riscv_pmp_memattr_entries, NULL, NULL, NULL, NULL, NULL); | 
0 commit comments