aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRuslan Trofymenko2019-07-05 07:37:32 -0500
committerAlistair Delva2019-07-09 12:52:03 -0500
commit18d2bcdecfecdfb97cc8d4f83968accc90d292c1 (patch)
treea97b4bd2c1776e23995ed483f882348de6408ab5
parentb828018b0197850e435a7def8e7324277f5ed5a2 (diff)
downloadu-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/Kconfig10
-rw-r--r--common/Makefile1
-rw-r--r--common/android_ab.c300
-rw-r--r--include/android_ab.h34
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
817config 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
817endmenu 827endmenu
818 828
819menu "Blob list" 829menu "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
107endif 107endif
108 108
109obj-y += image.o 109obj-y += image.o
110obj-$(CONFIG_ANDROID_AB) += android_ab.o
110obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o 111obj-$(CONFIG_ANDROID_BOOT_IMAGE) += image-android.o
111obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o 112obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
112obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o 113obj-$(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 */
22static 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 */
38static 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 */
81static 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 */
131static 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 */
161static 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
179int 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 */
32int ab_select_slot(struct blk_desc *dev_desc, disk_partition_t *part_info);
33
34#endif /* __ANDROID_AB_H */