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