1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <commonlib/region.h>
5 #include <tests/test.h>
7 /* We'd like to test overflow conditions, but for tests size_t is dependent on the HOSTCC
8 architecture. We use this to normalize the available address space to [VAL(0x0):VAL(0xf)). */
9 #define VAL(v) ((size_t)(v##ULL << (sizeof(size_t) * 8 - 4)))
11 static void test_region(void **state
)
13 /* Self-test: make sure VAL() overflow works as intended. */
14 assert_true(VAL(5) + VAL(10) > VAL(10));
15 assert_true(VAL(7) + VAL(10) < VAL(10));
17 struct region outer
= {.offset
= VAL(2), .size
= VAL(4)};
18 assert_int_equal(region_offset(&outer
), VAL(2));
19 assert_int_equal(region_sz(&outer
), VAL(4));
20 assert_int_equal(region_last(&outer
), VAL(6) - 1);
22 struct region inner
= {.offset
= VAL(3), .size
= VAL(2)};
23 assert_true(region_is_subregion(&outer
, &inner
));
25 struct region touching_bottom
= {.offset
= VAL(2), .size
= VAL(1)};
26 assert_true(region_is_subregion(&outer
, &touching_bottom
));
28 struct region touching_top
= {.offset
= VAL(5), .size
= VAL(1)};
29 assert_true(region_is_subregion(&outer
, &touching_top
));
31 struct region overlap_bottom
= {.offset
= VAL(1), .size
= VAL(2)};
32 assert_false(region_is_subregion(&outer
, &overlap_bottom
));
34 struct region overlap_top
= {.offset
= VAL(5), .size
= VAL(2)};
35 assert_false(region_is_subregion(&outer
, &overlap_top
));
37 struct region below
= {.offset
= 0, .size
= VAL(1)};
38 assert_false(region_is_subregion(&outer
, &below
));
40 struct region above
= {.offset
= VAL(0xf), .size
= VAL(1)};
41 assert_false(region_is_subregion(&outer
, &above
));
44 static void *mock_mmap(const struct region_device
*rdev
, size_t offset
, size_t size
)
46 check_expected_ptr(rdev
);
47 check_expected(offset
);
50 return mock_ptr_type(void *);
53 static int mock_unmap(const struct region_device
*rdev
, void *mapping
)
55 check_expected_ptr(rdev
);
56 check_expected_ptr(mapping
);
61 static ssize_t
mock_readat(const struct region_device
*rdev
, void *buffer
, size_t offset
,
64 check_expected_ptr(rdev
);
65 check_expected_ptr(buffer
);
66 check_expected(offset
);
76 static ssize_t
mock_writeat(const struct region_device
*rdev
, const void *buffer
, size_t offset
,
79 check_expected_ptr(rdev
);
80 check_expected_ptr(buffer
);
81 check_expected(offset
);
91 static ssize_t
mock_eraseat(const struct region_device
*rdev
, size_t offset
, size_t size
)
93 check_expected_ptr(rdev
);
94 check_expected(offset
);
104 struct region_device_ops mock_rdev_ops
= {
106 .munmap
= mock_unmap
,
107 .readat
= mock_readat
,
108 .writeat
= mock_writeat
,
109 .eraseat
= mock_eraseat
,
112 struct region_device mock_rdev
= REGION_DEV_INIT(&mock_rdev_ops
, 0, ~(size_t)0);
113 void *mmap_result
= (void *)0x12345678;
114 const size_t mock_size
= 256;
117 static void test_rdev_basics(void **state
)
119 assert_int_equal(region_device_offset(&mock_rdev
), 0);
120 assert_int_equal(region_device_sz(&mock_rdev
), ~(size_t)0);
121 assert_int_equal(region_device_last(&mock_rdev
), ~(size_t)0 - 1);
125 * This function sets up defaults for the mock_rdev_ops functions so we don't have to explicitly
126 * mock every parameter every time. cmocka doesn't really work well for this sort of use case
127 * and won't let you override these anymore once they're set (because these are stored as
128 * queues, not stacks, and once you store an "infinite" element the test can never proceed
129 * behind it), so tests will always have to enqueue any custom values they may need for the rest
130 * of the test function before calling this.
132 static void rdev_mock_defaults(void)
134 will_return_maybe(mock_mmap
, mmap_result
);
135 will_return_maybe(mock_unmap
, 0);
136 will_return_maybe(mock_readat
, 0);
137 will_return_maybe(mock_writeat
, 0);
138 will_return_maybe(mock_eraseat
, 0);
140 expect_value_count(mock_mmap
, rdev
, &mock_rdev
, -2);
141 expect_value_count(mock_unmap
, rdev
, &mock_rdev
, -2);
142 expect_value_count(mock_readat
, rdev
, &mock_rdev
, -2);
143 expect_value_count(mock_writeat
, rdev
, &mock_rdev
, -2);
144 expect_value_count(mock_eraseat
, rdev
, &mock_rdev
, -2);
146 expect_value_count(mock_readat
, buffer
, &mock_buffer
, -2);
147 expect_value_count(mock_writeat
, buffer
, &mock_buffer
, -2);
149 expect_value_count(mock_mmap
, offset
, 0, -2);
150 expect_value_count(mock_readat
, offset
, 0, -2);
151 expect_value_count(mock_writeat
, offset
, 0, -2);
152 expect_value_count(mock_eraseat
, offset
, 0, -2);
154 expect_value_count(mock_mmap
, size
, mock_size
, -2);
155 expect_value_count(mock_readat
, size
, mock_size
, -2);
156 expect_value_count(mock_writeat
, size
, mock_size
, -2);
157 expect_value_count(mock_eraseat
, size
, mock_size
, -2);
159 expect_value_count(mock_unmap
, mapping
, mmap_result
, -2);
162 static void test_rdev_success(void **state
)
164 expect_value(mock_mmap
, size
, region_device_sz(&mock_rdev
));
166 rdev_mock_defaults();
168 assert_ptr_equal(rdev_mmap_full(&mock_rdev
), mmap_result
);
170 assert_ptr_equal(rdev_mmap(&mock_rdev
, 0, mock_size
), mmap_result
);
171 assert_int_equal(rdev_munmap(&mock_rdev
, mmap_result
), 0);
172 assert_int_equal(rdev_readat(&mock_rdev
, mock_buffer
, 0, mock_size
), mock_size
);
173 assert_int_equal(rdev_writeat(&mock_rdev
, mock_buffer
, 0, mock_size
), mock_size
);
174 assert_int_equal(rdev_eraseat(&mock_rdev
, 0, mock_size
), mock_size
);
177 static void test_rdev_failure(void **state
)
179 will_return(mock_mmap
, NULL
);
180 will_return(mock_unmap
, -1);
181 will_return(mock_readat
, -1);
182 will_return(mock_writeat
, -1);
183 will_return(mock_eraseat
, -1);
185 rdev_mock_defaults();
187 assert_null(rdev_mmap(&mock_rdev
, 0, mock_size
));
188 assert_int_equal(rdev_munmap(&mock_rdev
, mmap_result
), -1);
189 assert_int_equal(rdev_readat(&mock_rdev
, mock_buffer
, 0, mock_size
), -1);
190 assert_int_equal(rdev_writeat(&mock_rdev
, mock_buffer
, 0, mock_size
), -1);
191 assert_int_equal(rdev_eraseat(&mock_rdev
, 0, mock_size
), -1);
194 static void test_rdev_wrap(void **state
)
196 struct region_device child
;
197 const size_t offs
= VAL(0xf);
198 const size_t wrap_size
= VAL(2);
199 /* Known API limitation -- can't exactly touch address space limit from below. */
200 const size_t fit_size
= VAL(1) - 1;
202 /* For the 'wrap' cases, the underlying rdev_ops aren't even called, so only add
203 expectations for the 'fit' cases. */
204 expect_value(mock_mmap
, offset
, offs
);
205 expect_value(mock_readat
, offset
, offs
);
206 expect_value(mock_writeat
, offset
, offs
);
207 expect_value(mock_eraseat
, offset
, offs
);
209 expect_value(mock_mmap
, size
, fit_size
);
210 expect_value(mock_readat
, size
, fit_size
);
211 expect_value(mock_writeat
, size
, fit_size
);
212 expect_value(mock_eraseat
, size
, fit_size
);
214 rdev_mock_defaults();
216 /* Accesses to regions that wrap around the end of the address space should fail. */
217 assert_null(rdev_mmap(&mock_rdev
, offs
, wrap_size
));
218 assert_int_equal(rdev_readat(&mock_rdev
, mock_buffer
, offs
, wrap_size
), -1);
219 assert_int_equal(rdev_writeat(&mock_rdev
, mock_buffer
, offs
, wrap_size
), -1);
220 assert_int_equal(rdev_eraseat(&mock_rdev
, offs
, wrap_size
), -1);
221 assert_int_equal(rdev_chain(&child
, &mock_rdev
, offs
, wrap_size
), -1);
223 /* Just barely touching the end of the address space (and the rdev) should be fine. */
224 assert_ptr_equal(rdev_mmap(&mock_rdev
, offs
, fit_size
), mmap_result
);
225 assert_int_equal(rdev_readat(&mock_rdev
, mock_buffer
, offs
, fit_size
), fit_size
);
226 assert_int_equal(rdev_writeat(&mock_rdev
, mock_buffer
, offs
, fit_size
), fit_size
);
227 assert_int_equal(rdev_eraseat(&mock_rdev
, offs
, fit_size
), fit_size
);
228 assert_int_equal(rdev_chain(&child
, &mock_rdev
, offs
, fit_size
), 0);
231 static void test_rdev_chain(void **state
)
233 struct region_device child
;
234 const size_t child_offs
= VAL(2);
235 const size_t child_size
= VAL(4);
236 const size_t offs
= VAL(1);
237 const size_t ovrflw_size
= child_size
- offs
+ 1;
239 /* The mock_size test is the only one that will go through to underlying rdev_ops. */
240 expect_value(mock_mmap
, offset
, child_offs
+ offs
);
241 expect_value(mock_readat
, offset
, child_offs
+ offs
);
242 expect_value(mock_writeat
, offset
, child_offs
+ offs
);
243 expect_value(mock_eraseat
, offset
, child_offs
+ offs
);
245 rdev_mock_defaults();
247 /* First a quick test for rdev_chain_full(). */
248 assert_int_equal(rdev_chain_full(&child
, &mock_rdev
), 0);
249 assert_int_equal(region_device_sz(&child
), region_device_sz(&mock_rdev
));
250 assert_int_equal(region_device_offset(&child
), region_device_offset(&mock_rdev
));
251 assert_int_equal(rdev_relative_offset(&mock_rdev
, &child
), 0);
253 /* Remaining tests use rdev chained to [child_offs:child_size) subregion. */
254 assert_int_equal(rdev_chain(&child
, &mock_rdev
, child_offs
, child_size
), 0);
255 assert_int_equal(region_device_sz(&child
), child_size
);
256 assert_int_equal(region_device_offset(&child
), child_offs
);
257 assert_int_equal(region_device_last(&child
), child_offs
+ child_size
- 1);
258 assert_int_equal(rdev_relative_offset(&mock_rdev
, &child
), child_offs
);
259 assert_int_equal(rdev_relative_offset(&child
, &mock_rdev
), -1);
261 /* offs + mock_size < child_size, so will succeed. */
262 assert_ptr_equal(rdev_mmap(&child
, offs
, mock_size
), mmap_result
);
263 assert_int_equal(rdev_munmap(&child
, mmap_result
), 0);
264 assert_int_equal(rdev_readat(&child
, mock_buffer
, offs
, mock_size
), mock_size
);
265 assert_int_equal(rdev_writeat(&child
, mock_buffer
, offs
, mock_size
), mock_size
);
266 assert_int_equal(rdev_eraseat(&child
, offs
, mock_size
), mock_size
);
268 /* offs + ovrflw_size > child_size, so will fail. */
269 assert_null(rdev_mmap(&child
, offs
, ovrflw_size
));
270 assert_int_equal(rdev_readat(&child
, mock_buffer
, offs
, ovrflw_size
), -1);
271 assert_int_equal(rdev_writeat(&child
, mock_buffer
, offs
, ovrflw_size
), -1);
272 assert_int_equal(rdev_eraseat(&child
, offs
, ovrflw_size
), -1);
274 /* Using child_size as offset, the start of the area will already be out of range. */
275 assert_null(rdev_mmap(&child
, child_size
, mock_size
));
276 assert_int_equal(rdev_readat(&child
, mock_buffer
, child_size
, mock_size
), -1);
277 assert_int_equal(rdev_writeat(&child
, mock_buffer
, child_size
, mock_size
), -1);
278 assert_int_equal(rdev_eraseat(&child
, child_size
, mock_size
), -1);
281 static void test_rdev_double_chain(void **state
)
283 struct region_device first
, second
;
284 const size_t first_offs
= VAL(2);
285 const size_t first_size
= VAL(6);
286 const size_t second_offs
= VAL(2);
287 const size_t second_size
= VAL(2);
288 const size_t offs
= VAL(1);
289 const size_t ovrflw_size
= second_size
- offs
+ 1;
291 /* The mock_size test is the only one that will go through to underlying rdev_ops. */
292 expect_value(mock_mmap
, offset
, first_offs
+ second_offs
+ offs
);
293 expect_value(mock_readat
, offset
, first_offs
+ second_offs
+ offs
);
294 expect_value(mock_writeat
, offset
, first_offs
+ second_offs
+ offs
);
295 expect_value(mock_eraseat
, offset
, first_offs
+ second_offs
+ offs
);
297 rdev_mock_defaults();
299 /* First, chain an rdev to root over [first_offs:first_size). */
300 assert_int_equal(rdev_chain(&first
, &mock_rdev
, first_offs
, first_size
), 0);
302 /* Trying to chain a second to first beyond its end should fail. */
303 assert_int_equal(rdev_chain(&second
, &first
, second_offs
, first_size
), -1);
305 /* Chain second to first at [second_offs:second_size). */
306 assert_int_equal(rdev_chain(&second
, &first
, second_offs
, second_size
), 0);
307 assert_int_equal(rdev_relative_offset(&first
, &second
), second_offs
);
308 assert_int_equal(rdev_relative_offset(&mock_rdev
, &second
), first_offs
+ second_offs
);
310 /* offs + mock_size < second_size, so will succeed. */
311 assert_ptr_equal(rdev_mmap(&second
, offs
, mock_size
), mmap_result
);
312 assert_int_equal(rdev_munmap(&second
, mmap_result
), 0);
313 assert_int_equal(rdev_readat(&second
, mock_buffer
, offs
, mock_size
), mock_size
);
314 assert_int_equal(rdev_writeat(&second
, mock_buffer
, offs
, mock_size
), mock_size
);
315 assert_int_equal(rdev_eraseat(&second
, offs
, mock_size
), mock_size
);
317 /* offs + ovrflw_size > second_size, so will fail. */
318 assert_null(rdev_mmap(&second
, offs
, ovrflw_size
));
319 assert_int_equal(rdev_readat(&second
, mock_buffer
, offs
, ovrflw_size
), -1);
320 assert_int_equal(rdev_writeat(&second
, mock_buffer
, offs
, ovrflw_size
), -1);
321 assert_int_equal(rdev_eraseat(&second
, offs
, ovrflw_size
), -1);
323 /* offs + second_size + offs way out of range. */
324 assert_null(rdev_mmap(&second
, second_size
+ offs
, mock_size
));
325 assert_int_equal(rdev_readat(&second
, mock_buffer
, second_size
+ offs
, mock_size
), -1);
326 assert_int_equal(rdev_writeat(&second
, mock_buffer
, second_size
+ offs
, mock_size
), -1);
327 assert_int_equal(rdev_eraseat(&second
, second_size
+ offs
, mock_size
), -1);
330 static void test_mem_rdev(void **state
)
332 const size_t size
= 256;
336 struct region_device mem
;
337 rdev_chain_mem_rw(&mem
, backing
, size
);
339 /* Test writing to and reading from full mapping. */
340 memset(backing
, 0xa5, size
);
341 u8
*mapping
= rdev_mmap_full(&mem
);
342 assert_non_null(mapping
);
343 for (i
= 0; i
< size
; i
++)
344 assert_int_equal(mapping
[i
], 0xa5);
345 memset(mapping
, 0x5a, size
);
346 for (i
= 0; i
< size
; i
++)
347 assert_int_equal(backing
[i
], 0x5a);
348 assert_int_equal(rdev_munmap(&mem
, mapping
), 0);
350 /* Test read/write/erase of single bytes. */
351 for (i
= 0; i
< size
; i
++) {
354 assert_int_equal(rdev_writeat(&mem
, &scratch
, i
, 1), 1);
355 assert_int_equal(backing
[i
], val
);
356 assert_int_equal(scratch
[0], val
);
359 assert_int_equal(rdev_readat(&mem
, &scratch
, i
, 1), 1);
360 assert_int_equal(scratch
[0], val
);
361 assert_int_equal(backing
[i
], val
);
362 assert_int_equal(rdev_eraseat(&mem
, i
, 1), 1);
363 assert_int_equal(backing
[i
], 0);
366 /* Test read/write/erase of larger chunk. */
369 memset(backing
, 0, size
);
370 memset(scratch
, 0, size
);
371 memset(scratch
+ offs
, 0x39, chunk
);
372 assert_int_equal(rdev_writeat(&mem
, scratch
+ offs
, offs
, chunk
), chunk
);
373 assert_memory_equal(backing
, scratch
, size
);
374 memset(backing
, 0, size
);
375 assert_int_equal(rdev_readat(&mem
, scratch
+ offs
, offs
, chunk
), chunk
);
376 assert_memory_equal(backing
, scratch
, size
);
377 memset(scratch
+ offs
+ 1, 0, chunk
- 1);
378 assert_int_equal(rdev_eraseat(&mem
, offs
+ 1, chunk
- 1), chunk
- 1);
379 assert_memory_equal(backing
, scratch
, size
);
381 /* Test mapping of larger chunk. */
382 memset(backing
, 0, size
);
383 mapping
= rdev_mmap(&mem
, offs
, chunk
);
384 assert_non_null(mapping
);
385 memset(scratch
, 0x93, size
);
386 memcpy(mapping
, scratch
, chunk
);
387 memset(scratch
, 0, size
);
388 memset(scratch
+ offs
, 0x93, chunk
);
389 assert_memory_equal(backing
, scratch
, size
);
390 assert_int_equal(rdev_munmap(&mem
, mapping
), 0);
391 assert_memory_equal(backing
, scratch
, size
);
396 const struct CMUnitTest tests
[] = {
397 cmocka_unit_test(test_region
),
398 cmocka_unit_test(test_rdev_basics
),
399 cmocka_unit_test(test_rdev_success
),
400 cmocka_unit_test(test_rdev_failure
),
401 cmocka_unit_test(test_rdev_wrap
),
402 cmocka_unit_test(test_rdev_chain
),
403 cmocka_unit_test(test_rdev_double_chain
),
404 cmocka_unit_test(test_mem_rdev
),
407 return cb_run_group_tests(tests
, NULL
, NULL
);