blob: ff0dc68262da998edd3c43da209df1cac7c5984c [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
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#include "bootloader.h"
18#include "common.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040019extern "C" {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080020#include "mtdutils/mtdutils.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040021}
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080022#include "roots.h"
23
24#include <errno.h>
25#include <stdio.h>
26#include <string.h>
Doug Zongkercfd256a2011-04-22 09:26:44 -070027#include <sys/stat.h>
28#include <unistd.h>
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080029
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070030static int get_bootloader_message_mtd(struct bootloader_message *out, const Volume* v);
31static int set_bootloader_message_mtd(const struct bootloader_message *in, const Volume* v);
32static int get_bootloader_message_block(struct bootloader_message *out, const Volume* v);
33static int set_bootloader_message_block(const struct bootloader_message *in, const Volume* v);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080034
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070035int get_bootloader_message(struct bootloader_message *out) {
Dees_Troy2673cec2013-04-02 20:22:16 +000036 Volume* v = NULL;//volume_for_path("/misc");
Adam Blissb2ceb692011-07-13 15:13:54 -070037 if (v == NULL) {
38 LOGE("Cannot load volume /misc!\n");
39 return -1;
40 }
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070041 if (strcmp(v->fs_type, "mtd") == 0) {
42 return get_bootloader_message_mtd(out, v);
43 } else if (strcmp(v->fs_type, "emmc") == 0) {
44 return get_bootloader_message_block(out, v);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080045 }
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070046 LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
47 return -1;
Doug Zongker04611da2010-08-12 15:35:29 -070048}
49
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070050int set_bootloader_message(const struct bootloader_message *in) {
Dees_Troy2673cec2013-04-02 20:22:16 +000051 Volume* v = NULL;//volume_for_path("/misc");
Adam Blissb2ceb692011-07-13 15:13:54 -070052 if (v == NULL) {
53 LOGE("Cannot load volume /misc!\n");
54 return -1;
55 }
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070056 if (strcmp(v->fs_type, "mtd") == 0) {
57 return set_bootloader_message_mtd(in, v);
58 } else if (strcmp(v->fs_type, "emmc") == 0) {
59 return set_bootloader_message_block(in, v);
Doug Zongker04611da2010-08-12 15:35:29 -070060 }
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070061 LOGE("unknown misc partition fs_type \"%s\"\n", v->fs_type);
62 return -1;
Doug Zongker04611da2010-08-12 15:35:29 -070063}
64
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070065// ------------------------------
66// for misc partitions on MTD
67// ------------------------------
Doug Zongker04611da2010-08-12 15:35:29 -070068
69static const int MISC_PAGES = 3; // number of pages to save
70static const int MISC_COMMAND_PAGE = 1; // bootloader command is this page
71
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070072static int get_bootloader_message_mtd(struct bootloader_message *out,
73 const Volume* v) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080074 size_t write_size;
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070075 mtd_scan_partitions();
76 const MtdPartition *part = mtd_find_partition_by_name(v->device);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080077 if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070078 LOGE("Can't find %s\n", v->device);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080079 return -1;
80 }
81
82 MtdReadContext *read = mtd_read_partition(part);
83 if (read == NULL) {
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070084 LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080085 return -1;
86 }
87
88 const ssize_t size = write_size * MISC_PAGES;
89 char data[size];
90 ssize_t r = mtd_read_data(read, data, size);
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070091 if (r != size) LOGE("Can't read %s\n(%s)\n", v->device, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080092 mtd_read_close(read);
93 if (r != size) return -1;
94
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080095 memcpy(out, &data[write_size * MISC_COMMAND_PAGE], sizeof(*out));
96 return 0;
97}
Doug Zongkercc8cd3f2010-09-20 12:16:13 -070098static int set_bootloader_message_mtd(const struct bootloader_message *in,
99 const Volume* v) {
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800100 size_t write_size;
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700101 mtd_scan_partitions();
102 const MtdPartition *part = mtd_find_partition_by_name(v->device);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800103 if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700104 LOGE("Can't find %s\n", v->device);
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800105 return -1;
106 }
107
108 MtdReadContext *read = mtd_read_partition(part);
109 if (read == NULL) {
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700110 LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800111 return -1;
112 }
113
114 ssize_t size = write_size * MISC_PAGES;
115 char data[size];
116 ssize_t r = mtd_read_data(read, data, size);
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700117 if (r != size) LOGE("Can't read %s\n(%s)\n", v->device, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800118 mtd_read_close(read);
119 if (r != size) return -1;
120
121 memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in));
122
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800123 MtdWriteContext *write = mtd_write_partition(part);
124 if (write == NULL) {
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700125 LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800126 return -1;
127 }
128 if (mtd_write_data(write, data, size) != size) {
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700129 LOGE("Can't write %s\n(%s)\n", v->device, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800130 mtd_write_close(write);
131 return -1;
132 }
133 if (mtd_write_close(write)) {
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700134 LOGE("Can't finish %s\n(%s)\n", v->device, strerror(errno));
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800135 return -1;
136 }
137
138 LOGI("Set boot command \"%s\"\n", in->command[0] != 255 ? in->command : "");
139 return 0;
140}
Doug Zongker04611da2010-08-12 15:35:29 -0700141
Dees_Troy2673cec2013-04-02 20:22:16 +0000142int set_bootloader_message_mtd_name(const struct bootloader_message *in,
143 const char* mtd_name) {
144 size_t write_size;
145 mtd_scan_partitions();
146 const MtdPartition *part = mtd_find_partition_by_name(mtd_name);
147 if (part == NULL || mtd_partition_info(part, NULL, NULL, &write_size)) {
148 printf("Can't find %s\n", mtd_name);
149 return -1;
150 }
151
152 MtdReadContext *read = mtd_read_partition(part);
153 if (read == NULL) {
154 printf("Can't open %s\n(%s)\n", mtd_name, strerror(errno));
155 return -1;
156 }
157
158 ssize_t size = write_size * MISC_PAGES;
159 char data[size];
160 ssize_t r = mtd_read_data(read, data, size);
161 if (r != size) printf("Can't read %s\n(%s)\n", mtd_name, strerror(errno));
162 mtd_read_close(read);
163 if (r != size) return -1;
164
165 memcpy(&data[write_size * MISC_COMMAND_PAGE], in, sizeof(*in));
166
167 MtdWriteContext *write = mtd_write_partition(part);
168 if (write == NULL) {
169 printf("Can't open %s\n(%s)\n", mtd_name, strerror(errno));
170 return -1;
171 }
172 if (mtd_write_data(write, data, size) != size) {
173 printf("Can't write %s\n(%s)\n", mtd_name, strerror(errno));
174 mtd_write_close(write);
175 return -1;
176 }
177 if (mtd_write_close(write)) {
178 printf("Can't finish %s\n(%s)\n", mtd_name, strerror(errno));
179 return -1;
180 }
181
182 printf("Set boot command \"%s\"\n", in->command[0] != 255 ? in->command : "");
183 return 0;
184}
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700185
186// ------------------------------------
187// for misc partitions on block devices
188// ------------------------------------
189
Doug Zongkercfd256a2011-04-22 09:26:44 -0700190static void wait_for_device(const char* fn) {
191 int tries = 0;
192 int ret;
193 struct stat buf;
194 do {
195 ++tries;
196 ret = stat(fn, &buf);
197 if (ret) {
198 printf("stat %s try %d: %s\n", fn, tries, strerror(errno));
199 sleep(1);
200 }
201 } while (ret && tries < 10);
202 if (ret) {
203 printf("failed to stat %s\n", fn);
204 }
205}
206
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700207static int get_bootloader_message_block(struct bootloader_message *out,
208 const Volume* v) {
Doug Zongkercfd256a2011-04-22 09:26:44 -0700209 wait_for_device(v->device);
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700210 FILE* f = fopen(v->device, "rb");
211 if (f == NULL) {
212 LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
213 return -1;
214 }
215 struct bootloader_message temp;
216 int count = fread(&temp, sizeof(temp), 1, f);
217 if (count != 1) {
218 LOGE("Failed reading %s\n(%s)\n", v->device, strerror(errno));
219 return -1;
220 }
221 if (fclose(f) != 0) {
222 LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno));
223 return -1;
224 }
225 memcpy(out, &temp, sizeof(temp));
226 return 0;
227}
228
229static int set_bootloader_message_block(const struct bootloader_message *in,
230 const Volume* v) {
Doug Zongkercfd256a2011-04-22 09:26:44 -0700231 wait_for_device(v->device);
Doug Zongkercc8cd3f2010-09-20 12:16:13 -0700232 FILE* f = fopen(v->device, "wb");
233 if (f == NULL) {
234 LOGE("Can't open %s\n(%s)\n", v->device, strerror(errno));
235 return -1;
236 }
237 int count = fwrite(in, sizeof(*in), 1, f);
238 if (count != 1) {
239 LOGE("Failed writing %s\n(%s)\n", v->device, strerror(errno));
240 return -1;
241 }
242 if (fclose(f) != 0) {
243 LOGE("Failed closing %s\n(%s)\n", v->device, strerror(errno));
244 return -1;
245 }
246 return 0;
247}
Dees_Troy2673cec2013-04-02 20:22:16 +0000248
249int set_bootloader_message_block_name(const struct bootloader_message *in,
250 const char* block_name) {
251 wait_for_device(block_name);
252 FILE* f = fopen(block_name, "wb");
253 if (f == NULL) {
254 printf("Can't open %s\n(%s)\n", block_name, strerror(errno));
255 return -1;
256 }
257 int count = fwrite(in, sizeof(*in), 1, f);
258 if (count != 1) {
259 printf("Failed writing %s\n(%s)\n", block_name, strerror(errno));
260 return -1;
261 }
262 if (fclose(f) != 0) {
263 printf("Failed closing %s\n(%s)\n", block_name, strerror(errno));
264 return -1;
265 }
266 return 0;
267}
Dees_Troya449a6f2013-04-07 17:50:11 -0500268
269static const char *COMMAND_FILE = "/cache/recovery/command";
270static const int MAX_ARG_LENGTH = 4096;
271static const int MAX_ARGS = 100;
272
273// command line args come from, in decreasing precedence:
274// - the actual command line
275// - the bootloader control block (one per line, after "recovery")
276// - the contents of COMMAND_FILE (one per line)
277void
278get_args(int *argc, char ***argv) {
279 struct bootloader_message boot;
280 memset(&boot, 0, sizeof(boot));
281 get_bootloader_message(&boot); // this may fail, leaving a zeroed structure
282
283 if (boot.command[0] != 0 && boot.command[0] != 255) {
284 LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
285 }
286
287 if (boot.status[0] != 0 && boot.status[0] != 255) {
288 LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
289 }
290
291 // --- if arguments weren't supplied, look in the bootloader control block
292 if (*argc <= 1) {
293 boot.recovery[sizeof(boot.recovery) - 1] = '\0'; // Ensure termination
294 const char *arg = strtok(boot.recovery, "\n");
295 if (arg != NULL && !strcmp(arg, "recovery")) {
296 *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
297 (*argv)[0] = strdup(arg);
298 for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
299 if ((arg = strtok(NULL, "\n")) == NULL) break;
300 (*argv)[*argc] = strdup(arg);
301 }
302 LOGI("Got arguments from boot message\n");
303 } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
304 LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
305 }
306 }
307
308 // --- if that doesn't work, try the command file
309 if (*argc <= 1) {
310 FILE *fp = fopen(COMMAND_FILE, "r");
311 if (fp != NULL) {
312 char *argv0 = (*argv)[0];
313 *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
314 (*argv)[0] = argv0; // use the same program name
315
316 char buf[MAX_ARG_LENGTH];
317 for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
318 if (!fgets(buf, sizeof(buf), fp)) break;
319 (*argv)[*argc] = strdup(strtok(buf, "\r\n")); // Strip newline.
320 }
321
322 fflush(fp);
323 if (ferror(fp)) LOGE("Error in %s\n(%s)\n", COMMAND_FILE, strerror(errno));
324 fclose(fp);
325 LOGI("Got arguments from %s\n", COMMAND_FILE);
326 }
327 }
328
329 // --> write the arguments we have back into the bootloader control block
330 // always boot into recovery after this (until finish_recovery() is called)
331 strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
332 strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
333 int i;
334 for (i = 1; i < *argc; ++i) {
335 strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
336 strlcat(boot.recovery, "\n", sizeof(boot.recovery));
337 }
338 set_bootloader_message(&boot);
339}