PMP V

USB Boot流程


參考資訊:
1. jzboot
2. jz4760 boot rom

君正MIPS CPU支援USB開機啟動,傳輸的USB端點為控制和巨量兩種類型


傳輸流程如下:


支援命令如下:

CommandDescription
0x00: VR_GET_CPU_INFOquery cpu information
0x01: VR_SET_DATA_ADDRESSset data address
0x02: VR_SET_DATA_LENGTHset data length
0x03: VR_FLUSH_CACHESflush d-cache and i-cache
0x04: VR_PROGRAM_START1execute stage1 program (<16KB) to initialize gpio and sdram
0x05: VR_PROGRAM_START2execute 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


返回上一頁