xref: /aosp_15_r20/external/coreboot/tests/lib/region_file-test.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include "../lib/region_file.c"
4 
5 #include <tests/test.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <commonlib/region.h>
9 #include <tests/lib/region_file_data.h>
10 
clear_region_file(struct region_device * rdev)11 static void clear_region_file(struct region_device *rdev)
12 {
13 	memset(rdev_mmap_full(rdev), 0xff, REGION_FILE_BUFFER_SIZE);
14 }
15 
setup_region_file_test_group(void ** state)16 static int setup_region_file_test_group(void **state)
17 {
18 	void *mem_buffer = malloc(REGION_FILE_BUFFER_SIZE);
19 	struct region_device *dev = malloc(sizeof(struct region_device));
20 
21 	if (mem_buffer == NULL || dev == NULL) {
22 		free(mem_buffer);
23 		free(dev);
24 		return -1;
25 	}
26 
27 	rdev_chain_mem_rw(dev, mem_buffer, REGION_FILE_BUFFER_SIZE);
28 	*state = dev;
29 
30 	clear_region_file(dev);
31 
32 	return 0;
33 }
34 
teardown_region_file_test_group(void ** state)35 static int teardown_region_file_test_group(void **state)
36 {
37 	struct region_device *dev = *state;
38 	void *mem_buffer = rdev_mmap_full(dev);
39 
40 	free(mem_buffer);
41 	free(dev);
42 
43 	return 0;
44 }
45 
46 /* This function clears buffer associated with used region_device, so tests will be in clear
47    state at the beginning and leave no trace after successful execution. The cost of memsetting
48    everything twice is known, but acceptable as it grants safety and makes tests independent. */
setup_teardown_region_file_test(void ** state)49 static int setup_teardown_region_file_test(void **state)
50 {
51 	struct region_device *dev = *state;
52 
53 	clear_region_file(dev);
54 
55 	return 0;
56 }
57 
test_region_file_init_empty(void ** state)58 static void test_region_file_init_empty(void **state)
59 {
60 	struct region_device *rdev = *state;
61 	struct region_file regf;
62 
63 	/* Test general approach using valid mem_region_device with buffer filled with 0xff.
64 	   Parameters cannot be NULL. */
65 	assert_int_equal(0, region_file_init(&regf, rdev));
66 	assert_int_equal(RF_EMPTY, regf.slot);
67 }
68 
test_region_file_init_invalid_metadata(void ** state)69 static void test_region_file_init_invalid_metadata(void **state)
70 {
71 	struct region_device *rdev = *state;
72 	uint16_t *mem_buffer16 = (uint16_t *)rdev_mmap_full(rdev);
73 	struct region_file regf;
74 
75 	/* Set number of metadata blocks to 0 */
76 	mem_buffer16[0] = 0;
77 	assert_int_equal(0, region_file_init(&regf, rdev));
78 	assert_int_equal(RF_NEED_TO_EMPTY, regf.slot);
79 }
80 
test_region_file_init_valid_no_data(void ** state)81 static void test_region_file_init_valid_no_data(void **state)
82 {
83 	struct region_device *rdev = *state;
84 	uint16_t *mem_buffer16 = (uint16_t *)rdev_mmap_full(rdev);
85 	struct region_file regf;
86 
87 	/* Manually allocate 4 metadata blocks and no data. */
88 	mem_buffer16[0] = 4;
89 	assert_int_equal(0, region_file_init(&regf, rdev));
90 	assert_int_equal(0, regf.slot);
91 }
92 
test_region_file_init_invalid_data_offset(void ** state)93 static void test_region_file_init_invalid_data_offset(void **state)
94 {
95 	struct region_device *rdev = *state;
96 	uint16_t *mem_buffer16 = (uint16_t *)rdev_mmap_full(rdev);
97 	struct region_file regf;
98 
99 	/* Manually allocate 4 metadata blocks and no data. */
100 	mem_buffer16[0] = 4;
101 	mem_buffer16[1] = 4;
102 	assert_int_equal(0, region_file_init(&regf, rdev));
103 	assert_int_equal(RF_NEED_TO_EMPTY, regf.slot);
104 
105 	/* Set data size to be larger than region */
106 	mem_buffer16[0] = 4;
107 	mem_buffer16[1] = 4 + 4096;
108 	assert_int_equal(0, region_file_init(&regf, rdev));
109 	assert_int_equal(RF_NEED_TO_EMPTY, regf.slot);
110 }
111 
test_region_file_init_correct_data_offset(void ** state)112 static void test_region_file_init_correct_data_offset(void **state)
113 {
114 	struct region_device *rdev = *state;
115 	uint16_t *mem_buffer16 = (uint16_t *)rdev_mmap_full(rdev);
116 	struct region_file regf;
117 
118 	/* Set data size to 8 blocks which is correct value. */
119 	mem_buffer16[0] = 4;
120 	mem_buffer16[1] = 4 + 8;
121 	assert_int_equal(0, region_file_init(&regf, rdev));
122 	assert_int_equal(1, regf.slot);
123 }
124 
test_region_file_init_real_data(void ** state)125 static void test_region_file_init_real_data(void **state)
126 {
127 	struct region_device rdev;
128 	struct region_file regf;
129 
130 	rdev_chain_mem_rw(&rdev, region_file_data_buffer1, REGION_FILE_BUFFER_SIZE);
131 
132 	/* Check on real example with one update */
133 	assert_int_equal(0, region_file_init(&regf, &rdev));
134 	/* There is one update available */
135 	assert_int_equal(1, regf.slot);
136 
137 
138 	/* Check on real example with multiple updates */
139 	rdev_chain_mem_rw(&rdev, region_file_data_buffer2, REGION_FILE_BUFFER_SIZE);
140 	assert_int_equal(0, region_file_init(&regf, &rdev));
141 	/* There are three update available */
142 	assert_int_equal(3, regf.slot);
143 }
144 
test_region_file_init_invalid_region_device(void ** state)145 static void test_region_file_init_invalid_region_device(void **state)
146 {
147 	struct region_device bad_dev;
148 	struct region_file regf;
149 
150 	rdev_chain_mem_rw(&bad_dev, NULL, 0);
151 
152 	/* Expect fail when passing invalid region_device. */
153 	assert_int_equal(-1, region_file_init(&regf, &bad_dev));
154 }
155 
test_region_file_data(void ** state)156 static void test_region_file_data(void **state)
157 {
158 	/* region_device with empty data buffer */
159 	struct region_device *mrdev = *state;
160 	/* region_device with prepared data buffer */
161 	struct region_device rdev;
162 	rdev_chain_mem_rw(&rdev, region_file_data_buffer1, REGION_FILE_BUFFER_SIZE);
163 
164 	struct region_file regf;
165 	struct region_device read_rdev;
166 	int ret;
167 
168 	/* Check if region_file_data() fails to return region_device for empty region_file */
169 	ret = region_file_init(&regf, mrdev);
170 	assert_int_equal(0, ret);
171 	ret = region_file_data(&regf, &read_rdev);
172 	assert_int_equal(-1, ret);
173 
174 	/* Check if region_file_data() correctly returns region_device for hardcoded
175 	   region_file data with update of 256 bytes */
176 	ret = region_file_init(&regf, &rdev);
177 	assert_int_equal(0, ret);
178 	ret = region_file_data(&regf, &read_rdev);
179 	assert_int_equal(0, ret);
180 	assert_int_equal(region_device_sz(&read_rdev),
181 			 ALIGN_UP(region_file_data_buffer1_update_sz, 16));
182 }
183 
test_region_file_update_data(void ** state)184 static void test_region_file_update_data(void **state)
185 {
186 	struct region_device *rdev = *state;
187 	struct region_file regf;
188 	struct region_device read_rdev;
189 	const size_t dummy_data_size = 256;
190 	uint8_t dummy_data[dummy_data_size];
191 	uint8_t output_buffer[dummy_data_size];
192 	int ret;
193 
194 	for (int i = 0; i < dummy_data_size; ++i)
195 		dummy_data[i] = 'A' + i % ('Z' - 'A');
196 
197 	ret = region_file_init(&regf, rdev);
198 	assert_int_equal(0, ret);
199 
200 	/* Write half of buffer, read it and check, if it is the same.
201 	   region_file_update_data() should be able to deal with empty region_file. */
202 	ret = region_file_update_data(&regf, dummy_data, dummy_data_size / 2);
203 	assert_int_equal(0, ret);
204 	region_file_data(&regf, &read_rdev);
205 	assert_int_equal(ALIGN_UP(dummy_data_size / 2, 16), region_device_sz(&read_rdev));
206 	rdev_readat(&read_rdev, output_buffer, 0, dummy_data_size / 2);
207 	assert_memory_equal(dummy_data, output_buffer, dummy_data_size / 2);
208 
209 	/* Update data to a bigger size */
210 	ret = region_file_update_data(&regf, dummy_data, dummy_data_size);
211 	assert_int_equal(0, ret);
212 	region_file_data(&regf, &read_rdev);
213 	assert_int_equal(ALIGN_UP(dummy_data_size, 16), region_device_sz(&read_rdev));
214 	rdev_readat(&read_rdev, output_buffer, 0, dummy_data_size);
215 	assert_memory_equal(dummy_data, output_buffer, dummy_data_size);
216 
217 	/* Update data to smaller size and check if it was properly stored */
218 	ret = region_file_update_data(&regf, dummy_data, dummy_data_size / 2 + 3);
219 	assert_int_equal(0, ret);
220 	region_file_data(&regf, &read_rdev);
221 	assert_int_equal(ALIGN_UP(dummy_data_size / 2 + 3, 16), region_device_sz(&read_rdev));
222 	rdev_readat(&read_rdev, output_buffer, 0, dummy_data_size / 2 + 3);
223 	assert_memory_equal(dummy_data, output_buffer, dummy_data_size / 2 + 3);
224 }
225 
test_region_file_update_data_arr(void ** state)226 static void test_region_file_update_data_arr(void **state)
227 {
228 	struct region_device *rdev = *state;
229 	struct region_file regf;
230 	struct region_device read_rdev;
231 	const size_t dummy_data_size = 256;
232 	uint8_t dummy_data[dummy_data_size];
233 	uint8_t output_buffer[dummy_data_size * 4];
234 	struct update_region_file_entry update_entries[3];
235 	const size_t data1_size = dummy_data_size;
236 	const size_t data2_size = dummy_data_size / 2;
237 	const size_t data3_size = dummy_data_size / 4 + 3;
238 	const size_t data1_offset = 0;
239 	const size_t data2_offset = dummy_data_size / 4 + 2;
240 	const size_t data3_offset = dummy_data_size / 8 + 5;
241 	int ret;
242 
243 	for (int i = 0; i < dummy_data_size; ++i)
244 		dummy_data[i] = 'A' + i % ('Z' - 'A');
245 
246 	update_entries[0] = (struct update_region_file_entry){
247 		.size = data1_size, .data = &dummy_data[data1_offset]};
248 	update_entries[1] = (struct update_region_file_entry){
249 		.size = data2_size, .data = &dummy_data[data2_offset]};
250 	update_entries[2] = (struct update_region_file_entry){
251 		.size = data3_size, .data = &dummy_data[data3_offset]};
252 
253 	ret = region_file_init(&regf, rdev);
254 	assert_int_equal(0, ret);
255 
256 	/* Write two update blocks as first data state. region_file_update_data_arr() should
257 	   be able to deal with empty region_file. */
258 	ret = region_file_update_data_arr(&regf, update_entries, 2);
259 	assert_int_equal(0, ret);
260 	region_file_data(&regf, &read_rdev);
261 	assert_int_equal(ALIGN_UP(data1_size + data2_size, 16), region_device_sz(&read_rdev));
262 	ret = rdev_readat(&read_rdev, output_buffer, 0, data1_size + data2_size);
263 	assert_int_equal(data1_size + data2_size, ret);
264 	assert_memory_equal(&dummy_data[data1_offset], output_buffer, data1_size);
265 	assert_memory_equal(&dummy_data[data1_offset + data2_offset],
266 			    &output_buffer[data1_size], data2_size);
267 
268 	/* Check if new block of data is added correctly */
269 	ret = region_file_update_data_arr(&regf, update_entries, 3);
270 	assert_int_equal(0, ret);
271 	region_file_data(&regf, &read_rdev);
272 	assert_int_equal(ALIGN_UP(data1_size + data2_size + data3_size, 16),
273 			 region_device_sz(&read_rdev));
274 	ret = rdev_readat(&read_rdev, output_buffer, 0, data1_size + data2_size + data3_size);
275 	assert_int_equal(data1_size + data2_size + data3_size, ret);
276 	assert_memory_equal(&dummy_data[data1_offset], output_buffer, data1_size);
277 	assert_memory_equal(&dummy_data[data2_offset], &output_buffer[data1_size], data2_size);
278 	assert_memory_equal(&dummy_data[data3_offset], &output_buffer[data1_size + data2_size],
279 			    data3_size);
280 
281 	/* Check if data is correctly shrunk down to smaller size and different content */
282 	ret = region_file_update_data_arr(&regf, &update_entries[1], 2);
283 	assert_int_equal(0, ret);
284 	region_file_data(&regf, &read_rdev);
285 	assert_int_equal(ALIGN_UP(data2_size + data3_size, 16), region_device_sz(&read_rdev));
286 	ret = rdev_readat(&read_rdev, output_buffer, 0, data2_size + data3_size);
287 	assert_int_equal(data2_size + data3_size, ret);
288 	assert_memory_equal(&dummy_data[data2_offset], &output_buffer[0], data2_size);
289 	assert_memory_equal(&dummy_data[data3_offset], &output_buffer[data2_size], data3_size);
290 }
291 
main(void)292 int main(void)
293 {
294 	const struct CMUnitTest tests[] = {
295 		cmocka_unit_test_setup_teardown(test_region_file_init_empty,
296 						setup_teardown_region_file_test,
297 						setup_teardown_region_file_test),
298 		cmocka_unit_test_setup_teardown(test_region_file_init_invalid_metadata,
299 						setup_teardown_region_file_test,
300 						setup_teardown_region_file_test),
301 		cmocka_unit_test_setup_teardown(test_region_file_init_valid_no_data,
302 						setup_teardown_region_file_test,
303 						setup_teardown_region_file_test),
304 		cmocka_unit_test_setup_teardown(test_region_file_init_invalid_data_offset,
305 						setup_teardown_region_file_test,
306 						setup_teardown_region_file_test),
307 		cmocka_unit_test_setup_teardown(test_region_file_init_correct_data_offset,
308 						setup_teardown_region_file_test,
309 						setup_teardown_region_file_test),
310 		cmocka_unit_test_setup_teardown(test_region_file_init_real_data,
311 						setup_teardown_region_file_test,
312 						setup_teardown_region_file_test),
313 		cmocka_unit_test_setup_teardown(test_region_file_init_invalid_region_device,
314 						setup_teardown_region_file_test,
315 						setup_teardown_region_file_test),
316 		cmocka_unit_test_setup_teardown(test_region_file_data,
317 						setup_teardown_region_file_test,
318 						setup_teardown_region_file_test),
319 		cmocka_unit_test_setup_teardown(test_region_file_update_data,
320 						setup_teardown_region_file_test,
321 						setup_teardown_region_file_test),
322 		cmocka_unit_test_setup_teardown(test_region_file_update_data_arr,
323 						setup_teardown_region_file_test,
324 						setup_teardown_region_file_test),
325 	};
326 
327 	return cb_run_group_tests(tests, setup_region_file_test_group,
328 				  teardown_region_file_test_group);
329 }
330