summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootctrl/Android.bp40
-rw-r--r--bootctrl/boot_control.cc351
-rw-r--r--bootctrl/bootloader_message.cpp249
-rw-r--r--bootctrl/bootloader_message.h249
4 files changed, 889 insertions, 0 deletions
diff --git a/bootctrl/Android.bp b/bootctrl/Android.bp
new file mode 100644
index 0000000..ce5041f
--- /dev/null
+++ b/bootctrl/Android.bp
@@ -0,0 +1,40 @@
1// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15cc_library_shared {
16 name: "bootctrl.am57x",
17
18 vendor: true,
19 relative_install_path: "hw",
20
21 srcs: [
22 "boot_control.cc",
23 "bootloader_message.cpp"
24 ],
25
26 cflags: [
27 "-DLOG_TAG=\"ti_bootcontrol\"",
28 ],
29
30 header_libs: ["libhardware_headers"],
31
32 static_libs: ["libfstab",],
33
34 shared_libs: [
35 "liblog",
36 "libcutils",
37 "libbase",
38 "libz"
39 ],
40}
diff --git a/bootctrl/boot_control.cc b/bootctrl/boot_control.cc
new file mode 100644
index 0000000..28c30d4
--- /dev/null
+++ b/bootctrl/boot_control.cc
@@ -0,0 +1,351 @@
1/*
2 * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <log/log.h>
18#include <cutils/properties.h>
19#include <zlib.h>
20#include <hardware/boot_control.h>
21#include <bootloader_message.h>
22
23#include <string>
24
25#define BOOT_SLOT_PROP "ro.boot.slot_suffix"
26
27struct BootControlPrivate {
28 // The base struct needs to be first in the list.
29 boot_control_module_t base;
30
31 // Whether this struct was initialized with data from the bootloader message
32 // that doesn't change until next reboot.
33 bool initialized;
34
35 // The path to the misc_device as reported in the fstab.
36 const char* misc_device;
37
38 // The number of slots present on the device.
39 unsigned int num_slots;
40
41 // The slot where we are running from.
42 unsigned int current_slot;
43};
44
45constexpr unsigned int kMaxNumSlots =
46 sizeof(bootloader_control::slot_info) /
47 sizeof(bootloader_control::slot_info[0]);
48
49constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" };
50
51// Return the little-endian representation of the CRC-32 of the first fields
52// in |boot_ctrl| up to the crc32_le field.
53static uint32_t GetBootloaderControlCRC(const bootloader_control* boot_ctrl) {
54 return crc32(0, (const uint8_t*)boot_ctrl,
55 offsetof(bootloader_control, crc32_le));
56}
57
58static bool LoadBootloaderControl(const char* misc_device,
59 bootloader_control* boot_ctrl) {
60 std::string str_err;
61 if (read_bootloader_control_from(boot_ctrl, misc_device, &str_err))
62 return true;
63
64 ALOGE("%s", str_err.c_str());
65
66 return false;
67}
68
69static bool SaveBootloaderControl(const char* misc_device,
70 bootloader_control* boot_ctrl) {
71 boot_ctrl->crc32_le = GetBootloaderControlCRC(boot_ctrl);
72
73 std::string str_err;
74 if (write_bootloader_control_to(boot_ctrl, misc_device, &str_err))
75 return true;
76
77 ALOGE("%s", str_err.c_str());
78
79 return false;
80}
81
82// Return the index of the slot suffix passed or -1 if not a valid slot suffix.
83static int SlotSuffixToIndex(const char* suffix) {
84 for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
85 if (!strcmp(kSlotSuffixes[slot], suffix)) return slot;
86 }
87
88 return -1;
89}
90
91static bool IsInitialized(const BootControlPrivate* module) {
92 if (!module->initialized) {
93 ALOGW("Module not initialized");
94 return false;
95 }
96
97 return true;
98}
99
100void BootControlInit(boot_control_module_t* module) {
101 struct BootControlPrivate* bootctrl_module =
102 reinterpret_cast<BootControlPrivate*>(module);
103
104 if (bootctrl_module->initialized) return;
105
106 if (!module) {
107 ALOGE("Invalid argument passed to %s", __func__);
108 return;
109 }
110
111 ALOGI("Init %s", module->common.name);
112
113 // Initialize the current_slot from the read-only property. If the property
114 // was not set (from either the command line or the device tree), we can later
115 // initialize it from the bootloader_control struct.
116 char suffix_prop[PROPERTY_VALUE_MAX] = {0};
117 property_get(BOOT_SLOT_PROP, suffix_prop, "");
118 bootctrl_module->current_slot = SlotSuffixToIndex(suffix_prop);
119
120 std::string err;
121 std::string device = get_bootloader_message_blk_device(&err);
122
123 bootloader_control boot_ctrl;
124 if (!LoadBootloaderControl(device.c_str(), &boot_ctrl))
125 ALOGE("Error loading metadata");
126
127 // Note that since there isn't a module unload function this memory is leaked.
128 bootctrl_module->misc_device = strdup(device.c_str());
129 uint32_t computed_crc32 = GetBootloaderControlCRC(&boot_ctrl);
130 if (boot_ctrl.crc32_le != computed_crc32) {
131 ALOGE("Invalid boot control found, expected CRC-32 0x%04X, "
132 "but found 0x%04X. Should re-initializing A/B metadata.",
133 computed_crc32, boot_ctrl.crc32_le);
134 return;
135 }
136
137 std::string metadata_suffix = "_" + std::string(boot_ctrl.slot_suffix);
138 if (SlotSuffixToIndex(metadata_suffix.c_str()) !=
139 bootctrl_module->current_slot) {
140 ALOGE("Kernel slot argument and A/B metadata do not match, "
141 "%s=%s, slot metadata=%s", BOOT_SLOT_PROP, suffix_prop,
142 boot_ctrl.slot_suffix);
143 return;
144 }
145
146 bootctrl_module->initialized = true;
147 bootctrl_module->num_slots = boot_ctrl.nb_slot;
148
149 ALOGI("Current slot: %s(%d), number of slots: %d", boot_ctrl.slot_suffix,
150 bootctrl_module->current_slot, bootctrl_module->num_slots);
151
152 return;
153}
154
155unsigned int GetNumberSlots(boot_control_module_t* module) {
156 BootControlPrivate* const bootctrl_module =
157 reinterpret_cast<BootControlPrivate*>(module);
158
159 if (!IsInitialized(bootctrl_module)) return -1;
160
161 return bootctrl_module->num_slots;
162}
163
164unsigned int GetCurrentSlot(boot_control_module_t* module) {
165 BootControlPrivate* const bootctrl_module =
166 reinterpret_cast<BootControlPrivate*>(module);
167
168 if (!IsInitialized(bootctrl_module)) return -1;
169
170 return bootctrl_module->current_slot;
171}
172
173int IsSlotMarkedSuccessful(boot_control_module_t* module, unsigned int slot) {
174 BootControlPrivate* const bootctrl_module =
175 reinterpret_cast<BootControlPrivate*>(module);
176
177 if (!IsInitialized(bootctrl_module)) return -1;
178
179 if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
180 // Invalid slot number.
181 return -1;
182 }
183
184 bootloader_control bootctrl;
185 if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
186 return -1;
187
188 return (bootctrl.slot_info[slot].successful_boot &&
189 bootctrl.slot_info[slot].tries_remaining);
190}
191
192int MarkBootSuccessful(boot_control_module_t* module) {
193 BootControlPrivate* const bootctrl_module =
194 reinterpret_cast<BootControlPrivate*>(module);
195
196 if (!IsInitialized(bootctrl_module)) return -1;
197
198 bootloader_control bootctrl;
199 if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
200 return -1;
201
202 bootctrl.slot_info[bootctrl_module->current_slot].successful_boot = 1;
203 // tries_remaining == 0 means that the slot is not bootable anymore, make
204 // sure we mark the current slot as bootable if it succeeds in the last
205 // attempt.
206 bootctrl.slot_info[bootctrl_module->current_slot].tries_remaining = 1;
207 if (!SaveBootloaderControl(bootctrl_module->misc_device, &bootctrl))
208 return -1;
209
210 ALOGI("Slot %d is marked as successfully booted",
211 bootctrl_module->current_slot);
212
213 return 0;
214}
215
216int SetActiveBootSlot(boot_control_module_t* module, unsigned int slot) {
217 BootControlPrivate* const bootctrl_module =
218 reinterpret_cast<BootControlPrivate*>(module);
219
220 if (!IsInitialized(bootctrl_module))
221 return -1;
222
223 if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
224 // Invalid slot number.
225 return -1;
226 }
227
228 bootloader_control bootctrl;
229 if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
230 return -1;
231
232 // Set every other slot with a lower priority than the new "active" slot.
233 const unsigned int kActivePriority = 15;
234 const unsigned int kActiveTries = 6;
235 for (unsigned int i = 0; i < bootctrl_module->num_slots; ++i) {
236 if (i != slot) {
237 if (bootctrl.slot_info[i].priority >= kActivePriority)
238 bootctrl.slot_info[i].priority = kActivePriority - 1;
239 }
240 }
241
242 // Note that setting a slot as active doesn't change the successful bit.
243 // The successful bit will only be changed by setSlotAsUnbootable().
244 bootctrl.slot_info[slot].priority = kActivePriority;
245 bootctrl.slot_info[slot].tries_remaining = kActiveTries;
246
247 // Setting the current slot as active is a way to revert the operation that
248 // set *another* slot as active at the end of an updater. This is commonly
249 // used to cancel the pending update. We should only reset the verity_corrpted
250 // bit when attempting a new slot, otherwise the verity bit on the current
251 // slot would be flip.
252 if (slot != bootctrl_module->current_slot)
253 bootctrl.slot_info[slot].verity_corrupted = 0;
254
255 if (!SaveBootloaderControl(bootctrl_module->misc_device, &bootctrl))
256 return -1;
257
258 ALOGI("Slot %d is set as active", slot);
259
260 return 0;
261}
262
263int SetSlotAsUnbootable(boot_control_module_t* module, unsigned int slot) {
264 BootControlPrivate* const bootctrl_module =
265 reinterpret_cast<BootControlPrivate*>(module);
266
267 if (!IsInitialized(bootctrl_module))
268 return -1;
269
270 if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
271 // Invalid slot number.
272 return -1;
273 }
274
275 bootloader_control bootctrl;
276 if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
277 return -1;
278
279 // The only way to mark a slot as unbootable, regardless of the priority is to
280 // set the tries_remaining to 0.
281 bootctrl.slot_info[slot].successful_boot = 0;
282 bootctrl.slot_info[slot].tries_remaining = 0;
283 if (!SaveBootloaderControl(bootctrl_module->misc_device, &bootctrl))
284 return -1;
285
286 ALOGI("Slot %d is marked as unbootable", slot);
287
288 return 0;
289}
290
291int IsSlotBootable(struct boot_control_module* module, unsigned int slot) {
292 BootControlPrivate* const bootctrl_module =
293 reinterpret_cast<BootControlPrivate*>(module);
294
295 if (!IsInitialized(bootctrl_module)) return -1;
296
297 if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
298 // Invalid slot number.
299 return -1;
300 }
301
302 bootloader_control bootctrl;
303 if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl))
304 return -1;
305
306 return bootctrl.slot_info[slot].tries_remaining;
307}
308
309const char* GetSuffix(boot_control_module_t* module, unsigned int slot) {
310 BootControlPrivate* const bootctrl_module =
311 reinterpret_cast<BootControlPrivate*>(module);
312
313 if (!IsInitialized(bootctrl_module)) return NULL;
314
315 if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) return NULL;
316
317 return kSlotSuffixes[slot];
318}
319
320static hw_module_methods_t boot_control_module_methods = {
321 .open = NULL,
322};
323
324BootControlPrivate HAL_MODULE_INFO_SYM = {
325 .base = {
326 .common ={
327 .tag = HARDWARE_MODULE_TAG,
328 .module_api_version = 1,
329 .hal_api_version = 0,
330 .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
331 .name = "AM57xx Boot control HAL",
332 .author = "Texas Instruments",
333 .methods = &boot_control_module_methods
334 },
335
336 .init = BootControlInit,
337 .getNumberSlots = GetNumberSlots,
338 .getCurrentSlot = GetCurrentSlot,
339 .markBootSuccessful = MarkBootSuccessful,
340 .setActiveBootSlot = SetActiveBootSlot,
341 .setSlotAsUnbootable = SetSlotAsUnbootable,
342 .isSlotBootable = IsSlotBootable,
343 .getSuffix = GetSuffix,
344 .isSlotMarkedSuccessful = IsSlotMarkedSuccessful
345 },
346
347 .initialized = false,
348 .misc_device = nullptr,
349 .num_slots = 0,
350 .current_slot = 0
351};
diff --git a/bootctrl/bootloader_message.cpp b/bootctrl/bootloader_message.cpp
new file mode 100644
index 0000000..a4634ed
--- /dev/null
+++ b/bootctrl/bootloader_message.cpp
@@ -0,0 +1,249 @@
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "bootloader_message.h"
18
19#include <errno.h>
20#include <fcntl.h>
21#include <string.h>
22
23#include <string>
24#include <vector>
25
26#include <android-base/file.h>
27#include <android-base/properties.h>
28#include <android-base/stringprintf.h>
29#include <android-base/unique_fd.h>
30#include <fstab/fstab.h>
31
32constexpr off_t kBootloaderControlOffset = offsetof(bootloader_message_ab, slot_suffix);
33
34static std::string get_misc_blk_device(std::string* err) {
35 std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
36 fs_mgr_free_fstab);
37 if (!fstab) {
38 *err = "failed to read default fstab";
39 return "";
40 }
41 fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab.get(), "/misc");
42 if (record == nullptr) {
43 *err = "failed to find /misc partition";
44 return "";
45 }
46 return record->blk_device;
47}
48
49// In recovery mode, recovery can get started and try to access the misc
50// device before the kernel has actually created it.
51static bool wait_for_device(const std::string& blk_device, std::string* err) {
52 int tries = 0;
53 int ret;
54 err->clear();
55 do {
56 ++tries;
57 struct stat buf;
58 ret = stat(blk_device.c_str(), &buf);
59 if (ret == -1) {
60 *err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
61 blk_device.c_str(), tries, strerror(errno));
62 sleep(1);
63 }
64 } while (ret && tries < 10);
65
66 if (ret) {
67 *err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
68 }
69 return ret == 0;
70}
71
72static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
73 size_t offset, std::string* err) {
74 if (!wait_for_device(misc_blk_device, err)) {
75 return false;
76 }
77 android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
78 if (fd == -1) {
79 *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
80 strerror(errno));
81 return false;
82 }
83 if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
84 *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
85 strerror(errno));
86 return false;
87 }
88 if (!android::base::ReadFully(fd, p, size)) {
89 *err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
90 strerror(errno));
91 return false;
92 }
93 return true;
94}
95
96static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
97 size_t offset, std::string* err) {
98 android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
99 if (fd == -1) {
100 *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
101 strerror(errno));
102 return false;
103 }
104 if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
105 *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
106 strerror(errno));
107 return false;
108 }
109 if (!android::base::WriteFully(fd, p, size)) {
110 *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
111 strerror(errno));
112 return false;
113 }
114 if (fsync(fd) == -1) {
115 *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
116 strerror(errno));
117 return false;
118 }
119 return true;
120}
121
122std::string get_bootloader_message_blk_device(std::string* err) {
123 std::string misc_blk_device = get_misc_blk_device(err);
124 if (misc_blk_device.empty()) return "";
125 if (!wait_for_device(misc_blk_device, err)) return "";
126 return misc_blk_device;
127}
128
129bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
130 std::string* err) {
131 return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
132 BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
133}
134
135bool read_bootloader_message(bootloader_message* boot, std::string* err) {
136 std::string misc_blk_device = get_misc_blk_device(err);
137 if (misc_blk_device.empty()) {
138 return false;
139 }
140 return read_bootloader_message_from(boot, misc_blk_device, err);
141}
142
143bool read_bootloader_control_from(bootloader_control* boot_ctrl, const std::string& misc_blk_device,
144 std::string* err) {
145 return read_misc_partition(boot_ctrl, sizeof(bootloader_control), misc_blk_device,
146 kBootloaderControlOffset, err);
147}
148
149bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device,
150 std::string* err) {
151 return write_misc_partition(&boot, sizeof(boot), misc_blk_device,
152 BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
153}
154
155bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
156 std::string misc_blk_device = get_misc_blk_device(err);
157 if (misc_blk_device.empty()) {
158 return false;
159 }
160 return write_bootloader_message_to(boot, misc_blk_device, err);
161}
162
163bool write_bootloader_control_to(const bootloader_control* boot_ctrl, const std::string& misc_blk_device,
164 std::string* err) {
165 return write_misc_partition(boot_ctrl, sizeof(bootloader_control), misc_blk_device,
166 kBootloaderControlOffset, err);
167}
168
169bool clear_bootloader_message(std::string* err) {
170 bootloader_message boot = {};
171 return write_bootloader_message(boot, err);
172}
173
174bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
175 bootloader_message boot = {};
176 update_bootloader_message_in_struct(&boot, options);
177
178 return write_bootloader_message(boot, err);
179}
180
181bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
182 bootloader_message boot;
183 if (!read_bootloader_message(&boot, err)) {
184 return false;
185 }
186 update_bootloader_message_in_struct(&boot, options);
187
188 return write_bootloader_message(boot, err);
189}
190
191bool update_bootloader_message_in_struct(bootloader_message* boot,
192 const std::vector<std::string>& options) {
193 if (!boot) return false;
194 // Replace the command & recovery fields.
195 memset(boot->command, 0, sizeof(boot->command));
196 memset(boot->recovery, 0, sizeof(boot->recovery));
197
198 strlcpy(boot->command, "boot-recovery", sizeof(boot->command));
199 strlcpy(boot->recovery, "recovery\n", sizeof(boot->recovery));
200 for (const auto& s : options) {
201 strlcat(boot->recovery, s.c_str(), sizeof(boot->recovery));
202 if (s.back() != '\n') {
203 strlcat(boot->recovery, "\n", sizeof(boot->recovery));
204 }
205 }
206 return true;
207}
208
209bool write_reboot_bootloader(std::string* err) {
210 bootloader_message boot;
211 if (!read_bootloader_message(&boot, err)) {
212 return false;
213 }
214 if (boot.command[0] != '\0') {
215 *err = "Bootloader command pending.";
216 return false;
217 }
218 strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
219 return write_bootloader_message(boot, err);
220}
221
222bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
223 std::string misc_blk_device = get_misc_blk_device(err);
224 if (misc_blk_device.empty()) {
225 return false;
226 }
227 package_data->resize(size);
228 return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
229 WIPE_PACKAGE_OFFSET_IN_MISC, err);
230}
231
232bool write_wipe_package(const std::string& package_data, std::string* err) {
233 std::string misc_blk_device = get_misc_blk_device(err);
234 if (misc_blk_device.empty()) {
235 return false;
236 }
237 return write_misc_partition(package_data.data(), package_data.size(), misc_blk_device,
238 WIPE_PACKAGE_OFFSET_IN_MISC, err);
239}
240
241extern "C" bool write_reboot_bootloader(void) {
242 std::string err;
243 return write_reboot_bootloader(&err);
244}
245
246extern "C" bool write_bootloader_message(const char* options) {
247 std::string err;
248 return write_bootloader_message({options}, &err);
249}
diff --git a/bootctrl/bootloader_message.h b/bootctrl/bootloader_message.h
new file mode 100644
index 0000000..e5b9489
--- /dev/null
+++ b/bootctrl/bootloader_message.h
@@ -0,0 +1,249 @@
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef _BOOTLOADER_MESSAGE_H
18#define _BOOTLOADER_MESSAGE_H
19
20#include <assert.h>
21#include <stddef.h>
22#include <stdint.h>
23
24// Spaces used by misc partition are as below:
25// 0 - 2K For bootloader_message
26// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used
27// as bootloader_message_ab struct)
28// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
29// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
30// are not configurable without changing all of them.
31static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
32static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
33
34/* Bootloader Message (2-KiB)
35 *
36 * This structure describes the content of a block in flash
37 * that is used for recovery and the bootloader to talk to
38 * each other.
39 *
40 * The command field is updated by linux when it wants to
41 * reboot into recovery or to update radio or bootloader firmware.
42 * It is also updated by the bootloader when firmware update
43 * is complete (to boot into recovery for any final cleanup)
44 *
45 * The status field was used by the bootloader after the completion
46 * of an "update-radio" or "update-hboot" command, which has been
47 * deprecated since Froyo.
48 *
49 * The recovery field is only written by linux and used
50 * for the system to send a message to recovery or the
51 * other way around.
52 *
53 * The stage field is written by packages which restart themselves
54 * multiple times, so that the UI can reflect which invocation of the
55 * package it is. If the value is of the format "#/#" (eg, "1/3"),
56 * the UI will add a simple indicator of that status.
57 *
58 * We used to have slot_suffix field for A/B boot control metadata in
59 * this struct, which gets unintentionally cleared by recovery or
60 * uncrypt. Move it into struct bootloader_message_ab to avoid the
61 * issue.
62 */
63struct bootloader_message {
64 char command[32];
65 char status[32];
66 char recovery[768];
67
68 // The 'recovery' field used to be 1024 bytes. It has only ever
69 // been used to store the recovery command line, so 768 bytes
70 // should be plenty. We carve off the last 256 bytes to store the
71 // stage string (for multistage packages) and possible future
72 // expansion.
73 char stage[32];
74
75 // The 'reserved' field used to be 224 bytes when it was initially
76 // carved off from the 1024-byte recovery field. Bump it up to
77 // 1184-byte so that the entire bootloader_message struct rounds up
78 // to 2048-byte.
79 char reserved[1184];
80};
81
82/**
83 * We must be cautious when changing the bootloader_message struct size,
84 * because A/B-specific fields may end up with different offsets.
85 */
86#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
87static_assert(sizeof(struct bootloader_message) == 2048,
88 "struct bootloader_message size changes, which may break A/B devices");
89#endif
90
91/**
92 * The A/B-specific bootloader message structure (4-KiB).
93 *
94 * We separate A/B boot control metadata from the regular bootloader
95 * message struct and keep it here. Everything that's A/B-specific
96 * stays after struct bootloader_message, which should be managed by
97 * the A/B-bootloader or boot control HAL.
98 *
99 * The slot_suffix field is used for A/B implementations where the
100 * bootloader does not set the androidboot.ro.boot.slot_suffix kernel
101 * commandline parameter. This is used by fs_mgr to mount /system and
102 * other partitions with the slotselect flag set in fstab. A/B
103 * implementations are free to use all 32 bytes and may store private
104 * data past the first NUL-byte in this field. It is encouraged, but
105 * not mandatory, to use 'struct bootloader_control' described below.
106 *
107 * The update_channel field is used to store the Omaha update channel
108 * if update_engine is compiled with Omaha support.
109 */
110struct bootloader_message_ab {
111 struct bootloader_message message;
112 char slot_suffix[32];
113 char update_channel[128];
114
115 // Round up the entire struct to 4096-byte.
116 char reserved[1888];
117};
118
119/**
120 * Be cautious about the struct size change, in case we put anything post
121 * bootloader_message_ab struct (b/29159185).
122 */
123#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
124static_assert(sizeof(struct bootloader_message_ab) == 4096,
125 "struct bootloader_message_ab size changes");
126#endif
127
128#define BOOT_CTRL_MAGIC 0x42414342 /* Bootloader Control AB */
129#define BOOT_CTRL_VERSION 1
130
131struct slot_metadata {
132 // Slot priority with 15 meaning highest priority, 1 lowest
133 // priority and 0 the slot is unbootable.
134 uint8_t priority : 4;
135 // Number of times left attempting to boot this slot.
136 uint8_t tries_remaining : 3;
137 // 1 if this slot has booted successfully, 0 otherwise.
138 uint8_t successful_boot : 1;
139 // 1 if this slot is corrupted from a dm-verity corruption, 0
140 // otherwise.
141 uint8_t verity_corrupted : 1;
142 // Reserved for further use.
143 uint8_t reserved : 7;
144} __attribute__((packed));
145
146/* Bootloader Control AB
147 *
148 * This struct can be used to manage A/B metadata. It is designed to
149 * be put in the 'slot_suffix' field of the 'bootloader_message'
150 * structure described above. It is encouraged to use the
151 * 'bootloader_control' structure to store the A/B metadata, but not
152 * mandatory.
153 */
154struct bootloader_control {
155 // NUL terminated active slot suffix.
156 char slot_suffix[4];
157 // Bootloader Control AB magic number (see BOOT_CTRL_MAGIC).
158 uint32_t magic;
159 // Version of struct being used (see BOOT_CTRL_VERSION).
160 uint8_t version;
161 // Number of slots being managed.
162 uint8_t nb_slot : 3;
163 // Number of times left attempting to boot recovery.
164 uint8_t recovery_tries_remaining : 3;
165 // Ensure 4-bytes alignment for slot_info field.
166 uint8_t reserved0[2];
167 // Per-slot information. Up to 4 slots.
168 struct slot_metadata slot_info[4];
169 // Reserved for further use.
170 uint8_t reserved1[8];
171 // CRC32 of all 28 bytes preceding this field (little endian
172 // format).
173 uint32_t crc32_le;
174} __attribute__((packed));
175
176#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
177static_assert(sizeof(struct bootloader_control) ==
178 sizeof(((struct bootloader_message_ab *)0)->slot_suffix),
179 "struct bootloader_control has wrong size");
180#endif
181
182#ifdef __cplusplus
183
184#include <string>
185#include <vector>
186
187// Return the block device name for the bootloader message partition and waits
188// for the device for up to 10 seconds. In case of error returns the empty
189// string.
190std::string get_bootloader_message_blk_device(std::string* err);
191
192// Read bootloader message into boot. Error message will be set in err.
193bool read_bootloader_message(bootloader_message* boot, std::string* err);
194
195// Read bootloader message from the specified misc device into boot.
196bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
197 std::string* err);
198
199// Read bootloader control block from the specified misc device into boot_ctrl.
200bool read_bootloader_control_from(bootloader_control* boot_ctrl, const std::string& misc_blk_device,
201 std::string* err);
202
203// Write bootloader message to BCB.
204bool write_bootloader_message(const bootloader_message& boot, std::string* err);
205
206// Write bootloader message to the specified BCB device.
207bool write_bootloader_message_to(const bootloader_message& boot,
208 const std::string& misc_blk_device, std::string* err);
209
210// Write bootloader message (boots into recovery with the options) to BCB. Will
211// set the command and recovery fields, and reset the rest.
212bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
213
214// Write bootloader control block to the specified BCB device.
215bool write_bootloader_control_to(const bootloader_control* boot_ctrl, const std::string& misc_blk_device,
216 std::string* err);
217
218// Update bootloader message (boots into recovery with the options) to BCB. Will
219// only update the command and recovery fields.
220bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
221
222// Update bootloader message (boots into recovery with the |options|) in |boot|. Will only update
223// the command and recovery fields.
224bool update_bootloader_message_in_struct(bootloader_message* boot,
225 const std::vector<std::string>& options);
226
227// Clear BCB.
228bool clear_bootloader_message(std::string* err);
229
230// Writes the reboot-bootloader reboot reason to the bootloader_message.
231bool write_reboot_bootloader(std::string* err);
232
233// Read the wipe package from BCB (from offset WIPE_PACKAGE_OFFSET_IN_MISC).
234bool read_wipe_package(std::string* package_data, size_t size, std::string* err);
235
236// Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC).
237bool write_wipe_package(const std::string& package_data, std::string* err);
238
239#else
240
241#include <stdbool.h>
242
243// C Interface.
244bool write_bootloader_message(const char* options);
245bool write_reboot_bootloader(void);
246
247#endif // ifdef __cplusplus
248
249#endif // _BOOTLOADER_MESSAGE_H