1#include "mem/tests/test_internal.h"
2
3TEST_GROUP_DECLARE(page_table);
4
5/* Tagged PTEs:
6 * [ payload high 53 ][ AVAIL2 ][ LOCK ][ payload low 6 ][ type 2 ][ P=0 ]
7 */
8
9#define PTE_PAYLOAD_MAX ((1ULL << PTE_TAGGED_PAYLOAD_BITS) - 1)
10
11TEST_DECLARE_UNIT(page_table, tagged_roundtrip) {
12 static const uint64_t payloads[] = {
13 0, 1, 0x3F, /* fills the low chunk */
14 0x40, /* first bit of the high chunk */
15 0x3FF, 0xDEADBEEF, 1ULL << 32, PTE_PAYLOAD_MAX - 1, PTE_PAYLOAD_MAX,
16 };
17
18 for (size_t i = 0; i < TEST_ARRAY_LEN(payloads); i++) {
19 struct pte_tagged in = {.type = PTE_TAG_TYPE_DEMAND_PAGED,
20 .payload = payloads[i]};
21
22 struct pte_tagged out = pte_tagged_unpack(pte: pte_tagged_pack(pt: &in));
23
24 TEST_ASSERT_EQ(out.type, in.type);
25 TEST_ASSERT_EQ(out.payload, in.payload);
26 }
27
28 return TEST_SUCCESS;
29}
30
31/* Walking a single bit across the whole payload width catches shifts
32 * that are short or long, which some values can step over */
33TEST_DECLARE_UNIT(page_table, tagged_payload_walk) {
34 for (unsigned bit = 0; bit < PTE_TAGGED_PAYLOAD_BITS; bit++) {
35 struct pte_tagged in = {.type = PTE_TAG_TYPE_DEMAND_PAGED,
36 .payload = 1ULL << bit};
37
38 struct pte_tagged out = pte_tagged_unpack(pte: pte_tagged_pack(pt: &in));
39 TEST_ASSERT_EQ(out.payload, in.payload);
40 }
41
42 return TEST_SUCCESS;
43}
44
45/* Packed format must be safe to OR into live PTE, so it cannot be PRESENT,
46 * LOCK or AVAIL2 no matter what */
47TEST_DECLARE_UNIT(page_table, tagged_pack_reserved_bits) {
48 const uint64_t reserved = PAGE_PRESENT | PTE_LOCK_BIT | PTE_AVAIL2_BIT;
49
50 for (unsigned bit = 0; bit < PTE_TAGGED_PAYLOAD_BITS; bit++) {
51 struct pte_tagged in = {.type = PTE_TAG_TYPE_DEMAND_PAGED,
52 .payload = 1ULL << bit};
53 TEST_ASSERT_EQ((pte_tagged_pack(&in) & reserved), 0);
54 }
55
56 struct pte_tagged full = {.type = PTE_TAG_TYPE_DEMAND_PAGED,
57 .payload = PTE_PAYLOAD_MAX};
58 TEST_ASSERT_EQ((pte_tagged_pack(&full) & reserved), 0);
59
60 return TEST_SUCCESS;
61}
62
63/* Unpack ignores */
64TEST_DECLARE_UNIT(page_table, tagged_unpack_reserved_bits) {
65 struct pte_tagged in = {.type = PTE_TAG_TYPE_DEMAND_PAGED,
66 .payload = 0x1234567};
67
68 uint64_t packed = pte_tagged_pack(pt: &in);
69
70 struct pte_tagged plain = pte_tagged_unpack(pte: packed);
71 struct pte_tagged locked =
72 pte_tagged_unpack(pte: packed | PTE_LOCK_BIT | PTE_AVAIL2_BIT);
73
74 TEST_ASSERT_EQ(locked.type, plain.type);
75 TEST_ASSERT_EQ(locked.payload, plain.payload);
76
77 return TEST_SUCCESS;
78}
79
80/* Type sits above PRESENT */
81TEST_DECLARE_UNIT(page_table, tagged_type_field) {
82 struct pte_tagged in = {.type = PTE_TAG_TYPE_DEMAND_PAGED, .payload = 0};
83 uint64_t packed = pte_tagged_pack(pt: &in);
84
85 TEST_ASSERT_EQ(PTE_TAGGED_GET_TYPE(packed), PTE_TAG_TYPE_DEMAND_PAGED);
86 TEST_ASSERT_EQ(pte_tagged_unpack(packed).type, PTE_TAG_TYPE_DEMAND_PAGED);
87
88 /* Untyped empty tags pack to nothing */
89 struct pte_tagged none = {.type = PTE_TAG_TYPE_NONE, .payload = 0};
90 TEST_ASSERT_EQ(pte_tagged_pack(&none), 0);
91
92 return TEST_SUCCESS;
93}
94
95/* Two chunk widths and positions they are stored at have to add up to
96 * payload width */
97TEST_DECLARE_UNIT(page_table, tagged_layout_consistency) {
98 TEST_ASSERT_EQ(PTE_TAGGED_PAYLOAD_BITS,
99 PTE_TAGGED_PAYLOAD_LOW_BITS +
100 (64 - PTE_TAGGED_PAYLOAD_HIGH_SHIFT));
101
102 /* low chunk must end exactly where LOCK begins */
103 TEST_ASSERT_EQ(PTE_TAGGED_PAYLOAD_LOW_SHIFT + PTE_TAGGED_PAYLOAD_LOW_BITS,
104 PTE_LOCK_SHIFT);
105
106 /* high chunk must start exactly above AVAIL2 */
107 TEST_ASSERT_EQ(PTE_TAGGED_PAYLOAD_HIGH_SHIFT, PTE_AVAIL2_SHIFT + 1);
108
109 /* type must fit below low chunk without touching PRESENT */
110 TEST_ASSERT_EQ(PTE_TAGGED_TYPE_SHIFT, PAGE_PRESENT_SHIFT + 1);
111 TEST_ASSERT_LE(PTE_TAGGED_TYPE_SHIFT + 2, PTE_TAGGED_PAYLOAD_LOW_SHIFT);
112
113 return TEST_SUCCESS;
114}
115
116/* pte_is_shared only means something on present entries, SHARED
117 * is used as payload space when tagged encoding is used */
118TEST_DECLARE_UNIT(page_table, is_shared_requires_present) {
119 TEST_ASSERT(!pte_is_shared(0));
120 TEST_ASSERT(!pte_is_shared(PTE_SHARED_BIT));
121 TEST_ASSERT(!pte_is_shared(PAGE_PRESENT));
122 TEST_ASSERT(pte_is_shared(PAGE_PRESENT | PTE_SHARED_BIT));
123
124 return TEST_SUCCESS;
125}
126