xref: /rk3399_rockchip-uboot/common/spl/spl_ab.c (revision d50ae2019e8c020d508dcfe7bf68a933dbd70e9e)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
4  */
5 
6 #include <common.h>
7 #include <blk.h>
8 #include <malloc.h>
9 #include <spl_ab.h>
10 #include <fdt_support.h>
11 
12 int safe_memcmp(const void *s1, const void *s2, size_t n)
13 {
14 	const unsigned char *us1 = s1;
15 	const unsigned char *us2 = s2;
16 	int result = 0;
17 
18 	if (0 == n)
19 		return 0;
20 
21 	/*
22 	 * Code snippet without data-dependent branch due to Nate Lawson
23 	 * (nate@root.org) of Root Labs.
24 	 */
25 	while (n--)
26 		result |= *us1++ ^ *us2++;
27 
28 	return result != 0;
29 }
30 
31 static uint32_t htobe32(uint32_t in)
32 {
33 	union {
34 		uint32_t word;
35 		uint8_t bytes[4];
36 	} ret;
37 
38 	ret.bytes[0] = (in >> 24) & 0xff;
39 	ret.bytes[1] = (in >> 16) & 0xff;
40 	ret.bytes[2] = (in >> 8) & 0xff;
41 	ret.bytes[3] = in & 0xff;
42 
43 	return ret.word;
44 }
45 
46 static uint32_t be32toh(uint32_t in)
47 {
48 	uint8_t *d = (uint8_t *)&in;
49 	uint32_t ret;
50 
51 	ret = ((uint32_t)d[0]) << 24;
52 	ret |= ((uint32_t)d[1]) << 16;
53 	ret |= ((uint32_t)d[2]) << 8;
54 	ret |= ((uint32_t)d[3]);
55 
56 	return ret;
57 }
58 
59 static bool spl_ab_data_verify_and_byteswap(const AvbABData *src,
60 					    AvbABData *dest)
61 {
62 	/* Ensure magic is correct. */
63 	if (safe_memcmp(src->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) {
64 		printf("Magic is incorrect.\n");
65 		return false;
66 	}
67 
68 	memcpy(dest, src, sizeof(AvbABData));
69 	dest->crc32 = be32toh(dest->crc32);
70 
71 	/* Ensure we don't attempt to access any fields if the major version
72 	 * is not supported.
73 	 */
74 	if (dest->version_major > AVB_AB_MAJOR_VERSION) {
75 		printf("No support for given major version.\n");
76 		return false;
77 	}
78 
79 	/* Bail if CRC32 doesn't match. */
80 	if (dest->crc32 !=
81 		crc32(0, (const uint8_t *)dest,
82 		      sizeof(AvbABData) - sizeof(uint32_t))) {
83 		printf("CRC32 does not match.\n");
84 		return false;
85 	}
86 
87 	return true;
88 }
89 
90 static void spl_ab_data_update_crc_and_byteswap(const AvbABData *src,
91 						AvbABData *dest)
92 {
93 	memcpy(dest, src, sizeof(AvbABData));
94 	dest->crc32 = htobe32(crc32(0, (const uint8_t *)dest,
95 			    sizeof(AvbABData) - sizeof(uint32_t)));
96 }
97 
98 static void spl_ab_data_init(AvbABData *data)
99 {
100 	memset(data, '\0', sizeof(AvbABData));
101 	memcpy(data->magic, AVB_AB_MAGIC, AVB_AB_MAGIC_LEN);
102 	data->version_major = AVB_AB_MAJOR_VERSION;
103 	data->version_minor = AVB_AB_MINOR_VERSION;
104 	data->last_boot = 0;
105 	data->slots[0].priority = AVB_AB_MAX_PRIORITY;
106 	data->slots[0].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
107 	data->slots[0].successful_boot = 0;
108 	data->slots[1].priority = AVB_AB_MAX_PRIORITY - 1;
109 	data->slots[1].tries_remaining = AVB_AB_MAX_TRIES_REMAINING;
110 	data->slots[1].successful_boot = 0;
111 }
112 
113 static int spl_read_ab_metadata(struct blk_desc *dev_desc, AvbABData *ab_data,
114 				char *partition)
115 {
116 	disk_partition_t part_info;
117 	char temp[512];
118 	int ret;
119 
120 	if (!dev_desc || !partition || !ab_data)
121 		return -EFAULT;
122 
123 	if (part_get_info_by_name(dev_desc, partition, &part_info) < 0)
124 		return -ENOENT;
125 
126 	ret = blk_dread(dev_desc, part_info.start + AB_METADATA_OFFSET, 1,
127 			temp);
128 	if (ret != 1)
129 		return -ENODEV;
130 
131 	if (sizeof(AvbABData) > 512)
132 		return -ENOMEM;
133 
134 	memcpy(ab_data, temp, sizeof(AvbABData));
135 
136 	return 0;
137 }
138 
139 static int spl_write_ab_metadata(struct blk_desc *dev_desc, AvbABData *ab_data,
140 				 char *partition)
141 {
142 	disk_partition_t part_info;
143 	char temp[512];
144 	int ret;
145 
146 	if (!dev_desc || !partition || !ab_data)
147 		return -EFAULT;
148 
149 	if (sizeof(AvbABData) > 512)
150 		return -ENOMEM;
151 
152 	memcpy(temp, ab_data, sizeof(AvbABData));
153 	if (part_get_info_by_name(dev_desc, partition, &part_info) < 0)
154 		return -ENOENT;
155 
156 	ret = blk_dwrite(dev_desc, part_info.start + AB_METADATA_OFFSET, 1,
157 			 temp);
158 	if (ret != 1)
159 		return -ENODEV;
160 
161 	return 0;
162 }
163 
164 static int spl_ab_data_write(struct blk_desc *dev_desc, AvbABData *ab_data,
165 			     char *partition)
166 {
167 	AvbABData serialized;
168 
169 	spl_ab_data_update_crc_and_byteswap(ab_data, &serialized);
170 
171 	return spl_write_ab_metadata(dev_desc, &serialized, partition);
172 }
173 
174 static int spl_ab_data_read(struct blk_desc *dev_desc, AvbABData *ab_data,
175 			    char *partition)
176 {
177 	int ret;
178 	AvbABData serialized;
179 
180 	ret = spl_read_ab_metadata(dev_desc, &serialized, partition);
181 	if (ret)
182 		return ret;
183 
184 	if (!spl_ab_data_verify_and_byteswap(&serialized, ab_data)) {
185 		printf("Error validating A/B metadata from disk. "
186 		       "Resetting and writing new A/B metadata to disk.\n");
187 		spl_ab_data_init(ab_data);
188 		spl_ab_data_write(dev_desc, ab_data, partition);
189 	}
190 
191 	return 0;
192 }
193 
194 static bool spl_slot_is_bootable(AvbABSlotData *slot)
195 {
196 	return slot->priority > 0 &&
197 		(slot->successful_boot || (slot->tries_remaining > 0));
198 }
199 
200 static int spl_get_lastboot(AvbABData *ab_data)
201 {
202 	return ab_data->last_boot;
203 }
204 
205 int spl_get_current_slot(struct blk_desc *dev_desc, char *partition, char *slot)
206 {
207 	static int last_slot_index = -1;
208 	size_t slot_index_to_boot;
209 	AvbABData ab_data;
210 	int ret;
211 
212 	/*
213 	 * 1. Call spl_ab_decrease_tries() before load kernel to be compatible with
214 	 * the case when storage is eMMC, because preload image will occupy eMMC.
215 	 *
216 	 * 2. (For solving Boundary problem) Need to record slot_suffix before ab_decrease,
217 	 * otherwise when boot kernel, it will use the result after decrease.
218 	 *
219 	 * 3. For example, current slot_suffix is _a, tries-remaining is 1. Without
220 	 * recording slot_suffix before decrease, after decrease, slot_suffix is _b,
221 	 * tries-remaining is 7. SPL will parse boot_b instead of boot_a that expected.
222 	 */
223 #ifdef CONFIG_SPL_KERNEL_BOOT
224 	if (last_slot_index == 0) {
225 		memcpy(slot, "_a", 2);
226 		return 0;
227 	} else if (last_slot_index == 1) {
228 		memcpy(slot, "_b", 2);
229 		return 0;
230 	}
231 #endif
232 
233 	ret = spl_ab_data_read(dev_desc, &ab_data, partition);
234 	if (ret)
235 		return ret;
236 
237 	if (spl_slot_is_bootable(&ab_data.slots[0]) &&
238 	    spl_slot_is_bootable(&ab_data.slots[1])) {
239 		if (ab_data.slots[1].priority > ab_data.slots[0].priority)
240 			slot_index_to_boot = 1;
241 		else
242 			slot_index_to_boot = 0;
243 	} else if (spl_slot_is_bootable(&ab_data.slots[0])) {
244 		slot_index_to_boot = 0;
245 	} else if (spl_slot_is_bootable(&ab_data.slots[1])) {
246 		slot_index_to_boot = 1;
247 	} else {
248 		printf("No bootable slots found, use lastboot.\n");
249 		if (spl_get_lastboot(&ab_data) == 0) {
250 			memcpy(slot, "_a", 2);
251 			goto out;
252 		} else if (spl_get_lastboot(&ab_data) == 1) {
253 			memcpy(slot, "_b", 2);
254 			goto out;
255 		} else {
256 			return -ENODEV;
257 		}
258 	}
259 
260 	if (slot_index_to_boot == 0)
261 		memcpy(slot, "_a", 2);
262 	else if (slot_index_to_boot == 1)
263 		memcpy(slot, "_b", 2);
264 
265 	if (last_slot_index != slot_index_to_boot) {
266 		last_slot_index = slot_index_to_boot;
267 		printf("SPL: A/B-slot: %s, successful: %d, tries-remain: %d\n",
268 		       slot,
269 		       ab_data.slots[slot_index_to_boot].successful_boot,
270 		       ab_data.slots[slot_index_to_boot].tries_remaining);
271 	}
272 
273 out:
274 	return 0;
275 }
276 
277 int spl_ab_append_part_slot(struct blk_desc *dev_desc,
278 			    const char *part_name,
279 			    char *new_name)
280 {
281 	char slot_suffix[3] = {0};
282 
283 	if (!strcmp(part_name, "misc")) {
284 		strcat(new_name, part_name);
285 		return 0;
286 	}
287 
288 	if (spl_get_current_slot(dev_desc, "misc", slot_suffix)) {
289 		printf("No misc partition\n");
290 		strcat(new_name, part_name);
291 		return 0;
292 	}
293 
294 	strcpy(new_name, part_name);
295 	strcat(new_name, slot_suffix);
296 
297 	return 0;
298 }
299 
300 static int spl_save_metadata_if_changed(struct blk_desc *dev_desc,
301 					AvbABData *ab_data,
302 					AvbABData *ab_data_orig)
303 {
304 	if (safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)))
305 		return spl_ab_data_write(dev_desc, ab_data, "misc");
306 
307 	return 0;
308 }
309 
310 static void spl_slot_set_unbootable(AvbABSlotData* slot)
311 {
312 	slot->priority = 0;
313 	slot->tries_remaining = 0;
314 	slot->successful_boot = 0;
315 }
316 
317 /* Ensure all unbootable and/or illegal states are marked as the
318  * canonical 'unbootable' state, e.g. priority=0, tries_remaining=0,
319  * and successful_boot=0.
320  */
321 static void spl_slot_normalize(AvbABSlotData* slot)
322 {
323 	if (slot->priority > 0) {
324 		if (slot->tries_remaining == 0 && !slot->successful_boot) {
325 			/* We've exhausted all tries -> unbootable. */
326 			spl_slot_set_unbootable(slot);
327 		}
328 		if (slot->tries_remaining > 0 && slot->successful_boot) {
329 			/* Illegal state - avb_ab_mark_slot_successful() and so on
330 			 * will clear tries_remaining when setting successful_boot.
331 			 */
332 			spl_slot_set_unbootable(slot);
333 		}
334 	} else {
335 		spl_slot_set_unbootable(slot);
336 	}
337 }
338 
339 /* If verify fail in a/b system, then decrease 1. */
340 int spl_ab_decrease_tries(struct blk_desc *dev_desc)
341 {
342 	AvbABData ab_data, ab_data_orig;
343 	size_t slot_index = 0;
344 	char slot_suffix[3] = {0};
345 	int ret = -1;
346 
347 	ret = spl_get_current_slot(dev_desc, "misc", slot_suffix);
348 	if (ret)
349 		goto out;
350 
351 	if (!strncmp(slot_suffix, "_a", 2))
352 		slot_index = 0;
353 	else if (!strncmp(slot_suffix, "_b", 2))
354 		slot_index = 1;
355 	else
356 		slot_index = 0;
357 
358 	ret = spl_ab_data_read(dev_desc, &ab_data, "misc");
359 	if (ret)
360 		goto out;
361 
362 	memcpy(&ab_data_orig, &ab_data, sizeof(AvbABData));
363 
364 	/* Ensure data is normalized, e.g. illegal states will be marked as
365 	 * unbootable and all unbootable states are represented with
366 	 * (priority=0, tries_remaining=0, successful_boot=0).
367 	 */
368 	spl_slot_normalize(&ab_data.slots[0]);
369 	spl_slot_normalize(&ab_data.slots[1]);
370 
371 	/* ... and decrement tries remaining, if applicable. */
372 	if (!ab_data.slots[slot_index].successful_boot &&
373 	    ab_data.slots[slot_index].tries_remaining > 0)
374 		ab_data.slots[slot_index].tries_remaining -= 1;
375 
376 	ret = spl_save_metadata_if_changed(dev_desc, &ab_data, &ab_data_orig);
377 
378 out:
379 	return ret;
380 }
381 
382 /*
383  * If boot A/B system fail, tries-remaining decrease 1
384  * and do reset automatically if still bootable.
385  */
386 int spl_ab_decrease_reset(struct blk_desc *dev_desc)
387 {
388 	AvbABData ab_data;
389 	int ret;
390 
391 	ret = spl_ab_data_read(dev_desc, &ab_data, "misc");
392 	if (ret)
393 		return ret;
394 
395 	/* If current device cannot boot, return and try other devices. */
396 	if (!spl_slot_is_bootable(&ab_data.slots[0]) &&
397 	    !spl_slot_is_bootable(&ab_data.slots[1])) {
398 		printf("A/B: no bootable slot\n");
399 		return -ENODEV;
400 	}
401 
402 	/* If current device still can boot, decrease and do reset. */
403 	ret = spl_ab_decrease_tries(dev_desc);
404 	if (ret)
405 		return ret;
406 
407 	printf("A/B: slot boot fail, do reset\n");
408 	do_reset(NULL, 0, 0, NULL);
409 
410 	/*
411 	 * Only do_reset() fail will arrive here, return a
412 	 * negative number, then enter maskrom in the caller.
413 	 */
414 	return -EINVAL;
415 }
416 
417 int spl_ab_bootargs_append_slot(void *fdt, char *slot)
418 {
419 	char *str;
420 	int len, ret = 0;
421 
422 	if (!slot)
423 		return 0;
424 
425 	len = strlen(ANDROID_ARG_SLOT_SUFFIX) + strlen(slot) + 1;
426 	str = malloc(len);
427 	if (!str)
428 		return -ENOMEM;
429 
430 	snprintf(str, len, "%s%s", ANDROID_ARG_SLOT_SUFFIX, slot);
431 	ret = fdt_bootargs_append(fdt, str);
432 	if (ret)
433 		printf("Append slot info to bootargs fail");
434 
435 	free(str);
436 
437 	return ret;
438 }
439