xref: /rk3399_rockchip-uboot/common/spl/spl_ab.c (revision daae0a01d6548c381f7126747a8ef273189b547e)
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 	size_t slot_index_to_boot;
206 	AvbABData ab_data;
207 	int ret;
208 
209 	ret = spl_ab_data_read(dev_desc, &ab_data, partition);
210 	if (ret)
211 		return ret;
212 
213 	if (spl_slot_is_bootable(&ab_data.slots[0]) &&
214 	    spl_slot_is_bootable(&ab_data.slots[1])) {
215 		if (ab_data.slots[1].priority > ab_data.slots[0].priority)
216 			slot_index_to_boot = 1;
217 		else
218 			slot_index_to_boot = 0;
219 	} else if (spl_slot_is_bootable(&ab_data.slots[0])) {
220 		slot_index_to_boot = 0;
221 	} else if (spl_slot_is_bootable(&ab_data.slots[1])) {
222 		slot_index_to_boot = 1;
223 	} else {
224 		printf("No bootable slots found, use lastboot.\n");
225 		if (spl_get_lastboot(&ab_data) == 0) {
226 			memcpy(slot, "_a", 2);
227 			goto out;
228 		} else if (spl_get_lastboot(&ab_data) == 1) {
229 			memcpy(slot, "_b", 2);
230 			goto out;
231 		} else {
232 			return -ENODEV;
233 		}
234 	}
235 
236 	if (slot_index_to_boot == 0)
237 		memcpy(slot, "_a", 2);
238 	else if (slot_index_to_boot == 1)
239 		memcpy(slot, "_b", 2);
240 
241 out:
242 	return 0;
243 }
244 
245 int spl_ab_append_part_slot(struct blk_desc *dev_desc,
246 			    const char *part_name,
247 			    char *new_name)
248 {
249 	char slot_suffix[3] = {0};
250 
251 	if (!strcmp(part_name, "misc")) {
252 		strcat(new_name, part_name);
253 		return 0;
254 	}
255 
256 	if (spl_get_current_slot(dev_desc, "misc", slot_suffix)) {
257 		printf("No misc partition\n");
258 		strcat(new_name, part_name);
259 		return 0;
260 	}
261 
262 	strcpy(new_name, part_name);
263 	strcat(new_name, slot_suffix);
264 
265 	return 0;
266 }
267