PMP V
USB Boot流程
參考資訊:
1. jzboot
2. jz4760 boot rom
君正MIPS CPU支援USB開機啟動,傳輸的USB端點為控制和巨量兩種類型
傳輸流程如下:
支援命令如下:
Command | Description |
---|---|
0x00: VR_GET_CPU_INFO | query cpu information |
0x01: VR_SET_DATA_ADDRESS | set data address |
0x02: VR_SET_DATA_LENGTH | set data length |
0x03: VR_FLUSH_CACHES | flush d-cache and i-cache |
0x04: VR_PROGRAM_START1 | execute stage1 program (<16KB) to initialize gpio and sdram |
0x05: VR_PROGRAM_START2 | execute stage2 program from sdram |
流程圖如下:
命令0x00的內容:
命令0x01的內容:
命令0x02的內容:
命令0x03的內容:
命令0x04的內容:
命令0x05的內容:
jzboot.c
#include <errno.h> #include <getopt.h> #include <libusb-1.0/libusb.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define MY_NAME "jzboot" #define STAGE1_LOAD_ADDR 0x80000000 #define STAGE2_LOAD_ADDR 0x81000000 #define TIMEOUT_MS 5000 #define ARRAY_SIZE(x) (sizeof(x) ? sizeof(x) / sizeof((x)[0]) : 0) enum commands { CMD_GET_CPU_INFO, CMD_SET_DATA_ADDR, CMD_SET_DATA_LEN, CMD_FLUSH_CACHES, CMD_START1, CMD_START2, }; static const uint16_t ingenic_ids[] = { 0x601a, 0xa108, }; static const uint16_t ingenic_product_ids[] = { 0x4740, 0x4750, 0x4760, 0x4770, 0x4780, }; static uint16_t pid; static unsigned int stage1_load_addr = STAGE1_LOAD_ADDR; static unsigned int stage2_load_addr = STAGE2_LOAD_ADDR; static FILE *stage1; static FILE *stage2; static FILE *devicetree; static const struct option options[] = { {"help", no_argument, 0, 'h'}, {"stage1-addr", required_argument, 0, 'a'}, {"stage2-addr", required_argument, 0, 'b'}, {0, 0, 0, 0}, }; static const char *options_descriptions[] = { "Show this help and quit.", "Set the load and boot address of the 1st-stage bootloader.", "Set the load and boot address of the program.", }; static void usage(void) { unsigned int i; printf("Usage:\n\t" MY_NAME " [OPTIONS ...] stage1 [kernel] [devicetree]\n\nOptions:\n"); for (i = 0; options[i].name; i++) printf("\t-%c, --%s\n\t\t\t%s\n", options[i].val, options[i].name, options_descriptions[i]); } static int cmd_get_info(libusb_device_handle *hdl) { unsigned char info[8]; int ret; ret = libusb_control_transfer(hdl, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, CMD_GET_CPU_INFO, 0, 0, info, sizeof(info), TIMEOUT_MS); if (ret != sizeof(info)) return -EIO; return 0; } static int cmd_control(libusb_device_handle *hdl, uint32_t cmd, uint32_t attr) { return libusb_control_transfer(hdl, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, cmd, (attr >> 16) & 0xffff, attr & 0xffff, NULL, 0, TIMEOUT_MS); } static int cmd_load_data(libusb_device_handle *hdl, FILE *f, uint32_t addr, size_t *data_size) { int ret, bytes_transferred; size_t size, to_read, to_write; unsigned char *data; char *ptr; /* Get the file size */ fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); if (data_size) *data_size = size; data = malloc(size); if (!data) return -ENOMEM; ptr = (char *)data; to_read = size; do { size_t bytes_read = fread(ptr, 1, to_read, f); if (!bytes_read) { ret = -EIO; goto out_free; } ptr += bytes_read; to_read -= bytes_read; } while (to_read > 0); /* Send the SET_DATA_LEN command */ ret = cmd_control(hdl, CMD_SET_DATA_LEN, size); if (ret) goto out_free; /* Send the SET_DATA_ADDR command */ ret = cmd_control(hdl, CMD_SET_DATA_ADDR, addr); if (ret) goto out_free; ptr = (char *)data; to_write = size; do { ret = libusb_bulk_transfer(hdl, LIBUSB_ENDPOINT_OUT | 0x1, ptr, (int)to_write, &bytes_transferred, TIMEOUT_MS); if (ret) goto out_free; to_write -= bytes_transferred; ptr += bytes_transferred; } while (to_write > 0); printf("Uploaded %lu bytes at address 0x%08x\n", (unsigned long)size, addr); out_free: free(data); return ret; } int main(int argc, char **argv) { libusb_context *usb_ctx; libusb_device_handle *hdl = NULL; int exit_code = EXIT_FAILURE; size_t kernel_size; unsigned int i, j; int ret, c; char *end; while ((c = getopt_long(argc, argv, "+ha:b:", options, NULL)) != -1) { switch (c) { case 'h': usage(); return EXIT_SUCCESS; case 'a': stage1_load_addr = strtol(optarg, &end, 16); if (optarg == end) { fprintf(stderr, "Unable to parse stage1 addr\n"); return EXIT_FAILURE; } break; case 'b': stage2_load_addr = strtol(optarg, &end, 16); if (optarg == end) { fprintf(stderr, "Unable to parse stage2 addr\n"); return EXIT_FAILURE; } break; case '?': return EXIT_FAILURE; } } if (optind == argc || argc > optind + 3) { fprintf(stderr, "Unable to parse arguments.\n"); usage(); return EXIT_FAILURE; } stage1 = fopen(argv[optind], "rb"); if (!stage1) { fprintf(stderr, "Unable to open stage1 program\n"); return EXIT_FAILURE; } if (argc >= optind + 2) { stage2 = fopen(argv[optind + 1], "rb"); if (!stage2) { fprintf(stderr, "Unable to open stage2 program\n"); return EXIT_FAILURE; } } if (argc >= optind + 3) { devicetree = fopen(argv[optind + 2], "rb"); if (!devicetree) { fprintf(stderr, "Unable to open devicetree\n"); return EXIT_FAILURE; } } ret = libusb_init(&usb_ctx); if (ret) { fprintf(stderr, "Unable to init libusb\n"); goto out_close_files; } for (j = 0; !hdl && j < ARRAY_SIZE(ingenic_ids); j++) { for (i = 0; !hdl && i < ARRAY_SIZE(ingenic_product_ids); i++) { hdl = libusb_open_device_with_vid_pid(usb_ctx, ingenic_ids[j], ingenic_product_ids[i]); } } if (!hdl) { fprintf(stderr, "Unable to find Ingenic device.\n"); goto out_exit_libusb; } pid = ingenic_product_ids[i - 1]; ret = libusb_claim_interface(hdl, 0); if (ret) { fprintf(stderr, "Unable to claim interface 0\n"); goto out_close_dev_handle; } if (cmd_get_info(hdl)) { fprintf(stderr, "Unable to read CPU info\n"); goto out_close_dev_handle; } printf("Found Ingenic JZ%x based device\n", pid); ret = cmd_load_data(hdl, stage1, stage1_load_addr, NULL); if (ret) { fprintf(stderr, "Unable to upload stage1 bootloader\n"); goto out_close_dev_handle; } ret = cmd_control(hdl, CMD_START1, stage1_load_addr); if (ret) { fprintf(stderr, "Unable to execute stage1 bootloader\n"); goto out_close_dev_handle; } if (!stage2) goto out_complete; printf("Waiting for stage1 bootloader to complete operation...\n"); for (i = 0; i < 100; i++) { if (!cmd_get_info(hdl)) break; usleep(10000); /* 10ms * 100 = 1s */ } if (i == 100) { fprintf(stderr, "Stage1 bootloader did not return.\n"); goto out_close_dev_handle; } ret = cmd_load_data(hdl, stage2, stage2_load_addr, &kernel_size); if (ret) { fprintf(stderr, "Unable to upload stage2 program\n"); goto out_close_dev_handle; } if (devicetree) { ret = cmd_load_data(hdl, devicetree, stage2_load_addr + kernel_size, NULL); if (ret) { fprintf(stderr, "Unable to upload devicetree\n"); goto out_close_dev_handle; } } ret = cmd_control(hdl, CMD_FLUSH_CACHES, 0); if (ret) { fprintf(stderr, "Unable to flush caches\n"); goto out_close_dev_handle; } ret = cmd_control(hdl, CMD_START2, stage2_load_addr); if (ret) { fprintf(stderr, "Unable to execute program\n"); goto out_close_dev_handle; } out_complete: printf("Operation complete.\n"); exit_code = EXIT_SUCCESS; out_close_dev_handle: libusb_close(hdl); out_exit_libusb: libusb_exit(usb_ctx); out_close_files: fclose(stage1); if (stage2) fclose(stage2); if (devicetree) fclose(devicetree); return exit_code; }
編譯命令:
$ gcc jzboot.c -o jzboot -lusb-1.0