From 8f4202db285b2139cfee6269b838733d6d2a2306 Mon Sep 17 00:00:00 2001 From: Marcin Bukat Date: Mon, 30 May 2011 21:10:43 +0000 Subject: Rockchip rk27xx utils git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29936 a1c6a512-1295-4272-9138-f99709370657 --- utils/rk27utils/README | 37 +++ utils/rk27utils/rk27load/Makefile | 7 + utils/rk27utils/rk27load/checksum.c | 35 +++ utils/rk27utils/rk27load/checksum.h | 1 + utils/rk27utils/rk27load/common.c | 16 ++ utils/rk27utils/rk27load/common.h | 1 + utils/rk27utils/rk27load/main.c | 165 ++++++++++++ utils/rk27utils/rk27load/rk27load.h | 11 + utils/rk27utils/rk27load/scramble.c | 46 ++++ utils/rk27utils/rk27load/scramble.h | 1 + utils/rk27utils/rk27load/stage1/Makefile | 48 ++++ utils/rk27utils/rk27load/stage1/main.S | 42 ++++ utils/rk27utils/rk27load/stage1/stage1.lds | 23 ++ utils/rk27utils/rk27load/stage1_upload.c | 113 +++++++++ utils/rk27utils/rk27load/stage1_upload.h | 3 + utils/rk27utils/rk27load/stage2/Makefile | 48 ++++ utils/rk27utils/rk27load/stage2/crt0.S | 55 ++++ utils/rk27utils/rk27load/stage2/irq.S | 103 ++++++++ utils/rk27utils/rk27load/stage2/main.S | 89 +++++++ utils/rk27utils/rk27load/stage2/stage2.lds | 40 +++ utils/rk27utils/rk27load/stage2_upload.c | 102 ++++++++ utils/rk27utils/rk27load/stage2_upload.h | 3 + utils/rk27utils/rk27load/stage3_upload.c | 93 +++++++ utils/rk27utils/rk27load/stage3_upload.h | 1 + utils/rk27utils/rkboottool/Makefile | 7 + utils/rk27utils/rkboottool/rkboottool.c | 360 ++++++++++++++++++++++++++ utils/rk27utils/rkusbtool/Makefile | 7 + utils/rk27utils/rkusbtool/rkusbtool.c | 388 +++++++++++++++++++++++++++++ 28 files changed, 1845 insertions(+) create mode 100644 utils/rk27utils/README create mode 100644 utils/rk27utils/rk27load/Makefile create mode 100644 utils/rk27utils/rk27load/checksum.c create mode 100644 utils/rk27utils/rk27load/checksum.h create mode 100644 utils/rk27utils/rk27load/common.c create mode 100644 utils/rk27utils/rk27load/common.h create mode 100644 utils/rk27utils/rk27load/main.c create mode 100644 utils/rk27utils/rk27load/rk27load.h create mode 100644 utils/rk27utils/rk27load/scramble.c create mode 100644 utils/rk27utils/rk27load/scramble.h create mode 100644 utils/rk27utils/rk27load/stage1/Makefile create mode 100644 utils/rk27utils/rk27load/stage1/main.S create mode 100644 utils/rk27utils/rk27load/stage1/stage1.lds create mode 100644 utils/rk27utils/rk27load/stage1_upload.c create mode 100644 utils/rk27utils/rk27load/stage1_upload.h create mode 100644 utils/rk27utils/rk27load/stage2/Makefile create mode 100644 utils/rk27utils/rk27load/stage2/crt0.S create mode 100644 utils/rk27utils/rk27load/stage2/irq.S create mode 100644 utils/rk27utils/rk27load/stage2/main.S create mode 100644 utils/rk27utils/rk27load/stage2/stage2.lds create mode 100644 utils/rk27utils/rk27load/stage2_upload.c create mode 100644 utils/rk27utils/rk27load/stage2_upload.h create mode 100644 utils/rk27utils/rk27load/stage3_upload.c create mode 100644 utils/rk27utils/rk27load/stage3_upload.h create mode 100644 utils/rk27utils/rkboottool/Makefile create mode 100644 utils/rk27utils/rkboottool/rkboottool.c create mode 100644 utils/rk27utils/rkusbtool/Makefile create mode 100644 utils/rk27utils/rkusbtool/rkusbtool.c diff --git a/utils/rk27utils/README b/utils/rk27utils/README new file mode 100644 index 0000000000..a43d69a88f --- /dev/null +++ b/utils/rk27utils/README @@ -0,0 +1,37 @@ +This is the collection of small utilities needed to hack Rockchip rk27xx +series based DAPs. This tools were tested on linux only. + + +rk27load +This directory contains tool which can send arbitrary image(s) to the device +in rockchip recovery mode (VID:PID 0x071B:0x3201). + +The first image can not exceed 510 bytes (+2 bytes checksum) and entry +point is 0x18020e00. Usually this code is used to configure SDRAM controller. +One can use first stage image extracted from Rock27Boot.bin file (a bit +more sofisticated) or the one provided in rk27load/stage1 directory. + +The second image is loaded at the begining of the dram (0x60000000) +and executed. For some reason (which is still unclear) the size of +2nd stage image is limited to about 3-4 kB. + +You can find example of custom 2nd stage image in rk27load/stage2 directory. +The purpose of this image is to configure bulk transfer and allow to +load usercode without size restriction mentioned above (the max size +is 8MB actually). The entry point of usercode is 0x60000000. + +You need libusb 1.0 + header files in order to compile this utility. +You need working arm-eabi crosscompiler in order to compile stage1/stage2 +bootloader binaries (but You should have one already if You tinker whith this) + + +rkboottool +This directory contains tool which allows to extract (and decrypt) images +stored in Rock27Boot.bin recovery file. + + +rkusbtool +This directory contains tool which sends custom scsi commands to the +rockchip player. + +You need libusb-1.0 + header files in order to compile this utility. diff --git a/utils/rk27utils/rk27load/Makefile b/utils/rk27utils/rk27load/Makefile new file mode 100644 index 0000000000..f777e76c36 --- /dev/null +++ b/utils/rk27utils/rk27load/Makefile @@ -0,0 +1,7 @@ +all: rk27load + +rk27load: main.c scramble.c checksum.c common.c stage1_upload.c stage2_upload.c stage3_upload.c + gcc -g -std=c99 -o $@ -W -Wall -lusb-1.0 -I/usr/include/libusb-1.0/ $^ + +clean: + rm -fr *.o rk27load diff --git a/utils/rk27utils/rk27load/checksum.c b/utils/rk27utils/rk27load/checksum.c new file mode 100644 index 0000000000..f0fe59350e --- /dev/null +++ b/utils/rk27utils/rk27load/checksum.c @@ -0,0 +1,35 @@ +#include +#include "checksum.h" + +uint16_t checksum(void *buff, uint32_t size) +{ + uint32_t r2 = 0xffff; + uint32_t r3 = 0; + uint32_t i, j; + + for (i=0; i>= 16; + r2 ^= 0x1000; + r2 ^= 0x21; + } + else { + r2 <<= 17; + r2 >>= 16; + } + + if ((((uint8_t *)buff)[i] & r3) != 0) { + r2 ^= 0x1000; + r2 ^= 0x21; + } + + r3 >>= 1; + } + } + + return r2 & 0xffff; +} + diff --git a/utils/rk27utils/rk27load/checksum.h b/utils/rk27utils/rk27load/checksum.h new file mode 100644 index 0000000000..468ca6643e --- /dev/null +++ b/utils/rk27utils/rk27load/checksum.h @@ -0,0 +1 @@ +uint16_t checksum(void *buff, uint32_t size); diff --git a/utils/rk27utils/rk27load/common.c b/utils/rk27utils/rk27load/common.c new file mode 100644 index 0000000000..b97cfbcd79 --- /dev/null +++ b/utils/rk27utils/rk27load/common.c @@ -0,0 +1,16 @@ +#include +#include + +#include "common.h" + +uint32_t filesize(FILE * f) +{ + uint32_t filesize; + + fseek(f, 0, SEEK_END); + filesize = ftell(f); + fseek(f, 0, SEEK_SET); + + return filesize; +} + diff --git a/utils/rk27utils/rk27load/common.h b/utils/rk27utils/rk27load/common.h new file mode 100644 index 0000000000..f22ec7de40 --- /dev/null +++ b/utils/rk27utils/rk27load/common.h @@ -0,0 +1 @@ +uint32_t filesize(FILE * f); diff --git a/utils/rk27utils/rk27load/main.c b/utils/rk27utils/rk27load/main.c new file mode 100644 index 0000000000..d183ae2df1 --- /dev/null +++ b/utils/rk27utils/rk27load/main.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include + +#include + +#include "rk27load.h" +#include "common.h" +#include "stage1_upload.h" +#include "stage2_upload.h" +#include "stage3_upload.h" + +#define VERSION "v0.2" + +enum { + NONE = 0, + ENCODE_S1 = 1, + ENCODE_S2 = 2 +}; + +static void usage(char *name) +{ + printf("usage: (sudo) %s [-e1 -e2] -s1 stage1.bin -s2 stage2.bin -s3 usercode.bin\n", name); + printf("stage1.bin - binary of the stage1 (sdram init)\n"); + printf("stage2.bin - binary of the stage2 bootloader\n"); + printf("usercode.bin - binary of the custom usercode\n"); + printf("\n"); + printf("options:\n"); + printf("-e1 - encode stage1 bootloader\n"); + printf("-e2 - encode stage2 bootloader\n"); +} + +int main(int argc, char **argv) +{ + libusb_device_handle *hdev; + char *filenames[3]; + int i=1, action=0, ret=0; + + while (i < argc) + { + if (strcmp(argv[i],"-e1") == 0) + { + action |= ENCODE_S1; + i++; + } + else if (strcmp(argv[i],"-e2") == 0) + { + action |= ENCODE_S2; + i++; + } + else if (strcmp(argv[i],"-s1") == 0) + { + i++; + if (i == argc) + { + usage(argv[0]); + return -1; + } + filenames[0] = argv[i]; + printf("%s", argv[i]); + i++; + } + else if (strcmp(argv[i],"-s2") == 0) + { + i++; + if (i == argc) + { + usage(argv[0]); + return -2; + } + filenames[1] = argv[i]; + i++; + } + else if (strcmp(argv[i],"-s3") == 0) + { + i++; + if (i == argc) + { + usage(argv[0]); + return -3; + } + filenames[2] = argv[i]; + i++; + } + else + { + usage(argv[0]); + return -4; + } + } + + + fprintf(stderr,"rk27load " VERSION "\n"); + fprintf(stderr,"(C) Marcin Bukat 2011\n"); + fprintf(stderr,"Based on rk27load ver. 0.1 written by AleMaxx (alemaxx at hotmail.de)\n\n"); + fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); + fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); + + /* initialize libusb */ + libusb_init(NULL); + + /* configure device */ + fprintf(stderr, "[info]: Initializing device... "); + hdev = libusb_open_device_with_vid_pid(NULL, VENDORID, PRODUCTID); + + if (hdev == NULL) + { + fprintf(stderr, "\n[error]: Could not find rockchip device\n"); + ret = -2; + goto finish; + } + + ret = libusb_set_configuration(hdev, 1); + if (ret < 0) + { + fprintf(stderr, "\n[error]: Could not select configuration (1)\n"); + ret = -3; + goto finish; + } + + ret = libusb_claim_interface(hdev, 0); + if (ret < 0) + { + fprintf(stderr, "\n[error]: Could not claim interface #0\n"); + ret = -4; + goto finish; + } + + ret = libusb_set_interface_alt_setting(hdev, 0, 0); + if (ret < 0) + { + fprintf(stderr, "\n[error]: Could not set alternate interface #0\n"); + ret = -5; + goto finish; + } + + fprintf(stderr, "done\n"); + + + ret = upload_stage1_code(hdev, filenames[0], (action & ENCODE_S1)); + if (ret < 0) + goto finish; + + ret = upload_stage2_code(hdev, filenames[1], (action & ENCODE_S2)); + if (ret < 0) + goto finish; + + ret = upload_stage3_code(hdev, filenames[2]); + if (ret < 0) + goto finish; + + /* done */ + ret = 0; + + finish: + if (hdev != NULL) + libusb_close(hdev); + + if (ret < 0) + fprintf(stderr, "[error]: Error %d\n", ret); + + return ret; +} diff --git a/utils/rk27utils/rk27load/rk27load.h b/utils/rk27utils/rk27load/rk27load.h new file mode 100644 index 0000000000..8239176a0d --- /dev/null +++ b/utils/rk27utils/rk27load/rk27load.h @@ -0,0 +1,11 @@ +#define USB_TIMEOUT 512 + +#define VENDORID 0x71b +#define PRODUCTID 0x3201 + +#define USB_EP0 0x41 + +#define VCMD_UPLOAD 0x0c +#define VCMD_INDEX_STAGE1 0x471 +#define VCMD_INDEX_STAGE2 0x472 + diff --git a/utils/rk27utils/rk27load/scramble.c b/utils/rk27utils/rk27load/scramble.c new file mode 100644 index 0000000000..7e5b1518d7 --- /dev/null +++ b/utils/rk27utils/rk27load/scramble.c @@ -0,0 +1,46 @@ +#include +#include "scramble.h" + +void scramble(uint8_t *in, uint8_t *out, const int size) +{ + /* table extracted from bootrom */ + static const uint8_t key[] = { + 0x7C, 0x4E, 0x03, 0x04, + 0x55, 0x05, 0x09, 0x07, + 0x2D, 0x2C, 0x7B, 0x38, + 0x17, 0x0D, 0x17, 0x11 + }; + + int i, i3, x, val, idx; + + uint8_t key1[0x100]; + uint8_t key2[0x100]; + + for (i=0; i<0x100; i++) { + key1[i] = i; + key2[i] = key[i&0xf]; + } + + i3 = 0; + for (i=0; i<0x100; i++) { + x = key1[i]; + i3 = key1[i] + i3; + i3 += key2[i]; + i3 &= 0xff; + key1[i] = key1[i3]; + key1[i3] = x; + } + + idx = 0; + for (i=0; i $(TARGET).asm + +clean : + rm -f $(OBJS) + rm -f $(TARGET).elf + rm -f $(TARGET).bin + rm -f $(TARGET).asm + rm -f $(TARGET).map diff --git a/utils/rk27utils/rk27load/stage1/main.S b/utils/rk27utils/rk27load/stage1/main.S new file mode 100644 index 0000000000..44e7e2f914 --- /dev/null +++ b/utils/rk27utils/rk27load/stage1/main.S @@ -0,0 +1,42 @@ +.section .text,"ax",%progbits +.global start + +start: + msr cpsr_c, #0xd3 /* enter supervisor mode, disable IRQ/FIQ */ + +pll_setup: + mov r0, #0x18000000 + add r0, r0, #0x1c000 + + /* setup ARM core freq = 200MHz */ + /* AHB bus freq (HCLK) = 100MHz */ + /* APB bus freq (PCLK) = 50MHz */ + ldr r1, [r0,#0x14] /* SCU_DIVCON1 */ + orr r1, #9 /* ARM slow mode, HCLK:PCLK = 2:1 */ + str r1, [r0,#0x14] + + ldr r1,=0x01970c70 /* (1<<24) | (1<<23) | (23<<16) | (199<<4) */ + str r1, [r0,#0x08] + + ldr r2,=0x40000 +1: + ldr r1, [r0,#0x2c] /* SCU_STATUS */ + tst r1, #1 /* ARM pll lock */ + bne 1f + subs r2, #1 + bne 1b +1: + ldr r1, [r0,#0x14] /* SCU_DIVCON1 */ + bic r1, #5 /* leave ARM slow mode, ARMclk:HCLK = 2:1 */ + str r1, [r0,#0x14] + +sdram_config: + add r0, r0, #0x94000 /* SDRAM base */ + + mov r1, #1 + str r1, [r0,#0x10c] /* MCSDR_BASIC Round-robin, SDRAM width 16bits */ + + add r1, #0x10 + str r1, [r0,#0x108] /* MCSDR_ADDCFG 12 bits row/9 bits col addr */ + + mov pc, lr /* we are done, return to bootrom code */ diff --git a/utils/rk27utils/rk27load/stage1/stage1.lds b/utils/rk27utils/rk27load/stage1/stage1.lds new file mode 100644 index 0000000000..4af8b93c55 --- /dev/null +++ b/utils/rk27utils/rk27load/stage1/stage1.lds @@ -0,0 +1,23 @@ +ENTRY(start) +OUTPUT_FORMAT(elf32-littlearm) +OUTPUT_ARCH(arm) +/* STARTUP(crt0.o) */ + +/* this is where bootrom loads sdram init code */ +MEMORY +{ + IRAM : ORIGIN = 0x18200E00, LENGTH = 0x00000200 +} + +SECTIONS +{ + .text : { + *(.text*) + *(.glue_7*) + } > IRAM + + .data : { + *(.rodata*) + *(.data*) + } > IRAM +} diff --git a/utils/rk27utils/rk27load/stage1_upload.c b/utils/rk27utils/rk27load/stage1_upload.c new file mode 100644 index 0000000000..8eb4ae9e37 --- /dev/null +++ b/utils/rk27utils/rk27load/stage1_upload.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "rk27load.h" +#include "common.h" +#include "scramble.h" +#include "checksum.h" +#include "stage1_upload.h" + +/* ### upload sdram init code ### */ +int upload_stage1_code(libusb_device_handle *hdev, char *fn_stage1, + bool do_scramble) +{ + FILE *f; + int ret; + uint8_t *code; + uint32_t codesize; + uint16_t cks; + + if ((f = fopen(fn_stage1, "rb")) == NULL) + { + fprintf(stderr, "[error]: Could not open file \"%s\"\n", fn_stage1); + return -10; + } + + codesize = filesize(f); + + if (codesize > 0x1fe) + { + fprintf(stderr, "[error]: Code too big for stage1\n"); + return -11; + } + + fprintf(stderr, "[stage1]: Loading %d bytes (%s) of code... ", codesize, fn_stage1); + + code = (uint8_t *)malloc(0x200); + if (code == NULL) + { + fprintf(stderr, "\n[error]: Out of memory\n"); + fclose(f); + return -12; + } + + memset(code, 0, 0x200); + if (fread(code, 1, codesize, f) != codesize) + { + fprintf(stderr, "\n[error]: I/O error\n"); + fclose(f); + free(code); + return -13; + } + + fprintf(stderr, "done\n"); + fclose(f); + + /* encode data if requested */ + if (do_scramble) + { + + fprintf(stderr, "[stage1]: Encoding %d bytes of data ... ", codesize); + scramble(code, code, codesize); + fprintf(stderr, "done\n"); + } + + + fprintf(stderr, "[stage1]: codesize = %d (0x%x)\n", codesize, codesize); + + fprintf(stderr, "[stage1]: Calculating checksum... "); + cks = checksum((void *)code, codesize); + fprintf(stderr, "0x%04x\n", cks); + code[0x1fe] = (cks >> 8) & 0xff; + code[0x1ff] = cks & 0xff; + codesize += 2; + + fprintf(stderr, "[stage1]: Uploading code (%d bytes)... ", codesize); + + ret = libusb_control_transfer(hdev, /* device handle */ + USB_EP0, /* bmRequestType */ + VCMD_UPLOAD, /* bRequest */ + 0, /* wValue */ + VCMD_INDEX_STAGE1, /* wIndex */ + code, /* data */ + codesize, /* wLength */ + USB_TIMEOUT /* timeout */ + ); + if (ret < 0) + { + fprintf(stderr, "\n[error]: Code upload request failed (ret=%d)\n", ret); + free(code); + return -14; + } + + if (ret != (int)codesize) + { + fprintf(stderr, "\n[error]: Sent %d of %d total\n", ret, codesize); + free(code); + return -15; + } + + sleep(1); /* wait for code to finish */ + fprintf(stderr, "done\n"); + + /* free code */ + free(code); + + return 0; +} + diff --git a/utils/rk27utils/rk27load/stage1_upload.h b/utils/rk27utils/rk27load/stage1_upload.h new file mode 100644 index 0000000000..efb1c3407e --- /dev/null +++ b/utils/rk27utils/rk27load/stage1_upload.h @@ -0,0 +1,3 @@ +int upload_stage1_code(libusb_device_handle * hdev, char *fn_stage1, + bool do_scramble); + diff --git a/utils/rk27utils/rk27load/stage2/Makefile b/utils/rk27utils/rk27load/stage2/Makefile new file mode 100644 index 0000000000..4b216db2f3 --- /dev/null +++ b/utils/rk27utils/rk27load/stage2/Makefile @@ -0,0 +1,48 @@ + +TARGET = stage2 + +TOOLCHAIN = arm-elf-eabi- + +CC = $(TOOLCHAIN)gcc +CPP = $(TOOLCHAIN)cpp +LD = $(TOOLCHAIN)gcc +AS = $(TOOLCHAIN)as +OBJCOPY = $(TOOLCHAIN)objcopy +OBJDUMP = $(TOOLCHAIN)objdump + +CFLAGS = -Wundef -marm -march=armv5te -nostdlib -mfpu=fpa -O0 -c +#ASFLAGS = -mcpu=arm926ej-s + +OBJS = crt0.o main.o irq.o +LDSCRIPT= stage2.lds + +#LIBDIRS = -L../arm/lib/gcc/arm-elf/4.1.0/ -L../lib +#LIBS = -lgcc +LIBS = +LDFLAGS = -Wundef -marm -march=armv5te -T$(LDSCRIPT) -nostartfiles \ + -mfpu=fpa -nostdlib -Xlinker -Map=$(TARGET).map + +all : $(TARGET).bin + ls -ls $(TARGET).bin + +%.o : %.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(INCDIRS) $< -o $@ + +%.o : %.S + $(CC) $(CFLAGS) -c $< -o $@ + +$(TARGET).elf : $(OBJS) + $(LD) $(LDFLAGS) $(OBJS) $(LIBDIRS) $(LIBS) -o $(TARGET).elf + +$(TARGET).bin : $(TARGET).elf + $(OBJCOPY) -O binary $(TARGET).elf $(TARGET).bin + +dasm : $(TARGET).bin + $(OBJDUMP) -m arm -D $(TARGET).elf | cat > $(TARGET).asm + +clean : + rm -f $(OBJS) + rm -f $(TARGET).elf + rm -f $(TARGET).bin + rm -f $(TARGET).asm + rm -f $(TARGET).map diff --git a/utils/rk27utils/rk27load/stage2/crt0.S b/utils/rk27utils/rk27load/stage2/crt0.S new file mode 100644 index 0000000000..c85477546d --- /dev/null +++ b/utils/rk27utils/rk27load/stage2/crt0.S @@ -0,0 +1,55 @@ +// +// startup code +// +// + +#define PSR_MODE 0x0000001f +#define PSR_USR_MODE 0x00000010 +#define PSR_IRQ_MODE 0x00000012 +#define PSR_SVC_MODE 0x00000013 + +#define PSR_INT_MASK 0x000000c0 +#define PSR_FIQ_DIS 0x00000040 +#define PSR_IRQ_DIS 0x00000080 + +.section .init.text,"ax",%progbits +.global start +.extern _interrupt_disable + +// ----------------------------------------------------- +// startup code (setup stacks, branch to main) +// ----------------------------------------------------- +start: + // setup IRQ stack + mov r0, #(PSR_IRQ_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS) + msr cpsr, r0 + ldr sp,=irqstackend + + // setup SVC stack + mov r0, #(PSR_SVC_MODE|PSR_FIQ_DIS|PSR_IRQ_DIS) + msr cpsr, r0 + ldr sp,=stackend + + // disbale interrupts + mrs r0, cpsr + orr r0, r0, #0xc0 + msr cpsr_c, r0 + + // remap + mov r0, #0x18000000 + add r0, r0, #0x1C000 + ldr r1,=0xdeadbeef + str r1, [r0, #4] + + // relocate itself + ldr r0,=_relocstart + ldr r1,=_relocend + ldr r2,=0x0 +1: + cmp r1,r0 + ldrhi r3,[r0],#4 + strhi r3,[r2],#4 + bhi 1b + + // continue running in SVC (supervisor mode) + ldr pc,=0x0 diff --git a/utils/rk27utils/rk27load/stage2/irq.S b/utils/rk27utils/rk27load/stage2/irq.S new file mode 100644 index 0000000000..043bf185a5 --- /dev/null +++ b/utils/rk27utils/rk27load/stage2/irq.S @@ -0,0 +1,103 @@ + .section .text + .align 4 + + .global irq_handler + #define BUFF_ADDR 0x60800000 + +irq_handler: + stmfd sp!, {r0-r7, ip, lr} + + // get interrupt number + mov r4, #0x18000000 + add r4, r4, #0x80000 + ldr r5, [r4, #0x104] + and r5, r5, #0x1f + cmp r5, #0x10 // UDC interrupt + + bleq udc_irq + + // clear pending interrupt + mov r3, #1 + mov r2, r3, LSL r5 + str r2, [r4, #0x118] + + ldmfd sp!, {r0-r7, ip, lr} + subs pc, lr, #4 + +udc_irq: + stmfd sp!, {r4-r8, lr} + + // handle usb interrupt + ldr r4,=0x180A0000 + ldr r5, [r4, #0x18] // UDC_INTFLAG + + // ep0 in intr + tst r5, #0x04 + beq bulk_recv_intr + + // write_reg32(UDC_TX0STAT, read_reg32(UDC_TX0STAT) & ~0x7FF); + ldr r5, [r4, #0x40] + mov r5, r5, lsr #10 + mov r5, r5, lsl #10 // clear clower 10 bits + str r5, [r4, #0x40] + + // write_reg32(UDC_DMA0LM_OADDR, (uint32_t)(state.ctrlep_data)); + mov r5, #0x60000000 + str r5, [r4, #0x3c] + + // write_reg32(UDC_DMA0CTLO, read_reg32(UDC_DMA0CTLO) | ENP_DMA_START); + mov r5, #1 + str r5, [r4, #0x38] + + ldmfd sp!, {r4-r8, pc} + +// bulk out interrupt +bulk_recv_intr: + tst r5, #0x100 + ldmeqfd sp!, {r4-r8, pc} + + // read UDC_RX1STAT + ldr r5, [r4, #0x54] + mov r5, r5, lsl #21 + mov r5, r5, lsr #21 // r5 = length + + ldr r6,=usb_sz + ldr r6, [r6] + ldr r7, [r6] // r7 = total_code_length expected + + subs r7, r7, r5 + bne usb_bulk_out1_recv + + // copy from buff to the begining of the ram + ldr r0,=BUFF_ADDR + ldr r1,[r0,#-4] // size + + ldr r1,=0x800000 // buffer size + + add r1,r1,r0 // end address + ldr r2,=0x60000000 // destination +1: + cmp r1,r0 + ldrhi r3,[r0],#4 + strhi r3,[r2],#4 + bhi 1b + + // execute user code + ldr r0,=0x60000000 + bx r0 // jump to 0x60000000 + +usb_bulk_out1_recv: + str r7, [r6] // size = size - received + + ldr r6,=usb_write_addr + ldr r7, [r6] + + add r7, r7, r5 + str r7, [r6] // usb_write_addr += length + + str r7, [r4, #0x60] // DMA1LM_OADDR = usb_write_addr + + mov r5, #1 + str r5, [r4, #0x5c] // DMA1_CTL0 = ENP_DMA_START + + ldmfd sp!, {r4-r8, pc} diff --git a/utils/rk27utils/rk27load/stage2/main.S b/utils/rk27utils/rk27load/stage2/main.S new file mode 100644 index 0000000000..c8474b0579 --- /dev/null +++ b/utils/rk27utils/rk27load/stage2/main.S @@ -0,0 +1,89 @@ + + .section .text + .align 4 + + .arm + + .global main + .global _interrupt_disable + .global _interrupt_enable + + .global usb_write_addr + .global usb_sz + + #define BUFF_ADDR 0x60800000 + +// ----------------------------------------------------- +// vector table +// ----------------------------------------------------- + ldr pc, =main + ldr pc, =main + ldr pc, =main + ldr pc, =main + ldr pc, =main + ldr pc, =main + ldr pc, =irq_handler + ldr pc, =main + +// ----------------------------------------------------- +// main +// ----------------------------------------------------- +main: + // turn on usb interrupts + mov r0, #0x18000000 + add r0, r0, #0x80000 + ldr r1, [r0, #0x10c] + orr r1, r1, #0x10000 + str r1, [r0, #0x10c] + + // enable usb-bulk + add r0, r0, #0x20000 // R0 = 0x180A0000 (UDC_BASE) + + // enable EP1, write_reg32(UDC_RX1CON, (0x1 << 8) | RxACKINTEN | RxEPEN); + mov r1, #0x190 // bits 8,7,4 -> 0x190 + str r1, [r0, #0x58] + + // setup receive buffer (must be aligned on dword boundary) + ldr r1,=usb_write_addr // write_reg32(UDC_DMA1LM_OADDR, (uint32_t)rx_buff); + ldr r1, [r1] + str r1, [r0, #0x60] // UDC_DMA1LM_OADDR = usb_write_addr + + // write_reg32(UDC_DMA1CTRLO, read_reg32(UDC_DMA1CTRLO) | ENP_DMA_START); + ldr r1, [r0, #0x5c] + orr r1, r1, #2 + str r1, [r0, #0x5c] + + // enable bulk_out1 interrupt + ldr r1, [r0, #0x14] // UDC_ENINT + orr r1, r1, #0x100 // EN_BOUT1_INTR + str r1, [r0, #0x14] + + bl _interrupt_enable +idle: + b idle + +// ----------------------------------------------------- +// _interrupt_enable - enables interrupts +// ----------------------------------------------------- +_interrupt_enable: + mrs r0, cpsr + bic r0, r0, #0x80 + msr cpsr_c, r0 + mov pc, lr + +// ----------------------------------------------------- +// _interrupt_disable - disables interrupts +// ----------------------------------------------------- +_interrupt_disable: + mrs r0, cpsr + orr r0, r0, #0xc0 + msr cpsr_c, r0 + mov pc, lr + + + .section .data +usb_write_addr: + .word (BUFF_ADDR-4) + +usb_sz: + .word (BUFF_ADDR-4) diff --git a/utils/rk27utils/rk27load/stage2/stage2.lds b/utils/rk27utils/rk27load/stage2/stage2.lds new file mode 100644 index 0000000000..2c07b201f7 --- /dev/null +++ b/utils/rk27utils/rk27load/stage2/stage2.lds @@ -0,0 +1,40 @@ +ENTRY(start) +OUTPUT_FORMAT(elf32-littlearm) +OUTPUT_ARCH(arm) +/* STARTUP(crt0.o) */ + +MEMORY +{ + DRAM : ORIGIN = 0x60000000, LENGTH = 0x01000000 + IRAM : ORIGIN = 0x00000000, LENGTH = 0x00002000 +} + +SECTIONS +{ + .init.text : { + *(.init.text) + } > DRAM + + .text : { + *(.text*) + *(.glue_7*) + } > IRAM AT > DRAM + + .data : { + *(.data*) + } > IRAM AT > DRAM + + _relocstart = LOADADDR(.text); + _relocend = LOADADDR(.data) + SIZEOF(.data); + + .stack (NOLOAD) : { + . = ALIGN(0x100); + *(.stack) + stackbegin = .; + . += 0x200; + stackend = .; + irqstackbegin = .; + . += 0x200; + irqstackend = .; + } > IRAM +} diff --git a/utils/rk27utils/rk27load/stage2_upload.c b/utils/rk27utils/rk27load/stage2_upload.c new file mode 100644 index 0000000000..820ad4463c --- /dev/null +++ b/utils/rk27utils/rk27load/stage2_upload.c @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include + +#include "rk27load.h" +#include "common.h" +#include "scramble.h" +#include "checksum.h" +#include "stage2_upload.h" + +int upload_stage2_code(libusb_device_handle *hdev, char *fn_stage2, + bool do_scramble) +{ + FILE *f; + uint32_t codesize; + uint8_t *code; + uint16_t cks; + int ret; + + if ((f = fopen(fn_stage2, "rb")) == NULL) + { + fprintf(stderr, "[error]: Could not open file \"%s\"\n", fn_stage2); + return -21; + } + + codesize = filesize(f); + + fprintf(stderr, "[stage1]: Loading %d bytes (%s) of code... ", codesize, fn_stage2); + + code = (uint8_t *) malloc(codesize + 0x400); + if (code == NULL) + { + fprintf(stderr, "\n[error]: Out of memory\n"); + fclose(f); + return -22; + + } + + memset(code, 0, codesize + 0x400); + + if (fread(code, 1, codesize, f) != codesize) + { + fprintf(stderr, "\n[error]: I/O error\n"); + fclose(f); + free(code); + return -23; + } + fprintf(stderr, "done\n"); + fclose(f); + + codesize = ((codesize + 0x201) & 0xfffffe00) - 2; + + if (do_scramble) + { + /* encode data if its user code */ + fprintf(stderr, "[stage2]: Encoding %d bytes data... ", codesize); + scramble(code, code, codesize); + fprintf(stderr, "done\n"); + } + + fprintf(stderr, "[stage2]: Calculating checksum... "); + cks = checksum(code, codesize); + code[codesize + 0] = (cks >> 8) & 0xff; + code[codesize + 1] = cks & 0xff; + codesize += 2; + fprintf(stderr, "0x%04x\n", cks); + + fprintf(stderr, "[stage2]: Uploading code (%d bytes)... ", codesize); + + ret = libusb_control_transfer(hdev, /* device handle */ + USB_EP0, /* bmRequestType */ + VCMD_UPLOAD, /* bRequest */ + 0, /* wValue */ + VCMD_INDEX_STAGE2, /* wIndex */ + code, /* data */ + codesize, /* wLength */ + USB_TIMEOUT /* timeout */ + ); + + if (ret < 0) + { + fprintf(stderr, "\n[error]: Code upload request failed (ret=%d)\n", ret); + free(code); + return -24; + } + + if (ret != (int)codesize) + { + fprintf(stderr, "[error]: Sent %d of %d total\n", ret, codesize); + free(code); + return -25; + } + + fprintf(stderr, "done\n"); + + free(code); + return 0; +} + diff --git a/utils/rk27utils/rk27load/stage2_upload.h b/utils/rk27utils/rk27load/stage2_upload.h new file mode 100644 index 0000000000..852d17adb2 --- /dev/null +++ b/utils/rk27utils/rk27load/stage2_upload.h @@ -0,0 +1,3 @@ +int upload_stage2_code(libusb_device_handle * hdev, char *fn_stage2, + bool do_scramble); + diff --git a/utils/rk27utils/rk27load/stage3_upload.c b/utils/rk27utils/rk27load/stage3_upload.c new file mode 100644 index 0000000000..6f10a7c995 --- /dev/null +++ b/utils/rk27utils/rk27load/stage3_upload.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +#include "rk27load.h" +#include "common.h" +#include "scramble.h" +#include "checksum.h" +#include "stage3_upload.h" + +int upload_stage3_code(libusb_device_handle *hdev, char *fn_stage3) +{ + FILE *f; + uint32_t codesize; + uint32_t remain; + uint8_t *code; + uint16_t send_size = 0x200; + uint32_t i = 0; + int ret, transfered; + + if ((f = fopen(fn_stage3, "rb")) == NULL) + { + fprintf(stderr, "[error]: Could not open file \"%s\"\n", fn_stage3); + return -31; + } + + codesize = filesize(f); + + fprintf(stderr, "[stage3]: Loading user code (%d bytes)... ", codesize); + + /* allocate buffer */ + code = (uint8_t *) malloc(codesize + 0x204); + if (code == NULL) + { + fprintf(stderr, "\n[error]: Out of memory\n"); + fclose(f); + return -32; + } + + memset(code, 0, codesize + 0x204); + /* read usercode into buffer */ + if (fread(&code[4], 1, codesize, f) != codesize) + { + fprintf(stderr, "\n[error]: I/O error\n"); + fclose(f); + free(f); + return -33; + } + fprintf(stderr, "done\n"); + + fclose(f); + + /* put code size at the first 4 bytes */ + codesize += 4; + code[0] = codesize & 0xff; + code[1] = (codesize >> 8) & 0xff; + code[2] = (codesize >> 16) & 0xff; + code[3] = (codesize >> 24) & 0xff; + + fprintf(stderr, "[stage3]: Uploading user code (%d bytes)... ", codesize); + + remain = codesize; + + while (remain > 0) + { + if (remain < 0x200) + send_size = remain; + + ret = libusb_bulk_transfer(hdev, /* handle */ + 1, /* EP */ + &code[i * 0x200], /* data */ + send_size, /* length */ + &transfered, /* xfered */ + USB_TIMEOUT /* timeout */ + ); + + if (ret != LIBUSB_SUCCESS) + { + fprintf(stderr, "\n[error]: Bulk transfer error (%d, %d)\n", ret, i); + free(code); + return -34; + } + + remain -= send_size; + i++; + } + + fprintf(stderr,"done (sent %d blocks)\n", i); + return 0; +} + diff --git a/utils/rk27utils/rk27load/stage3_upload.h b/utils/rk27utils/rk27load/stage3_upload.h new file mode 100644 index 0000000000..03f9f0e46a --- /dev/null +++ b/utils/rk27utils/rk27load/stage3_upload.h @@ -0,0 +1 @@ +int upload_stage3_code(libusb_device_handle *hdev, char *fn_stage3); diff --git a/utils/rk27utils/rkboottool/Makefile b/utils/rk27utils/rkboottool/Makefile new file mode 100644 index 0000000000..895dfc87cc --- /dev/null +++ b/utils/rk27utils/rkboottool/Makefile @@ -0,0 +1,7 @@ +all: rkboottool + +rkboottool: rkboottool.c + gcc -g -std=c99 -o $@ -W -Wall $^ + +clean: + rm -fr rkboottool diff --git a/utils/rk27utils/rkboottool/rkboottool.c b/utils/rk27utils/rkboottool/rkboottool.c new file mode 100644 index 0000000000..ad08b0b5f6 --- /dev/null +++ b/utils/rk27utils/rkboottool/rkboottool.c @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include + +#define VERSION "v0.3" + +/* time field stucture */ +struct rktime_t +{ + uint16_t year; + uint16_t month; + uint16_t day; + uint16_t hour; + uint16_t minute; + uint16_t second; +}; + +/* Rock27Boot.bin header structure */ +struct rkboot_info_t +{ + char sign[32]; + uint8_t check_values[16]; + struct rktime_t time; + uint32_t ui_master_version; + uint32_t ui_slave_version; + uint32_t s1_offset; + int32_t s1_len; + uint32_t s2_offset; + int32_t s2_len; + uint32_t s3_offset; + int32_t s3_len; + uint32_t s4_offset; + int32_t s4_len; + uint32_t version_flag; +}; + +/* actions */ +enum { + NONE = 0, + INFO = 1, + EXTRACT = 2, + SCRAMBLE = 4 +}; + +/* scramble mode */ +enum { + CONTINOUS_ENC, /* scramble whole block at once */ + PAGE_ENC /* nand bootloader is scrambled in 0x200 chunks */ +}; + +/* scrambling/descrambling reverse engineered by AleMaxx */ +static void encode_page(uint8_t *inpg, uint8_t *outpg, const int size) +{ + +uint8_t key[] = { + 0x7C, 0x4E, 0x03, 0x04, + 0x55, 0x05, 0x09, 0x07, + 0x2D, 0x2C, 0x7B, 0x38, + 0x17, 0x0D, 0x17, 0x11 +}; + int i, i3, x, val, idx; + + uint8_t key1[0x100]; + uint8_t key2[0x100]; + + for (i=0; i<0x100; i++) { + key1[i] = i; + key2[i] = key[i&0xf]; + } + + i3 = 0; + for (i=0; i<0x100; i++) { + x = key1[i]; + i3 = key1[i] + i3; + i3 += key2[i]; + i3 &= 0xff; + key1[i] = key1[i3]; + key1[i3] = x; + } + + idx = 0; + for (i=0; i= 0x200) + { + encode_page((uint8_t *)buff_ptr, + (uint8_t *)buff_ptr, + 0x200); + + buff_ptr += 0x200; + len -= 0x200; + } + } + encode_page((uint8_t *)buff_ptr, (uint8_t *)buff_ptr, len); + } + + return buff; +} + +static void usage(void) +{ + printf("Usage: rkboottool [options] Rock27Boot.bin\n"); + printf("-h|--help This help message\n"); + printf("-e|--extract Extract binary images from Rock27Boot.bin file\n"); + printf("-d|--descramble Descramble extracted binary images\n"); + printf("-i|--info Print info about Rock27Boot.bin file\n"); + printf("\n"); + printf("Usually you would like to use -d -e together to obtain raw binary\n"); + printf("(out files rkboot_s1.bin, rkboot_s2.bin, rkboot_s3.bin, rkboot_s4.bin)\n"); +} + +int main (int argc, char **argv) +{ + struct rkboot_info_t rkboot_info; + FILE *fp_in, *fp_out; + int32_t i = 0, action = NONE; + int32_t ret; + void *buff; + char *in_filename = NULL; + + if ( argc < 2 ) + { + usage(); + return -1; + } + + /* print banner */ + fprintf(stderr,"rkboottool " VERSION "\n"); + fprintf(stderr,"(C) Marcin Bukat 2011\n"); + fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); + fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); + + /* arguments handling */ + while (i < argc) + { + if ((strcmp(argv[i],"-i")==0) || (strcmp(argv[i],"--info")==0)) + { + action |= INFO; + } + else if ((strcmp(argv[i],"-e")==0) || (strcmp(argv[i],"--extract")==0)) + { + action |= EXTRACT; + } + else if ((strcmp(argv[i],"-d")==0) || (strcmp(argv[i],"--descramble")==0)) + { + action |= SCRAMBLE; + } + else if ((strcmp(argv[i],"-h")==0) || (strcmp(argv[i],"--help")==0)) + { + usage(); + return 0; + } + else if ( argv[i][0] != '-' ) + { + /* file argument */ + in_filename = argv[i]; + } + i++; + } + + if ( (fp_in = fopen(in_filename, "rb")) == NULL ) + { + fprintf(stderr, "error: can't open %s file for reading\n", in_filename); + return -1; + } + + ret = fread(&rkboot_info, 1, sizeof(rkboot_info), fp_in); + + if (ret != sizeof(rkboot_info)) + { + fclose(fp_in); + fprintf(stderr, "error: can't read %s file header\n", in_filename); + fprintf(stderr, "read %d, expected %d\n", ret, sizeof(rkboot_info)); + return -2; + } + + if (action & INFO) + { + printf("file: %s\n", in_filename); + printf("signature: %s\n", rkboot_info.sign); + printf("check bytes: "); + for (i = 0; i < 16; i++) + printf("0x%0x ", rkboot_info.check_values[i]); + + printf("\n"); + printf("timestamp %d.%d.%d %d:%d:%d\n", rkboot_info.time.day, + rkboot_info.time.month, + rkboot_info.time.year, + rkboot_info.time.hour, + rkboot_info.time.minute, + rkboot_info.time.second); + printf("UI master version: 0x%0x\n", rkboot_info.ui_master_version); + printf("UI slave version: 0x%0x\n", rkboot_info.ui_slave_version); + printf("s1 data offset: 0x%0x\n", rkboot_info.s1_offset); + printf("s1 data len: 0x%0x\n", rkboot_info.s1_len); + printf("s2 offset: 0x%0x\n", rkboot_info.s2_offset); + printf("s2 len: 0x%0x\n", rkboot_info.s2_len); + printf("s3 offset: 0x%0x\n", rkboot_info.s3_offset); + printf("s3 len: 0x%0x\n", rkboot_info.s3_len); + printf("s4 offset: 0x%0x\n", rkboot_info.s4_offset); + printf("s4 len: 0x%0x\n", rkboot_info.s4_len); + printf("UI version flag: 0x%0x\n", rkboot_info.version_flag); + } + + if (action & EXTRACT) + { + /* first stage */ + buff = binary_extract(fp_in, rkboot_info.s1_offset, + rkboot_info.s1_len, + action & SCRAMBLE, + CONTINOUS_ENC); + + if ( buff == NULL ) + { + fclose(fp_in); + fprintf(stderr, "error: can't extract image\n"); + return -2; + } + + /* output */ + if ((fp_out = fopen("rkboot_s1.bin", "wb")) == NULL) + { + free(buff); + fclose(fp_in); + fprintf(stderr, "[error]: can't open rkboot_s1.bin for writing\n"); + return -3; + } + + fwrite(buff, 1, rkboot_info.s1_len, fp_out); + + fprintf(stderr, "[info]: extracted rkboot_s1.bin file\n"); + free(buff); + fclose(fp_out); + + /* second stage */ + buff = binary_extract(fp_in, rkboot_info.s2_offset, + rkboot_info.s2_len, + action & SCRAMBLE, + CONTINOUS_ENC); + + if ( buff == NULL ) + { + fclose(fp_in); + fprintf(stderr, "error: can't extract image\n"); + return -2; + } + + if ((fp_out = fopen("rkboot_s2.bin", "wb")) == NULL) + { + free(buff); + fclose(fp_in); + fprintf(stderr, "[error]: can't open rkboot_s2.bin for writing\n"); + return -4; + } + + fwrite(buff, 1, rkboot_info.s2_len, fp_out); + + fprintf(stderr, "[info]: extracted rkboot_s2.bin file\n"); + free(buff); + fclose(fp_out); + + /* third stage */ + buff = binary_extract(fp_in, rkboot_info.s3_offset, + rkboot_info.s3_len, + action & SCRAMBLE, + PAGE_ENC); + if ( buff == NULL ) + { + fclose(fp_in); + fprintf(stderr, "[error]: can't extract image.\n"); + return -2; + } + + if ((fp_out = fopen("rkboot_s3.bin", "wb")) == NULL) + { + free(buff); + fclose(fp_in); + fprintf(stderr, "[error]: can't open rkboot_s3.bin for writing\n"); + return -4; + } + + fwrite(buff, 1, rkboot_info.s3_len, fp_out); + + fprintf(stderr, "[info]: extracted rkboot_s3.bin file\n"); + free(buff); + fclose(fp_out); + + /* forth stage */ + buff = binary_extract(fp_in, rkboot_info.s4_offset, + rkboot_info.s4_len, + action & SCRAMBLE, + CONTINOUS_ENC); + if ( buff == NULL ) + { + fclose(fp_in); + fprintf(stderr, "[error]: can't extract image\n"); + return -2; + } + + if ((fp_out = fopen("rkboot_s4.bin", "wb")) == NULL) + { + free(buff); + fclose(fp_in); + fprintf(stderr, "[error]: can't open rkboot_s4.bin for writing\n"); + return -4; + } + + fwrite(buff, 1, rkboot_info.s4_len, fp_out); + + fprintf(stderr, "[info]: extracted rkboot_s4.bin file\n"); + free(buff); + fclose(fp_out); + } + + fclose(fp_in); + return 0; +} + diff --git a/utils/rk27utils/rkusbtool/Makefile b/utils/rk27utils/rkusbtool/Makefile new file mode 100644 index 0000000000..785a09a1f1 --- /dev/null +++ b/utils/rk27utils/rkusbtool/Makefile @@ -0,0 +1,7 @@ +all: rkusbtool + +rkusbtool: rkusbtool.c + gcc -g -std=c99 -o $@ -W -Wall -lusb-1.0 -I/usr/include/libusb-1.0/ $^ + +clean: + rm -fr rkusbtool diff --git a/utils/rk27utils/rkusbtool/rkusbtool.c b/utils/rk27utils/rkusbtool/rkusbtool.c new file mode 100644 index 0000000000..06fb7e860c --- /dev/null +++ b/utils/rk27utils/rkusbtool/rkusbtool.c @@ -0,0 +1,388 @@ +/* on ubuntu compile with gcc -W rkusbtool.c -o rkusbtool -lusb-1.0 -I/usr/include/libusb-1.0/ */ +#include +#include +#include +#include + +#define VERSION "v0.1" + +#define RETRY_MAX 5 +#define USB_TIMEOUT 512 +#define VENDORID 0x071b +#define PRODUCTID 0x3203 + +#define OUT_EP 0x01 +#define IN_EP 0x82 + +#define CBW_SIGNATURE 0x43425355 +#define CSW_SIGNATURE 0x53425355 +#define SCSICMD_READ_12 0xa8 + +/* rockchip specific commands */ +#define RK_CMD 0xe0 +#define RK_GET_VERSION 0xffffffff +#define RK_SWITCH_ROCKUSB 0xfeffffff +#define RK_CHECK_USB 0xfdffffff +#define RK_OPEN_SYSDISK 0xfcffffff + +enum { + NONE = 0, + INFO = 1, + RKUSB = 2, + SYSDISK = 4, + CHECKUSB = 8 +}; + +enum { + COMMAND_PASSED = 0, + COMMAND_FAILED = 1, + PHASE_ERROR = 2 +}; + +struct CBWCB_t +{ + uint8_t cbCode; + uint8_t cbLun; + uint32_t LBA; + uint32_t cbLen; + uint8_t reseved; + uint8_t control; +} __attribute__((__packed__)); + +struct CBW_t +{ + uint32_t dCBWSignature; + uint32_t dCBWTag; + uint32_t dCBWDataTransferLength; + uint8_t bmCBWFlags; + uint8_t bCBWLUN; + uint8_t bCBWCBLength; + uint8_t CBWCB[16]; +} __attribute__((__packed__)); + +struct CSW_t +{ + uint32_t dCSWSignature; + uint32_t dCSWTag; + uint32_t dCSWDataResidue; + uint8_t bCSWStatus; +} __attribute__((__packed__)); + +static int send_msc_cmd(libusb_device_handle *hdev, struct CBWCB_t *cbwcb, uint32_t data_len, uint32_t *reftag) +{ + struct CBW_t cbw; + int ret, repeat, transferred; + static uint32_t tag = 0xdaefbc01; + + memset(&cbw, 0, sizeof(cbw)); + cbw.dCBWSignature = CBW_SIGNATURE; + cbw.dCBWTag = tag++; + cbw.dCBWDataTransferLength = data_len; + cbw.bmCBWFlags = 0x80; /* device to host */ + cbw.bCBWLUN = 0; + cbw.bCBWCBLength = sizeof(struct CBWCB_t); + memcpy(cbw.CBWCB, cbwcb, sizeof(struct CBWCB_t)); + + *reftag = cbw.dCBWTag; + do + { + /* transfer command to the device */ + ret = libusb_bulk_transfer(hdev, OUT_EP, (unsigned char*)&cbw, 31, &transferred, USB_TIMEOUT); + if (ret == LIBUSB_ERROR_PIPE) + { + libusb_clear_halt(hdev, OUT_EP); + } + repeat++; + } while ((ret == LIBUSB_ERROR_PIPE) && (repeat < RETRY_MAX)); + + if (ret != LIBUSB_SUCCESS) + { + printf("error: command transfer error\n"); + return -1; + } + + return 0; +} + +static int get_msc_csw(libusb_device_handle *hdev, uint32_t reftag) +{ + struct CSW_t csw; + int ret, repeat, transferred; + + /* get CSW response from device */ + repeat = 0; + do + { + ret = libusb_bulk_transfer(hdev, IN_EP, (unsigned char *)&csw, 13, &transferred, USB_TIMEOUT); + if (ret == LIBUSB_ERROR_PIPE) + { + libusb_clear_halt(hdev, IN_EP); + } + repeat++; + } while ((ret == LIBUSB_ERROR_PIPE) && (repeat < RETRY_MAX)); + + if (ret != LIBUSB_SUCCESS) + { + printf("error reading CSW\n"); + return -3; + } + + if (transferred != 13) + { + printf("error wrong size of CSW packet\n"); + return -4; + } + + if (csw.dCSWSignature != CSW_SIGNATURE) + { + printf("error: wrong CSW signature.\n"); + return -5; + } + + if (csw.dCSWTag != reftag) + { + printf("error: CSW dCSWTag mismatch\n"); + return -6; + } + + if (csw.bCSWStatus) + { + /* In case of CSW indicating error dump the content of the packet */ + printf ("dCSWSignature: 0x%0x\n", csw.dCSWSignature); + printf ("dCSWTag: 0x%0x\n", csw.dCSWTag); + printf ("dCSWDataResidue: 0x%0x\n", csw.dCSWDataResidue); + printf ("bCSWStatus: 0x%0x\n", csw.bCSWStatus); + } + + return csw.bCSWStatus; +} + +static int rk_cmd(libusb_device_handle *hdev, uint32_t command, uint8_t *buf, uint8_t len) +{ + struct CBWCB_t cbwcb; + int ret, transferred; + uint32_t reftag; + + /* enter command */ + memset(&cbwcb, 0, sizeof(cbwcb)); + cbwcb.cbCode = SCSICMD_READ_12; + cbwcb.cbLun = RK_CMD; + cbwcb.LBA = command; /* RK_GET_VERSION, RK_OPEN_SYSDISK, RK_SWITCH_ROCKUSB */ + cbwcb.cbLen = len; /* size of transfer in response to this command */ + + ret = send_msc_cmd(hdev, &cbwcb, len, &reftag); + + /* get the response */ + if (len > 0) + { + ret = libusb_bulk_transfer(hdev, IN_EP, buf, len, &transferred, USB_TIMEOUT); + if (ret != LIBUSB_SUCCESS || transferred != len) + { + printf("error: reading response data failed\n"); + return -2; + } + } + + return get_msc_csw(hdev, reftag); +} + +static int get_sense(libusb_device_handle *hdev) +{ + struct CBWCB_t cbwcb; + unsigned char sense[0x12]; + int size, ret; + uint32_t reftag; + + memset(&cbwcb, 0, sizeof(cbwcb)); + cbwcb.cbCode = 0x03; + cbwcb.cbLun = 0; + cbwcb.LBA = 0; + cbwcb.cbLen = 0x12; + + ret = send_msc_cmd(hdev, &cbwcb, 0x12, &reftag); + libusb_bulk_transfer(hdev, IN_EP, (unsigned char*)&sense, 0x12, &size, USB_TIMEOUT); + + return get_msc_csw(hdev, reftag); +} + +static void usage(void) +{ + printf("Usage: rkusbtool [options]\n"); + printf("-h|--help This help message\n"); + printf("-i|--info Get version string from the device\n"); + printf("-d|--dfu Put device into DFU mode\n"); + printf("-s|--sysdisk Open system disk\n"); + printf("-c|--checkusb Check if dev is in System or Loader USB mode\n"); +} + +int main (int argc, char **argv) +{ + libusb_device_handle *hdev; + int ret; + int i = 0, action = NONE; + uint32_t ver[3]; + + if (argc < 2) + { + usage(); + return 1; + } + + /* print banner */ + fprintf(stderr,"rkusbtool " VERSION "\n"); + fprintf(stderr,"(C) Marcin Bukat 2011\n"); + fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); + fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); + + /* arguments handling */ + while (i < argc) + { + if ((strcmp(argv[i],"-i")==0) || (strcmp(argv[i],"--info")==0)) + { + action |= INFO; + } + else if ((strcmp(argv[i],"-d")==0) || (strcmp(argv[i],"--dfu")==0)) + { + action |= RKUSB; + } + else if ((strcmp(argv[i],"-s")==0) || (strcmp(argv[i],"--sysdisk")==0)) + { + action |= SYSDISK; + } + else if ((strcmp(argv[i],"-c")==0) || (strcmp(argv[i],"--checkusb")==0)) + { + action |= CHECKUSB; + } + else if ((strcmp(argv[i],"-h")==0) || (strcmp(argv[i],"--help")==0)) + { + usage(); + return 0; + } + i++; + } + + /* initialize libusb */ + libusb_init(NULL); + /* usb_set_debug(2); */ + + hdev = libusb_open_device_with_vid_pid(NULL, VENDORID, PRODUCTID); + if (hdev == NULL) + { + printf("error: can't open device\n"); + return -10; + } + + ret = libusb_kernel_driver_active(hdev, 0); + + if (ret < 0) + { + printf ("error checking kernel driver active\n"); + libusb_close(hdev); + return -3; + } + else + { + if (ret) + libusb_detach_kernel_driver(hdev, 0); + } + + ret = libusb_set_configuration(hdev, 1); + if (ret < 0) + { + printf("error: could not select configuration (1)\n"); + libusb_close(hdev); + return -3; + } + + ret = libusb_claim_interface(hdev, 0); + if (ret < 0) + { + printf("error: could not claim interface #0\n"); + libusb_close(hdev); + return -11; + } + + ret = libusb_set_interface_alt_setting(hdev, 0, 0); + if ( ret != LIBUSB_SUCCESS) + { + printf("error: could not set alt setting for interface #0\n"); + libusb_close(hdev); + return -11; + } + + /* BulkOnly reset */ + //ret = libusb_control_transfer(hdev, 0x21, 0xff, 0, 0, NULL, 0, USB_TIMEOUT); + + /* BulkOnly get max lun */ + //ret = libusb_control_transfer(hdev, 0xa1, 0xfe, 0, 0, &maxlun, 1, USB_TIMEOUT); + + /* Devices that do not support multiple LUNs may STALL this command. */ + //if (ret == 0) + // maxlun = -1; + + //printf("MAXLUN: %d\n", maxlun); + + get_sense(hdev); + + if (action & INFO) + { + ret = rk_cmd(hdev, RK_GET_VERSION, (uint8_t *)ver, 12); + + if (ret) + { + printf("error sending RK_GET_VERSION command. Err 0x%0x\n", ret); + libusb_close(hdev); + return ret; + } + + printf("Rockchip device info:\n"); + printf("loader ver: %x.%x\n", (ver[0]>>16)&0xff, ver[0]&0xff); + printf("kernel ver: %x.%x\n", (ver[1]>>16)&0xff, ver[1]&0xff); + printf("sdk ver: %x.%x\n", (ver[2]>>16)&0xff, ver[2]&0xff); + } + + if (action & CHECKUSB) + { + printf("Checking USB mode...\n"); + ret = rk_cmd(hdev, RK_CHECK_USB, (uint8_t *)ver, 1); + + //if (ret) + //{ + // libusb_close(hdev); + // return ret; + //} + + if (*(char *)ver) + printf("The device is in Loader USB mode\n"); + else + printf("The device is in System USB mode\n"); + } + + if (action & SYSDISK) + { + printf("Opening system disk...\n"); + ret = rk_cmd(hdev, RK_OPEN_SYSDISK, NULL, 0); + + if (ret) + { + libusb_close(hdev); + return ret; + } + } + + if (action & RKUSB) + { + printf("Switching into rk DFU mode...\n"); + ret = rk_cmd(hdev, RK_SWITCH_ROCKUSB, NULL, 0); + + if (ret) + { + libusb_close(hdev); + return ret; + } + } + + libusb_close(hdev); + libusb_exit(NULL); + return 0; +} -- cgit