From 5d5a1234fec60c92f3fd15a6746740ab90d84b59 Mon Sep 17 00:00:00 2001 From: ballaswag Date: Tue, 26 Sep 2023 23:15:12 -0700 Subject: [PATCH] Add option to dump partitions from the creality k1/max --- README.md | 43 +++++++++++++++++++++++++++++-- usbboot | Bin 41808 -> 46704 bytes usbboot.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 110 insertions(+), 7 deletions(-) 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 2ad170de4f131606795db1802467316c56f60d66..7830a64d5c1ca30eaa51088d061199413557458e 100755 GIT binary patch literal 46704 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 delta 15212 zcmb_jeO#1P_J8i3d0=LkVTSh?0S5&{RKOQd@eNZ7O${qsDh*IcDMVJ$tX&!k-6XQW zo2F$ewQgBip=j&c*3!#ny4gqD@+GZo(}Hx1tc>hse&2JS=NTU7_u1cHzx(;jbME<` zbI&>V+}C*qdd~C1Z}FD+NN<`VOQA-T!<$>Pp0a0pG&ZyKM2_!?{u*OYMHe!_RuUD` z1Eo+t1GFSk;|*$V7XnfHjhei@>T+66LX=*O4pfYW93Wwxb4d(ZZBX$ndpC8|OpOhu zP6`yoDK_(KQvaLR65VTwuv?Z!|B(5?O*3|Mz4@*45%;C~r>tY=)OLyoESbraF0v=J z)qBV+CoCPGjE*CgPguv>s*Q=c zcQUcqK1aN2&kvwfYA=_ywpZrupas&Y`rqN}Q0GzuT+{B9|?D4*#3io`0rqHyI) z@KlUk8}Z1vf$!DFz`#4HPbi&LiceMy!J;C`D&E^PYT#`%G`=the@e&8gYai{ydenR zspD+{T=TE&)digaf#&dUI(|9`@6>VS_R9l(TgSaY_#quH48q^{>4Neg!ATu&2*S_k zcv}#DPRBcg@C!PAItcgcxKF9Ryax__o_m9Ew~iME;qf|N9)u6l@rEFLsE*sTIqC~c zUL8;lFVzB~HDJ-I@_F2Zt0w%k36C)0T_${h3GXrCY5M$=Vq?{i@eEUgLVYz6KFEZZ zoA7iKe!NCwLP7L}A%W8-oHiR-X#RDX1jdpjVUG!yJA!P1t;>XylR(jXMwqd3NMbo?^n=O?ZD3zE{WdXzaZvL8qxfstJGFgb(a^!c*oJ?Vi=^ zyE1AxYV6vTfJ#2*Uc{}IH$62$Kb;-5Eo zQJB_m_JdeL{+MQ9-`$$+?!Q{jl;U+0{tBdcow|$2*xtIB;%2pm>X=k^|7mys22y-O zr_gVb`g!+WEq0{!;}fnH2hx(W9tWtb7&520aY7SV0*;->w3VL<6R_p9Fz`JE(h+TZn8G;UUxgF{yw&FT_a zTM>+M*=|tqx?>BH% z+@x}#aQ{;=$eK>+=bYKka`=J#WU+X8P`}t$Nexl{dI_eBewI!<(y6A?qA*->#UGmn24C&C9O@e6Tc=-@0q@hVX| zcocsrPCPhxK;jO}gFxRGQ|Qe_$BWm+yc#DC4j#bU;>1^j^T)NAwZ21)(CVRh%X($K zi|Q8CTOjCNSUkj-~$

4mzBt8#fg@z1b(haJeM_!FNhVNW)0vhPN+k? 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 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; } }