xref: /OK3568_Linux_fs/u-boot/common/spl/spl_ab.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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 
safe_memcmp(const void * s1,const void * s2,size_t n)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 
htobe32(uint32_t in)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 
be32toh(uint32_t in)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 
spl_ab_data_verify_and_byteswap(const AvbABData * src,AvbABData * dest)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 
spl_ab_data_update_crc_and_byteswap(const AvbABData * src,AvbABData * dest)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 
spl_ab_data_init(AvbABData * data)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 
spl_read_ab_metadata(struct blk_desc * dev_desc,AvbABData * ab_data,char * partition)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 
spl_write_ab_metadata(struct blk_desc * dev_desc,AvbABData * ab_data,char * partition)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 
spl_ab_data_write(struct blk_desc * dev_desc,AvbABData * ab_data,char * partition)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 
spl_ab_data_read(struct blk_desc * dev_desc,AvbABData * ab_data,char * partition)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 
spl_slot_is_bootable(AvbABSlotData * slot)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 
spl_get_lastboot(AvbABData * ab_data)198 static int spl_get_lastboot(AvbABData *ab_data)
199 {
200 	return ab_data->last_boot;
201 }
202 
spl_get_current_slot(struct blk_desc * dev_desc,char * partition,char * slot)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 	ret = spl_ab_data_read(dev_desc, &ab_data, partition);
211 	if (ret)
212 		return ret;
213 
214 	if (spl_slot_is_bootable(&ab_data.slots[0]) &&
215 	    spl_slot_is_bootable(&ab_data.slots[1])) {
216 		if (ab_data.slots[1].priority > ab_data.slots[0].priority)
217 			slot_index_to_boot = 1;
218 		else
219 			slot_index_to_boot = 0;
220 	} else if (spl_slot_is_bootable(&ab_data.slots[0])) {
221 		slot_index_to_boot = 0;
222 	} else if (spl_slot_is_bootable(&ab_data.slots[1])) {
223 		slot_index_to_boot = 1;
224 	} else {
225 		printf("No bootable slots found, use lastboot.\n");
226 		if (spl_get_lastboot(&ab_data) == 0) {
227 			memcpy(slot, "_a", 2);
228 			goto out;
229 		} else if (spl_get_lastboot(&ab_data) == 1) {
230 			memcpy(slot, "_b", 2);
231 			goto out;
232 		} else {
233 			return -ENODEV;
234 		}
235 	}
236 
237 	if (slot_index_to_boot == 0)
238 		memcpy(slot, "_a", 2);
239 	else if (slot_index_to_boot == 1)
240 		memcpy(slot, "_b", 2);
241 
242 	if (last_slot_index != slot_index_to_boot) {
243 		last_slot_index = slot_index_to_boot;
244 		printf("SPL: A/B-slot: %s, successful: %d, tries-remain: %d\n",
245 		       slot,
246 		       ab_data.slots[slot_index_to_boot].successful_boot,
247 		       ab_data.slots[slot_index_to_boot].tries_remaining);
248 	}
249 
250 out:
251 	return 0;
252 }
253 
spl_ab_append_part_slot(struct blk_desc * dev_desc,const char * part_name,char * new_name)254 int spl_ab_append_part_slot(struct blk_desc *dev_desc,
255 			    const char *part_name,
256 			    char *new_name)
257 {
258 	char slot_suffix[3] = {0};
259 
260 	if (!strcmp(part_name, "misc")) {
261 		strcat(new_name, part_name);
262 		return 0;
263 	}
264 
265 	if (spl_get_current_slot(dev_desc, "misc", slot_suffix)) {
266 		printf("No misc partition\n");
267 		strcat(new_name, part_name);
268 		return 0;
269 	}
270 
271 	strcpy(new_name, part_name);
272 	strcat(new_name, slot_suffix);
273 
274 	return 0;
275 }
276 
spl_save_metadata_if_changed(struct blk_desc * dev_desc,AvbABData * ab_data,AvbABData * ab_data_orig)277 static int spl_save_metadata_if_changed(struct blk_desc *dev_desc,
278 					AvbABData *ab_data,
279 					AvbABData *ab_data_orig)
280 {
281 	if (safe_memcmp(ab_data, ab_data_orig, sizeof(AvbABData)))
282 		return spl_ab_data_write(dev_desc, ab_data, "misc");
283 
284 	return 0;
285 }
286 
287 /* If verify fail in a/b system, then decrease 1. */
spl_ab_decrease_tries(struct blk_desc * dev_desc)288 int spl_ab_decrease_tries(struct blk_desc *dev_desc)
289 {
290 	AvbABData ab_data, ab_data_orig;
291 	size_t slot_index = 0;
292 	char slot_suffix[3] = {0};
293 	int ret = -1;
294 
295 	ret = spl_get_current_slot(dev_desc, "misc", slot_suffix);
296 	if (ret)
297 		goto out;
298 
299 	if (!strncmp(slot_suffix, "_a", 2))
300 		slot_index = 0;
301 	else if (!strncmp(slot_suffix, "_b", 2))
302 		slot_index = 1;
303 	else
304 		slot_index = 0;
305 
306 	ret = spl_ab_data_read(dev_desc, &ab_data, "misc");
307 	if (ret)
308 		goto out;
309 
310 	memcpy(&ab_data_orig, &ab_data, sizeof(AvbABData));
311 
312 	/* ... and decrement tries remaining, if applicable. */
313 	if (!ab_data.slots[slot_index].successful_boot &&
314 	    ab_data.slots[slot_index].tries_remaining > 0)
315 		ab_data.slots[slot_index].tries_remaining -= 1;
316 
317 	ret = spl_save_metadata_if_changed(dev_desc, &ab_data, &ab_data_orig);
318 
319 out:
320 	return ret;
321 }
322 
323 /*
324  * If boot A/B system fail, tries-remaining decrease 1
325  * and do reset automatically if still bootable.
326  */
spl_ab_decrease_reset(struct blk_desc * dev_desc)327 int spl_ab_decrease_reset(struct blk_desc *dev_desc)
328 {
329 	AvbABData ab_data;
330 	int ret;
331 
332 	ret = spl_ab_data_read(dev_desc, &ab_data, "misc");
333 	if (ret)
334 		return ret;
335 
336 	/* If current device cannot boot, return and try other devices. */
337 	if (!spl_slot_is_bootable(&ab_data.slots[0]) &&
338 	    !spl_slot_is_bootable(&ab_data.slots[1])) {
339 		printf("A/B: no bootable slot\n");
340 		return -ENODEV;
341 	}
342 
343 	/* If current device still can boot, decrease and do reset. */
344 	ret = spl_ab_decrease_tries(dev_desc);
345 	if (ret)
346 		return ret;
347 
348 	printf("A/B: slot boot fail, do reset\n");
349 	do_reset(NULL, 0, 0, NULL);
350 
351 	/*
352 	 * Only do_reset() fail will arrive here, return a
353 	 * negative number, then enter maskrom in the caller.
354 	 */
355 	return -EINVAL;
356 }
357