blob: eb99bdb42665f198416ed4ff6ce4bfb01ec51efb [file] [log] [blame]
Yabin Cuia58a6db2016-04-06 15:52:18 -07001/*
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
Yabin Cui2f272c02016-06-24 18:22:02 -070017#include <bootloader_message/bootloader_message.h>
18
Yabin Cuia58a6db2016-04-06 15:52:18 -070019#include <errno.h>
20#include <fcntl.h>
21#include <string.h>
Ethan Yonkerb5236502016-11-19 22:24:59 -060022#include <stdlib.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <sys/types.h>
Yabin Cuia58a6db2016-04-06 15:52:18 -070026#include <sys/system_properties.h>
27
28#include <string>
29#include <vector>
30
Ethan Yonkerb5236502016-11-19 22:24:59 -060031/*
Yabin Cuia58a6db2016-04-06 15:52:18 -070032#include <android-base/file.h>
33#include <android-base/stringprintf.h>
34#include <android-base/unique_fd.h>
Ethan Yonkerb5236502016-11-19 22:24:59 -060035*/
36#ifndef EXCLUDE_FS_MGR
Yabin Cuia58a6db2016-04-06 15:52:18 -070037#include <fs_mgr.h>
Ethan Yonkerb5236502016-11-19 22:24:59 -060038#endif
Yabin Cuia58a6db2016-04-06 15:52:18 -070039
Ethan Yonkerb5236502016-11-19 22:24:59 -060040static std::string misc_blkdev;
41
42void set_misc_device(const char* type, const char* name) {
43 misc_blkdev = name;
44}
45
46#ifndef EXCLUDE_FS_MGR
Yabin Cuia58a6db2016-04-06 15:52:18 -070047static struct fstab* read_fstab(std::string* err) {
48 // The fstab path is always "/fstab.${ro.hardware}".
49 std::string fstab_path = "/fstab.";
50 char value[PROP_VALUE_MAX];
51 if (__system_property_get("ro.hardware", value) == 0) {
52 *err = "failed to get ro.hardware";
53 return nullptr;
54 }
55 fstab_path += value;
56 struct fstab* fstab = fs_mgr_read_fstab(fstab_path.c_str());
57 if (fstab == nullptr) {
58 *err = "failed to read " + fstab_path;
59 }
60 return fstab;
61}
Ethan Yonkerb5236502016-11-19 22:24:59 -060062#endif
Yabin Cuia58a6db2016-04-06 15:52:18 -070063
64static std::string get_misc_blk_device(std::string* err) {
Ethan Yonkerb5236502016-11-19 22:24:59 -060065#ifdef EXCLUDE_FS_MGR
66 return misc_blkdev;
67#else
Yabin Cuia58a6db2016-04-06 15:52:18 -070068 struct fstab* fstab = read_fstab(err);
69 if (fstab == nullptr) {
70 return "";
71 }
72 fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
73 if (record == nullptr) {
74 *err = "failed to find /misc partition";
75 return "";
76 }
77 return record->blk_device;
Ethan Yonkerb5236502016-11-19 22:24:59 -060078#endif
Yabin Cuia58a6db2016-04-06 15:52:18 -070079}
80
Ethan Yonkerb5236502016-11-19 22:24:59 -060081
Yabin Cui2f272c02016-06-24 18:22:02 -070082// In recovery mode, recovery can get started and try to access the misc
83// device before the kernel has actually created it.
84static bool wait_for_device(const std::string& blk_device, std::string* err) {
85 int tries = 0;
86 int ret;
87 err->clear();
88 do {
89 ++tries;
90 struct stat buf;
91 ret = stat(blk_device.c_str(), &buf);
92 if (ret == -1) {
Ethan Yonkerb5236502016-11-19 22:24:59 -060093 char buffer[2048];
94 sprintf(buffer, "failed to stat %s try %d: %s\n",
95 blk_device.c_str(), tries, strerror(errno));
96 *err += buffer;
97 /*
Yabin Cui2f272c02016-06-24 18:22:02 -070098 *err += android::base::StringPrintf("failed to stat %s try %d: %s\n",
99 blk_device.c_str(), tries, strerror(errno));
Ethan Yonkerb5236502016-11-19 22:24:59 -0600100 */
Yabin Cui2f272c02016-06-24 18:22:02 -0700101 sleep(1);
102 }
103 } while (ret && tries < 10);
104
105 if (ret) {
Ethan Yonkerb5236502016-11-19 22:24:59 -0600106 *err += "failed to stat " + blk_device + "\n";
107 /*
Yabin Cui2f272c02016-06-24 18:22:02 -0700108 *err += android::base::StringPrintf("failed to stat %s\n", blk_device.c_str());
Ethan Yonkerb5236502016-11-19 22:24:59 -0600109 */
Yabin Cui2f272c02016-06-24 18:22:02 -0700110 }
111 return ret == 0;
112}
113
114static bool read_misc_partition(void* p, size_t size, size_t offset, std::string* err) {
115 std::string misc_blk_device = get_misc_blk_device(err);
116 if (misc_blk_device.empty()) {
117 return false;
118 }
119 if (!wait_for_device(misc_blk_device, err)) {
120 return false;
121 }
Ethan Yonkerb5236502016-11-19 22:24:59 -0600122 int fd(open(misc_blk_device.c_str(), O_RDONLY));
123 if (fd < 0) {
124 *err = "failed to open " + misc_blk_device + ": ";
125 *err += strerror(errno);
126 /*
Yabin Cui2f272c02016-06-24 18:22:02 -0700127 *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
128 strerror(errno));
Ethan Yonkerb5236502016-11-19 22:24:59 -0600129 */
Yabin Cui2f272c02016-06-24 18:22:02 -0700130 return false;
131 }
Ethan Yonkerb5236502016-11-19 22:24:59 -0600132 if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
133 *err = "failed to lseek " + misc_blk_device + ": ";
134 *err += strerror(errno);
135 close(fd);
136 /*
Yabin Cui2f272c02016-06-24 18:22:02 -0700137 *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
138 strerror(errno));
Ethan Yonkerb5236502016-11-19 22:24:59 -0600139 */
Yabin Cui2f272c02016-06-24 18:22:02 -0700140 return false;
141 }
Ethan Yonkerb5236502016-11-19 22:24:59 -0600142 if (read(fd, p, size) != size) {
143 *err = "failed to read " + misc_blk_device + ": ";
144 *err += strerror(errno);
145 close(fd);
146 /*
Yabin Cui2f272c02016-06-24 18:22:02 -0700147 *err = android::base::StringPrintf("failed to read %s: %s", misc_blk_device.c_str(),
148 strerror(errno));
Ethan Yonkerb5236502016-11-19 22:24:59 -0600149 */
Yabin Cui2f272c02016-06-24 18:22:02 -0700150 return false;
151 }
Ethan Yonkerb5236502016-11-19 22:24:59 -0600152 close(fd);
Yabin Cui2f272c02016-06-24 18:22:02 -0700153 return true;
154}
155
156static bool write_misc_partition(const void* p, size_t size, size_t offset, std::string* err) {
Yabin Cuia58a6db2016-04-06 15:52:18 -0700157 std::string misc_blk_device = get_misc_blk_device(err);
158 if (misc_blk_device.empty()) {
Ethan Yonkerb5236502016-11-19 22:24:59 -0600159 *err = "no misc device set";
Yabin Cuia58a6db2016-04-06 15:52:18 -0700160 return false;
161 }
Ethan Yonkerb5236502016-11-19 22:24:59 -0600162 int fd = (open(misc_blk_device.c_str(), O_WRONLY | O_SYNC));
163 if (fd == -1) {
164 *err = "failed to open " + misc_blk_device + ": ";
165 *err += strerror(errno);
166 /*
Yabin Cuia58a6db2016-04-06 15:52:18 -0700167 *err = android::base::StringPrintf("failed to open %s: %s", misc_blk_device.c_str(),
168 strerror(errno));
Ethan Yonkerb5236502016-11-19 22:24:59 -0600169 */
Yabin Cuia58a6db2016-04-06 15:52:18 -0700170 return false;
171 }
Ethan Yonkerb5236502016-11-19 22:24:59 -0600172 if (lseek(fd, static_cast<off_t>(offset), SEEK_SET) != static_cast<off_t>(offset)) {
173 *err = "failed to lseek " + misc_blk_device + ": ";
174 *err += strerror(errno);
175 close(fd);
176 /*
Yabin Cui6faf0262016-06-09 14:09:39 -0700177 *err = android::base::StringPrintf("failed to lseek %s: %s", misc_blk_device.c_str(),
178 strerror(errno));
Ethan Yonkerb5236502016-11-19 22:24:59 -0600179 */
Yabin Cui6faf0262016-06-09 14:09:39 -0700180 return false;
181 }
Ethan Yonkerb5236502016-11-19 22:24:59 -0600182 if (write(fd, p, size) != size) {
183 *err = "failed to write " + misc_blk_device + ": ";
184 *err += strerror(errno);
185 close(fd);
186 /*
Yabin Cuia58a6db2016-04-06 15:52:18 -0700187 *err = android::base::StringPrintf("failed to write %s: %s", misc_blk_device.c_str(),
188 strerror(errno));
Ethan Yonkerb5236502016-11-19 22:24:59 -0600189 */
Yabin Cuia58a6db2016-04-06 15:52:18 -0700190 return false;
191 }
Yabin Cui6faf0262016-06-09 14:09:39 -0700192
Yabin Cuia58a6db2016-04-06 15:52:18 -0700193 // TODO: O_SYNC and fsync duplicates each other?
Ethan Yonkerb5236502016-11-19 22:24:59 -0600194 if (fsync(fd) == -1) {
195 *err = "failed to fsync " + misc_blk_device + ": ";
196 *err += strerror(errno);
197 close(fd);
198 /*
Yabin Cuia58a6db2016-04-06 15:52:18 -0700199 *err = android::base::StringPrintf("failed to fsync %s: %s", misc_blk_device.c_str(),
200 strerror(errno));
Ethan Yonkerb5236502016-11-19 22:24:59 -0600201 */
Yabin Cuia58a6db2016-04-06 15:52:18 -0700202 return false;
203 }
Ethan Yonkerb5236502016-11-19 22:24:59 -0600204 close(fd);
Yabin Cuia58a6db2016-04-06 15:52:18 -0700205 return true;
206}
207
Yabin Cui2f272c02016-06-24 18:22:02 -0700208bool read_bootloader_message(bootloader_message* boot, std::string* err) {
209 return read_misc_partition(boot, sizeof(*boot), BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
210}
211
212bool write_bootloader_message(const bootloader_message& boot, std::string* err) {
Yabin Cui6faf0262016-06-09 14:09:39 -0700213 return write_misc_partition(&boot, sizeof(boot), BOOTLOADER_MESSAGE_OFFSET_IN_MISC, err);
214}
215
Yabin Cuia58a6db2016-04-06 15:52:18 -0700216bool clear_bootloader_message(std::string* err) {
217 bootloader_message boot = {};
218 return write_bootloader_message(boot, err);
219}
220
221bool write_bootloader_message(const std::vector<std::string>& options, std::string* err) {
222 bootloader_message boot = {};
223 strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
224 strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
225 for (const auto& s : options) {
226 strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery));
Ethan Yonkerb5236502016-11-19 22:24:59 -0600227 if (s.substr(s.size() - 1) != "\n") {
Yabin Cuia58a6db2016-04-06 15:52:18 -0700228 strlcat(boot.recovery, "\n", sizeof(boot.recovery));
229 }
230 }
231 return write_bootloader_message(boot, err);
232}
233
Yabin Cui2f272c02016-06-24 18:22:02 -0700234bool read_wipe_package(std::string* package_data, size_t size, std::string* err) {
235 package_data->resize(size);
236 return read_misc_partition(&(*package_data)[0], size, WIPE_PACKAGE_OFFSET_IN_MISC, err);
237}
238
Yabin Cui6faf0262016-06-09 14:09:39 -0700239bool write_wipe_package(const std::string& package_data, std::string* err) {
240 return write_misc_partition(package_data.data(), package_data.size(),
241 WIPE_PACKAGE_OFFSET_IN_MISC, err);
242}
243
Yabin Cuia58a6db2016-04-06 15:52:18 -0700244extern "C" bool write_bootloader_message(const char* options) {
245 std::string err;
Ethan Yonkerb5236502016-11-19 22:24:59 -0600246 bootloader_message boot = {};
247 memcpy(&boot, options, sizeof(boot));
248 return write_bootloader_message(boot, &err);
249}
250
251static const char *COMMAND_FILE = "/cache/recovery/command";
252static const int MAX_ARG_LENGTH = 4096;
253static const int MAX_ARGS = 100;
254
255// command line args come from, in decreasing precedence:
256// - the actual command line
257// - the bootloader control block (one per line, after "recovery")
258// - the contents of COMMAND_FILE (one per line)
259void
260get_args(int *argc, char ***argv) {
261 bootloader_message boot = {};
262 std::string err;
263 if (!read_bootloader_message(&boot, &err)) {
264 printf("%s\n", err.c_str());
265 // If fails, leave a zeroed bootloader_message.
266 memset(&boot, 0, sizeof(boot));
267 }
268 //stage = strndup(boot.stage, sizeof(boot.stage));
269
270 if (boot.command[0] != 0 && boot.command[0] != 255) {
271 printf("Boot command: %.*s\n", (int)sizeof(boot.command), boot.command);
272 }
273
274 if (boot.status[0] != 0 && boot.status[0] != 255) {
275 printf("Boot status: %.*s\n", (int)sizeof(boot.status), boot.status);
276 }
277
278 // --- if arguments weren't supplied, look in the bootloader control block
279 if (*argc <= 1) {
280 boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
281 const char *arg = strtok(boot.recovery, "\n");
282 if (arg != NULL && !strcmp(arg, "recovery")) {
283 *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
284 (*argv)[0] = strdup(arg);
285 for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
286 if ((arg = strtok(NULL, "\n")) == NULL) break;
287 (*argv)[*argc] = strdup(arg);
288 }
289 printf("Got arguments from boot message\n");
290 } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
291 printf("Bad boot message\n\"%.20s\"\n", boot.recovery);
292 }
293 }
294
295 // --- if that doesn't work, try the command file (if we have /cache).
296 if (*argc <= 1/* && has_cache*/) {
297 FILE *fp = fopen(COMMAND_FILE, "r");
298 if (fp != NULL) {
299 char *token;
300 char *argv0 = (*argv)[0];
301 *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
302 (*argv)[0] = argv0; // use the same program name
303
304 char buf[MAX_ARG_LENGTH];
305 for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
306 if (!fgets(buf, sizeof(buf), fp)) break;
307 token = strtok(buf, "\r\n");
308 if (token != NULL) {
309 (*argv)[*argc] = strdup(token); // Strip newline.
310 } else {
311 --*argc;
312 }
313 }
314
315 fclose(fp);
316 printf("Got arguments from %s\n", COMMAND_FILE);
317 }
318 }
319
320 // --> write the arguments we have back into the bootloader control block
321 // always boot into recovery after this (until finish_recovery() is called)
322 strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
323 strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
324 int i;
325 for (i = 1; i < *argc; ++i) {
326 strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
327 strlcat(boot.recovery, "\n", sizeof(boot.recovery));
328 }
329 if (!write_bootloader_message(boot, &err)) {
330 printf("%s\n", err.c_str());
331 }
Yabin Cuia58a6db2016-04-06 15:52:18 -0700332}