1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3 * Unit tests for PDX compression.
4 *
5 * Copyright (C) 2025 Cloud Software Group
6 */
7
8 #include "harness.h"
9
10 #include "../../xen/common/pdx.c"
11
12 struct range {
13 /* Ranges are defined as [start, end). */
14 unsigned long start, end;
15 };
16
print_ranges(const struct range * r)17 static void print_ranges(const struct range *r)
18 {
19 unsigned int i;
20
21 printf("Ranges:\n");
22
23 for ( i = 0; i < MAX_RANGES; i++ )
24 {
25 if ( !r[i].start && !r[i].end )
26 break;
27
28 printf(" %013lx-%013lx\n", r[i].start, r[i].end);
29 }
30 }
31
main(int argc,char ** argv)32 int main(int argc, char **argv)
33 {
34 static const struct {
35 struct range ranges[MAX_RANGES];
36 bool compress;
37 } tests[] = {
38 #ifdef __LP64__
39 /*
40 * Only for targets where unsigned long is 64bits, otherwise compiler
41 * will complain about truncation from 'long long' -> 'long' conversion.
42 *
43 * Real memory map from a 4s Intel GNR. Not compressible using PDX
44 * mask compression.
45 */
46 {
47 .ranges = {
48 { .start = 0, .end = 0x80000UL },
49 { .start = 0x0100000UL, .end = 0x8080000UL },
50 { .start = 0x63e80000UL, .end = 0x6be80000UL },
51 { .start = 0xc7e80000UL, .end = 0xcfe80000UL },
52 { .start = 0x12be80000UL, .end = 0x133e80000UL },
53 },
54 #ifdef CONFIG_PDX_OFFSET_COMPRESSION
55 .compress = true,
56 #else
57 .compress = false,
58 #endif
59 },
60 /* Simple hole. */
61 {
62 .ranges = {
63 { .start = 0,
64 .end = (1UL << MAX_ORDER) * 1 },
65 { .start = (1UL << (MAX_ORDER * 2)) | 0,
66 .end = (1UL << (MAX_ORDER * 2)) | (1UL << MAX_ORDER) * 1 },
67 },
68 .compress = true,
69 },
70 /* Simple hole, unsorted ranges. */
71 {
72 .ranges = {
73 { .start = (1UL << (MAX_ORDER * 2)) | 0,
74 .end = (1UL << (MAX_ORDER * 2)) | (1UL << MAX_ORDER) * 1 },
75 { .start = 0,
76 .end = (1UL << MAX_ORDER) * 1 },
77 },
78 .compress = true,
79 },
80 /* PDX compression, 2 ranges covered by the lower mask. */
81 {
82 .ranges = {
83 { .start = 0,
84 .end = (1 << MAX_ORDER) * 1 },
85 { .start = (1 << MAX_ORDER) * 2,
86 .end = (1 << MAX_ORDER) * 3 },
87 { .start = (1 << MAX_ORDER) * 20,
88 .end = (1 << MAX_ORDER) * 22 },
89 },
90 .compress = true,
91 },
92 /* Single range not starting at 0. */
93 {
94 .ranges = {
95 { .start = (1 << MAX_ORDER) * 10,
96 .end = (1 << MAX_ORDER) * 11 },
97 },
98 .compress = true,
99 },
100 /* Resulting PDX region size leads to no compression. */
101 {
102 .ranges = {
103 { .start = 0,
104 .end = (1 << MAX_ORDER) * 1 },
105 { .start = (1 << MAX_ORDER) * 2,
106 .end = (1 << MAX_ORDER) * 3 },
107 { .start = (1 << MAX_ORDER) * 4,
108 .end = (1 << MAX_ORDER) * 7 },
109 { .start = (1 << MAX_ORDER) * 8,
110 .end = (1 << MAX_ORDER) * 12 },
111 },
112 .compress = false,
113 },
114 /* AMD Versal Gen 2 ARM board. */
115 {
116 .ranges = {
117 { .start = 0, .end = 0x80000UL },
118 { .start = 0x800000UL, .end = 0x880000UL },
119 { .start = 0x50000000UL, .end = 0x50080000UL },
120 { .start = 0x60000000UL, .end = 0x60080000UL },
121 { .start = 0x70000000UL, .end = 0x70080000UL },
122 },
123 .compress = true,
124 },
125 /* Unsorted ranges, lower one not starting at 0. */
126 {
127 .ranges = {
128 { .start = (1UL << (35 - PAGE_SHIFT)) + (1 << MAX_ORDER) * 2,
129 .end = (1UL << (35 - PAGE_SHIFT)) + (1 << MAX_ORDER) * 3 },
130 { .start = (1 << MAX_ORDER) * 2,
131 .end = (1 << MAX_ORDER) * 3 },
132 },
133 .compress = true,
134 },
135 /* Two ranges with the same high bit set. */
136 {
137 .ranges = {
138 { .start = (1UL << (51 - PAGE_SHIFT)) + (1 << MAX_ORDER) * 0,
139 .end = (1UL << (51 - PAGE_SHIFT)) + (1 << MAX_ORDER) * 1 },
140 { .start = (1UL << (51 - PAGE_SHIFT)) + (1 << MAX_ORDER) * 3,
141 .end = (1UL << (51 - PAGE_SHIFT)) + (1 << MAX_ORDER) * 4 },
142 },
143 .compress = true,
144 },
145 #endif
146 /* AMD Naples Epyc 7281 2 sockets, 8 NUMA nodes. */
147 {
148 .ranges = {
149 { .start = 0, .end = 0xa0UL },
150 { .start = 0x100UL, .end = 0xb0000UL },
151 { .start = 0x100000UL, .end = 0x430000UL },
152 { .start = 0x430000UL, .end = 0x830000UL },
153 { .start = 0x830000UL, .end = 0xc30000UL },
154 { .start = 0xc30000UL, .end = 0x1030000UL },
155 { .start = 0x1030000UL, .end = 0x1430000UL },
156 { .start = 0x1430000UL, .end = 0x1830000UL },
157 { .start = 0x1830000UL, .end = 0x1c30000UL },
158 { .start = 0x1c30000UL, .end = 0x2030000UL },
159 },
160 .compress = false,
161 },
162 /* 2-node 2GB per-node QEMU layout. */
163 {
164 .ranges = {
165 { .start = 0, .end = 0x80000UL },
166 { .start = 0x100000UL, .end = 0x180000UL },
167 },
168 .compress = true,
169 },
170 /* Not compressible, smaller than MAX_ORDER. */
171 {
172 .ranges = {
173 { .start = 0, .end = 1 },
174 { .start = 0x100UL, .end = 0x101UL },
175 },
176 .compress = false,
177 },
178 /* Compressible, requires adjusting size to (1 << MAX_ORDER). */
179 {
180 .ranges = {
181 { .start = 0, .end = 1 },
182 { .start = 0x100000UL, .end = 0x100001UL },
183 },
184 .compress = true,
185 },
186 /* 2s Intel CLX with contiguous ranges, no compression. */
187 {
188 .ranges = {
189 { .start = 0 , .end = 0x180000UL },
190 { .start = 0x180000UL, .end = 0x3040000UL },
191 },
192 .compress = false,
193 },
194 };
195 int ret_code = EXIT_SUCCESS;
196
197 for ( unsigned int i = 0 ; i < ARRAY_SIZE(tests); i++ )
198 {
199 unsigned int j;
200
201 pfn_pdx_compression_reset();
202
203 for ( j = 0; j < ARRAY_SIZE(tests[i].ranges); j++ )
204 {
205 unsigned long size = tests[i].ranges[j].end -
206 tests[i].ranges[j].start;
207
208 if ( !tests[i].ranges[j].start && !tests[i].ranges[j].end )
209 break;
210
211 pfn_pdx_add_region(tests[i].ranges[j].start << PAGE_SHIFT,
212 size << PAGE_SHIFT);
213 }
214
215 if ( pfn_pdx_compression_setup(0) != tests[i].compress )
216 {
217 printf("PFN compression diverge, expected %scompressible\n",
218 tests[i].compress ? "" : "un");
219 print_ranges(tests[i].ranges);
220
221 ret_code = EXIT_FAILURE;
222 continue;
223 }
224
225 if ( !tests[i].compress )
226 continue;
227
228 for ( j = 0; j < ARRAY_SIZE(tests[i].ranges); j++ )
229 {
230 unsigned long start = tests[i].ranges[j].start;
231 unsigned long end = tests[i].ranges[j].end;
232
233 if ( !start && !end )
234 break;
235
236 if ( !pdx_is_region_compressible(start << PAGE_SHIFT, 1) ||
237 !pdx_is_region_compressible((end - 1) << PAGE_SHIFT, 1) )
238 {
239 printf(
240 "PFN compression invalid, pages %#lx and %#lx should be compressible\n",
241 start, end - 1);
242 print_ranges(tests[i].ranges);
243 ret_code = EXIT_FAILURE;
244 }
245
246 if ( start != pdx_to_pfn(pfn_to_pdx(start)) ||
247 end - 1 != pdx_to_pfn(pfn_to_pdx(end - 1)) )
248 {
249 printf("Compression is not bi-directional:\n");
250 printf(" PFN %#lx -> PDX %#lx -> PFN %#lx\n",
251 start, pfn_to_pdx(start), pdx_to_pfn(pfn_to_pdx(start)));
252 printf(" PFN %#lx -> PDX %#lx -> PFN %#lx\n",
253 end - 1, pfn_to_pdx(end - 1),
254 pdx_to_pfn(pfn_to_pdx(end - 1)));
255 print_ranges(tests[i].ranges);
256 ret_code = EXIT_FAILURE;
257 }
258 }
259 }
260
261 return ret_code;
262 }
263
264 /*
265 * Local variables:
266 * mode: C
267 * c-file-style: "BSD"
268 * c-basic-offset: 4
269 * indent-tabs-mode: nil
270 * End:
271 */
272