diff options
Diffstat (limited to 'fastboot')
-rw-r--r-- | fastboot/Android.mk | 2 | ||||
-rw-r--r-- | fastboot/engine.c | 41 | ||||
-rw-r--r-- | fastboot/fastboot.c | 301 | ||||
-rw-r--r-- | fastboot/fastboot.h | 5 | ||||
-rw-r--r-- | fastboot/protocol.c | 187 | ||||
-rw-r--r-- | fastboot/util_windows.c | 45 |
6 files changed, 486 insertions, 95 deletions
diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 089b9bb..e3261a7 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -48,7 +48,7 @@ ifeq ($(HOST_OS),windows) LOCAL_C_INCLUDES += development/host/windows/usb/api endif -LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz libext4_utils libz +LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz libext4_utils libsparse libz ifneq ($(HOST_OS),windows) ifeq ($(HAVE_SELINUX), true) diff --git a/fastboot/engine.c b/fastboot/engine.c index 46b0828..93be3de 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -28,7 +28,6 @@ #include "fastboot.h" #include "make_ext4fs.h" -#include "ext4_utils.h" #include <stdio.h> #include <stdlib.h> @@ -77,6 +76,7 @@ char *mkmsg(const char *fmt, ...) #define OP_QUERY 3 #define OP_NOTICE 4 #define OP_FORMAT 5 +#define OP_DOWNLOAD_SPARSE 6 typedef struct Action Action; @@ -111,6 +111,20 @@ struct image_data { void generate_ext4_image(struct image_data *image); void cleanup_image(struct image_data *image); +int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...) +{ + char cmd[CMD_SIZE] = "getvar:"; + int getvar_len = strlen(cmd); + va_list args; + + response[FB_RESPONSE_SZ] = '\0'; + va_start(args, fmt); + vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args); + va_end(args); + cmd[CMD_SIZE - 1] = '\0'; + return fb_command_response(usb, cmd, response); +} + struct generator { char *fs_type; @@ -278,9 +292,7 @@ int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported) unsigned i; char cmd[CMD_SIZE]; - response[FB_RESPONSE_SZ] = '\0'; - snprintf(cmd, sizeof(cmd), "getvar:partition-type:%s", partition); - status = fb_command_response(usb, cmd, response); + status = fb_getvar(usb, response, "partition-type:%s", partition); if (status) { if (skip_if_not_supported) { fprintf(stderr, @@ -312,9 +324,7 @@ int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported) return -1; } - response[FB_RESPONSE_SZ] = '\0'; - snprintf(cmd, sizeof(cmd), "getvar:partition-size:%s", partition); - status = fb_command_response(usb, cmd, response); + status = fb_getvar(usb, response, "partition-size:%s", partition); if (status) { if (skip_if_not_supported) { fprintf(stderr, @@ -372,6 +382,19 @@ void fb_queue_flash(const char *ptn, void *data, unsigned sz) a->msg = mkmsg("writing '%s'", ptn); } +void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz) +{ + Action *a; + + a = queue_action(OP_DOWNLOAD_SPARSE, ""); + a->data = s; + a->size = 0; + a->msg = mkmsg("sending sparse '%s' (%d KB)", ptn, sz / 1024); + + a = queue_action(OP_COMMAND, "flash:%s", ptn); + a->msg = mkmsg("writing '%s'", ptn); +} + static int match(char *str, const char **value, unsigned count) { const char *val; @@ -570,6 +593,10 @@ int fb_execute_queue(usb_handle *usb) status = fb_format(a, usb, (int)a->data); status = a->func(a, status, status ? fb_get_error() : ""); if (status) break; + } else if (a->op == OP_DOWNLOAD_SPARSE) { + status = fb_download_data_sparse(usb, a->data); + status = a->func(a, status, status ? fb_get_error() : ""); + if (status) break; } else { die("bogus action"); } diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index 848cea3..ff99173 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -26,22 +26,36 @@ * SUCH DAMAGE. */ +#define _LARGEFILE64_SOURCE + #include <stdio.h> #include <stdlib.h> #include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <limits.h> #include <ctype.h> +#include <getopt.h> #include <sys/time.h> +#include <sys/types.h> + #include <bootimg.h> +#include <sparse/sparse.h> #include <zipfile/zipfile.h> #include "fastboot.h" +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#define DEFAULT_SPARSE_LIMIT (256 * 1024 * 1024) + char cur_product[FB_RESPONSE_SZ + 1]; void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline); @@ -58,6 +72,8 @@ static const char *product = 0; static const char *cmdline = 0; static int wipe_data = 0; static unsigned short vendor_id = 0; +static int64_t sparse_limit = -1; +static int64_t target_sparse_limit = -1; static unsigned base_addr = 0x10000000; @@ -116,7 +132,27 @@ char *find_item(const char *item, const char *product) #ifdef _WIN32 void *load_file(const char *fn, unsigned *_sz); +int64_t file_size(const char *fn); #else +#if defined(__APPLE__) && defined(__MACH__) +#define lseek64 lseek +#define off64_t off_t +#endif + +int64_t file_size(const char *fn) +{ + off64_t off; + int fd; + + fd = open(fn, O_RDONLY); + if (fd < 0) return -1; + + off = lseek64(fd, 0, SEEK_END); + close(fd); + + return off; +} + void *load_file(const char *fn, unsigned *_sz) { char *data; @@ -244,6 +280,8 @@ void usage(void) " -i <vendor id> specify a custom USB vendor id\n" " -b <base_addr> specify a custom kernel base address\n" " -n <page size> specify the nand page size. default: 2048\n" + " -S <size>[K|M|G] automatically sparse files greater than\n" + " size. default: 256M, 0 to disable\n" ); } @@ -429,6 +467,110 @@ void queue_info_dump(void) fb_queue_notice("--------------------------------------------"); } + +struct sparse_file **load_sparse_files(const char *fname, int max_size) +{ + int fd; + struct sparse_file *s; + int files; + struct sparse_file **out_s; + + fd = open(fname, O_RDONLY | O_BINARY); + if (fd < 0) { + die("cannot open '%s'\n", fname); + } + + s = sparse_file_import_auto(fd, false); + if (!s) { + die("cannot sparse read file '%s'\n", fname); + } + + files = sparse_file_resparse(s, max_size, NULL, 0); + if (files < 0) { + die("Failed to resparse '%s'\n", fname); + } + + out_s = calloc(sizeof(struct sparse_file *), files + 1); + if (!out_s) { + die("Failed to allocate sparse file array\n"); + } + + files = sparse_file_resparse(s, max_size, out_s, files); + if (files < 0) { + die("Failed to resparse '%s'\n", fname); + } + + return out_s; +} + +static int64_t get_target_sparse_limit(struct usb_handle *usb) +{ + int64_t limit = 0; + char response[FB_RESPONSE_SZ + 1]; + int status = fb_getvar(usb, response, "max-download-size"); + + if (!status) { + limit = strtoul(response, NULL, 0); + if (limit > 0) { + fprintf(stderr, "target reported max download size of %lld bytes\n", + limit); + } + } + + return limit; +} + +static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size) +{ + int64_t limit; + + if (sparse_limit == 0) { + return 0; + } else if (sparse_limit > 0) { + limit = sparse_limit; + } else { + if (target_sparse_limit == -1) { + target_sparse_limit = get_target_sparse_limit(usb); + } + if (target_sparse_limit > 0) { + limit = target_sparse_limit; + } else { + limit = DEFAULT_SPARSE_LIMIT; + } + } + + if (size > limit) { + return limit; + } + + return 0; +} + +void do_flash(usb_handle *usb, const char *pname, const char *fname) +{ + int64_t sz64; + void *data; + int64_t limit; + + sz64 = file_size(fname); + limit = get_sparse_limit(usb, sz64); + if (limit) { + struct sparse_file **s = load_sparse_files(fname, limit); + if (s == NULL) { + die("cannot sparse load '%s'\n", fname); + } + while (*s) { + sz64 = sparse_file_len(*s, true, false); + fb_queue_flash_sparse(pname, *s++, sz64); + } + } else { + unsigned int sz; + data = load_file(fname, &sz); + if (data == 0) die("cannot load '%s'\n", fname); + fb_queue_flash(pname, data, sz); + } +} + void do_update_signature(zipfile_t zip, char *fn) { void *data; @@ -566,6 +708,47 @@ int do_oem_command(int argc, char **argv) return 0; } +static int64_t parse_num(const char *arg) +{ + char *endptr; + unsigned long long num; + + num = strtoull(arg, &endptr, 0); + if (endptr == arg) { + return -1; + } + + if (*endptr == 'k' || *endptr == 'K') { + if (num >= (-1ULL) / 1024) { + return -1; + } + num *= 1024LL; + endptr++; + } else if (*endptr == 'm' || *endptr == 'M') { + if (num >= (-1ULL) / (1024 * 1024)) { + return -1; + } + num *= 1024LL * 1024LL; + endptr++; + } else if (*endptr == 'g' || *endptr == 'G') { + if (num >= (-1ULL) / (1024 * 1024 * 1024)) { + return -1; + } + num *= 1024LL * 1024LL * 1024LL; + endptr++; + } + + if (*endptr != '\0') { + return -1; + } + + if (num > INT64_MAX) { + return -1; + } + + return num; +} + int main(int argc, char **argv) { int wants_wipe = 0; @@ -575,62 +758,83 @@ int main(int argc, char **argv) unsigned sz; unsigned page_size = 2048; int status; + int c; + int r; + + const struct option longopts = { 0, 0, 0, 0 }; + + serial = getenv("ANDROID_SERIAL"); + + while (1) { + c = getopt_long(argc, argv, "wb:n:s:S:p:c:i:m:h", &longopts, NULL); + if (c < 0) { + break; + } + + switch (c) { + case 'w': + wants_wipe = 1; + break; + case 'b': + base_addr = strtoul(optarg, 0, 16); + break; + case 'n': + page_size = (unsigned)strtoul(optarg, NULL, 0); + if (!page_size) die("invalid page size"); + break; + case 's': + serial = optarg; + break; + case 'S': + sparse_limit = parse_num(optarg); + if (sparse_limit < 0) { + die("invalid sparse limit"); + } + break; + case 'p': + product = optarg; + break; + case 'c': + cmdline = optarg; + break; + case 'i': { + char *endptr = NULL; + unsigned long val; + + val = strtoul(optarg, &endptr, 0); + if (!endptr || *endptr != '\0' || (val & ~0xffff)) + die("invalid vendor id '%s'", optarg); + vendor_id = (unsigned short)val; + break; + } + case 'h': + usage(); + return 1; + case '?': + return 1; + default: + abort(); + } + } - skip(1); - if (argc == 0) { + argc -= optind; + argv += optind; + + if (argc == 0 && !wants_wipe) { usage(); return 1; } if (!strcmp(*argv, "devices")) { + skip(1); list_devices(); return 0; } - if (!strcmp(*argv, "help")) { - usage(); - return 0; - } - - - serial = getenv("ANDROID_SERIAL"); + usb = open_device(); while (argc > 0) { - if(!strcmp(*argv, "-w")) { - wants_wipe = 1; - skip(1); - } else if(!strcmp(*argv, "-b")) { - require(2); - base_addr = strtoul(argv[1], 0, 16); - skip(2); - } else if(!strcmp(*argv, "-n")) { - require(2); - page_size = (unsigned)strtoul(argv[1], NULL, 0); - if (!page_size) die("invalid page size"); - skip(2); - } else if(!strcmp(*argv, "-s")) { - require(2); - serial = argv[1]; - skip(2); - } else if(!strcmp(*argv, "-p")) { - require(2); - product = argv[1]; - skip(2); - } else if(!strcmp(*argv, "-c")) { - require(2); - cmdline = argv[1]; - skip(2); - } else if(!strcmp(*argv, "-i")) { - char *endptr = NULL; - unsigned long val; - - require(2); - val = strtoul(argv[1], &endptr, 0); - if (!endptr || *endptr != '\0' || (val & ~0xffff)) - die("invalid vendor id '%s'", argv[1]); - vendor_id = (unsigned short)val; - skip(2); - } else if(!strcmp(*argv, "getvar")) { + if(!strcmp(*argv, "getvar")) { require(2); fb_queue_display(argv[1], argv[1]); skip(2); @@ -687,9 +891,7 @@ int main(int argc, char **argv) skip(2); } if (fname == 0) die("cannot determine image filename for '%s'", pname); - data = load_file(fname, &sz); - if (data == 0) die("cannot load '%s'\n", fname); - fb_queue_flash(pname, data, sz); + do_flash(usb, pname, fname); } else if(!strcmp(*argv, "flash:raw")) { char *pname = argv[1]; char *kname = argv[2]; @@ -719,6 +921,9 @@ int main(int argc, char **argv) wants_reboot = 1; } else if(!strcmp(*argv, "oem")) { argc = do_oem_command(argc, argv); + } else if (!strcmp(*argv, "help")) { + usage(); + return 0; } else { usage(); return 1; @@ -737,8 +942,6 @@ int main(int argc, char **argv) fb_queue_command("reboot-bootloader", "rebooting into bootloader"); } - usb = open_device(); - status = fb_execute_queue(usb); return (status) ? 1 : 0; } diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 1d3e2b8..9177932 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -31,17 +31,22 @@ #include "usb.h" +struct sparse_file; + /* protocol.c - fastboot protocol */ int fb_command(usb_handle *usb, const char *cmd); int fb_command_response(usb_handle *usb, const char *cmd, char *response); int fb_download_data(usb_handle *usb, const void *data, unsigned size); +int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s); char *fb_get_error(void); #define FB_COMMAND_SZ 64 #define FB_RESPONSE_SZ 64 /* engine.c - high level command queue engine */ +int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...); void fb_queue_flash(const char *ptn, void *data, unsigned sz);; +void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz); void fb_queue_erase(const char *ptn); void fb_queue_format(const char *ptn, int skip_if_not_supported); void fb_queue_require(const char *prod, const char *var, int invert, diff --git a/fastboot/protocol.c b/fastboot/protocol.c index e871113..a0e0fd4 100644 --- a/fastboot/protocol.c +++ b/fastboot/protocol.c @@ -26,11 +26,18 @@ * SUCH DAMAGE. */ +#define min(a, b) \ + ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; }) +#define round_down(a, b) \ + ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); }) + #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> +#include <sparse/sparse.h> + #include "fastboot.h" static char ERROR[128]; @@ -40,8 +47,7 @@ char *fb_get_error(void) return ERROR; } -static int check_response(usb_handle *usb, unsigned size, - unsigned data_okay, char *response) +static int check_response(usb_handle *usb, unsigned int size, char *response) { unsigned char status[65]; int r; @@ -82,7 +88,7 @@ static int check_response(usb_handle *usb, unsigned size, return -1; } - if(!memcmp(status, "DATA", 4) && data_okay){ + if(!memcmp(status, "DATA", 4) && size > 0){ unsigned dsize = strtoul((char*) status + 4, 0, 16); if(dsize > size) { strcpy(ERROR, "data size too large"); @@ -100,9 +106,8 @@ static int check_response(usb_handle *usb, unsigned size, return -1; } -static int _command_send(usb_handle *usb, const char *cmd, - const void *data, unsigned size, - char *response) +static int _command_start(usb_handle *usb, const char *cmd, unsigned size, + char *response) { int cmdsize = strlen(cmd); int r; @@ -122,46 +127,81 @@ static int _command_send(usb_handle *usb, const char *cmd, return -1; } - if(data == 0) { - return check_response(usb, size, 0, response); + return check_response(usb, size, response); +} + +static int _command_data(usb_handle *usb, const void *data, unsigned size) +{ + int r; + + r = usb_write(usb, data, size); + if(r < 0) { + sprintf(ERROR, "data transfer failure (%s)", strerror(errno)); + usb_close(usb); + return -1; + } + if(r != ((int) size)) { + sprintf(ERROR, "data transfer failure (short transfer)"); + usb_close(usb); + return -1; } - r = check_response(usb, size, 1, 0); + return r; +} + +static int _command_end(usb_handle *usb) +{ + int r; + r = check_response(usb, 0, 0); if(r < 0) { return -1; } - size = r; + return 0; +} - if(size) { - r = usb_write(usb, data, size); - if(r < 0) { - sprintf(ERROR, "data transfer failure (%s)", strerror(errno)); - usb_close(usb); - return -1; - } - if(r != ((int) size)) { - sprintf(ERROR, "data transfer failure (short transfer)"); - usb_close(usb); - return -1; - } +static int _command_send(usb_handle *usb, const char *cmd, + const void *data, unsigned size, + char *response) +{ + int r; + if (size == 0) { + return -1; } - r = check_response(usb, 0, 0, 0); + r = _command_start(usb, cmd, size, response); + if (r < 0) { + return -1; + } + + r = _command_data(usb, data, size); + if (r < 0) { + return -1; + } + + r = _command_end(usb); if(r < 0) { return -1; - } else { - return size; } + + return size; +} + +static int _command_send_no_data(usb_handle *usb, const char *cmd, + char *response) +{ + int r; + + return _command_start(usb, cmd, 0, response); } int fb_command(usb_handle *usb, const char *cmd) { - return _command_send(usb, cmd, 0, 0, 0); + return _command_send_no_data(usb, cmd, 0); } int fb_command_response(usb_handle *usb, const char *cmd, char *response) { - return _command_send(usb, cmd, 0, 0, response); + return _command_send_no_data(usb, cmd, response); } int fb_download_data(usb_handle *usb, const void *data, unsigned size) @@ -179,3 +219,96 @@ int fb_download_data(usb_handle *usb, const void *data, unsigned size) } } +#define USB_BUF_SIZE 512 +static char usb_buf[USB_BUF_SIZE]; +static int usb_buf_len; + +static int fb_download_data_sparse_write(void *priv, const void *data, int len) +{ + int r; + usb_handle *usb = priv; + int to_write; + const char *ptr = data; + + if (usb_buf_len) { + to_write = min(USB_BUF_SIZE - usb_buf_len, len); + + memcpy(usb_buf + usb_buf_len, ptr, to_write); + usb_buf_len += to_write; + ptr += to_write; + len -= to_write; + } + + if (usb_buf_len == USB_BUF_SIZE) { + r = _command_data(usb, usb_buf, USB_BUF_SIZE); + if (r != USB_BUF_SIZE) { + return -1; + } + usb_buf_len = 0; + } + + if (len > USB_BUF_SIZE) { + if (usb_buf_len > 0) { + sprintf(ERROR, "internal error: usb_buf not empty\n"); + return -1; + } + to_write = round_down(len, USB_BUF_SIZE); + r = _command_data(usb, ptr, to_write); + if (r != to_write) { + return -1; + } + ptr += to_write; + len -= to_write; + } + + if (len > 0) { + if (len > USB_BUF_SIZE) { + sprintf(ERROR, "internal error: too much left for usb_buf\n"); + return -1; + } + memcpy(usb_buf, ptr, len); + usb_buf_len = len; + } + + return 0; +} + +static int fb_download_data_sparse_flush(usb_handle *usb) +{ + int r; + + if (usb_buf_len > 0) { + r = _command_data(usb, usb_buf, usb_buf_len); + if (r != usb_buf_len) { + return -1; + } + usb_buf_len = 0; + } + + return 0; +} + +int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s) +{ + char cmd[64]; + int r; + int size = sparse_file_len(s, true, false); + if (size <= 0) { + return -1; + } + + sprintf(cmd, "download:%08x", size); + r = _command_start(usb, cmd, size, 0); + if (r < 0) { + return -1; + } + + r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, usb); + if (r < 0) { + return -1; + } + + fb_download_data_sparse_flush(usb); + + return _command_end(usb); +} diff --git a/fastboot/util_windows.c b/fastboot/util_windows.c index c3d545c..9e029fd 100644 --- a/fastboot/util_windows.c +++ b/fastboot/util_windows.c @@ -36,6 +36,29 @@ #include <windows.h> +int64_t file_size(const char *fn) +{ + HANDLE file; + char *data; + DWORD sz; + + file = CreateFile( fn, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if (file == INVALID_HANDLE_VALUE) + return -1; + + sz = GetFileSize( file, NULL ); + CloseHandle( file ); + + return sz; +} + void get_my_path(char exe[PATH_MAX]) { char* r; @@ -52,7 +75,7 @@ void *load_file(const char *fn, unsigned *_sz) { HANDLE file; char *data; - DWORD file_size; + DWORD sz; file = CreateFile( fn, GENERIC_READ, @@ -65,29 +88,29 @@ void *load_file(const char *fn, unsigned *_sz) if (file == INVALID_HANDLE_VALUE) return NULL; - file_size = GetFileSize( file, NULL ); + sz = GetFileSize( file, NULL ); data = NULL; - if (file_size > 0) { - data = (char*) malloc( file_size ); + if (sz > 0) { + data = (char*) malloc( sz ); if (data == NULL) { - fprintf(stderr, "load_file: could not allocate %ld bytes\n", file_size ); - file_size = 0; + fprintf(stderr, "load_file: could not allocate %ld bytes\n", sz ); + sz = 0; } else { DWORD out_bytes; - if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) || - out_bytes != file_size ) + if ( !ReadFile( file, data, sz, &out_bytes, NULL ) || + out_bytes != sz ) { - fprintf(stderr, "load_file: could not read %ld bytes from '%s'\n", file_size, fn); + fprintf(stderr, "load_file: could not read %ld bytes from '%s'\n", sz, fn); free(data); data = NULL; - file_size = 0; + sz = 0; } } } CloseHandle( file ); - *_sz = (unsigned) file_size; + *_sz = (unsigned) sz; return data; } |