diff options
author | Ruslan Trofymenko | 2019-07-05 07:37:32 -0500 |
---|---|---|
committer | Alistair Delva | 2019-07-09 12:52:03 -0500 |
commit | 18d2bcdecfecdfb97cc8d4f83968accc90d292c1 (patch) | |
tree | a97b4bd2c1776e23995ed483f882348de6408ab5 | |
parent | b828018b0197850e435a7def8e7324277f5ed5a2 (diff) | |
download | u-boot-18d2bcdecfecdfb97cc8d4f83968accc90d292c1.tar.gz u-boot-18d2bcdecfecdfb97cc8d4f83968accc90d292c1.tar.xz u-boot-18d2bcdecfecdfb97cc8d4f83968accc90d292c1.zip |
FROMLIST: common: Implement A/B metadata
This patch determines the A/B-specific bootloader message structure
that is the basis for implementation of recovery and A/B update
functions. A/B metadata is stored in this structure and used to decide
which slot should we use to boot the device. Also some basic functions
for A/B metadata manipulation are implemented (like slot selection).
The patch was extracted from commits [1], [2] with some coding style
fixes.
[1] https://android-review.googlesource.com/c/platform/external/u-boot/+/729878/2
[2] https://android-review.googlesource.com/c/platform/external/u-boot/+/729880/2
Bug: 137034204
Change-Id: I8fc0b2be23522bf7f6d2e892a68070eec1d02ab0
Link: https://patchwork.ozlabs.org/patch/1128010/
Signed-off-by: Ruslan Trofymenko <ruslan.trofymenko@linaro.org>
Signed-off-by: Igor Opaniuk <igor.opaniuk@gmail.com>
Reviewed-by: Sam Protsenko <semen.protsenko@linaro.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Alistair Delva <adelva@google.com>
-rw-r--r-- | common/Kconfig | 10 | ||||
-rw-r--r-- | common/Makefile | 1 | ||||
-rw-r--r-- | common/android_ab.c | 300 | ||||
-rw-r--r-- | include/android_ab.h | 34 |
4 files changed, 345 insertions, 0 deletions
diff --git a/common/Kconfig b/common/Kconfig index af66496e75..63287865ef 100644 --- a/common/Kconfig +++ b/common/Kconfig | |||
@@ -814,6 +814,16 @@ config UPDATE_TFTP_MSEC_MAX | |||
814 | default 100 | 814 | default 100 |
815 | depends on UPDATE_TFTP | 815 | depends on UPDATE_TFTP |
816 | 816 | ||
817 | config ANDROID_AB | ||
818 | bool "Android A/B updates" | ||
819 | default n | ||
820 | help | ||
821 | If enabled, adds support for the new Android A/B update model. This | ||
822 | allows the bootloader to select which slot to boot from based on the | ||
823 | information provided by userspace via the Android boot_ctrl HAL. This | ||
824 | allows a bootloader to try a new version of the system but roll back | ||
825 | to previous version if the new one didn't boot all the way. | ||
826 | |||
817 | endmenu | 827 | endmenu |
818 | 828 | ||
819 | menu "Blob list" | 829 | menu "Blob list" |
diff --git a/common/Makefile b/common/Makefile index c7e41ef307..302d8beaf3 100644 --- a/common/Makefile +++ b/common/Makefile | |||
@@ -107,6 +107,7 @@ endif | |||
107 | endif | 107 | endif |
108 | 108 | ||
109 | obj-y += image.o | 109 | obj-y += image.o |
110 | obj-$(CONFIG_ANDROID_AB) += android_ab.o | ||
110 | obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o | 111 | obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o |
111 | obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o | 112 | obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o |
112 | obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o | 113 | obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o |
diff --git a/common/android_ab.c b/common/android_ab.c new file mode 100644 index 0000000000..05ffc6f4e5 --- /dev/null +++ b/common/android_ab.c | |||
@@ -0,0 +1,300 @@ | |||
1 | // SPDX-License-Identifier: BSD-2-Clause | ||
2 | /* | ||
3 | * Copyright (C) 2017 The Android Open Source Project | ||
4 | */ | ||
5 | #include <common.h> | ||
6 | #include <android_ab.h> | ||
7 | #include <android_bootloader_message.h> | ||
8 | #include <linux/err.h> | ||
9 | #include <memalign.h> | ||
10 | #include <u-boot/crc.h> | ||
11 | |||
12 | /** | ||
13 | * Compute the CRC-32 of the bootloader control struct. | ||
14 | * | ||
15 | * Only the bytes up to the crc32_le field are considered for the CRC-32 | ||
16 | * calculation. | ||
17 | * | ||
18 | * @param[in] abc bootloader control block | ||
19 | * | ||
20 | * @return crc32 sum | ||
21 | */ | ||
22 | static uint32_t ab_control_compute_crc(struct bootloader_control *abc) | ||
23 | { | ||
24 | return crc32(0, (void *)abc, offsetof(typeof(*abc), crc32_le)); | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * Initialize bootloader_control to the default value. | ||
29 | * | ||
30 | * It allows us to boot all slots in order from the first one. This value | ||
31 | * should be used when the bootloader message is corrupted, but not when | ||
32 | * a valid message indicates that all slots are unbootable. | ||
33 | * | ||
34 | * @param[in] abc bootloader control block | ||
35 | * | ||
36 | * @return 0 on success and a negative on error | ||
37 | */ | ||
38 | static int ab_control_default(struct bootloader_control *abc) | ||
39 | { | ||
40 | int i; | ||
41 | const struct slot_metadata metadata = { | ||
42 | .priority = 15, | ||
43 | .tries_remaining = 7, | ||
44 | .successful_boot = 0, | ||
45 | .verity_corrupted = 0, | ||
46 | .reserved = 0 | ||
47 | }; | ||
48 | |||
49 | if (!abc) | ||
50 | return -EFAULT; | ||
51 | |||
52 | memcpy(abc->slot_suffix, "a\0\0\0", 4); | ||
53 | abc->magic = BOOT_CTRL_MAGIC; | ||
54 | abc->version = BOOT_CTRL_VERSION; | ||
55 | abc->nb_slot = NUM_SLOTS; | ||
56 | memset(abc->reserved0, 0, sizeof(abc->reserved0)); | ||
57 | for (i = 0; i < abc->nb_slot; ++i) | ||
58 | abc->slot_info[i] = metadata; | ||
59 | |||
60 | memset(abc->reserved1, 0, sizeof(abc->reserved1)); | ||
61 | abc->crc32_le = ab_control_compute_crc(abc); | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * Load the boot_control struct from disk into newly allocated memory. | ||
68 | * | ||
69 | * This function allocates and returns an integer number of disk blocks, | ||
70 | * based on the block size of the passed device to help performing a | ||
71 | * read-modify-write operation on the boot_control struct. | ||
72 | * The boot_control struct offset (2 KiB) must be a multiple of the device | ||
73 | * block size, for simplicity. | ||
74 | * | ||
75 | * @param[in] dev_desc Device where to read the boot_control struct from | ||
76 | * @param[in] part_info Partition in 'dev_desc' where to read from, normally | ||
77 | * the "misc" partition should be used | ||
78 | * @param[out] pointer to pointer to bootloader_control data | ||
79 | * @return 0 on success and a negative on error | ||
80 | */ | ||
81 | static int ab_control_create_from_disk(struct blk_desc *dev_desc, | ||
82 | const disk_partition_t *part_info, | ||
83 | struct bootloader_control **abc) | ||
84 | { | ||
85 | ulong abc_offset, abc_blocks, ret; | ||
86 | |||
87 | abc_offset = offsetof(struct bootloader_message_ab, slot_suffix); | ||
88 | if (abc_offset % part_info->blksz) { | ||
89 | log_err("ANDROID: Boot control block not block aligned.\n"); | ||
90 | return -EINVAL; | ||
91 | } | ||
92 | abc_offset /= part_info->blksz; | ||
93 | |||
94 | abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control), | ||
95 | part_info->blksz); | ||
96 | if (abc_offset + abc_blocks > part_info->size) { | ||
97 | log_err("ANDROID: boot control partition too small. Need at"); | ||
98 | log_err(" least %lu blocks but have %lu blocks.\n", | ||
99 | abc_offset + abc_blocks, part_info->size); | ||
100 | return -EINVAL; | ||
101 | } | ||
102 | *abc = malloc_cache_aligned(abc_blocks * part_info->blksz); | ||
103 | if (!*abc) | ||
104 | return -ENOMEM; | ||
105 | |||
106 | ret = blk_dread(dev_desc, part_info->start + abc_offset, abc_blocks, | ||
107 | *abc); | ||
108 | if (IS_ERR_VALUE(ret)) { | ||
109 | log_err("ANDROID: Could not read from boot ctrl partition\n"); | ||
110 | free(*abc); | ||
111 | return -EIO; | ||
112 | } | ||
113 | |||
114 | log_debug("ANDROID: Loaded ABC, %lu blocks\n", abc_blocks); | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * Store the loaded boot_control block. | ||
121 | * | ||
122 | * Store back to the same location it was read from with | ||
123 | * ab_control_create_from_misc(). | ||
124 | * | ||
125 | * @param[in] dev_desc Device where we should write the boot_control struct | ||
126 | * @param[in] part_info Partition on the 'dev_desc' where to write | ||
127 | * @param[in] abc Pointer to the boot control struct and the extra bytes after | ||
128 | * it up to the nearest block boundary | ||
129 | * @return 0 on success and a negative on error | ||
130 | */ | ||
131 | static int ab_control_store(struct blk_desc *dev_desc, | ||
132 | const disk_partition_t *part_info, | ||
133 | struct bootloader_control *abc) | ||
134 | { | ||
135 | ulong abc_offset, abc_blocks, ret; | ||
136 | |||
137 | abc_offset = offsetof(struct bootloader_message_ab, slot_suffix) / | ||
138 | part_info->blksz; | ||
139 | abc_blocks = DIV_ROUND_UP(sizeof(struct bootloader_control), | ||
140 | part_info->blksz); | ||
141 | ret = blk_dwrite(dev_desc, part_info->start + abc_offset, abc_blocks, | ||
142 | abc); | ||
143 | if (IS_ERR_VALUE(ret)) { | ||
144 | log_err("ANDROID: Could not write back the misc partition\n"); | ||
145 | return -EIO; | ||
146 | } | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * Compare two slots. | ||
153 | * | ||
154 | * The function determines slot which is should we boot from among the two. | ||
155 | * | ||
156 | * @param[in] a The first bootable slot metadata | ||
157 | * @param[in] b The second bootable slot metadata | ||
158 | * @return Negative if the slot "a" is better, positive of the slot "b" is | ||
159 | * better or 0 if they are equally good. | ||
160 | */ | ||
161 | static int ab_compare_slots(const struct slot_metadata *a, | ||
162 | const struct slot_metadata *b) | ||
163 | { | ||
164 | /* Higher priority is better */ | ||
165 | if (a->priority != b->priority) | ||
166 | return b->priority - a->priority; | ||
167 | |||
168 | /* Higher successful_boot value is better, in case of same priority */ | ||
169 | if (a->successful_boot != b->successful_boot) | ||
170 | return b->successful_boot - a->successful_boot; | ||
171 | |||
172 | /* Higher tries_remaining is better to ensure round-robin */ | ||
173 | if (a->tries_remaining != b->tries_remaining) | ||
174 | return b->tries_remaining - a->tries_remaining; | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | int ab_select_slot(struct blk_desc *dev_desc, disk_partition_t *part_info) | ||
180 | { | ||
181 | struct bootloader_control *abc = NULL; | ||
182 | u32 crc32_le; | ||
183 | int slot, i, ret; | ||
184 | bool store_needed = false; | ||
185 | char slot_suffix[4]; | ||
186 | |||
187 | ret = ab_control_create_from_disk(dev_desc, part_info, &abc); | ||
188 | if (ret < 0) { | ||
189 | /* | ||
190 | * This condition represents an actual problem with the code or | ||
191 | * the board setup, like an invalid partition information. | ||
192 | * Signal a repair mode and do not try to boot from either slot. | ||
193 | */ | ||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | crc32_le = ab_control_compute_crc(abc); | ||
198 | if (abc->crc32_le != crc32_le) { | ||
199 | log_err("ANDROID: Invalid CRC-32 (expected %.8x, found %.8x),", | ||
200 | crc32_le, abc->crc32_le); | ||
201 | log_err("re-initializing A/B metadata.\n"); | ||
202 | |||
203 | ret = ab_control_default(abc); | ||
204 | if (ret < 0) { | ||
205 | free(abc); | ||
206 | return -ENODATA; | ||
207 | } | ||
208 | store_needed = true; | ||
209 | } | ||
210 | |||
211 | if (abc->magic != BOOT_CTRL_MAGIC) { | ||
212 | log_err("ANDROID: Unknown A/B metadata: %.8x\n", abc->magic); | ||
213 | free(abc); | ||
214 | return -ENODATA; | ||
215 | } | ||
216 | |||
217 | if (abc->version > BOOT_CTRL_VERSION) { | ||
218 | log_err("ANDROID: Unsupported A/B metadata version: %.8x\n", | ||
219 | abc->version); | ||
220 | free(abc); | ||
221 | return -ENODATA; | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * At this point a valid boot control metadata is stored in abc, | ||
226 | * followed by other reserved data in the same block. We select a with | ||
227 | * the higher priority slot that | ||
228 | * - is not marked as corrupted and | ||
229 | * - either has tries_remaining > 0 or successful_boot is true. | ||
230 | * If the selected slot has a false successful_boot, we also decrement | ||
231 | * the tries_remaining until it eventually becomes unbootable because | ||
232 | * tries_remaining reaches 0. This mechanism produces a bootloader | ||
233 | * induced rollback, typically right after a failed update. | ||
234 | */ | ||
235 | |||
236 | /* Safety check: limit the number of slots. */ | ||
237 | if (abc->nb_slot > ARRAY_SIZE(abc->slot_info)) { | ||
238 | abc->nb_slot = ARRAY_SIZE(abc->slot_info); | ||
239 | store_needed = true; | ||
240 | } | ||
241 | |||
242 | slot = -1; | ||
243 | for (i = 0; i < abc->nb_slot; ++i) { | ||
244 | if (abc->slot_info[i].verity_corrupted || | ||
245 | !abc->slot_info[i].tries_remaining) { | ||
246 | log_debug("ANDROID: unbootable slot %d tries: %d, ", | ||
247 | i, abc->slot_info[i].tries_remaining); | ||
248 | log_debug("corrupt: %d\n", | ||
249 | abc->slot_info[i].verity_corrupted); | ||
250 | continue; | ||
251 | } | ||
252 | log_debug("ANDROID: bootable slot %d pri: %d, tries: %d, ", | ||
253 | i, abc->slot_info[i].priority, | ||
254 | abc->slot_info[i].tries_remaining); | ||
255 | log_debug("corrupt: %d, successful: %d\n", | ||
256 | abc->slot_info[i].verity_corrupted, | ||
257 | abc->slot_info[i].successful_boot); | ||
258 | |||
259 | if (slot < 0 || | ||
260 | ab_compare_slots(&abc->slot_info[i], | ||
261 | &abc->slot_info[slot]) < 0) { | ||
262 | slot = i; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | if (slot >= 0 && !abc->slot_info[slot].successful_boot) { | ||
267 | log_err("ANDROID: Attempting slot %c, tries remaining %d\n", | ||
268 | BOOT_SLOT_NAME(slot), | ||
269 | abc->slot_info[slot].tries_remaining); | ||
270 | abc->slot_info[slot].tries_remaining--; | ||
271 | store_needed = true; | ||
272 | } | ||
273 | |||
274 | if (slot >= 0) { | ||
275 | /* | ||
276 | * Legacy user-space requires this field to be set in the BCB. | ||
277 | * Newer releases load this slot suffix from the command line | ||
278 | * or the device tree. | ||
279 | */ | ||
280 | memset(slot_suffix, 0, sizeof(slot_suffix)); | ||
281 | slot_suffix[0] = BOOT_SLOT_NAME(slot); | ||
282 | if (memcmp(abc->slot_suffix, slot_suffix, | ||
283 | sizeof(slot_suffix))) { | ||
284 | memcpy(abc->slot_suffix, slot_suffix, | ||
285 | sizeof(slot_suffix)); | ||
286 | store_needed = true; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | if (store_needed) { | ||
291 | abc->crc32_le = ab_control_compute_crc(abc); | ||
292 | ab_control_store(dev_desc, part_info, abc); | ||
293 | } | ||
294 | free(abc); | ||
295 | |||
296 | if (slot < 0) | ||
297 | return -EINVAL; | ||
298 | |||
299 | return slot; | ||
300 | } | ||
diff --git a/include/android_ab.h b/include/android_ab.h new file mode 100644 index 0000000000..810906d22b --- /dev/null +++ b/include/android_ab.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* SPDX-License-Identifier: BSD-2-Clause */ | ||
2 | /* | ||
3 | * Copyright (C) 2017 The Android Open Source Project | ||
4 | */ | ||
5 | |||
6 | #ifndef __ANDROID_AB_H | ||
7 | #define __ANDROID_AB_H | ||
8 | |||
9 | #include <common.h> | ||
10 | |||
11 | /* Android standard boot slot names are 'a', 'b', 'c', ... */ | ||
12 | #define BOOT_SLOT_NAME(slot_num) ('a' + (slot_num)) | ||
13 | |||
14 | /* Number of slots */ | ||
15 | #define NUM_SLOTS 2 | ||
16 | |||
17 | /** | ||
18 | * Select the slot where to boot from. | ||
19 | * | ||
20 | * On Android devices with more than one boot slot (multiple copies of the | ||
21 | * kernel and system images) selects which slot should be used to boot from and | ||
22 | * registers the boot attempt. This is used in by the new A/B update model where | ||
23 | * one slot is updated in the background while running from the other slot. If | ||
24 | * the selected slot did not successfully boot in the past, a boot attempt is | ||
25 | * registered before returning from this function so it isn't selected | ||
26 | * indefinitely. | ||
27 | * | ||
28 | * @param[in] dev_desc Place to store the device description pointer | ||
29 | * @param[in] part_info Place to store the partition information | ||
30 | * @return The slot number (>= 0) on success, or a negative on error | ||
31 | */ | ||
32 | int ab_select_slot(struct blk_desc *dev_desc, disk_partition_t *part_info); | ||
33 | |||
34 | #endif /* __ANDROID_AB_H */ | ||