summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/g/config_chip.h4
-rw-r--r--extra/usb_updater/.gitignore1
-rw-r--r--extra/usb_updater/Makefile36
-rw-r--r--extra/usb_updater/usb_updater.c455
4 files changed, 496 insertions, 0 deletions
diff --git a/chip/g/config_chip.h b/chip/g/config_chip.h
index 5f505020de..688ccb5582 100644
--- a/chip/g/config_chip.h
+++ b/chip/g/config_chip.h
@@ -6,8 +6,10 @@
#ifndef __CROS_EC_CONFIG_CHIP_H
#define __CROS_EC_CONFIG_CHIP_H
+#if defined(BOARD)
#include "core/cortex-m/config_core.h"
#include "hw_regdefs.h"
+#endif
/* Describe the RAM layout */
#define CONFIG_RAM_BASE 0x10000
@@ -34,8 +36,10 @@
/* Program is run directly from storage */
#define CONFIG_MAPPED_STORAGE_BASE CONFIG_PROGRAM_MEMORY_BASE
+#if defined(BOARD)
/* Compute the rest of the flash params from these */
#include "config_std_internal_flash.h"
+#endif
/* Interval between HOOK_TICK notifications */
#define HOOK_TICK_INTERVAL_MS 500
diff --git a/extra/usb_updater/.gitignore b/extra/usb_updater/.gitignore
new file mode 100644
index 0000000000..c65c3dae7b
--- /dev/null
+++ b/extra/usb_updater/.gitignore
@@ -0,0 +1 @@
+usb_updater
diff --git a/extra/usb_updater/Makefile b/extra/usb_updater/Makefile
new file mode 100644
index 0000000000..8a55f750f6
--- /dev/null
+++ b/extra/usb_updater/Makefile
@@ -0,0 +1,36 @@
+# Copyright 2015 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+PROGRAM := usb_updater
+SOURCE := $(PROGRAM).c
+LIBS :=
+LFLAGS :=
+CFLAGS := -std=gnu99 \
+ -g3 \
+ -O3 \
+ -Wall \
+ -Werror \
+ -Wpointer-arith \
+ -Wcast-align \
+ -Wcast-qual \
+ -Wundef \
+ -Wsign-compare \
+ -Wredundant-decls \
+ -Wmissing-declarations
+
+#
+# Add libusb-1.0 required flags
+#
+LIBS += $(shell pkg-config --libs libusb-1.0) -lcrypto
+CFLAGS += $(shell pkg-config --cflags libusb-1.0)
+
+CFLAGS += -I../../board/cr50 -I ../../chip/g -I../../util
+
+$(PROGRAM): $(SOURCE) Makefile
+ gcc $(CFLAGS) $(SOURCE) $(LFLAGS) $(LIBS) -o $@
+
+.PHONY: clean
+
+clean:
+ rm -rf $(PROGRAM) *~
diff --git a/extra/usb_updater/usb_updater.c b/extra/usb_updater/usb_updater.c
new file mode 100644
index 0000000000..f1183de852
--- /dev/null
+++ b/extra/usb_updater/usb_updater.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2015 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <endian.h>
+#include <getopt.h>
+#include <libusb.h>
+#include <openssl/sha.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "misc_util.h"
+#include "usb_upgrade.h"
+#include "config_chip.h"
+
+/* Google Cr50 */
+#define VID 0x18d1
+#define PID 0x5014
+#define SUBCLASS UNOFFICIAL_USB_SUBCLASS_GOOGLE_CR50
+#define PROTOCOL 0xff
+
+/* Globals */
+static char *progname;
+static char *short_opts = ":d:h";
+static const struct option long_opts[] = {
+ /* name hasarg *flag val */
+ {"device", 1, NULL, 'd'},
+ {"help", 0, NULL, 'h'},
+ {NULL, 0, NULL, 0},
+};
+
+static void usage(int errs)
+{
+ printf("\nUsage: %s [options] ec.bin\n"
+ "\n"
+ "This updates the Cr50 RW firmware over USB.\n"
+ "The required argument is the full RO+RW image.\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " -d,--device VID:PID USB device (default %04x:%04x)\n"
+ " -h,--help Show this message\n"
+ "\n", progname, VID, PID);
+
+ exit(!!errs);
+}
+
+/* Read file into buffer */
+static uint8_t *get_file_or_die(const char *filename, uint32_t *len_ptr)
+{
+ FILE *fp;
+ struct stat st;
+ uint8_t *data;
+ uint32_t len;
+
+ fp = fopen(filename, "rb");
+ if (!fp) {
+ perror(filename);
+ exit(1);
+ }
+ if (fstat(fileno(fp), &st)) {
+ perror("stat");
+ exit(1);
+ }
+
+ len = st.st_size;
+
+ data = malloc(len);
+ if (!data) {
+ perror("malloc");
+ exit(1);
+ }
+
+ if (1 != fread(data, st.st_size, 1, fp)) {
+ perror("fread");
+ exit(1);
+ }
+
+ fclose(fp);
+
+ *len_ptr = len;
+ return data;
+}
+
+#define USB_ERROR(m, r) \
+ fprintf(stderr, "%s:%d, %s returned %d (%s)\n", __FILE__, __LINE__, \
+ m, r, libusb_strerror(r))
+
+static void xfer(struct libusb_device_handle *devh, uint8_t ep_num,
+ void *outbuf, int outlen, void *inbuf, int inlen) {
+
+ int r, actual;
+
+ /* Send data out */
+ if (outbuf && outlen) {
+ actual = 0;
+ r = libusb_bulk_transfer(devh, ep_num,
+ outbuf, outlen,
+ &actual, 1000);
+ if (r < 0) {
+ USB_ERROR("libusb_bulk_transfer", r);
+ exit(1);
+ }
+ if (actual != outlen) {
+ fprintf(stderr, "%s:%d, only sent %d/%d bytes\n",
+ __FILE__, __LINE__, actual, outlen);
+ exit(1);
+ }
+ }
+
+ /* Read reply back */
+ if (inbuf && inlen) {
+
+ actual = 0;
+ r = libusb_bulk_transfer(devh, ep_num | 0x80,
+ inbuf, inlen,
+ &actual, 1000);
+ if (r < 0) {
+ USB_ERROR("libusb_bulk_transfer", r);
+ exit(1);
+ }
+ if (actual != inlen) {
+ fprintf(stderr, "%s:%d, only received %d/%d bytes\n",
+ __FILE__, __LINE__, actual, inlen);
+ exit(1);
+ }
+ }
+}
+
+
+/* Return 0 on error, since it's never gonna be EP 0 */
+static int find_endpoint(const struct libusb_interface_descriptor *iface,
+ uint8_t *ep_num_ptr, int *chunk_len_ptr)
+{
+ const struct libusb_endpoint_descriptor *ep;
+
+ if (iface->bInterfaceClass == 255 &&
+ iface->bInterfaceSubClass == SUBCLASS &&
+ iface->bInterfaceProtocol == PROTOCOL &&
+ iface->bNumEndpoints) {
+ ep = &iface->endpoint[0];
+ *ep_num_ptr = (ep->bEndpointAddress & 0x7f);
+ *chunk_len_ptr = ep->wMaxPacketSize;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return -1 on error */
+static int find_interface(struct libusb_device_handle *devh,
+ uint8_t *ep_num_ptr, int *chunk_len_ptr)
+{
+ int iface_num = -1;
+ int r, i, j;
+ struct libusb_device *dev;
+ struct libusb_config_descriptor *conf = 0;
+ const struct libusb_interface *iface0;
+ const struct libusb_interface_descriptor *iface;
+
+ dev = libusb_get_device(devh);
+ r = libusb_get_active_config_descriptor(dev, &conf);
+ if (r < 0) {
+ USB_ERROR("libusb_get_active_config_descriptor", r);
+ goto out;
+ }
+
+ for (i = 0; i < conf->bNumInterfaces; i++) {
+ iface0 = &conf->interface[i];
+ for (j = 0; j < iface0->num_altsetting; j++) {
+ iface = &iface0->altsetting[j];
+ if (find_endpoint(iface, ep_num_ptr, chunk_len_ptr)) {
+ iface_num = i;
+ goto out;
+ }
+ }
+ }
+
+out:
+ libusb_free_config_descriptor(conf);
+ return iface_num;
+}
+
+/* Returns true if parsed. */
+static int parse_vidpid(const char *input, uint16_t *vid_ptr, uint16_t *pid_ptr)
+{
+ char *copy, *s, *e = 0;
+
+ copy = strdup(input);
+
+ s = strchr(copy, ':');
+ if (!s)
+ return 0;
+ *s++ = '\0';
+
+ *vid_ptr = (uint16_t) strtoul(copy, &e, 16);
+ if (!*optarg || (e && *e))
+ return 0;
+
+ *pid_ptr = (uint16_t) strtoul(s, &e, 16);
+ if (!*optarg || (e && *e))
+ return 0;
+
+ return 1;
+}
+
+
+static struct libusb_device_handle *usb_connect(uint16_t vid, uint16_t pid,
+ uint8_t *ep_num, int *chunk_len)
+{
+ struct libusb_device_handle *devh;
+ int iface_num, r;
+
+ r = libusb_init(NULL);
+ if (r < 0) {
+ USB_ERROR("libusb_init", r);
+ exit(1);
+ }
+
+ printf("open_device %04x:%04x\n", vid, pid);
+ /* NOTE: This doesn't handle multiple matches! */
+ devh = libusb_open_device_with_vid_pid(NULL, vid, pid);
+ if (!devh) {
+ fprintf(stderr, "can't find device\n");
+ exit(1);
+ }
+
+ iface_num = find_interface(devh, ep_num, chunk_len);
+ if (iface_num < 0) {
+ fprintf(stderr, "USB FW update not supported by that device\n");
+ exit(1);
+ }
+ if (!chunk_len) {
+ fprintf(stderr, "wMaxPacketSize isn't valid\n");
+ exit(1);
+ }
+
+ printf("found interface %d endpoint %d, chunk_len %d\n",
+ iface_num, *ep_num, *chunk_len);
+
+ libusb_set_auto_detach_kernel_driver(devh, 1);
+ r = libusb_claim_interface(devh, iface_num);
+ if (r < 0) {
+ USB_ERROR("libusb_claim_interface", r);
+ exit(1);
+ }
+
+ printf("READY\n-------\n");
+ return devh;
+}
+
+#define SIGNED_TRANSFER_SIZE 1024
+struct upgrade_command {
+ uint32_t block_digest;
+ uint32_t block_base;
+};
+
+struct update_pdu {
+ uint32_t block_size; /* Total block size, include this field's size. */
+ struct upgrade_command cmd;
+ /* The actual payload goes here. */
+};
+
+#define FLASH_BASE 0x40000
+
+static void transfer_and_reboot(struct libusb_device_handle *devh,
+ uint8_t *data, uint32_t data_len,
+ uint8_t ep_num, int chunk_len)
+{
+ uint32_t out;
+ uint32_t reply;
+ uint8_t *data_ptr;
+ uint32_t next_offset;
+ struct update_pdu updu;
+
+ /* Send start/erase request */
+ printf("erase\n");
+
+ memset(&updu, 0, sizeof(updu));
+ updu.block_size = htobe32(sizeof(updu));
+ xfer(devh, ep_num, &updu, sizeof(updu), &reply, sizeof(reply));
+/* check the offset here. */
+ next_offset = be32toh(reply) - FLASH_BASE;
+ printf("Updating at offset 0x%08x\n", next_offset);
+
+ data_ptr = data + next_offset;
+ data_len = CONFIG_RW_SIZE;
+
+ /* Actually, we can skip trailing chunks of 0xff */
+ while (data_len && (data_ptr[data_len - 1] == 0xff))
+ data_len--;
+
+ printf("sending 0x%x/0x%x bytes\n", data_len, CONFIG_RW_SIZE);
+
+ while (data_len) {
+ size_t payload_size;
+ SHA_CTX ctx;
+ uint8_t digest[SHA_DIGEST_LENGTH];
+
+ uint8_t *transfer_data_ptr;
+ size_t transfer_size;
+
+ /* prepare the header to prepend to the block. */
+ payload_size = MIN(data_len, SIGNED_TRANSFER_SIZE);
+ updu.block_size = htobe32(payload_size +
+ sizeof(struct update_pdu));
+
+ updu.cmd.block_base = htobe32(next_offset + FLASH_BASE);
+
+ /* Calculate the digest. */
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, &updu.cmd.block_base,
+ sizeof(updu.cmd.block_base));
+ SHA1_Update(&ctx, data_ptr, payload_size);
+ SHA1_Final(digest, &ctx);
+
+ /* Copy the first few bytes. */
+ memcpy(&updu.cmd.block_digest, digest,
+ sizeof(updu.cmd.block_digest));
+
+ /* Now send the header. */
+ xfer(devh, ep_num, &updu, sizeof(updu), NULL, 0);
+ /* Now send the block, chunk by chunk. */
+ transfer_data_ptr = data_ptr;
+ for (transfer_size = 0; transfer_size < payload_size;) {
+ int chunk_size;
+
+ chunk_size = MIN(chunk_len,
+ payload_size - transfer_size);
+ xfer(devh, ep_num, transfer_data_ptr, chunk_size,
+ NULL, 0);
+ transfer_data_ptr += chunk_size;
+ transfer_size += chunk_size;
+ }
+
+ /* Now get the reply. */
+ xfer(devh, ep_num, NULL, 0, &reply, sizeof(reply));
+ if (reply) {
+ fprintf(stderr, "error: status %#08x remaining %#08x\n",
+ be32toh(reply), data_len);
+ exit(1);
+ }
+ /*
+ if (!IS_EXPECT_RW(in.status)) {
+ fprintf(stderr, "error: status 0x%08x offset 0x%08x\n",
+ in.status, in.offset);
+ exit(1);
+ }
+ */
+ /* Block transferred and programmed successfully. */
+ /* Show status occasionally
+ if (!(in.offset & 0x00003FFF))
+ printf("offset 0x%x\n", in.offset);
+ */
+
+ data_len -= payload_size;
+ data_ptr += payload_size;
+ next_offset += payload_size;
+ }
+
+ printf("-------\nupdate complete\n");
+
+ /* Send stop request, ignorign reply. */
+ out = htobe32(UPGRADE_DONE);
+ xfer(devh, ep_num, &out, sizeof(out), &reply, sizeof(reply));
+
+ printf("reboot\n");
+
+ /* Send a second stop request, which should reboot without replying */
+ xfer(devh, ep_num, &out, sizeof(out), 0, 0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct libusb_device_handle *devh;
+ int errorcnt;
+ uint8_t *data = 0;
+ uint32_t data_len = 0;
+ uint8_t ep_num = 0;
+ int chunk_len = 0;
+ uint16_t vid = VID, pid = PID;
+ int i;
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+
+ errorcnt = 0;
+ opterr = 0; /* quiet, you */
+ while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) {
+ switch (i) {
+ case 'd':
+ if (!parse_vidpid(optarg, &vid, &pid)) {
+ printf("Invalid argument: \"%s\"\n", optarg);
+ errorcnt++;
+ }
+ break;
+ case 'h':
+ usage(errorcnt);
+ break;
+ case 0: /* auto-handled option */
+ break;
+ case '?':
+ if (optopt)
+ printf("Unrecognized option: -%c\n", optopt);
+ else
+ printf("Unrecognized option: %s\n",
+ argv[optind - 1]);
+ errorcnt++;
+ break;
+ case ':':
+ printf("Missing argument to %s\n", argv[optind - 1]);
+ errorcnt++;
+ break;
+ default:
+ printf("Internal error at %s:%d\n", __FILE__, __LINE__);
+ exit(1);
+ }
+ }
+
+ if (errorcnt)
+ usage(errorcnt);
+
+ if (optind >= argc) {
+ fprintf(stderr, "\nERROR: Missing required ec.bin file\n\n");
+ usage(1);
+ }
+
+ data = get_file_or_die(argv[optind], &data_len);
+ printf("read 0x%x bytes from %s\n", data_len, argv[optind]);
+ if (data_len != CONFIG_FLASH_SIZE) {
+ fprintf(stderr, "Image file is not %d bytes\n",
+ CONFIG_FLASH_SIZE);
+ exit(1);
+ }
+
+ devh = usb_connect(vid, pid, &ep_num, &chunk_len);
+
+ transfer_and_reboot(devh, data, data_len, ep_num, chunk_len);
+
+ printf("bye\n");
+ free(data);
+ libusb_close(devh);
+ libusb_exit(NULL);
+
+ return 0;
+}