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