├── flash_tool ├── meson.build ├── io_handler.h ├── util.h ├── args.h ├── util.c ├── io_handler.c ├── args.c └── main.c ├── src ├── util.h ├── meson.build ├── mtk_preloader.c ├── mtk_device.c └── mtk_da.c ├── meson.build ├── LICENSE ├── include ├── mtk_preloader.h ├── mtk_device.h └── mtk_da.h └── README.md /flash_tool/meson.build: -------------------------------------------------------------------------------- 1 | executable('flash_tool', [ 2 | 'main.c', 3 | 4 | 'args.c', 5 | 'io_handler.c', 6 | 'util.c', 7 | ], dependencies : mtk_dep, install : true) 8 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #define MIN(X, Y) \ 5 | __extension__ ({ __typeof__(X) _X = (X); __typeof__(Y) _Y = (Y); _X < _Y ? _X : _Y; }) 6 | 7 | #endif /* UTIL_H */ 8 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('mtk_flash_tool', 'c', default_options : [ 2 | 'warning_level=3', 3 | 'werror=true', 4 | ]) 5 | 6 | include = include_directories('include') 7 | 8 | subdir('src') 9 | 10 | subdir('flash_tool') 11 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | libusb = dependency('libusb-1.0', static : true) 2 | 3 | mtk_lib = static_library('mtk', [ 4 | 'mtk_da.c', 5 | 'mtk_device.c', 6 | 'mtk_preloader.c', 7 | ], include_directories : include, dependencies : libusb) 8 | 9 | mtk_dep = declare_dependency(link_with : mtk_lib, include_directories : include, dependencies : libusb) 10 | -------------------------------------------------------------------------------- /flash_tool/io_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef IO_HANDLER_H 2 | #define IO_HANDLER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct file_info { 9 | int fd; 10 | size_t offset; 11 | }; 12 | 13 | int io_handler(bool flashing, size_t offset, size_t total_length, uint8_t *buffer, size_t count, void *user_data); 14 | 15 | #endif /* IO_HANDLER_H */ 16 | -------------------------------------------------------------------------------- /flash_tool/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include 5 | 6 | void check_errnum(int errnum, const char *s); 7 | void check_libusb(int errnum, const char *s); 8 | 9 | void check_mtk_preloader(uint16_t status, const char *cmd); 10 | 11 | void check_mtk_da_ack(uint8_t retval); 12 | void check_mtk_da_cont_char(uint8_t retval); 13 | void check_mtk_da_soc_ok(uint8_t retval); 14 | 15 | #endif /* UTIL_H */ 16 | -------------------------------------------------------------------------------- /flash_tool/args.h: -------------------------------------------------------------------------------- 1 | #ifndef ARGS_H 2 | #define ARGS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define MAX_OPERATIONS (64) 9 | 10 | enum device_state { 11 | DEVICE_STATE_NONE, 12 | DEVICE_STATE_PRELOADER, 13 | DEVICE_STATE_DA_STAGE2, 14 | }; 15 | 16 | struct operation { 17 | int key; 18 | uint64_t address; 19 | uint64_t length; 20 | int fd; 21 | }; 22 | 23 | struct arguments { 24 | enum device_state state; 25 | const char *download_agent; 26 | uint64_t address; 27 | uint64_t length; 28 | bool reboot; 29 | bool verbose; 30 | 31 | struct operation operations[MAX_OPERATIONS]; 32 | size_t operations_count; 33 | 34 | int download_agent_fd; 35 | }; 36 | 37 | void args_parse(int argc, char **argv, struct arguments *arguments); 38 | 39 | #endif /* ARGS_H */ 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Saleem Rashid 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /flash_tool/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "mtk_da.h" 11 | 12 | void check_errnum(int errnum, const char *s) { 13 | if (errnum != 0) { 14 | errx(1, "%s: %s", s, strerror(errnum)); 15 | } 16 | } 17 | 18 | void check_libusb(int errnum, const char *s) { 19 | if (errnum < 0) { 20 | errx(1, "%s: %s", s, libusb_strerror(errnum)); 21 | } 22 | } 23 | 24 | void check_mtk_preloader(uint16_t status, const char *cmd) { 25 | if (status != 0) { 26 | errx(2, "%s failed: 0x%04" PRIx16, cmd, status); 27 | } 28 | } 29 | 30 | void check_mtk_da_ack(uint8_t retval) { 31 | if (retval != MTK_DA_ACK) { 32 | errx(2, "DA did not ACK: 0x%02" PRIx8, retval); 33 | } 34 | } 35 | 36 | void check_mtk_da_cont_char(uint8_t retval) { 37 | if (retval != MTK_DA_CONT_CHAR) { 38 | errx(2, "DA did not return continuation character: 0x%02" PRIx8, retval); 39 | } 40 | } 41 | 42 | void check_mtk_da_soc_ok(uint8_t retval) { 43 | if (retval != MTK_DA_SOC_OK) { 44 | errx(2, "DA did not return OK: 0x%02" PRIx8, retval); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /include/mtk_preloader.h: -------------------------------------------------------------------------------- 1 | #ifndef MTK_PRELOADER_H 2 | #define MTK_PRELOADER_H 3 | 4 | #include "mtk_device.h" 5 | 6 | #include 7 | 8 | enum { 9 | MTK_PRELOADER_CMD_GET_HW_SW_VER = 0xfc, 10 | MTK_PRELOADER_CMD_GET_HW_CODE = 0xfd, 11 | 12 | MTK_PRELOADER_CMD_WRITE32 = 0xd4, 13 | MTK_PRELOADER_CMD_JUMP_DA = 0xd5, 14 | MTK_PRELOADER_CMD_SEND_DA = 0xd7, 15 | MTK_PRELOADER_CMD_GET_TARGET_CONFIG = 0xd8, 16 | }; 17 | 18 | int mtk_preloader_start(mtk_device *device); 19 | 20 | int mtk_preloader_get_tgt_config(mtk_device *device, uint32_t *tgt_config, uint16_t *status); 21 | 22 | int mtk_preloader_get_hw_code(mtk_device *device, uint16_t *hw_code, uint16_t *status); 23 | int mtk_preloader_get_hw_sw_ver(mtk_device *device, uint16_t *hw_subcode, uint16_t *hw_ver, uint16_t *sw_ver, uint16_t *status); 24 | 25 | int mtk_preloader_write32(mtk_device *device, uint32_t base_addr, uint32_t len32, const uint32_t *data, uint16_t *status); 26 | 27 | int mtk_preloader_disable_wdt(mtk_device *device, uint16_t *status); 28 | 29 | int mtk_preloader_send_da(mtk_device *device, uint32_t da_addr, uint32_t da_len, uint32_t sig_len, uint16_t *status, const mtk_io_handler handler, void *user_data); 30 | int mtk_preloader_jump_da(mtk_device *device, uint32_t da_addr, uint16_t *status); 31 | 32 | #endif /* MTK_PRELOADER_H */ 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MediaTek Flash Tool 2 | 3 | Library and command line tool for interacting with the MediaTek bootloader, for 4 | dumping and flashing firmware. 5 | 6 | ## Dependencies 7 | 8 | * Argp (included with glibc and gnulib) or argp-standalone 9 | * libusb >= 1.0.16 10 | 11 | ## Limitations 12 | 13 | * Only tested on MT6580, with Download Agent from SP Flash Tool 14 | * Only supports EMMC devices 15 | 16 | ## Features 17 | 18 | * Supports auto-detecting device (requires hotplug capability in libusb) 19 | * Supports sending Download Agent to Preloader 20 | * Supports multiple dumping or flashing operations 21 | * Supports arbitrary address and length without scatter file 22 | * Supports rebooting the device after operations are completed 23 | * Enables USB 2.0 mode in Download Agent 24 | 25 | ## Examples 26 | 27 | Dumping the GPT, using the appropriate Download Agent from `MTK_AllInOne_DA.bin`. 28 | 29 | ```bash 30 | flash_tool -d MTK_AllInOne_DA.bin -l 17408 -D GPT.bin 31 | ``` 32 | 33 | Assume the boot partition starts at 0x1d80000, with a length of 0x1000000. 34 | 35 | Dumping the boot partition to `boot.bak`, flashing `boot.img` to the boot 36 | partition, and rebooting. 37 | 38 | ```bash 39 | flash_tool -d MTK_AllInOne_DA.bin -R -a 0x1d80000 -l 0x1000000 -D boot.bak -F boot.img 40 | ``` 41 | 42 | Dumping the boot partition to `boot.bak`, patching it, flashing it back to the 43 | boot partition, and rebooting. 44 | 45 | ```bash 46 | flash_tool -d MTK_AllInOne_DA.bin -a 0x1d80000 -l 0x1000000 -D boot.bak 47 | ./patch.sh boot.bak boot.img 48 | flash_tool -2 -R -a 0x1d80000 -l 0x1000000 -F boot.img 49 | ``` 50 | -------------------------------------------------------------------------------- /include/mtk_device.h: -------------------------------------------------------------------------------- 1 | #ifndef MTK_DEVICE_H 2 | #define MTK_DEVICE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #define MTK_DEVICE_PKTSIZE (512) 11 | 12 | #define MTK_DEVICE_TMOUT (1000) 13 | 14 | #define MTK_DEVICE_INTERFACE (0) 15 | 16 | #define MTK_DEVICE_EPIN (0x1 | LIBUSB_ENDPOINT_IN) 17 | #define MTK_DEVICE_EPOUT (0x1 | LIBUSB_ENDPOINT_OUT) 18 | 19 | #define MTK_DEVICE_VID (0x0e8d) 20 | #define MTK_DEVICE_PID (0x2000) 21 | 22 | typedef struct { 23 | libusb_device_handle *dev; 24 | 25 | uint8_t buffer[MTK_DEVICE_PKTSIZE]; 26 | size_t buffer_available; 27 | size_t buffer_offset; 28 | } mtk_device; 29 | 30 | typedef int (*mtk_io_handler)(bool, size_t, size_t, uint8_t *, size_t, void *); 31 | 32 | int mtk_device_open(mtk_device *device, libusb_device_handle *dev); 33 | 34 | int mtk_device_detect(mtk_device *device, libusb_context *ctx); 35 | 36 | int mtk_device_read(mtk_device *device, uint8_t *buffer, size_t size); 37 | int mtk_device_write(mtk_device *device, const uint8_t *buffer, size_t size); 38 | 39 | static inline void mtk_device_flush_buffer(mtk_device *device) { 40 | device->buffer_available = 0; 41 | } 42 | 43 | int mtk_device_read8(mtk_device *device, uint8_t *data); 44 | int mtk_device_read16(mtk_device *device, uint16_t *data); 45 | int mtk_device_read32(mtk_device *device, uint32_t *data); 46 | int mtk_device_read64(mtk_device *device, uint64_t *data); 47 | 48 | int mtk_device_write8(mtk_device *device, uint8_t data); 49 | int mtk_device_write16(mtk_device *device, uint16_t data); 50 | int mtk_device_write32(mtk_device *device, uint32_t data); 51 | int mtk_device_write64(mtk_device *device, uint64_t data); 52 | 53 | int mtk_device_echo8(mtk_device *device, uint8_t data); 54 | int mtk_device_echo16(mtk_device *device, uint16_t data); 55 | int mtk_device_echo32(mtk_device *device, uint32_t data); 56 | int mtk_device_echo64(mtk_device *device, uint64_t data); 57 | 58 | #endif /* MTK_DEVICE_H */ 59 | -------------------------------------------------------------------------------- /flash_tool/io_handler.c: -------------------------------------------------------------------------------- 1 | #include "io_handler.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define PROGRESS_BAR_WIDTH (48) 10 | #define SI_UNITS_BUFSIZ (16) 11 | 12 | static void print_progress(bool flashing, size_t offset, size_t length); 13 | static void format_si_units(size_t length, char *str, size_t size); 14 | 15 | int io_handler(bool flashing, size_t offset, size_t total_length, uint8_t *buffer, size_t count, void *user_data) { 16 | const struct file_info *fi = user_data; 17 | 18 | if (lseek(fi->fd, fi->offset + offset, SEEK_SET) < 0) { 19 | errx(1, "Unable to seek file descriptor: %s", strerror(errno)); 20 | } 21 | 22 | if (flashing) { 23 | ssize_t n; 24 | if ((n = read(fi->fd, buffer, count)) < 0) { 25 | errx(1, "Unable to read from file descriptor: %s", strerror(errno)); 26 | } 27 | if ((size_t) n != count) { 28 | errx(1, "Not enough data read from file descriptor"); 29 | } 30 | } else { 31 | ssize_t n; 32 | if ((n = write(fi->fd, buffer, count)) < 0) { 33 | errx(1, "Unable to write to file descriptor: %s", strerror(errno)); 34 | } 35 | if ((size_t) n != count) { 36 | errx(1, "Not enough data written to file descriptor"); 37 | } 38 | } 39 | 40 | print_progress(flashing, offset + count, total_length); 41 | return 0; 42 | } 43 | 44 | static void print_progress(bool flashing, size_t offset, size_t length) { 45 | if (!isatty(STDERR_FILENO)) { 46 | return; 47 | } 48 | 49 | double progress = (double) offset / length; 50 | 51 | char progress_bar[PROGRESS_BAR_WIDTH + 1]; 52 | 53 | size_t progress_bar_fill = progress * PROGRESS_BAR_WIDTH; 54 | memset(progress_bar, '#', progress_bar_fill); 55 | memset(progress_bar + progress_bar_fill, '-', PROGRESS_BAR_WIDTH - progress_bar_fill); 56 | progress_bar[PROGRESS_BAR_WIDTH] = '\0'; 57 | 58 | const char *verb = flashing ? "Flashing" : "Dumping"; 59 | int percent = progress * 100; 60 | 61 | char offset_str[SI_UNITS_BUFSIZ]; 62 | format_si_units(offset, offset_str, sizeof(offset_str)); 63 | char length_str[SI_UNITS_BUFSIZ]; 64 | format_si_units(length, length_str, sizeof(length_str)); 65 | 66 | fprintf(stderr, "%s %-8s of %-8s [%s] %3d%%%c", verb, offset_str, length_str, progress_bar, percent, (offset == length) ? '\n' : '\r'); 67 | fflush(stderr); 68 | } 69 | 70 | static void format_si_units(size_t length, char *str, size_t size) { 71 | static const char *suffix = "BKMG"; 72 | 73 | double dbl = length; 74 | const char *ptr = suffix; 75 | 76 | while (dbl > 1024 && *(ptr + 1) != '\0') { 77 | dbl /= 1024; 78 | ptr++; 79 | } 80 | 81 | snprintf(str, size, "%.5g %c", dbl, *ptr); 82 | } 83 | -------------------------------------------------------------------------------- /include/mtk_da.h: -------------------------------------------------------------------------------- 1 | #ifndef MTK_DA_H 2 | #define MTK_DA_H 3 | 4 | #include 5 | #include 6 | 7 | #include "mtk_device.h" 8 | 9 | #define MTK_DA_INFO_VER (0x4) 10 | #define MTK_DA_INFO_MAGIC (0x22668899) 11 | 12 | #define MTK_DA_ENTRY_MAGIC (0xdada) 13 | #define MTK_DA_ENTRY_LOAD_REGIONS (10) 14 | 15 | enum { 16 | MTK_DA_SOC_OK = 0xc1, 17 | MTK_DA_SOC_FAIL = 0xcf, 18 | MTK_DA_SYNC_CHAR = 0xc0, 19 | MTK_DA_CONT_CHAR = 0x69, 20 | MTK_DA_ACK = 0x5a, 21 | MTK_DA_NACK = 0xa5, 22 | }; 23 | 24 | enum { 25 | MTK_DA_SWITCH_PART_CMD = 0x60, 26 | MTK_DA_SDMMC_WRITE_DATA_CMD = 0x62, 27 | MTK_DA_USB_CHECK_STATUS_CMD = 0x72, 28 | MTK_DA_READ_CMD = 0xd6, 29 | MTK_DA_ENABLE_WATCHDOG_CMD = 0xdb, 30 | }; 31 | 32 | #define MTK_DA_NAND_NOT_FOUND (0xbc4) 33 | 34 | #define MTK_DA_FULL_REPORT_SIZE (235) 35 | 36 | enum { 37 | MTK_DA_HW_STORAGE_NOR = 0, 38 | MTK_DA_HW_STORAGE_NAND, 39 | MTK_DA_HW_STORAGE_EMMC, 40 | MTK_DA_HW_STORAGE_SDMMC, 41 | MTK_DA_HW_STORAGE_UFS, 42 | }; 43 | 44 | enum { 45 | MTK_DA_STORAGE_EMMC = 0x1, 46 | MTK_DA_STORAGE_SDMMC, 47 | }; 48 | 49 | enum { 50 | MTK_DA_EMMC_PART_BOOT1 = 1, 51 | MTK_DA_EMMC_PART_BOOT2, 52 | MTK_DA_EMMC_PART_RPMB, 53 | MTK_DA_EMMC_PART_GP1, 54 | MTK_DA_EMMC_PART_GP2, 55 | MTK_DA_EMMC_PART_GP3, 56 | MTK_DA_EMMC_PART_GP4, 57 | MTK_DA_EMMC_PART_USER, 58 | }; 59 | 60 | enum { 61 | MTK_DA_HOST_OS_MAC = 10, 62 | MTK_DA_HOST_OS_WINDOWS, 63 | MTK_DA_HOST_OS_LINUX, 64 | }; 65 | 66 | typedef struct { 67 | uint32_t offset; 68 | uint32_t len; 69 | uint32_t start_addr; 70 | uint32_t sig_offset; 71 | uint32_t sig_len; 72 | } __attribute__((packed)) mtk_da_load_region; 73 | 74 | typedef struct { 75 | uint16_t magic; 76 | uint16_t hw_code; 77 | uint16_t hw_sub_code; 78 | uint16_t hw_ver; 79 | uint16_t sw_ver; 80 | uint16_t chip_evolution; 81 | uint32_t da_feature_set; 82 | uint16_t entry_region_index; 83 | uint16_t load_regions_count; 84 | mtk_da_load_region load_regions[MTK_DA_ENTRY_LOAD_REGIONS]; 85 | } __attribute__((packed)) mtk_da_entry; 86 | 87 | typedef struct { 88 | char da_identifier[32]; 89 | char da_description[64]; 90 | uint32_t da_info_ver; 91 | uint32_t da_info_magic; 92 | uint32_t da_count; 93 | mtk_da_entry DA[]; 94 | } __attribute__((packed)) mtk_da_info; 95 | 96 | int mtk_da_info_load(int fd, const mtk_da_info **info); 97 | 98 | int mtk_da_sync(mtk_device *device, uint32_t *nand_ret, uint32_t *emmc_ret, uint32_t *emmc_id, uint8_t *da_major_ver, uint8_t *da_minor_ver); 99 | int mtk_da_send_da(mtk_device *device, uint32_t da_addr, uint32_t da_len, uint8_t *retval, const mtk_io_handler handler, void *user_data); 100 | 101 | int mtk_da_usb_check_status(mtk_device *device, uint8_t *usb_status, uint8_t *retval); 102 | 103 | int mtk_da_sdmmc_switch_part(mtk_device *device, uint8_t part, uint8_t *retval); 104 | int mtk_da_sdmmc_write_data(mtk_device *device, uint8_t storage_type, uint8_t part, uint64_t addr, uint64_t len, uint8_t *retval, const mtk_io_handler handler, void *user_data); 105 | int mtk_da_read(mtk_device *device, uint8_t hw_storage, uint64_t addr, uint64_t len, uint8_t *retval, const mtk_io_handler handler, void *user_data); 106 | 107 | int mtk_da_enable_watchdog(mtk_device *device, uint16_t timeout_ms, bool async, bool reboot, bool download_mode, bool no_reset_rtc_time, uint8_t *retval); 108 | 109 | #endif /* MTK_DA_H */ 110 | -------------------------------------------------------------------------------- /flash_tool/args.c: -------------------------------------------------------------------------------- 1 | #include "args.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static error_t parse_opt(int key, char *arg, struct argp_state *state); 11 | static uint64_t parse_uint64_opt(int key, const char *str, const struct argp_state *state); 12 | 13 | static const struct argp_option options[] = { 14 | { "da-stage2", '2', NULL, 0, "Device is in DA Stage 2", 0 }, 15 | { "preloader", 'P', NULL, 0, "Device is in Preloader mode", 0 }, 16 | { "download-agent", 'd', "FILE", 0, "Path to MediaTek Download Agent binary", 1 }, 17 | { "address", 'a', "ADDRESS", 0, "EMMC address to read/write", 2 }, 18 | { "length", 'l', "LENGTH", 0, "Length of data to read/write", 2 }, 19 | { "dump", 'D', "FILE", 0, "Path to dump data to", 3 }, 20 | { "flash", 'F', "FILE", 0, "Path to flash data from", 3 }, 21 | { "reboot", 'R', NULL, 0, "Reboot device after completion", 4 }, 22 | { "verbose", 'v', NULL, 0, "Produce verbose output", -1 }, 23 | { NULL, 0, NULL, 0, NULL, 0 }, 24 | }; 25 | 26 | static const struct argp argp = { 27 | .options = options, 28 | .parser = parse_opt, 29 | 30 | .args_doc = NULL, 31 | .doc = NULL, 32 | .children = NULL, 33 | .help_filter = NULL, 34 | .argp_domain = NULL, 35 | }; 36 | 37 | void args_parse(int argc, char **argv, struct arguments *arguments) { 38 | argp_parse(&argp, argc, argv, 0, NULL, arguments); 39 | } 40 | 41 | static error_t parse_opt(int key, char *arg, struct argp_state *state) { 42 | struct arguments *arguments = state->input; 43 | bool flashing; 44 | 45 | switch (key) { 46 | case ARGP_KEY_INIT: 47 | arguments->state = DEVICE_STATE_NONE; 48 | arguments->download_agent = NULL; 49 | arguments->address = 0; 50 | arguments->length = 0; 51 | arguments->reboot = false; 52 | arguments->verbose = false; 53 | arguments->operations_count = 0; 54 | arguments->download_agent_fd = -1; 55 | break; 56 | 57 | case '2': 58 | arguments->state = DEVICE_STATE_DA_STAGE2; 59 | break; 60 | case 'P': 61 | arguments->state = DEVICE_STATE_PRELOADER; 62 | break; 63 | case 'd': 64 | arguments->download_agent = arg; 65 | break; 66 | case 'a': 67 | arguments->address = parse_uint64_opt(key, arg, state); 68 | break; 69 | case 'l': 70 | arguments->length = parse_uint64_opt(key, arg, state); 71 | break; 72 | case 'R': 73 | arguments->reboot = true; 74 | break; 75 | case 'v': 76 | arguments->verbose = true; 77 | break; 78 | 79 | case 'D': 80 | case 'F': 81 | flashing = (key == 'F'); 82 | 83 | if (arguments->operations_count == MAX_OPERATIONS) { 84 | argp_error(state, "Too many operations"); 85 | } 86 | if (arguments->length == 0) { 87 | argp_error(state, "Cannot perform zero-length operation"); 88 | } 89 | 90 | struct operation *operation = &arguments->operations[arguments->operations_count++]; 91 | 92 | operation->key = key; 93 | operation->address = arguments->address; 94 | operation->length = arguments->length; 95 | 96 | int flags; 97 | const char *verb; 98 | 99 | if (flashing) { 100 | flags = O_RDONLY; 101 | verb = "flashing"; 102 | } else { 103 | flags = O_WRONLY | O_CREAT | O_TRUNC; 104 | verb = "dumping"; 105 | } 106 | 107 | if ((operation->fd = open(arg, flags, DEFFILEMODE)) < 0) { 108 | argp_failure(state, 1, errno, "Unable to open file for %s: %s", verb, arg); 109 | } 110 | 111 | if (flashing) { 112 | off_t maxlength; 113 | if ((maxlength = lseek(operation->fd, 0, SEEK_END)) < 0) { 114 | argp_failure(state, 1, errno, "Unable to seek file descriptor: %s", arg); 115 | } 116 | if ((uint64_t) maxlength < arguments->length) { 117 | argp_failure(state, 1, 0, "Write length is greater than file size: %s", arg); 118 | } 119 | } 120 | break; 121 | 122 | case ARGP_KEY_ARG: 123 | argp_usage(state); 124 | break; 125 | 126 | case ARGP_KEY_END: 127 | if (arguments->state != DEVICE_STATE_DA_STAGE2) { 128 | if (arguments->download_agent == NULL) { 129 | argp_error(state, "MediaTek Download Agent binary is mandatory, unless device is in DA Stage 2"); 130 | } 131 | if ((arguments->download_agent_fd = open(arguments->download_agent, O_RDONLY)) < 0) { 132 | argp_failure(state, 1, errno, "Unable to open Download Agent binary"); 133 | } 134 | } 135 | break; 136 | } 137 | 138 | return 0; 139 | } 140 | 141 | static uint64_t parse_uint64_opt(int key, const char *str, const struct argp_state *state) { 142 | if (isdigit(*str)) { 143 | char *endptr = NULL; 144 | 145 | _Static_assert(ULLONG_MAX == UINT64_MAX, "unsigned long long is incompatible with uint64_t"); 146 | 147 | errno = 0; 148 | uint64_t value = strtoull(str, &endptr, 0); 149 | 150 | if (errno == 0 && *endptr == '\0') { 151 | return value; 152 | } 153 | } 154 | 155 | argp_error(state, "option requires an integer -- '%c'", key); 156 | return 0; 157 | } 158 | -------------------------------------------------------------------------------- /src/mtk_preloader.c: -------------------------------------------------------------------------------- 1 | #include "mtk_preloader.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include "util.h" 9 | 10 | int mtk_preloader_start(mtk_device *device) { 11 | static const uint8_t start_command[] = { 0xa0, 0x0a, 0x50, 0x05 }; 12 | 13 | int err; 14 | 15 | if ((err = libusb_control_transfer(device->dev, LIBUSB_REQUEST_TYPE_CLASS, 0x20, 0, 0, NULL, 0, 0)) < 0) { 16 | return err; 17 | } 18 | 19 | size_t i = 0; 20 | while (i < sizeof(start_command)) { 21 | /* Ignore data read prior to start command */ 22 | mtk_device_flush_buffer(device); 23 | 24 | if ((err = mtk_device_write8(device, start_command[i])) < 0) { 25 | return err; 26 | } 27 | 28 | uint8_t reply; 29 | if ((err = mtk_device_read8(device, &reply)) < 0) { 30 | return err; 31 | } 32 | 33 | uint8_t expected_reply = ~start_command[i]; 34 | if (reply == expected_reply) { 35 | i++; 36 | } else { 37 | i = 0; 38 | } 39 | } 40 | 41 | return 0; 42 | } 43 | 44 | int mtk_preloader_get_tgt_config(mtk_device *device, uint32_t *tgt_config, uint16_t *status) { 45 | int err; 46 | 47 | if ((err = mtk_device_echo8(device, MTK_PRELOADER_CMD_GET_TARGET_CONFIG)) < 0) { 48 | return err; 49 | } 50 | if ((err = mtk_device_read32(device, tgt_config)) < 0) { 51 | return err; 52 | } 53 | if ((err = mtk_device_read16(device, status)) < 0) { 54 | return err; 55 | } 56 | 57 | return 0; 58 | } 59 | 60 | int mtk_preloader_get_hw_code(mtk_device *device, uint16_t *hw_code, uint16_t *status) { 61 | int err; 62 | 63 | if ((err = mtk_device_echo8(device, MTK_PRELOADER_CMD_GET_HW_CODE)) < 0) { 64 | return err; 65 | } 66 | if ((err = mtk_device_read16(device, hw_code)) < 0) { 67 | return err; 68 | } 69 | if ((err = mtk_device_read16(device, status)) < 0) { 70 | return err; 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | int mtk_preloader_get_hw_sw_ver(mtk_device *device, uint16_t *hw_subcode, uint16_t *hw_ver, uint16_t *sw_ver, uint16_t *status) { 77 | int err; 78 | 79 | if ((err = mtk_device_echo8(device, MTK_PRELOADER_CMD_GET_HW_SW_VER)) < 0) { 80 | return err; 81 | } 82 | if ((err = mtk_device_read16(device, hw_subcode)) < 0) { 83 | return err; 84 | } 85 | if ((err = mtk_device_read16(device, hw_ver)) < 0) { 86 | return err; 87 | } 88 | if ((err = mtk_device_read16(device, sw_ver)) < 0) { 89 | return err; 90 | } 91 | if ((err = mtk_device_read16(device, status)) < 0) { 92 | return err; 93 | } 94 | 95 | return 0; 96 | } 97 | 98 | int mtk_preloader_write32(mtk_device *device, uint32_t base_addr, uint32_t len32, const uint32_t *data, uint16_t *status) { 99 | int err; 100 | 101 | if ((err = mtk_device_echo8(device, MTK_PRELOADER_CMD_WRITE32)) < 0) { 102 | return err; 103 | } 104 | if ((err = mtk_device_echo32(device, base_addr)) < 0) { 105 | return err; 106 | } 107 | if ((err = mtk_device_echo32(device, len32)) < 0) { 108 | return err; 109 | } 110 | if ((err = mtk_device_read16(device, status)) < 0) { 111 | return err; 112 | } 113 | 114 | if (*status == 0) { 115 | for (size_t i = 0; i < len32; i++) { 116 | if ((err = mtk_device_echo32(device, data[i])) < 0) { 117 | return err; 118 | } 119 | } 120 | if ((err = mtk_device_read16(device, status)) < 0) { 121 | return err; 122 | } 123 | } 124 | 125 | return 0; 126 | } 127 | 128 | int mtk_preloader_disable_wdt(mtk_device *device, uint16_t *status) { 129 | static const uint32_t data32 = 0x22000064; 130 | return mtk_preloader_write32(device, 0x10007000, 1, &data32, status); 131 | } 132 | 133 | int mtk_preloader_send_da(mtk_device *device, uint32_t da_addr, uint32_t da_len, uint32_t sig_len, uint16_t *status, const mtk_io_handler handler, void *user_data) { 134 | int err; 135 | 136 | if ((err = mtk_device_echo8(device, MTK_PRELOADER_CMD_SEND_DA)) < 0) { 137 | return err; 138 | } 139 | if ((err = mtk_device_echo32(device, da_addr)) < 0) { 140 | return err; 141 | } 142 | if ((err = mtk_device_echo32(device, da_len)) < 0) { 143 | return err; 144 | } 145 | if ((err = mtk_device_echo32(device, sig_len)) < 0) { 146 | return err; 147 | } 148 | if ((err = mtk_device_read16(device, status)) < 0) { 149 | return err; 150 | } 151 | 152 | if (*status == 0) { 153 | uint8_t buffer[0x400]; 154 | uint16_t chksum = 0; 155 | 156 | size_t offset = 0; 157 | while (offset < da_len) { 158 | size_t count = MIN(sizeof(buffer), da_len - offset); 159 | 160 | if ((err = handler(true, offset, da_len, buffer, count, user_data)) < 0) { 161 | return err; 162 | } 163 | 164 | if ((err = mtk_device_write(device, buffer, count)) < 0) { 165 | return err; 166 | } 167 | 168 | for (size_t i = 0; i < (count >> 1); i++) { 169 | chksum ^= buffer[(i << 1)]; 170 | chksum ^= buffer[(i << 1) + 1] << 8; 171 | } 172 | if (count & 1) { 173 | chksum ^= buffer[count - 1]; 174 | } 175 | 176 | offset += count; 177 | } 178 | 179 | uint16_t chksum_device; 180 | if ((err = mtk_device_read16(device, &chksum_device)) < 0) { 181 | return err; 182 | } 183 | if ((err = mtk_device_read16(device, status)) < 0) { 184 | return err; 185 | } 186 | 187 | if (chksum != chksum_device) { 188 | return LIBUSB_ERROR_OTHER; 189 | } 190 | } 191 | 192 | return 0; 193 | } 194 | 195 | int mtk_preloader_jump_da(mtk_device *device, uint32_t da_addr, uint16_t *status) { 196 | int err; 197 | 198 | if ((err = mtk_device_echo8(device, MTK_PRELOADER_CMD_JUMP_DA)) < 0) { 199 | return err; 200 | } 201 | if ((err = mtk_device_echo32(device, da_addr)) < 0) { 202 | return err; 203 | } 204 | if ((err = mtk_device_read16(device, status)) < 0) { 205 | return err; 206 | } 207 | 208 | return 0; 209 | } 210 | -------------------------------------------------------------------------------- /src/mtk_device.c: -------------------------------------------------------------------------------- 1 | #include "mtk_device.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "util.h" 10 | 11 | int mtk_device_open(mtk_device *device, libusb_device_handle *dev) { 12 | device->dev = dev; 13 | device->buffer_offset = 0; 14 | device->buffer_available = 0; 15 | 16 | int err; 17 | 18 | if ((err = libusb_set_auto_detach_kernel_driver(dev, true)) < 0) { 19 | return err; 20 | } 21 | 22 | if ((err = libusb_claim_interface(dev, MTK_DEVICE_INTERFACE)) < 0) { 23 | return err; 24 | } 25 | 26 | if ((err = libusb_claim_interface(dev, MTK_DEVICE_INTERFACE)) < 0) { 27 | return err; 28 | } 29 | 30 | return 0; 31 | } 32 | 33 | static int hotplug_callback_fn(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data) { 34 | (void) ctx; 35 | (void) event; 36 | 37 | libusb_device **dev = user_data; 38 | *dev = device; 39 | 40 | return true; 41 | } 42 | 43 | int mtk_device_detect(mtk_device *device, libusb_context *ctx) { 44 | libusb_device *dev = NULL; 45 | 46 | int err = libusb_hotplug_register_callback(ctx, 47 | LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, 48 | LIBUSB_HOTPLUG_ENUMERATE, 49 | MTK_DEVICE_VID, 50 | MTK_DEVICE_PID, 51 | LIBUSB_CLASS_COMM, 52 | hotplug_callback_fn, 53 | &dev, 54 | NULL); 55 | 56 | if (err < 0) { 57 | return err; 58 | } 59 | 60 | while (dev == NULL) { 61 | libusb_handle_events(ctx); 62 | } 63 | 64 | libusb_device_handle *devh; 65 | if ((err = libusb_open(dev, &devh)) < 0) { 66 | return err; 67 | } 68 | 69 | return mtk_device_open(device, devh); 70 | } 71 | 72 | int mtk_device_read(mtk_device *device, uint8_t *buffer, size_t size) { 73 | size_t offset = 0; 74 | 75 | while (offset < size) { 76 | if (device->buffer_available == 0) { 77 | int transferred; 78 | 79 | int err; 80 | if ((err = libusb_bulk_transfer(device->dev, MTK_DEVICE_EPIN, device->buffer, MTK_DEVICE_PKTSIZE, &transferred, MTK_DEVICE_TMOUT)) < 0) { 81 | return err; 82 | } 83 | 84 | device->buffer_offset = 0; 85 | device->buffer_available = transferred; 86 | } 87 | 88 | size_t n = MIN(size - offset, device->buffer_available); 89 | if (buffer != NULL) { 90 | memcpy(buffer + offset, device->buffer + device->buffer_offset, n); 91 | } 92 | 93 | offset += n; 94 | device->buffer_offset += n; 95 | device->buffer_available -= n; 96 | } 97 | 98 | return 0; 99 | } 100 | 101 | int mtk_device_write(mtk_device *device, const uint8_t *buffer, size_t size) { 102 | size_t offset = 0; 103 | 104 | while (offset < size) { 105 | int transferred; 106 | 107 | int err = libusb_bulk_transfer(device->dev, MTK_DEVICE_EPOUT, (uint8_t *) buffer + offset, size - offset, &transferred, MTK_DEVICE_TMOUT); 108 | if (err < 0) { 109 | return err; 110 | } 111 | 112 | offset += transferred; 113 | } 114 | 115 | return 0; 116 | } 117 | 118 | int mtk_device_read8(mtk_device *device, uint8_t *data) { 119 | return mtk_device_read(device, data, sizeof(*data)); 120 | } 121 | 122 | int mtk_device_read16(mtk_device *device, uint16_t *data) { 123 | int err = mtk_device_read(device, (uint8_t *) data, sizeof(*data)); 124 | if (err < 0) { 125 | return err; 126 | } 127 | if (data != NULL) { 128 | *data = be16toh(*data); 129 | } 130 | return 0; 131 | } 132 | 133 | int mtk_device_read32(mtk_device *device, uint32_t *data) { 134 | int err = mtk_device_read(device, (uint8_t *) data, sizeof(*data)); 135 | if (err < 0) { 136 | return err; 137 | } 138 | if (data != NULL) { 139 | *data = be32toh(*data); 140 | } 141 | return 0; 142 | } 143 | 144 | int mtk_device_read64(mtk_device *device, uint64_t *data) { 145 | int err = mtk_device_read(device, (uint8_t *) data, sizeof(*data)); 146 | if (err < 0) { 147 | return err; 148 | } 149 | if (data != NULL) { 150 | *data = be64toh(*data); 151 | } 152 | return 0; 153 | } 154 | 155 | int mtk_device_write8(mtk_device *device, uint8_t data) { 156 | return mtk_device_write(device, &data, sizeof(data)); 157 | } 158 | 159 | int mtk_device_write16(mtk_device *device, uint16_t data) { 160 | data = htobe16(data); 161 | return mtk_device_write(device, (uint8_t *) &data, sizeof(data)); 162 | } 163 | 164 | int mtk_device_write32(mtk_device *device, uint32_t data) { 165 | data = htobe32(data); 166 | return mtk_device_write(device, (uint8_t *) &data, sizeof(data)); 167 | } 168 | 169 | int mtk_device_write64(mtk_device *device, uint64_t data) { 170 | data = htobe64(data); 171 | return mtk_device_write(device, (uint8_t *) &data, sizeof(data)); 172 | } 173 | 174 | int mtk_device_echo8(mtk_device *device, uint8_t data) { 175 | int err; 176 | 177 | if ((err = mtk_device_write8(device, data)) < 0) { 178 | return err; 179 | } 180 | 181 | uint8_t reply; 182 | if ((err = mtk_device_read8(device, &reply)) < 0) { 183 | return err; 184 | } 185 | if (reply != data) { 186 | return LIBUSB_ERROR_OTHER; 187 | } 188 | 189 | return 0; 190 | } 191 | 192 | int mtk_device_echo16(mtk_device *device, uint16_t data) { 193 | int err; 194 | 195 | if ((err = mtk_device_write16(device, data)) < 0) { 196 | return err; 197 | } 198 | 199 | uint16_t reply; 200 | if ((err = mtk_device_read16(device, &reply)) < 0) { 201 | return err; 202 | } 203 | if (reply != data) { 204 | return LIBUSB_ERROR_OTHER; 205 | } 206 | 207 | return 0; 208 | } 209 | 210 | int mtk_device_echo32(mtk_device *device, uint32_t data) { 211 | int err; 212 | 213 | if ((err = mtk_device_write32(device, data)) < 0) { 214 | return err; 215 | } 216 | 217 | uint32_t reply; 218 | if ((err = mtk_device_read32(device, &reply)) < 0) { 219 | return err; 220 | } 221 | if (reply != data) { 222 | return LIBUSB_ERROR_OTHER; 223 | } 224 | 225 | return 0; 226 | } 227 | 228 | int mtk_device_echo64(mtk_device *device, uint64_t data) { 229 | int err; 230 | 231 | if ((err = mtk_device_write64(device, data)) < 0) { 232 | return err; 233 | } 234 | 235 | uint64_t reply; 236 | if ((err = mtk_device_read64(device, &reply)) < 0) { 237 | return err; 238 | } 239 | if (reply != data) { 240 | return LIBUSB_ERROR_OTHER; 241 | } 242 | 243 | return 0; 244 | } 245 | -------------------------------------------------------------------------------- /flash_tool/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "args.h" 10 | #include "io_handler.h" 11 | #include "util.h" 12 | 13 | #include "mtk_da.h" 14 | #include "mtk_device.h" 15 | #include "mtk_preloader.h" 16 | 17 | static void handle_state_none(mtk_device *device); 18 | static void handle_state_preloader(mtk_device *device, int download_agent_fd, const mtk_da_info *info); 19 | static void handle_state_da_stage2(mtk_device *device, const struct operation *operations, size_t count, bool reboot); 20 | 21 | int main(int argc, char **argv) { 22 | struct arguments arguments; 23 | args_parse(argc, argv, &arguments); 24 | 25 | int err; 26 | 27 | const mtk_da_info *info = NULL; 28 | 29 | if (arguments.state != DEVICE_STATE_DA_STAGE2) { 30 | err = mtk_da_info_load(arguments.download_agent_fd, &info); 31 | check_errnum(-err, "Unable to load Download Agent binary"); 32 | 33 | printf("DA identifier: %.*s\n", (int) sizeof(info->da_identifier), info->da_identifier); 34 | printf("DA description: %.*s\n", (int) sizeof(info->da_description), info->da_description); 35 | printf("DA count: %" PRIu32 "\n", info->da_count); 36 | printf("\n"); 37 | } 38 | 39 | err = libusb_init(NULL); 40 | check_libusb(err, "libusb_init failed"); 41 | 42 | int level = arguments.verbose ? LIBUSB_LOG_LEVEL_DEBUG : LIBUSB_LOG_LEVEL_INFO; 43 | #if LIBUSB_API_VERSION >= 0x01000106 44 | libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, level); 45 | #else 46 | libusb_set_debug(NULL, level); 47 | #endif 48 | 49 | printf("Waiting for MediaTek device...\n"); 50 | 51 | mtk_device device; 52 | err = mtk_device_detect(&device, NULL); 53 | check_libusb(err, "Unable to detect MediaTek device"); 54 | 55 | switch (arguments.state) { 56 | case DEVICE_STATE_NONE: 57 | handle_state_none(&device); 58 | /* fallthrough */ 59 | case DEVICE_STATE_PRELOADER: 60 | handle_state_preloader(&device, arguments.download_agent_fd, info); 61 | /* fallthrough */ 62 | case DEVICE_STATE_DA_STAGE2: 63 | handle_state_da_stage2(&device, arguments.operations, arguments.operations_count, arguments.reboot); 64 | break; 65 | } 66 | 67 | return 0; 68 | } 69 | 70 | static void handle_state_none(mtk_device *device) { 71 | printf("Syncing with MediaTek Preloader...\n"); 72 | 73 | int err = mtk_preloader_start(device); 74 | check_libusb(err, "Unable to sync with MediaTek Preloader"); 75 | } 76 | 77 | static void handle_state_preloader(mtk_device *device, int download_agent_fd, const mtk_da_info *info) { 78 | int err; 79 | uint16_t status; 80 | struct file_info fi; 81 | 82 | uint16_t hw_code; 83 | err = mtk_preloader_get_hw_code(device, &hw_code, &status); 84 | check_libusb(err, "Unable to get chip code"); 85 | check_mtk_preloader(status, "GET_HW_CODE"); 86 | 87 | printf("\nHW code: 0x%04" PRIx16 "\n", hw_code); 88 | 89 | uint16_t hw_subcode, hw_ver, sw_ver; 90 | err = mtk_preloader_get_hw_sw_ver(device, &hw_subcode, &hw_ver, &sw_ver, &status); 91 | check_libusb(err, "Unable to get hardware/software version"); 92 | check_mtk_preloader(status, "GET_HW_SW_VER"); 93 | 94 | printf("HW subcode: 0x%04" PRIx16 "\n", hw_subcode); 95 | printf("HW version: 0x%04" PRIx16 "\n", hw_ver); 96 | printf("SW version: 0x%04" PRIx16 "\n", sw_ver); 97 | 98 | uint32_t tgt_config; 99 | err = mtk_preloader_get_tgt_config(device, &tgt_config, &status); 100 | check_libusb(err, "Unable to get target config"); 101 | check_mtk_preloader(status, "GET_TARGET_CONFIG"); 102 | 103 | printf("\nTarget config: 0x%08" PRIx32 "\n", tgt_config); 104 | 105 | const mtk_da_entry *entry = NULL; 106 | for (size_t i = 0; i < info->da_count; i++) { 107 | if (info->DA[i].magic != MTK_DA_ENTRY_MAGIC) { 108 | errx(1, "DA entry has invalid magic"); 109 | } 110 | if (info->DA[i].hw_code == hw_code && info->DA[i].hw_ver == hw_ver && info->DA[i].sw_ver == sw_ver) { 111 | entry = &info->DA[i]; 112 | break; 113 | } 114 | } 115 | if (entry == NULL) { 116 | errx(1, "Unable to find DA entry for HW code"); 117 | } 118 | if (entry->load_regions_count > MTK_DA_ENTRY_LOAD_REGIONS) { 119 | errx(1, "Invalid load regions count in DA entry"); 120 | } 121 | if (entry->entry_region_index >= entry->load_regions_count) { 122 | errx(1, "Invalid entry region index"); 123 | } 124 | 125 | const mtk_da_load_region *da_stage1 = NULL; 126 | for (size_t i = entry->entry_region_index; i + 1 < entry->load_regions_count; i++) { 127 | if (entry->load_regions[i].sig_len > 0) { 128 | da_stage1 = &entry->load_regions[i]; 129 | break; 130 | } 131 | } 132 | if (da_stage1 == NULL) { 133 | errx(1, "Unable to find valid load region for DA entry"); 134 | } 135 | if (da_stage1->sig_offset + da_stage1->sig_len != da_stage1->len) { 136 | errx(1, "DA Stage 1 signature is not at end of load region"); 137 | } 138 | 139 | const mtk_da_load_region *da_stage2 = da_stage1 + 1; 140 | if (da_stage2->sig_offset + da_stage2->sig_len != da_stage2->len) { 141 | errx(1, "DA Stage 2 signature is not at end of load region"); 142 | } 143 | 144 | printf("\nDisabling watchdog timer...\n"); 145 | err = mtk_preloader_disable_wdt(device, &status); 146 | check_libusb(err, "Unable to disable WDT"); 147 | check_mtk_preloader(status, "WRITE32"); 148 | 149 | fi.fd = download_agent_fd; 150 | fi.offset = da_stage1->offset; 151 | 152 | printf("Sending DA Stage 1...\n"); 153 | err = mtk_preloader_send_da(device, da_stage1->start_addr, da_stage1->len, da_stage1->sig_len, &status, io_handler, &fi); 154 | check_libusb(err, "Unable to send DA"); 155 | check_mtk_preloader(status, "SEND_DA"); 156 | 157 | printf("\nJumping to DA Stage 1...\n"); 158 | err = mtk_preloader_jump_da(device, da_stage1->start_addr, &status); 159 | check_libusb(err, "Unable to jump to DA"); 160 | check_mtk_preloader(status, "JUMP_DA"); 161 | 162 | uint32_t nand_ret, emmc_ret, emmc_id[4]; 163 | uint8_t da_major_ver, da_minor_ver; 164 | 165 | err = mtk_da_sync(device, &nand_ret, &emmc_ret, emmc_id, &da_major_ver, &da_minor_ver); 166 | check_libusb(err, "Unable to sync with DA Stage 1"); 167 | if (nand_ret != MTK_DA_NAND_NOT_FOUND) { 168 | errx(2, "NAND controller did not return NAND_NOT_FOUND: 0x%x", nand_ret); 169 | } 170 | if (emmc_ret != 0) { 171 | errx(2, "EMMC controller returned error: 0x%x", emmc_ret); 172 | } 173 | 174 | printf("EMMC ID: %08" PRIX32 " %08" PRIX32 " %08" PRIX32 " %08" PRIX32 "\n", 175 | emmc_id[0], emmc_id[1], emmc_id[2], emmc_id[3]); 176 | printf("DA version: DA_v%" PRIu8 ".%" PRIu8 "\n", da_major_ver, da_minor_ver); 177 | 178 | fi.fd = download_agent_fd; 179 | fi.offset = da_stage2->offset; 180 | 181 | printf("\nSending DA Stage 2...\n"); 182 | uint8_t retval; 183 | err = mtk_da_send_da(device, da_stage2->start_addr, da_stage2->len, &retval, io_handler, &fi); 184 | check_libusb(err, "Unable to send DA"); 185 | check_mtk_da_ack(retval); 186 | 187 | err = mtk_device_read(device, NULL, MTK_DA_FULL_REPORT_SIZE); 188 | check_libusb(err, "Unable to read DA report"); 189 | err = mtk_device_read8(device, &retval); 190 | check_libusb(err, "Unable to read DA return value"); 191 | check_mtk_da_soc_ok(retval); 192 | } 193 | 194 | static void handle_state_da_stage2(mtk_device *device, const struct operation *operations, size_t count, bool reboot) { 195 | int err; 196 | uint8_t retval; 197 | 198 | uint8_t usb_status; 199 | err = mtk_da_usb_check_status(device, &usb_status, &retval); 200 | check_libusb(err, "Unable to check USB status"); 201 | check_mtk_da_ack(retval); 202 | if (usb_status != 1) { 203 | errx(2, "DA did not return valid USB status: %02" PRIx8, usb_status); 204 | } 205 | 206 | printf("\n"); 207 | for (size_t i = 0; i < count; i++) { 208 | const struct operation *operation = &operations[i]; 209 | printf("Address: 0x%016" PRIx64 "\n", operation->address); 210 | printf("Length: 0x%016" PRIx64 "\n", operation->length); 211 | 212 | err = mtk_da_sdmmc_switch_part(device, MTK_DA_EMMC_PART_USER, &retval); 213 | check_libusb(err, "Unable to switch partition to EMMC_USER"); 214 | check_mtk_da_ack(retval); 215 | 216 | struct file_info fi = { 217 | .fd = operation->fd, 218 | .offset = 0, 219 | }; 220 | 221 | switch (operation->key) { 222 | case 'D': 223 | err = mtk_da_read(device, MTK_DA_HW_STORAGE_EMMC, operation->address, operation->length, &retval, io_handler, &fi); 224 | check_libusb(err, "Unable to perform dump operation"); 225 | check_mtk_da_ack(retval); 226 | break; 227 | 228 | case 'F': 229 | err = mtk_da_sdmmc_write_data(device, MTK_DA_STORAGE_EMMC, MTK_DA_EMMC_PART_USER, operation->address, operation->length, &retval, io_handler, &fi); 230 | check_libusb(err, "Unable to perform flash operation"); 231 | check_mtk_da_cont_char(retval); 232 | break; 233 | } 234 | 235 | printf("\n"); 236 | } 237 | 238 | if (reboot) { 239 | printf("Enabling WDT to reboot device...\n"); 240 | err = mtk_da_enable_watchdog(device, 0, false, false, false, true, &retval); 241 | check_libusb(err, "Unable to enable WDT"); 242 | check_mtk_da_ack(retval); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/mtk_da.c: -------------------------------------------------------------------------------- 1 | #include "mtk_da.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "util.h" 11 | 12 | int mtk_da_info_load(int fd, const mtk_da_info **info) { 13 | mtk_da_info tmp_info; 14 | if (read(fd, &tmp_info, sizeof(tmp_info)) != sizeof(tmp_info)) { 15 | return -errno; 16 | } 17 | 18 | if (tmp_info.da_info_magic != MTK_DA_INFO_MAGIC) { 19 | return -EINVAL; 20 | } 21 | if (tmp_info.da_info_ver != MTK_DA_INFO_VER) { 22 | return -EINVAL; 23 | } 24 | 25 | size_t length = offsetof(mtk_da_info, DA) + tmp_info.da_count * sizeof(mtk_da_entry); 26 | 27 | off_t maxlength; 28 | if ((maxlength = lseek(fd, 0, SEEK_END)) < 0) { 29 | return -errno; 30 | } 31 | 32 | if ((size_t) maxlength < length) { 33 | return -EFBIG; 34 | } 35 | 36 | const mtk_da_info *addr_info; 37 | if ((addr_info = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { 38 | return -errno; 39 | } 40 | 41 | *info = addr_info; 42 | 43 | return 0; 44 | } 45 | 46 | int mtk_da_sync(mtk_device *device, uint32_t *nand_ret, uint32_t *emmc_ret, uint32_t *emmc_id, uint8_t *da_major_ver, uint8_t *da_minor_ver) { 47 | int err; 48 | 49 | uint8_t sync_char; 50 | if ((err = mtk_device_read8(device, &sync_char)) < 0) { 51 | return err; 52 | } 53 | if (sync_char != MTK_DA_SYNC_CHAR) { 54 | return LIBUSB_ERROR_OTHER; 55 | } 56 | 57 | if ((err = mtk_device_read32(device, nand_ret)) < 0) { 58 | return err; 59 | } 60 | 61 | uint16_t nand_count; 62 | if ((err = mtk_device_read16(device, &nand_count)) < 0) { 63 | return err; 64 | } 65 | for (size_t i = 0; i < nand_count; i++) { 66 | if ((err = mtk_device_read16(device, NULL)) < 0) { 67 | return err; 68 | } 69 | } 70 | 71 | if ((err = mtk_device_read32(device, emmc_ret)) < 0) { 72 | return err; 73 | } 74 | for (size_t i = 0; i < 4; i++) { 75 | if ((err = mtk_device_read32(device, &emmc_id[i])) < 0) { 76 | return err; 77 | } 78 | } 79 | 80 | if ((err = mtk_device_write8(device, MTK_DA_ACK)) < 0) { 81 | return err; 82 | } 83 | 84 | if ((err = mtk_device_read8(device, da_major_ver)) < 0) { 85 | return err; 86 | } 87 | if ((err = mtk_device_read8(device, da_minor_ver)) < 0) { 88 | return err; 89 | } 90 | if ((err = mtk_device_read8(device, NULL)) < 0) { 91 | return err; 92 | } 93 | 94 | return 0; 95 | } 96 | 97 | static int send_device_config(mtk_device *device) { 98 | int err; 99 | 100 | if ((err = mtk_device_write8(device, 0xff)) < 0) { 101 | return err; 102 | } 103 | if ((err = mtk_device_write8(device, 1)) < 0) { 104 | return err; 105 | } 106 | if ((err = mtk_device_write16(device, 0x0008)) < 0) { 107 | return err; 108 | } 109 | if ((err = mtk_device_write8(device, 0x00)) < 0) { 110 | return err; 111 | } 112 | if ((err = mtk_device_write32(device, 0x7007ffff)) < 0) { 113 | return err; 114 | } 115 | if ((err = mtk_device_write8(device, 0x01)) < 0) { 116 | return err; 117 | } 118 | if ((err = mtk_device_write32(device, 0)) < 0) { 119 | return err; 120 | } 121 | if ((err = mtk_device_write8(device, 0x02)) < 0) { 122 | return err; 123 | } 124 | if ((err = mtk_device_write8(device, 0x01)) < 0) { 125 | return err; 126 | } 127 | if ((err = mtk_device_write8(device, 0x02)) < 0) { 128 | return err; 129 | } 130 | if ((err = mtk_device_write8(device, 0x00)) < 0) { 131 | return err; 132 | } 133 | if ((err = mtk_device_write32(device, 1)) < 0) { 134 | return err; 135 | } 136 | 137 | return 0; 138 | } 139 | 140 | int mtk_da_send_da(mtk_device *device, uint32_t da_addr, uint32_t da_len, uint8_t *retval, const mtk_io_handler handler, void *user_data) { 141 | int err; 142 | 143 | if ((err = send_device_config(device)) < 0) { 144 | return err; 145 | } 146 | 147 | static const uint8_t name[16] = { 0x46, 0x46 }; 148 | if ((err = mtk_device_write(device, name, sizeof(name))) < 0) { 149 | return err; 150 | } 151 | if ((err = mtk_device_write32(device, 0xff000000)) < 0) { 152 | return err; 153 | } 154 | 155 | uint32_t data32; 156 | if ((err = mtk_device_read32(device, &data32)) < 0) { 157 | return err; 158 | } 159 | if (data32 != 0) { 160 | return LIBUSB_ERROR_OTHER; 161 | } 162 | 163 | uint8_t buffer[0x1000]; 164 | 165 | if ((err = mtk_device_write32(device, da_addr)) < 0) { 166 | return err; 167 | } 168 | if ((err = mtk_device_write32(device, da_len)) < 0) { 169 | return err; 170 | } 171 | if ((err = mtk_device_write32(device, sizeof(buffer))) < 0) { 172 | return err; 173 | } 174 | 175 | if ((err = mtk_device_read8(device, retval)) < 0) { 176 | return err; 177 | } 178 | if (*retval != MTK_DA_ACK) { 179 | return 0; 180 | } 181 | 182 | size_t offset = 0; 183 | while (offset < da_len) { 184 | size_t count = MIN(sizeof(buffer), da_len - offset); 185 | 186 | if ((err = handler(true, offset, da_len, buffer, count, user_data)) < 0) { 187 | return err; 188 | } 189 | 190 | if ((err = mtk_device_write(device, buffer, count)) < 0) { 191 | return err; 192 | } 193 | 194 | offset += count; 195 | 196 | if ((err = mtk_device_read8(device, retval)) < 0) { 197 | return err; 198 | } 199 | if (*retval != MTK_DA_ACK) { 200 | return 0; 201 | } 202 | } 203 | 204 | if ((err = mtk_device_read8(device, retval)) < 0) { 205 | return err; 206 | } 207 | if (*retval != MTK_DA_ACK) { 208 | return 0; 209 | } 210 | 211 | if ((err = mtk_device_write8(device, MTK_DA_ACK)) < 0) { 212 | return err; 213 | } 214 | 215 | return 0; 216 | } 217 | 218 | int mtk_da_usb_check_status(mtk_device *device, uint8_t *usb_status, uint8_t *retval) { 219 | int err; 220 | 221 | if ((err = mtk_device_write8(device, MTK_DA_USB_CHECK_STATUS_CMD)) < 0) { 222 | return err; 223 | } 224 | 225 | if ((err = mtk_device_read8(device, retval)) < 0) { 226 | return err; 227 | } 228 | 229 | if (*retval == MTK_DA_ACK) { 230 | if ((err = mtk_device_read8(device, usb_status)) < 0) { 231 | return err; 232 | } 233 | } 234 | 235 | return 0; 236 | } 237 | 238 | int mtk_da_sdmmc_switch_part(mtk_device *device, uint8_t part, uint8_t *retval) { 239 | int err; 240 | 241 | if ((err = mtk_device_write8(device, MTK_DA_SWITCH_PART_CMD)) < 0) { 242 | return err; 243 | } 244 | if ((err = mtk_device_read8(device, retval)) < 0) { 245 | return err; 246 | } 247 | if (*retval == MTK_DA_ACK) { 248 | if ((err = mtk_device_write8(device, part)) < 0) { 249 | return err; 250 | } 251 | 252 | if ((err = mtk_device_read8(device, retval)) < 0) { 253 | return err; 254 | } 255 | } 256 | 257 | return 0; 258 | } 259 | 260 | int mtk_da_read(mtk_device *device, uint8_t hw_storage, uint64_t addr, uint64_t len, uint8_t *retval, const mtk_io_handler handler, void *user_data) { 261 | int err; 262 | 263 | if ((err = mtk_device_write8(device, MTK_DA_READ_CMD)) < 0) { 264 | return err; 265 | } 266 | if ((err = mtk_device_write8(device, MTK_DA_HOST_OS_LINUX)) < 0) { 267 | return err; 268 | } 269 | if ((err = mtk_device_write8(device, hw_storage)) < 0) { 270 | return err; 271 | } 272 | if ((err = mtk_device_write64(device, addr)) < 0) { 273 | return err; 274 | } 275 | if ((err = mtk_device_write64(device, len)) < 0) { 276 | return err; 277 | } 278 | 279 | if ((err = mtk_device_read8(device, retval)) < 0) { 280 | return err; 281 | } 282 | if (*retval != MTK_DA_ACK) { 283 | return 0; 284 | } 285 | 286 | uint8_t buffer[0x100000]; 287 | if ((err = mtk_device_write32(device, sizeof(buffer))) < 0) { 288 | return err; 289 | } 290 | 291 | size_t offset = 0; 292 | while (offset < len) { 293 | size_t count = MIN(sizeof(buffer), len - offset); 294 | 295 | if ((err = mtk_device_read(device, buffer, count)) < 0) { 296 | return err; 297 | } 298 | 299 | uint16_t chksum = 0; 300 | for (size_t i = 0; i < count; i++) { 301 | chksum += buffer[i]; 302 | } 303 | 304 | uint16_t chksum_device; 305 | if ((err = mtk_device_read16(device, &chksum_device)) < 0) { 306 | return err; 307 | } 308 | 309 | if (chksum != chksum_device) { 310 | return LIBUSB_ERROR_OTHER; 311 | } 312 | 313 | if ((err = mtk_device_write8(device, MTK_DA_ACK)) < 0) { 314 | return err; 315 | } 316 | 317 | if ((err = handler(false, offset, len, buffer, count, user_data)) < 0) { 318 | return err; 319 | } 320 | 321 | offset += count; 322 | } 323 | 324 | return 0; 325 | } 326 | 327 | int mtk_da_sdmmc_write_data(mtk_device *device, uint8_t storage_type, uint8_t part, uint64_t addr, uint64_t len, uint8_t *retval, const mtk_io_handler handler, void *user_data) { 328 | int err; 329 | 330 | if ((err = mtk_device_write8(device, MTK_DA_SDMMC_WRITE_DATA_CMD)) < 0) { 331 | return err; 332 | } 333 | if ((err = mtk_device_write8(device, storage_type)) < 0) { 334 | return err; 335 | } 336 | if ((err = mtk_device_write8(device, part)) < 0) { 337 | return err; 338 | } 339 | if ((err = mtk_device_write64(device, addr)) < 0) { 340 | return err; 341 | } 342 | if ((err = mtk_device_write64(device, len)) < 0) { 343 | return err; 344 | } 345 | 346 | uint8_t buffer[0x100000]; 347 | if ((err = mtk_device_write32(device, sizeof(buffer))) < 0) { 348 | return err; 349 | } 350 | 351 | if ((err = mtk_device_read8(device, retval)) < 0) { 352 | return err; 353 | } 354 | if (*retval != MTK_DA_ACK) { 355 | return 0; 356 | } 357 | 358 | size_t offset = 0; 359 | while (offset < len) { 360 | if ((err = mtk_device_write8(device, MTK_DA_ACK)) < 0) { 361 | return 0; 362 | } 363 | 364 | size_t count = MIN(sizeof(buffer), len - offset); 365 | 366 | if ((err = handler(true, offset, len, buffer, count, user_data)) < 0) { 367 | return err; 368 | } 369 | 370 | if ((err = mtk_device_write(device, buffer, count)) < 0) { 371 | return err; 372 | } 373 | 374 | uint16_t chksum = 0; 375 | for (size_t i = 0; i < count; i++) { 376 | chksum += buffer[i]; 377 | } 378 | 379 | if ((err = mtk_device_write16(device, chksum)) < 0) { 380 | return err; 381 | } 382 | 383 | if ((err = mtk_device_read8(device, retval)) < 0) { 384 | return err; 385 | } 386 | if (*retval != MTK_DA_CONT_CHAR) { 387 | return 0; 388 | } 389 | 390 | offset += count; 391 | } 392 | 393 | return 0; 394 | } 395 | 396 | int mtk_da_enable_watchdog(mtk_device *device, uint16_t timeout_ms, bool async, bool bootup, bool dlbit, bool not_reset_rtc_time, uint8_t *retval) { 397 | int err; 398 | 399 | if ((err = mtk_device_write8(device, MTK_DA_ENABLE_WATCHDOG_CMD)) < 0) { 400 | return err; 401 | } 402 | if ((err = mtk_device_write32(device, timeout_ms)) < 0) { 403 | return err; 404 | } 405 | if ((err = mtk_device_write8(device, async)) < 0) { 406 | return err; 407 | } 408 | if ((err = mtk_device_write8(device, bootup)) < 0) { 409 | return err; 410 | } 411 | if ((err = mtk_device_write8(device, dlbit)) < 0) { 412 | return err; 413 | } 414 | if ((err = mtk_device_write8(device, not_reset_rtc_time)) < 0) { 415 | return err; 416 | } 417 | if ((err = mtk_device_read8(device, retval))) { 418 | return err; 419 | } 420 | 421 | return 0; 422 | } 423 | --------------------------------------------------------------------------------