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 *)∈
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