/* * Copyright (C) 2008 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bootimg_utils.h" #include "diagnose_usb.h" #include "fastboot.h" #include "fs.h" #include "tcp.h" #include "transport.h" #include "udp.h" #include "usb.h" using android::base::unique_fd; #ifndef O_BINARY #define O_BINARY 0 #endif char cur_product[FB_RESPONSE_SZ + 1]; static const char* serial = nullptr; static const char* cmdline = nullptr; static unsigned short vendor_id = 0; static int long_listing = 0; // Don't resparse files in too-big chunks. // libsparse will support INT_MAX, but this results in large allocations, so // let's keep it at 1GB to avoid memory pressure on the host. static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024; static int64_t sparse_limit = -1; static int64_t target_sparse_limit = -1; static unsigned page_size = 2048; static unsigned base_addr = 0x10000000; static unsigned kernel_offset = 0x00008000; static unsigned ramdisk_offset = 0x01000000; static unsigned second_offset = 0x00f00000; static unsigned tags_offset = 0x00000100; static bool g_disable_verity = false; static bool g_disable_verification = false; static const std::string convert_fbe_marker_filename("convert_fbe"); enum fb_buffer_type { FB_BUFFER_FD, FB_BUFFER_SPARSE, }; struct fastboot_buffer { enum fb_buffer_type type; void* data; int64_t sz; int fd; }; static struct { const char* nickname; const char* img_name; const char* sig_name; const char* part_name; bool is_optional; bool is_secondary; } images[] = { // clang-format off { "boot", "boot.img", "boot.sig", "boot", false, false }, { nullptr, "boot_other.img", "boot.sig", "boot", true, true }, { "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, false }, { "dts", "dt.img", "dt.sig", "dts", true, false }, { "recovery", "recovery.img", "recovery.sig", "recovery", true, false }, { "system", "system.img", "system.sig", "system", false, false }, { nullptr, "system_other.img", "system.sig", "system", true, true }, { "vbmeta", "vbmeta.img", "vbmeta.sig", "vbmeta", true, false }, { "vendor", "vendor.img", "vendor.sig", "vendor", true, false }, { nullptr, "vendor_other.img", "vendor.sig", "vendor", true, true }, // clang-format on }; static std::string find_item_given_name(const char* img_name) { char* dir = getenv("ANDROID_PRODUCT_OUT"); if (dir == nullptr || dir[0] == '\0') { die("ANDROID_PRODUCT_OUT not set"); } return android::base::StringPrintf("%s/%s", dir, img_name); } static std::string find_item(const std::string& item) { for (size_t i = 0; i < arraysize(images); ++i) { if (images[i].nickname && item == images[i].nickname) { return find_item_given_name(images[i].img_name); } } if (item == "userdata") return find_item_given_name("userdata.img"); if (item == "cache") return find_item_given_name("cache.img"); fprintf(stderr, "unknown partition '%s'\n", item.c_str()); return ""; } static int64_t get_file_size(int fd) { struct stat sb; return fstat(fd, &sb) == -1 ? -1 : sb.st_size; } static void* load_fd(int fd, int64_t* sz) { int errno_tmp; char* data = nullptr; *sz = get_file_size(fd); if (*sz < 0) { goto oops; } data = (char*) malloc(*sz); if (data == nullptr) goto oops; if(read(fd, data, *sz) != *sz) goto oops; close(fd); return data; oops: errno_tmp = errno; close(fd); if(data != 0) free(data); errno = errno_tmp; return 0; } static void* load_file(const std::string& path, int64_t* sz) { int fd = open(path.c_str(), O_RDONLY | O_BINARY); if (fd == -1) return nullptr; return load_fd(fd, sz); } static int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) { // Require a matching vendor id if the user specified one with -i. if (vendor_id != 0 && info->dev_vendor != vendor_id) { return -1; } if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) { return -1; } // require matching serial number or device path if requested // at the command line with the -s option. if (local_serial && (strcmp(local_serial, info->serial_number) != 0 && strcmp(local_serial, info->device_path) != 0)) return -1; return 0; } static int match_fastboot(usb_ifc_info* info) { return match_fastboot_with_serial(info, serial); } static int list_devices_callback(usb_ifc_info* info) { if (match_fastboot_with_serial(info, nullptr) == 0) { std::string serial = info->serial_number; if (!info->writable) { serial = UsbNoPermissionsShortHelpText(); } if (!serial[0]) { serial = "????????????"; } // output compatible with "adb devices" if (!long_listing) { printf("%s\tfastboot", serial.c_str()); } else { printf("%-22s fastboot", serial.c_str()); if (strlen(info->device_path) > 0) printf(" %s", info->device_path); } putchar('\n'); } return -1; } // Opens a new Transport connected to a device. If |serial| is non-null it will be used to identify // a specific device, otherwise the first USB device found will be used. // // If |serial| is non-null but invalid, this prints an error message to stderr and returns nullptr. // Otherwise it blocks until the target is available. // // The returned Transport is a singleton, so multiple calls to this function will return the same // object, and the caller should not attempt to delete the returned Transport. static Transport* open_device() { static Transport* transport = nullptr; bool announce = true; if (transport != nullptr) { return transport; } Socket::Protocol protocol = Socket::Protocol::kTcp; std::string host; int port = 0; if (serial != nullptr) { const char* net_address = nullptr; if (android::base::StartsWith(serial, "tcp:")) { protocol = Socket::Protocol::kTcp; port = tcp::kDefaultPort; net_address = serial + strlen("tcp:"); } else if (android::base::StartsWith(serial, "udp:")) { protocol = Socket::Protocol::kUdp; port = udp::kDefaultPort; net_address = serial + strlen("udp:"); } if (net_address != nullptr) { std::string error; if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) { fprintf(stderr, "error: Invalid network address '%s': %s\n", net_address, error.c_str()); return nullptr; } } } while (true) { if (!host.empty()) { std::string error; if (protocol == Socket::Protocol::kTcp) { transport = tcp::Connect(host, port, &error).release(); } else if (protocol == Socket::Protocol::kUdp) { transport = udp::Connect(host, port, &error).release(); } if (transport == nullptr && announce) { fprintf(stderr, "error: %s\n", error.c_str()); } } else { transport = usb_open(match_fastboot); } if (transport != nullptr) { return transport; } if (announce) { announce = false; fprintf(stderr, "< waiting for %s >\n", serial ? serial : "any device"); } std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } static void list_devices() { // We don't actually open a USB device here, // just getting our callback called so we can // list all the connected devices. usb_open(list_devices_callback); } static void syntax_error(const char* fmt, ...) { fprintf(stderr, "fastboot: usage: "); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); exit(1); } static int show_help() { // clang-format off fprintf(stdout, /* 1234567890123456789012345678901234567890123456789012345678901234567890123456 */ "usage: fastboot [