1 /*
2  * Copyright 2018 The Hafnium Authors.
3  *
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/BSD-3-Clause.
7  */
8 
9 #include "hf/mm.h"
10 
11 #include "hf/arch/mm.h"
12 
13 #include "test/hftest.h"
14 
15 /** There must be at least two levels in the page table. */
16 #define MAX_LEVEL_LOWER_BOUND 1
17 
18 /**
19  * This is the number of levels that are tested and is constrained as it
20  * controls the depth of recursion in the memory management code.
21  */
22 #define MAX_LEVEL_UPPER_BOUND 3
23 
24 /** X macro to expand tests for all levels. */
25 #define EXPAND_LEVEL_TESTS \
26 	LEVEL_TEST(0)      \
27 	LEVEL_TEST(1)      \
28 	LEVEL_TEST(2)      \
29 	LEVEL_TEST(3)
30 
31 /* TODO: work out how to run these test against the host fake arch. */
32 
33 /**
34  * A block must be allowed at level 0 as this is the level which represents
35  * pages.
36  */
TEST(arch_mm,block_allowed_at_level0)37 TEST(arch_mm, block_allowed_at_level0)
38 {
39 	ASSERT_TRUE(arch_mm_is_block_allowed(0));
40 }
41 
42 /**
43  * The maximum level must be within acceptable bounds.
44  */
TEST(arch_mm,max_level_stage1)45 TEST(arch_mm, max_level_stage1)
46 {
47 	uint8_t max_level;
48 
49 	arch_mm_stage1_max_level_set(arch_mm_get_pa_range());
50 	max_level = arch_mm_stage1_max_level();
51 
52 	EXPECT_GE(max_level, MAX_LEVEL_LOWER_BOUND);
53 	EXPECT_LE(max_level, MAX_LEVEL_UPPER_BOUND);
54 }
55 
56 /* TODO: initialize arch_mm and check max level of stage-2. */
57 
58 /**
59  * An absent entry is not present, valid, a block nor a table.
60  */
61 #define LEVEL_TEST(lvl)                                                  \
62 	TEST(arch_mm, absent_properties_level##lvl)                      \
63 	{                                                                \
64 		uint8_t level = lvl;                                     \
65 		pte_t absent_pte;                                        \
66                                                                          \
67 		absent_pte = arch_mm_absent_pte(level);                  \
68                                                                          \
69 		EXPECT_FALSE(arch_mm_pte_is_present(absent_pte, level)); \
70 		EXPECT_FALSE(arch_mm_pte_is_valid(absent_pte, level));   \
71 		EXPECT_FALSE(arch_mm_pte_is_block(absent_pte, level));   \
72 		EXPECT_FALSE(arch_mm_pte_is_table(absent_pte, level));   \
73 	}
74 EXPAND_LEVEL_TESTS
75 #undef LEVEL_TEST
76 
77 /**
78  * An invalid block is present and mutually exclusive from a table.
79  */
80 #define LEVEL_TEST(lvl)                                                       \
81 	TEST(arch_mm, invalid_block_properties_level##lvl)                    \
82 	{                                                                     \
83 		uint8_t level = lvl;                                          \
84 		uint64_t attrs =                                              \
85 			arch_mm_mode_to_stage2_attrs(MM_MODE_INVALID);        \
86 		pte_t block_pte;                                              \
87                                                                               \
88 		/* Test doesn't apply if a block is not allowed. */           \
89 		if (!arch_mm_is_block_allowed(level)) {                       \
90 			return;                                               \
91 		}                                                             \
92                                                                               \
93 		block_pte = arch_mm_block_pte(level, pa_init(PAGE_SIZE * 19), \
94 					      attrs);                         \
95                                                                               \
96 		EXPECT_TRUE(arch_mm_pte_is_present(block_pte, level));        \
97 		EXPECT_FALSE(arch_mm_pte_is_valid(block_pte, level));         \
98 		EXPECT_TRUE(arch_mm_pte_is_block(block_pte, level));          \
99 		EXPECT_FALSE(arch_mm_pte_is_table(block_pte, level));         \
100 	}
101 EXPAND_LEVEL_TESTS
102 #undef LEVEL_TEST
103 
104 /**
105  * A valid block is present and mutually exclusive from a table.
106  */
107 #define LEVEL_TEST(lvl)                                                \
108 	TEST(arch_mm, valid_block_properties_level##lvl)               \
109 	{                                                              \
110 		uint8_t level = lvl;                                   \
111 		uint64_t attrs = arch_mm_mode_to_stage2_attrs(0);      \
112 		pte_t block_pte;                                       \
113                                                                        \
114 		/* Test doesn't apply if a block is not allowed. */    \
115 		if (!arch_mm_is_block_allowed(level)) {                \
116 			return;                                        \
117 		}                                                      \
118                                                                        \
119 		block_pte = arch_mm_block_pte(                         \
120 			level, pa_init(PAGE_SIZE * 12345678U), attrs); \
121                                                                        \
122 		EXPECT_TRUE(arch_mm_pte_is_present(block_pte, level)); \
123 		EXPECT_TRUE(arch_mm_pte_is_valid(block_pte, level));   \
124 		EXPECT_TRUE(arch_mm_pte_is_block(block_pte, level));   \
125 		EXPECT_FALSE(arch_mm_pte_is_table(block_pte, level));  \
126 	}
127 EXPAND_LEVEL_TESTS
128 #undef LEVEL_TEST
129 
130 /**
131  * A table is present, valid and mutually exclusive from a block.
132  */
133 #define LEVEL_TEST(lvl)                                                        \
134 	TEST(arch_mm, table_properties_level##lvl)                             \
135 	{                                                                      \
136 		uint8_t level = lvl;                                           \
137 		pte_t table_pte;                                               \
138                                                                                \
139 		/* Test doesn't apply to level 0 as there can't be a table. */ \
140 		if (level == 0) {                                              \
141 			return;                                                \
142 		}                                                              \
143                                                                                \
144 		table_pte = arch_mm_table_pte(                                 \
145 			level, pa_init(PAGE_SIZE * 999999999U));               \
146                                                                                \
147 		EXPECT_TRUE(arch_mm_pte_is_present(table_pte, level));         \
148 		EXPECT_TRUE(arch_mm_pte_is_valid(table_pte, level));           \
149 		EXPECT_FALSE(arch_mm_pte_is_block(table_pte, level));          \
150 		EXPECT_TRUE(arch_mm_pte_is_table(table_pte, level));           \
151 	}
152 EXPAND_LEVEL_TESTS
153 #undef LEVEL_TEST
154 
155 /**
156  * The address and attributes of a block must be preserved when encoding and
157  * decoding.
158  */
159 #define LEVEL_TEST(lvl)                                                      \
160 	TEST(arch_mm, block_addr_and_attrs_preserved_level##lvl)             \
161 	{                                                                    \
162 		uint8_t level = lvl;                                         \
163 		paddr_t addr;                                                \
164 		uint64_t attrs;                                              \
165 		pte_t block_pte;                                             \
166                                                                              \
167 		/* Test doesn't apply if a block is not allowed. */          \
168 		if (!arch_mm_is_block_allowed(level)) {                      \
169 			return;                                              \
170 		}                                                            \
171                                                                              \
172 		addr = pa_init(0);                                           \
173 		attrs = arch_mm_mode_to_stage2_attrs(0);                     \
174 		block_pte = arch_mm_block_pte(level, addr, attrs);           \
175 		EXPECT_EQ(arch_mm_pte_attrs(block_pte, level), attrs);       \
176 		EXPECT_EQ(pa_addr(arch_mm_block_from_pte(block_pte, level)), \
177 			  pa_addr(addr));                                    \
178                                                                              \
179 		addr = pa_init(PAGE_SIZE * 17);                              \
180 		attrs = arch_mm_mode_to_stage2_attrs(MM_MODE_INVALID);       \
181 		block_pte = arch_mm_block_pte(level, addr, attrs);           \
182 		EXPECT_EQ(arch_mm_pte_attrs(block_pte, level), attrs);       \
183 		EXPECT_EQ(pa_addr(arch_mm_block_from_pte(block_pte, level)), \
184 			  pa_addr(addr));                                    \
185                                                                              \
186 		addr = pa_init(PAGE_SIZE * 500);                             \
187 		attrs = arch_mm_mode_to_stage2_attrs(MM_MODE_R | MM_MODE_W); \
188 		block_pte = arch_mm_block_pte(level, addr, attrs);           \
189 		EXPECT_EQ(arch_mm_pte_attrs(block_pte, level), attrs);       \
190 		EXPECT_EQ(pa_addr(arch_mm_block_from_pte(block_pte, level)), \
191 			  pa_addr(addr));                                    \
192 	}
193 EXPAND_LEVEL_TESTS
194 #undef LEVEL_TEST
195