blob: db8c5ce5d76635a0ee71764e10df370bc9fe103c [file] [log] [blame]
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001/*
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_twrp/bootloader_message.h>
18
19#include <errno.h>
20#include <fcntl.h>
21#include <string.h>
22#include <stdlib.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <sys/types.h>
26#include <sys/system_properties.h>
27
28#include <string>
29#include <vector>
30
Alessandro Astone7ec5fd92018-10-12 11:12:22 +020031// Spaces used by misc partition are as below:
32// 0 - 2K For bootloader_message
33// 2K - 16K Used by Vendor's bootloader (the 2K - 4K range may be optionally used
34// as bootloader_message_ab struct)
35// 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices
36// Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
37// are not configurable without changing all of them.
38static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = BOARD_RECOVERY_BLDRMSG_OFFSET;
39static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024 + BOOTLOADER_MESSAGE_OFFSET_IN_MISC;
40
Ethan Yonker8373cfe2017-09-08 06:50:54 -050041static std::string misc_blkdev;
42
43void set_misc_device(const char* name) {
44 misc_blkdev = name;
45}
46
47static std::string get_misc_blk_device(std::string* err) {
48 *err = "";
49 return misc_blkdev;
50}
51
52// In recovery mode, recovery can get started and try to access the misc
53// device before the kernel has actually created it.
54static bool wait_for_device(const std::string& blk_device, std::string* err) {
55 int tries = 0;
56 int ret;
57 err->clear();
58 do {
59 ++tries;
60 struct stat buf;
61 ret = stat(blk_device.c_str(), &buf);
62 if (ret == -1) {
63 char buffer[2048];
64 sprintf(buffer, "failed to stat %s try %d: %s\n",
65 blk_device.c_str(), tries, strerror(errno));
66 *err += buffer;
67 /*
68 *err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
69 blk_device.c_str(), tries, strerror(errno));
70 */
71 sleep(1);
72 }
73 } while (ret && tries < 10);
74
75 if (ret) {
76 *err += "failed to stat " + blk_device + "\n";
77 /*
78 *err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
79 */
80 }
81 return ret == 0;
82}
83
84static bool read_misc_partition(void* p, size_t size, const std::string& misc_blk_device,
85 size_t offset, std::string* err) {
86 if (!wait_for_device(misc_blk_device, err)) {
87 return false;
88 }
89
90 int fd(open(misc_blk_device.c_str(), O_RDONLY));
91 if (fd < 0) {
92 *err = "failed to open " + misc_blk_device + ": ";
93 *err += strerror(errno);
94 /*
95 android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY));
96 if (fd == -1) {
97 *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
98 strerror(errno));
99 */
100 return false;
101 }
102 if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
103 *err = "failed to lseek " + misc_blk_device + ": ";
104 *err += strerror(errno);
105 close(fd);
106 /*
107
108 *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
109 strerror(errno));
110 */
111 return false;
112 }
113
114 if ((size_t)read(fd, p, size) != size) {
115 *err = "failed to read " + misc_blk_device + ": ";
116 *err += strerror(errno);
117 close(fd);
118 /*
119 if (!android::base::ReadFully(fd, p, size)) {
120 *err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
121 strerror(errno));
122 */
123 return false;
124 }
125 close(fd);
126 return true;
127}
128
129static bool write_misc_partition(const void* p, size_t size, size_t offset, std::string* err) {
130 std::string misc_blk_device = get_misc_blk_device(err);
131 if (misc_blk_device.empty()) {
132 *err = "no misc device set";
133 return false;
134 }
135 int fd = (open(misc_blk_device.c_str(), O_WRONLY | O_SYNC));
136 if (fd == -1) {
137 *err = "failed to open " + misc_blk_device + ": ";
138 *err += strerror(errno);
139 /*
140static bool write_misc_partition(const void* p, size_t size, const std::string& misc_blk_device,
141 size_t offset, std::string* err) {
142 android::base::unique_fd fd(open(misc_blk_device.c_str(), O_WRONLY));
143 if (fd == -1) {
144 *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
145 strerror(errno));
146 */
147 return false;
148 }
149 if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
150 *err = "failed to lseek " + misc_blk_device + ": ";
151 *err += strerror(errno);
152 close(fd);
153 /*
154 *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
155 strerror(errno));
156 */
157 return false;
158 }
159 if ((size_t)write(fd, p, size) != size) {
160 *err = "failed to write " + misc_blk_device + ": ";
161 *err += strerror(errno);
162 close(fd);
163 /*
164 if (!android::base::WriteFully(fd, p, size)) {
165 *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
166 strerror(errno));
167 */
168 return false;
169 }
170
171 // TODO: O_SYNC and fsync duplicates each other?
172 if (fsync(fd) == -1) {
173 *err = "failed to fsync " + misc_blk_device + ": ";
174 *err += strerror(errno);
175 close(fd);
176 /*
177 if (fsync(fd) == -1) {
178 *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
179 strerror(errno));
180 */
181 return false;
182 }
183 close(fd);
184 return true;
185}
186
187bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device,
188 std::string* err) {
189 return read_misc_partition(boot, sizeof(*boot), misc_blk_device,
190 BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
191}
192
193bool read_bootloader_message(bootloader_message* boot, std::string* err) {
194 std::string misc_blk_device = get_misc_blk_device(err);
195 if (misc_blk_device.empty()) {
196 return false;
197 }
198 return read_bootloader_message_from(boot, misc_blk_device, err);
199}
200
201bool write_bootloader_message_to(const bootloader_message& boot, __unused const std::string& misc_blk_device,
202 std::string* err) {
203 return write_misc_partition(&boot, sizeof(boot),
204 BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
205}
206
207bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
208 std::string misc_blk_device = get_misc_blk_device(err);
209 if (misc_blk_device.empty()) {
210 return false;
211 }
212 return write_bootloader_message_to(boot, misc_blk_device, err);
213}
214
215// libc++ in 5.1 does not know how to handle a std::string* so this craziness is needed
216bool clear_bootloader_message(void* err) {
217 std::string &s = *(static_cast<std::string*>(err));
218 return clear_bootloader_message(&s);
219}
220
221bool clear_bootloader_message(std::string* err) {
222 bootloader_message boot = {};
Alessandro Astone5667feb2019-05-04 20:06:48 +0200223 if (BOOTLOADER_MESSAGE_OFFSET_IN_MISC < sizeof(bootloader_message)) {
224 return write_misc_partition(&boot, sizeof(boot), 0 /* offset */, err);
225 }
Ethan Yonker8373cfe2017-09-08 06:50:54 -0500226 return write_bootloader_message(boot, err);
227}
228
229bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
230 bootloader_message boot = {};
231 strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
232 strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
233 for (const auto& s : options) {
234 strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
235 if (s.substr(s.size() - 1) != "\n") {
236 strlcat(boot.recovery, "\n", sizeof(boot.recovery));
237 }
238 }
239 return write_bootloader_message(boot, err);
240}
241
242bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
243 bootloader_message boot;
244 if (!read_bootloader_message(&boot, err)) {
245 return false;
246 }
247
248 // Zero out the entire fields.
249 memset(boot.command, 0, sizeof(boot.command));
250 memset(boot.recovery, 0, sizeof(boot.recovery));
251
252 strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
253 strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
254 for (const auto& s : options) {
255 strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
256 if (s.back() != '\n') {
257 strlcat(boot.recovery, "\n", sizeof(boot.recovery));
258 }
259 }
260 return write_bootloader_message(boot, err);
261}
262
263bool write_reboot_bootloader(std::string* err) {
264 bootloader_message boot;
265 if (!read_bootloader_message(&boot, err)) {
266 return false;
267 }
268 if (boot.command[0] != '\0') {
269 *err = "Bootloader command pending.";
270 return false;
271 }
272 strlcpy(boot.command, "bootonce-bootloader", sizeof(boot.command));
273 return write_bootloader_message(boot, err);
274}
275
276bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
277 std::string misc_blk_device = get_misc_blk_device(err);
278 if (misc_blk_device.empty()) {
279 return false;
280 }
281 package_data->resize(size);
282 return read_misc_partition(&(*package_data)[0], size, misc_blk_device,
283 WIPE_PACKAGE_OFFSET_IN_MISC, err);
284}
285
286bool write_wipe_package(const std::string& package_data, std::string* err) {
287 std::string misc_blk_device = get_misc_blk_device(err);
288 if (misc_blk_device.empty()) {
289 return false;
290 }
291 return write_misc_partition(package_data.data(), package_data.size(),
292 WIPE_PACKAGE_OFFSET_IN_MISC, err);
293}
294
295extern "C" bool write_reboot_bootloader(void) {
296 std::string err;
297 return write_reboot_bootloader(&err);
298}
299
300extern "C" bool write_bootloader_message(const char* options) {
301 std::string err;
302 bootloader_message boot = {};
303 memcpy(&boot, options, sizeof(boot));
304 return write_bootloader_message(boot, &err);
305}
306
307static const char *COMMAND_FILE = "/cache/recovery/command";
308static const int MAX_ARG_LENGTH = 4096;
309static const int MAX_ARGS = 100;
310
311// command line args come from, in decreasing precedence:
312// - the actual command line
313// - the bootloader control block (one per line, after "recovery")
314// - the contents of COMMAND_FILE (one per line)
315void
316get_args(int *argc, char ***argv) {
317 bootloader_message boot = {};
318 std::string err;
319 if (!read_bootloader_message(&boot, &err)) {
320 printf("%s\n", err.c_str());
321 // If fails, leave a zeroed bootloader_message.
322 memset(&boot, 0, sizeof(boot));
323 }
324 //stage = strndup(boot.stage, sizeof(boot.stage));
325
Ethan Yonker7c997912017-11-30 07:24:06 -0600326 if (boot.command[0] != 0 && boot.command[0] != (char)255) {
Ethan Yonker8373cfe2017-09-08 06:50:54 -0500327 printf("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
328 }
329
Ethan Yonker7c997912017-11-30 07:24:06 -0600330 if (boot.status[0] != 0 && boot.status[0] != (char)255) {
Ethan Yonker8373cfe2017-09-08 06:50:54 -0500331 printf("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
332 }
333
334 // --- if arguments weren't supplied, look in the bootloader control block
335 if (*argc <= 1) {
336 boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
337 const char *arg = strtok(boot.recovery, "\n");
338 if (arg != NULL && !strcmp(arg, "recovery")) {
339 *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
340 (*argv)[0] = strdup(arg);
341 for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
342 if ((arg = strtok(NULL, "\n")) == NULL) break;
343
344// if the device does not have an own recovery key combo we just want to open TWRP after
345// walking through the factory reset screen - without actually doing a factory reset
346#ifdef IGNORE_MISC_WIPE_DATA
347 if (!strcmp(arg, "--wipe_data")) {
Ethan Yonker606cc912017-11-30 10:24:33 -0600348 (*argv)[*argc] = NULL;
Ethan Yonker8373cfe2017-09-08 06:50:54 -0500349 *argc = *argc -1;
350 printf("Bootloader arg \"%s\" ignored because TWRP was compiled with TW_IGNORE_MISC_WIPE_DATA\n", arg);
351 continue;
352 }
353#endif
354 (*argv)[*argc] = strdup(arg);
355 }
356 printf("Got arguments from boot message\n");
Ethan Yonker32e803a2017-11-18 08:10:51 -0600357 } else if (boot.recovery[0] != 0 && boot.recovery[0] != (char)255) {
Ethan Yonker8373cfe2017-09-08 06:50:54 -0500358 printf("Bad boot message\n\"%.20s\"\n", boot.recovery);
359 }
360 }
361
362 // --- if that doesn't work, try the command file (if we have /cache).
363 if (*argc <= 1/* && has_cache*/) {
364 FILE *fp = fopen(COMMAND_FILE, "r");
365 if (fp != NULL) {
366 char *token;
367 char *argv0 = (*argv)[0];
368 *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
369 (*argv)[0] = argv0; // use the same program name
370
371 char buf[MAX_ARG_LENGTH];
372 for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
373 if (!fgets(buf, sizeof(buf), fp)) break;
374 token = strtok(buf, "\r\n");
375 if (token != NULL) {
376 (*argv)[*argc] = strdup(token); // Strip newline.
377 } else {
378 --*argc;
379 }
380 }
381
382 fclose(fp);
383 printf("Got arguments from %s\n", COMMAND_FILE);
384 }
385 }
386
387 // --> write the arguments we have back into the bootloader control block
388 // always boot into recovery after this (until finish_recovery() is called)
389 strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
390 strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
391 int i;
392 for (i = 1; i < *argc; ++i) {
393 strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
394 strlcat(boot.recovery, "\n", sizeof(boot.recovery));
395 }
396 if (!write_bootloader_message(boot, &err)) {
397 printf("%s\n", err.c_str());
398 }
399}