diff --git a/README.md b/README.md index a617ea4..418a4e6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ingenic-usbboot +# Creality K1 (X2000E) usbboot tool ## Building `make` @@ -12,7 +12,7 @@ To Enter USB Boot mode for the K1, hold the boot button, while holding boot hold BIG caveat, I haven't gotten keyboard input to work through serial, I can only see the uboot logs. Please reach out if you figure out how to get keyboard input over serial for uboot. -## Unbricking Creality K1 Mainboard +## Unbricking Creality K1 Mainboard (USE AT YOUR OWN RISK) The Creality K1 comes with a set of backup partitions. The OTA partition stores either the string `ota:kernel` or `ota:kernel2` to indicate which set of partitions to boot. This tool allows you to quickly switch between the two. This should fix a bricked Creality K1 mainboard given the backup partitions are functional. For cases where the partition table, main, and backup partitions are corrupted, read through this [guide](https://github.com/ballaswag/k1-discovery/blob/main/k1-ingenic-cloner-instruction.pdf) for a better chance of recoverying the mainboad. ``` @@ -27,6 +27,45 @@ Switched OTA to ota:kernel ``` +## Dumping Partitions +Use this to dump your existing partitions in the K1. Example K1 partition table. +``` +uboot(gpt/uboot): Offset 0x000000000, Length 0x0000100000 +ota: Offset 0x000100000, Length 0x0000100000 +sn_mac: Offset 0x000200000, Length 0x0000100000 +rtos: Offset 0x000300000, Length 0x0000400000 +rtos2: Offset 0x000700000, Length 0x0000400000 +kernel: Offset 0x000b00000, Length 0x0000800000 +kernel2: Offset 0x001300000, Length 0x0000800000 +rootfs: Offset 0x001b00000, Length 0x0012c00000 +rootfs2: Offset 0x014700000, Length 0x0012c00000 +rootfs_data: Offset 0x027300000, Length 0x0006400000 +userdata: Offset 0x02d700000, Length 0x01a4a00000 + +Total disk size:0x00000001d2104200, sectors:0x0000000000e90821 +``` + +``` +## start uboot +$ sudo ./usbboot --uboot + +## dump the kernel partition to file ./kernel.out +$ sudo ./usbboot -o 0x000b00000 -s 0x0000800000 --dump-partition ./kernel.out +dumping parition at offset 0xb00000, size 0x800000 +25.00% completed (1.00MB/s) +50.00% completed (1.00MB/s) +75.00% completed (1.00MB/s) +100.00% completed (1.00MB/s) + +## dump the rootfs partition to ./rootfs.out +$ sudo ./usbboot -o 0x001b00000 -s 0x0012c00000 --dump-partition ./rootfs.out +dumping parition at offset 0x1b00000, size 0x12c00000 +0.67% completed (0.67MB/s) +1.33% completed (1.00MB/s) +2.00% completed (1.00MB/s) +... +``` + ## SPL/u-boot The general idea is to write and execute loader codes at different memory spaces during the different stages of boot. SPL is a smaller piece of code that lives in TCSM/SRAM and executed to initialize DRAM. Then the bigger uboot code can be loaded and executed from DRAM. diff --git a/usbboot b/usbboot index 2ad170d..7830a64 100755 Binary files a/usbboot and b/usbboot differ diff --git a/usbboot.c b/usbboot.c index c706d82..8e10188 100644 --- a/usbboot.c +++ b/usbboot.c @@ -363,6 +363,44 @@ void mmc_read(uint32_t offset, uint32_t length, unsigned char* out) { } } +void mmc_read_partition(uint32_t offset, uint32_t length, const char* fname) { + enable_mmc(); + if(length == 0) + die("invalid partition length: %d", length); + + printf("dumping parition at offset 0x%x, size 0x%x\n", offset, length); + + uint32_t chunk_size = 1024 * 1024 * 2; // 2mb + unsigned char chunk[chunk_size]; + + FILE* f = fopen(fname, "wb"); + if(f == NULL) + die("Can't open file '%s' for writing", fname); + + uint32_t cursor = offset; + uint32_t end = offset + length; + while (cursor < end) { + uint32_t read_size = (end - cursor) >= chunk_size + ? chunk_size + : (end - cursor); + + uint32_t start = (uint32_t)time(NULL); + mmc_read(cursor, read_size, chunk); + uint32_t duration = (uint32_t)time(NULL) - start; + + cursor += read_size; + + printf("%.2f%% completed (%.2fMB/s)\n", + (cursor - offset) / (float)length * 100, + (read_size / 1024 / 1024) / (float)duration); + + if (fwrite(chunk, read_size, 1, f) != 1) + die("Failed to write data to %x", fname); + } + + fclose(f); +} + void mmc_write(uint32_t offset, uint32_t length, unsigned char* in) { WriteCmd *write = (WriteCmd*) malloc(sizeof(WriteCmd)); memset(write, 0, sizeof(WriteCmd)); @@ -536,7 +574,7 @@ int main(int argc, char* argv[]) OPT_CPUINFO, OPT_START1, OPT_START2, OPT_FLUSH_CACHES, OPT_RENUMERATE, OPT_WAIT, OPT_SWAP_OTA, - OPT_FORCE_SWAP_OTA + OPT_FORCE_SWAP_OTA, OPT_DUMP_PARITION }; static const struct option long_options[] = { @@ -549,6 +587,8 @@ int main(int argc, char* argv[]) {"cpuinfo", no_argument, 0, OPT_CPUINFO}, {"addr", required_argument, 0, 'a'}, {"length", required_argument, 0, 'l'}, + {"partition-size", required_argument, 0, 's'}, + {"partition-offset", required_argument, 0, 'o'}, {"upload", required_argument, 0, 'u'}, {"download", required_argument, 0, 'd'}, {"start1", required_argument, 0, OPT_START1}, @@ -557,7 +597,8 @@ int main(int argc, char* argv[]) {"renumerate", no_argument, 0, OPT_RENUMERATE}, {"wait", required_argument, 0, OPT_WAIT}, {"swap-ota", no_argument, 0, OPT_SWAP_OTA}, - {"force-swap-ota", no_argument, 0, OPT_FORCE_SWAP_OTA}, + {"force-swap-ota", no_argument, 0, OPT_FORCE_SWAP_OTA}, + {"dump-partition", required_argument, 0, OPT_DUMP_PARITION}, {"help", no_argument, 0, 'h'}, {"verbose", no_argument, 0, 'v'}, {0, 0, 0, 0} @@ -565,7 +606,9 @@ int main(int argc, char* argv[]) int opt; int data_length = -1; - while((opt = getopt_long(argc, argv, "bhvc:1:2:a:l:u:d:", long_options, NULL)) != -1) { + uint32_t partition_offset = 0; + uint32_t partition_size = 0; + while((opt = getopt_long(argc, argv, "bhvc:1:2:a:l:s:o:u:d:", long_options, NULL)) != -1) { unsigned long param; char* end; switch(opt) { @@ -579,7 +622,18 @@ int main(int argc, char* argv[]) param = strtoul(optarg, &end, 0); if(*end) die("Invalid argument '%s'", optarg); + break; + case 's': + partition_size = strtoul(optarg, &end, 0); + if(*end) + die("Invalid argument '%s'", optarg); + break; + case 'o': + partition_offset = strtoul(optarg, &end, 0); + if(*end) + die("Invalid argument '%s'", optarg); + break; default: break; } @@ -651,10 +705,20 @@ int main(int argc, char* argv[]) verbose("Force OTA to boot ota:kernel"); swap_ota_partition(true); break; + case OPT_DUMP_PARITION: + if (partition_size <= 0) + die("must provide a positive partition length with option --partition-size"); + + verbose("Dump a partition to file"); + mmc_read_partition(partition_offset, partition_size, optarg); + break; + case 'o': + case 's': + break; default: /* should only happen due to a bug */ - die("Bad option"); - break; + die("Bad option"); + break; } }