diff options
Diffstat (limited to 'extra/usb_updater')
-rw-r--r-- | extra/usb_updater/.gitignore | 6 | ||||
-rw-r--r-- | extra/usb_updater/Makefile | 55 | ||||
-rw-r--r-- | extra/usb_updater/c2d2.json | 15 | ||||
-rw-r--r-- | extra/usb_updater/desc_parser.c | 377 | ||||
-rw-r--r-- | extra/usb_updater/desc_parser.h | 58 | ||||
l--------- | extra/usb_updater/ecusb | 1 | ||||
-rwxr-xr-x | extra/usb_updater/fw_update.py | 426 | ||||
-rw-r--r-- | extra/usb_updater/sample_descriptor | 87 | ||||
-rw-r--r-- | extra/usb_updater/servo_micro.json | 15 | ||||
-rwxr-xr-x | extra/usb_updater/servo_updater.py | 456 | ||||
-rw-r--r-- | extra/usb_updater/servo_v4.json | 15 | ||||
-rw-r--r-- | extra/usb_updater/servo_v4p1.json | 15 | ||||
-rw-r--r-- | extra/usb_updater/sweetberry.json | 14 | ||||
-rw-r--r-- | extra/usb_updater/usb_updater2.c | 1244 |
14 files changed, 0 insertions, 2784 deletions
diff --git a/extra/usb_updater/.gitignore b/extra/usb_updater/.gitignore deleted file mode 100644 index 37c3bd3808..0000000000 --- a/extra/usb_updater/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -generated_version.h -gsctool -usb_updater2 -*.d -*.o -dp
\ No newline at end of file diff --git a/extra/usb_updater/Makefile b/extra/usb_updater/Makefile deleted file mode 100644 index 1dfbc55645..0000000000 --- a/extra/usb_updater/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -# 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. - -CC ?= gcc -PKG_CONFIG ?= pkg-config -PROGRAMS := usb_updater2 -LIBS := -LFLAGS := -CFLAGS := -std=gnu99 \ - -g \ - -Wall \ - -Werror \ - -Wpointer-arith \ - -Wcast-align \ - -Wcast-qual \ - -Wundef \ - -Wsign-compare \ - -Wredundant-decls \ - -Wmissing-declarations - -ifeq (DEBUG,) -CFLAGS += -O3 -else -CFLAGS += -O0 -endif - -# -# Add libusb-1.0 required flags -# -LIBS += $(shell $(PKG_CONFIG) --libs libusb-1.0) -CFLAGS += $(shell $(PKG_CONFIG) --cflags libusb-1.0) -CFLAGS += -I../../include -I../../util -I../../fuzz -I../../test - -VPATH = ../../util - -LIBS_common = -lfmap - -all: $(PROGRAMS) - -%.o: %.c - $(CC) $(CFLAGS) -c -MMD -MF $(basename $@).d -o $@ $< - -# common EC code USB updater -usb_updater2: usb_updater2.c Makefile - $(CC) $(CFLAGS) $< $(LFLAGS) $(LIBS) $(LIBS_common) -o $@ - -.PHONY: clean - -clean: - rm -rf $(PROGRAMS) *~ *.o *.d dp - -parser_debug: desc_parser.c - gcc -g -O0 -DTEST_PARSER desc_parser.c -o dp - diff --git a/extra/usb_updater/c2d2.json b/extra/usb_updater/c2d2.json deleted file mode 100644 index 79fc6f0992..0000000000 --- a/extra/usb_updater/c2d2.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Comment": "This file describes the updateable sections of the flash.", - "board": "c2d2", - "vid": "0x18d1", - "pid": "0x5041", - "console": "3", - "Comment on flash": "This is the base address of writeable flash", - "flash": "0x8000000", - "Comment on region format": "name: [baseoffset, length]", - "regions": { - "RW": ["0x10000", "0x10000"], - "PSTATE": ["0xf000", "0x1000"], - "RO": ["0x0000", "0xf000"] - } -} diff --git a/extra/usb_updater/desc_parser.c b/extra/usb_updater/desc_parser.c deleted file mode 100644 index 5bd996bdda..0000000000 --- a/extra/usb_updater/desc_parser.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright 2018 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 <ctype.h> -#include <errno.h> -#include <malloc.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> - -#include "desc_parser.h" - -static FILE *hash_file_; -static int line_count_; -static int section_count_; - -/* - * This is used to verify consistency of the description database, namely that - * all hash sections include the same number of hash variants. - */ -static size_t variant_count; - -/* Size of the retrieved string or negative OS error value. */ -static ssize_t get_next_line(char *next_line, size_t line_size) -{ - size_t index = 0; - - while (fgets(next_line + index, line_size - index, hash_file_)) { - line_count_++; - - if (next_line[index] == '#') - continue; /* Skip the comment */ - - if (next_line[index] == '\n') { - /* - * This is an empty line, return all collected data, - * pontintially an array of size zero if this is a - * repeated empty line. - */ - next_line[index] = '\0'; - return index; - } - - /* Make sure next string overwrites this string's newline. */ - index += strlen(next_line + index) - 1; - - if (index >= (line_size - 1)) { - fprintf(stderr, "%s: Input overflow in line %d\n", - __func__, line_count_); - return -EOVERFLOW; - } - } - - if (index) { - /* - * This must be the last line in the file with no empty line - * after it. Drop the closing newline, if it is there. - */ - if (next_line[index] == '\n') - next_line[index--] = '\0'; - - return index; - } - return errno ? -errno : -ENODATA; -} - -static int get_next_token(char *input, size_t expected_size, char **output) -{ - char *next_colon; - - next_colon = strchr(input, ':'); - if (next_colon) - *next_colon = '\0'; - if (!next_colon || (expected_size && - strlen(input) != expected_size)) { - fprintf(stderr, "Invalid entry in section %d\n", - section_count_); - return -EINVAL; - } - - *output = next_colon + 1; - return 0; -} - -static int get_hex_value(char *input, char **output) -{ - char *e; - long int value; - - if (strchr(input, ':')) - get_next_token(input, 0, output); - else - *output = NULL; - - value = strtol(input, &e, 16); - if ((e && *e) || (strlen(input) > 8)) { - fprintf(stderr, "Invalid hex value %s in section %d\n", - input, section_count_); - return -EINVAL; - } - - return value; -} - -static int parse_range(char *next_line, - size_t line_len, - struct addr_range *parsed_range) -{ - char *line_cursor; - char *next_token; - int is_a_hash_range; - struct result_node *node; - int value; - - section_count_++; - line_cursor = next_line; - - /* Range type. */ - if (get_next_token(line_cursor, 1, &next_token)) - return -EINVAL; - - switch (*line_cursor) { - case 'a': - parsed_range->range_type = AP_RANGE; - break; - case 'e': - parsed_range->range_type = EC_RANGE; - break; - case 'g': - parsed_range->range_type = EC_GANG_RANGE; - break; - default: - fprintf(stderr, "Invalid range type %c in section %d\n", - *line_cursor, section_count_); - return -EINVAL; - } - line_cursor = next_token; - - /* Hash or dump? */ - if (get_next_token(line_cursor, 1, &next_token)) - return -EINVAL; - - switch (*line_cursor) { - case 'd': - is_a_hash_range = 0; - break; - case 'h': - is_a_hash_range = 1; - break; - default: - fprintf(stderr, "Invalid entry kind %c in section %d\n", - *line_cursor, section_count_); - return -EINVAL; - } - line_cursor = next_token; - - /* Range base address. */ - value = get_hex_value(line_cursor, &next_token); - if (value < 0) - return -EINVAL; - parsed_range->base_addr = value; - - /* Range size. */ - line_cursor = next_token; - value = get_hex_value(line_cursor, &next_token); - if (value < 0) - return -EINVAL; - parsed_range->range_size = value; - - if (!next_token && is_a_hash_range) { - fprintf(stderr, "Missing hash in section %d\n", section_count_); - return -EINVAL; - } - - if (next_token && !is_a_hash_range) { - fprintf(stderr, "Unexpected data in section %d\n", - section_count_); - return -EINVAL; - } - - parsed_range->variant_count = 0; - if (!is_a_hash_range) - return 0; /* No more input for dump ranges. */ - - node = parsed_range->variants; - do { /* While line is not over. */ - char c; - int i = 0; - - line_cursor = next_token; - next_token = strchr(line_cursor, ':'); - if (next_token) - *next_token++ = '\0'; - if (strlen(line_cursor) != (2 * sizeof(*node))) { - fprintf(stderr, - "Invalid hash %zd size %zd in section %d\n", - parsed_range->variant_count + 1, - strlen(line_cursor), section_count_); - return -EINVAL; - } - - while ((c = *line_cursor++) != 0) { - uint8_t nibble; - - if (!isxdigit(c)) { - fprintf(stderr, - "Invalid hash %zd value in section %d\n", - parsed_range->variant_count + 1, - section_count_); - return -EINVAL; - } - - if (c <= '9') - nibble = c - '0'; - else if (c >= 'a') - nibble = c - 'a' + 10; - else - nibble = c - 'A' + 10; - - if (i & 1) - node->expected_result[i / 2] |= nibble; - else - node->expected_result[i / 2] = nibble << 4; - - i++; - } - - node++; - parsed_range->variant_count++; - - } while (next_token); - - return 0; -} - -int parser_get_next_range(struct addr_range **range) -{ - char next_line[1000]; /* Should be enough for the largest descriptor. */ - ssize_t entry_size; - struct addr_range *new_range; - int rv; - - /* - * We come here after hash descriptor database file was opened and the - * current board's section has been found. Just in case check if the - * file has been opened. - */ - if (!hash_file_ || !range) - return -EIO; - - *range = NULL; - do { - entry_size = get_next_line(next_line, sizeof(next_line)); - if (entry_size < 0) - return entry_size; - } while (!entry_size); /* Skip empty lines. */ - - if (entry_size == 4) /* Next board's entry must have been reached. */ - return -ENODATA; - - /* This sure will be enough to fit parsed structure contents. */ - new_range = malloc(sizeof(*new_range) + entry_size); - if (!new_range) { - fprintf(stderr, "Failed to allocate %zd bytes\n", - sizeof(*new_range) + entry_size); - return -ENOMEM; - } - - /* This must be a new descriptor section, lets parse it. */ - rv = parse_range(next_line, entry_size, new_range); - - if (rv) { - free(new_range); - return rv; - } - - if (new_range->variant_count) { - /* - * A new range was found, if this is the first hash range we - * encountered, save its dimensions for future reference. - * - * If this is not the first one - verify that it has the same - * number of hash variants as all previous hash blocks. - */ - if (!variant_count) { - variant_count = new_range->variant_count; - } else if (variant_count != new_range->variant_count) { - fprintf(stderr, - "Unexpected number of variants in section %d\n", - section_count_); - free(new_range); - return -EINVAL; - } - } - - *range = new_range; - return 0; - -} - -int parser_find_board(const char *hash_file_name, const char *board_id) -{ - char next_line[1000]; /* Should be enough for the largest descriptor. */ - ssize_t id_len = strlen(board_id); - - if (!hash_file_) { - hash_file_ = fopen(hash_file_name, "r"); - if (!hash_file_) { - fprintf(stderr, "Error:%s can not open file '%s'\n", - strerror(errno), hash_file_name); - return errno; - } - } - - while (1) { - ssize_t entry_size; - - entry_size = get_next_line(next_line, sizeof(next_line)); - if (entry_size < 0) { - return entry_size; - } - - if ((entry_size == id_len) && - !memcmp(next_line, board_id, id_len)) { - variant_count = 0; - return 0; - } - } - - return -ENODATA; -} - -void parser_done(void) -{ - if (!hash_file_) - return; - - fclose(hash_file_); - hash_file_ = NULL; -} - -#ifdef TEST_PARSER -int main(int argc, char **argv) -{ - const char *board_name = "QZUX"; - char next_line[1000]; /* Should be enough for the largest descriptor. */ - int rv; - int count; - - if (argc < 2) { - fprintf(stderr, "Name of the file to parse is required.\n"); - return -1; - } - - if (parser_find_board(argv[1], board_name)) { - fprintf(stderr, "Board %s NOT found\n", board_name); - return -1; - } - - count = 0; - do { - struct addr_range *range; - - rv = parser_get_next_range(&range); - count++; - printf("Section %d, rv %d\n", count, rv); - free(range); /* Freeing NULL is OK. */ - - } while (rv != -ENODATA); - - return 0; -} -#endif diff --git a/extra/usb_updater/desc_parser.h b/extra/usb_updater/desc_parser.h deleted file mode 100644 index faa80d1a63..0000000000 --- a/extra/usb_updater/desc_parser.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2018 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. - */ -#ifndef __EXTRA_USB_UPDATER_DESC_PARSER_H -#define __EXTRA_USB_UPDATER_DESC_PARSER_H - -#include <stddef.h> -#include <stdint.h> - -struct result_node { - uint8_t expected_result[32]; -}; - -enum range_type_t { - NOT_A_RANGE, - AP_RANGE, - EC_RANGE, - EC_GANG_RANGE, -}; - -struct addr_range { - enum range_type_t range_type; - uint32_t base_addr; - uint32_t range_size; - size_t variant_count; /* Set to zero for dump ranges. */ - struct result_node variants[0]; -}; - -/* Board description retrieval API includes the following functions. */ - -/* - * In the given hash database file find board by its ID. Return zero on - * success, or OS error of error. In particular ENODATA is returned if the - * section for the required board ID is not found in the file. - */ -int parser_find_board(const char *hash_file_name, const char board_id[4]); - -/* - * Find next range for the previousely defined board, parse it into the - * addr_range structure and return pointer to the parsed structure to the - * caller, set pointer to NULL if no more entries are available or in case of - * error. - * - * Caller of this function is responsible for returning memory allocated for - * the entry. - * - * Return value set to zero on success, or to OS error if one occurs. EIO is - * used if an attmept to get next range is made before hash database file was - * opened and board entry in it was found. - */ -int parser_get_next_range(struct addr_range **range); - -/* Close the hash database file. */ -void parser_done(void); - -#endif // __EXTRA_USB_UPDATER_DESC_PARSER_H diff --git a/extra/usb_updater/ecusb b/extra/usb_updater/ecusb deleted file mode 120000 index c06ee0f51b..0000000000 --- a/extra/usb_updater/ecusb +++ /dev/null @@ -1 +0,0 @@ -../tigertool/ecusb/
\ No newline at end of file diff --git a/extra/usb_updater/fw_update.py b/extra/usb_updater/fw_update.py deleted file mode 100755 index 0d7a570fc3..0000000000 --- a/extra/usb_updater/fw_update.py +++ /dev/null @@ -1,426 +0,0 @@ -#!/usr/bin/env python -# Copyright 2016 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. -# -# Ignore indention messages, since legacy scripts use 2 spaces instead of 4. -# pylint: disable=bad-indentation,docstring-section-indent -# pylint: disable=docstring-trailing-quotes - -# Upload firmware over USB -# Note: This is a py2/3 compatible file. - -from __future__ import print_function - -import argparse -import array -import json -import os -import struct -import sys -import time -from pprint import pprint -import usb - - -debug = False -def debuglog(msg): - if debug: - print(msg) - -def log(msg): - print(msg) - sys.stdout.flush() - - -"""Sends firmware update to CROS EC usb endpoint.""" - -class Supdate(object): - """Class to access firmware update endpoints. - - Usage: - d = Supdate() - - Instance Variables: - _dev: pyUSB device object - _read_ep: pyUSB read endpoint for this interface - _write_ep: pyUSB write endpoint for this interface - """ - USB_SUBCLASS_GOOGLE_UPDATE = 0x53 - USB_CLASS_VENDOR = 0xFF - - def __init__(self): - pass - - - def connect_usb(self, serialname=None): - """Initial discovery and connection to USB endpoint. - - This searches for a USB device matching the VID:PID specified - in the config file, optionally matching a specified serialname. - - Args: - serialname: Find the device with this serial, in case multiple - devices are attached. - - Returns: - True on success. - Raises: - Exception on error. - """ - # Find the stm32. - vendor = self._brdcfg['vid'] - product = self._brdcfg['pid'] - - dev_g = usb.core.find(idVendor=vendor, idProduct=product, find_all=True) - dev_list = list(dev_g) - if dev_list is None: - raise Exception("Update", "USB device not found") - - # Check if we have multiple stm32s and we've specified the serial. - dev = None - if serialname: - for d in dev_list: - if usb.util.get_string(d, d.iSerialNumber) == serialname: - dev = d - break - if dev is None: - raise SusbError("USB device(%s) not found" % serialname) - else: - try: - dev = dev_list[0] - except: - dev = dev_list.next() - - debuglog("Found stm32: %04x:%04x" % (vendor, product)) - self._dev = dev - - # Get an endpoint instance. - try: - dev.set_configuration() - except: - pass - cfg = dev.get_active_configuration() - - intf = usb.util.find_descriptor(cfg, custom_match=lambda i: \ - i.bInterfaceClass==self.USB_CLASS_VENDOR and \ - i.bInterfaceSubClass==self.USB_SUBCLASS_GOOGLE_UPDATE) - - self._intf = intf - debuglog("Interface: %s" % intf) - debuglog("InterfaceNumber: %s" % intf.bInterfaceNumber) - - read_ep = usb.util.find_descriptor( - intf, - # match the first IN endpoint - custom_match = \ - lambda e: \ - usb.util.endpoint_direction(e.bEndpointAddress) == \ - usb.util.ENDPOINT_IN - ) - - self._read_ep = read_ep - debuglog("Reader endpoint: 0x%x" % read_ep.bEndpointAddress) - - write_ep = usb.util.find_descriptor( - intf, - # match the first OUT endpoint - custom_match = \ - lambda e: \ - usb.util.endpoint_direction(e.bEndpointAddress) == \ - usb.util.ENDPOINT_OUT - ) - - self._write_ep = write_ep - debuglog("Writer endpoint: 0x%x" % write_ep.bEndpointAddress) - - return True - - - def wr_command(self, write_list, read_count=1, wtimeout=100, rtimeout=2000): - """Write command to logger logic.. - - This function writes byte command values list to stm, then reads - byte status. - - Args: - write_list: list of command byte values [0~255]. - read_count: number of status byte values to read. - wtimeout: mS to wait for write success - rtimeout: mS to wait for read success - - Returns: - status byte, if one byte is read, - byte list, if multiple bytes are read, - None, if no bytes are read. - - Interface: - write: [command, data ... ] - read: [status ] - """ - debuglog("wr_command(write_list=[%s] (%d), read_count=%s)" % ( - list(bytearray(write_list)), len(write_list), read_count)) - - # Clean up args from python style to correct types. - write_length = 0 - if write_list: - write_length = len(write_list) - if not read_count: - read_count = 0 - - # Send command to stm32. - if write_list: - cmd = write_list - ret = self._write_ep.write(cmd, wtimeout) - debuglog("RET: %s " % ret) - - # Read back response if necessary. - if read_count: - bytesread = self._read_ep.read(512, rtimeout) - debuglog("BYTES: [%s]" % bytesread) - - if len(bytesread) != read_count: - debuglog("Unexpected bytes read: %d, expected: %d" % (len(bytesread), read_count)) - pass - - debuglog("STATUS: 0x%02x" % int(bytesread[0])) - if read_count == 1: - return bytesread[0] - else: - return bytesread - - return None - - def stop(self): - """Finalize system flash and exit.""" - cmd = struct.pack(">I", 0xB007AB1E) - read = self.wr_command(cmd, read_count=4) - - if len(read) == 4: - log("Finished flashing") - return - - raise Exception("Update", "Stop failed [%s]" % read) - - - def write_file(self): - """Write the update region packet by packet to USB - - This sends write packets of size 128B out, in 32B chunks. - Overall, this will write all data in the inactive code region. - - Raises: - Exception if write failed or address out of bounds. - """ - region = self._region - flash_base = self._brdcfg["flash"] - offset = self._base - flash_base - if offset != self._brdcfg['regions'][region][0]: - raise Exception("Update", "Region %s offset 0x%x != available offset 0x%x" % ( - region, self._brdcfg['regions'][region][0], offset)) - - length = self._brdcfg['regions'][region][1] - log("Sending") - - # Go to the correct region in the ec.bin file. - self._binfile.seek(offset) - - # Send 32 bytes at a time. Must be less than the endpoint's max packet size. - maxpacket = 32 - - # While data is left, create update packets. - while length > 0: - # Update packets are 128B. We can use any number - # but the micro must malloc this memory. - pagesize = min(length, 128) - - # Packet is: - # packet size: page bytes transferred plus 3 x 32b values header. - # cmd: n/a - # base: flash address to write this packet. - # data: 128B of data to write into flash_base - cmd = struct.pack(">III", pagesize + 12, 0, offset + flash_base) - read = self.wr_command(cmd, read_count=0) - - # Push 'todo' bytes out the pipe. - todo = pagesize - while todo > 0: - packetsize = min(maxpacket, todo) - data = self._binfile.read(packetsize) - if len(data) != packetsize: - raise Exception("Update", "No more data from file") - for i in range(0, 10): - try: - self.wr_command(data, read_count=0) - break - except: - log("Timeout fail") - todo -= packetsize - # Done with this packet, move to the next one. - length -= pagesize - offset += pagesize - - # Validate that the micro thinks it successfully wrote the data. - read = self.wr_command(''.encode(), read_count=4) - result = struct.unpack("<I", read) - result = result[0] - if result != 0: - raise Exception("Update", "Upload failed with rc: 0x%x" % result) - - - def start(self): - """Start a transaction and erase currently inactive region. - - This function sends a start command, and receives the base of the - preferred inactive region. This could be RW, RW_B, - or RO (if there's no RW_B) - - Note that the region is erased here, so you'd better program the RO if - you just erased it. TODO(nsanders): Modify the protocol to allow active - region select or query before erase. - """ - - # Size is 3 uint32 fields - # packet: [packetsize, cmd, base] - size = 4 + 4 + 4 - # Return value is [status, base_addr] - expected = 4 + 4 - - cmd = struct.pack("<III", size, 0, 0) - read = self.wr_command(cmd, read_count=expected) - - if len(read) == 4: - raise Exception("Update", "Protocol version 0 not supported") - elif len(read) == expected: - base, version = struct.unpack(">II", read) - log("Update protocol v. %d" % version) - log("Available flash region base: %x" % base) - else: - raise Exception("Update", "Start command returned %d bytes" % len(read)) - - if base < 256: - raise Exception("Update", "Start returned error code 0x%x" % base) - - self._base = base - flash_base = self._brdcfg["flash"] - self._offset = self._base - flash_base - - # Find our active region. - for region in self._brdcfg['regions']: - if (self._offset >= self._brdcfg['regions'][region][0]) and \ - (self._offset < (self._brdcfg['regions'][region][0] + \ - self._brdcfg['regions'][region][1])): - log("Active region: %s" % region) - self._region = region - - - def load_board(self, brdfile): - """Load firmware layout file. - - example as follows: - { - "board": "servo micro", - "vid": 6353, - "pid": 20506, - "flash": 134217728, - "regions": { - "RW": [65536, 65536], - "PSTATE": [61440, 4096], - "RO": [0, 61440] - } - } - - Args: - brdfile: path to board description file. - """ - with open(brdfile) as data_file: - data = json.load(data_file) - - # TODO(nsanders): validate this data before moving on. - self._brdcfg = data; - if debug: - pprint(data) - - log("Board is %s" % self._brdcfg['board']) - # Cast hex strings to int. - self._brdcfg['flash'] = int(self._brdcfg['flash'], 0) - self._brdcfg['vid'] = int(self._brdcfg['vid'], 0) - self._brdcfg['pid'] = int(self._brdcfg['pid'], 0) - - log("Flash Base is %x" % self._brdcfg['flash']) - self._flashsize = 0 - for region in self._brdcfg['regions']: - base = int(self._brdcfg['regions'][region][0], 0) - length = int(self._brdcfg['regions'][region][1], 0) - log("region %s\tbase:0x%08x size:0x%08x" % ( - region, base, length)) - self._flashsize += length - - # Convert these to int because json doesn't support hex. - self._brdcfg['regions'][region][0] = base - self._brdcfg['regions'][region][1] = length - - log("Flash Size: 0x%x" % self._flashsize) - - def load_file(self, binfile): - """Open and verify size of the target ec.bin file. - - Args: - binfile: path to ec.bin - - Raises: - Exception on file not found or filesize not matching. - """ - self._filesize = os.path.getsize(binfile) - self._binfile = open(binfile, 'rb') - - if self._filesize != self._flashsize: - raise Exception("Update", "Flash size 0x%x != file size 0x%x" % (self._flashsize, self._filesize)) - - - -# Generate command line arguments -parser = argparse.ArgumentParser(description="Update firmware over usb") -parser.add_argument('-b', '--board', type=str, help="Board configuration json file", default="board.json") -parser.add_argument('-f', '--file', type=str, help="Complete ec.bin file", default="ec.bin") -parser.add_argument('-s', '--serial', type=str, help="Serial number", default="") -parser.add_argument('-l', '--list', action="store_true", help="List regions") -parser.add_argument('-v', '--verbose', action="store_true", help="Chatty output") - -def main(): - global debug - args = parser.parse_args() - - - brdfile = args.board - serial = args.serial - binfile = args.file - if args.verbose: - debug = True - - with open(brdfile) as data_file: - names = json.load(data_file) - - p = Supdate() - p.load_board(brdfile) - p.connect_usb(serialname=serial) - p.load_file(binfile) - - # List solely prints the config. - if (args.list): - return - - # Start transfer and erase. - p.start() - # Upload the bin file - log("Uploading %s" % binfile) - p.write_file() - - # Finalize - log("Done. Finalizing.") - p.stop() - -if __name__ == "__main__": - main() - - diff --git a/extra/usb_updater/sample_descriptor b/extra/usb_updater/sample_descriptor deleted file mode 100644 index 1566e9e2e1..0000000000 --- a/extra/usb_updater/sample_descriptor +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright 2018 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. -# -# Hash descriptor database file consists of sections for various Chrome OS -# boards. Each board description section starts with a line of 4 characters -# which is the board ID (the same as the board's RLZ code). -# -# Each board description section includes variable number of range -# descriptor entries, each entry consisting of semicolon separated fields: -# -# {a|e|g}:{h|d}:base_addr:size[:value[:value[:value...]]]] -# -# Where -# -# - the first sindgle character field defines the way the range is accessed: -# a - AP flash -# e - EC flash -# g - EC flash requiring gang programming mode -# - the second single character field defines the range type -# h - Cr50 returns the hash of the range -# d - Cr50 returns actual contents of the range (hex dump) -# - the third and and forth fields are base address and size of the range -# - ranges of type 'h' include one or more values for the hash of the range. -# -# Descriptor entries can be split along multiple lines. Each entry is -# terminated by an empty line. Board description section is completed when -# another board ID or end of file is encountered. -# -# All values are expressed in hex. Repeating empty lines and lines starting -# with '#' are ignored. -# - -QZUX - -# 1: Valid hash section. -a:h:0:10000: -756c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503: -336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503: -446c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503 - -# 2: Valid dump section. -a:d:10:10 - -# 3: Valid hash section. -e:h:0:100: -55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87: -444442badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87: -443322badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87 - -# 4: Invalid dump section (includes hash) -a:d:20:10:55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb87 - -# 5: Invalid hash section (does not include hash) -e:h:0:100: - -# 6: Another invalid hash section (does not include hash) -e:h:0:100: - -# extra empty lines - - -# 7: Invalid hash section (hash too short) -e:h:0:100: -55d262badc1116520a7ae1d3fda380c0382b4b87f0db10de6495053ba3aadb8 - -# 8: Invalid hash section (hash too long) -a:h:0:10000: -756c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf35034: -336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3503 - -# 9: Invalid hash section (hash includes non-hex value) -a:h:0:10000: -756c41b90ac9aa23a6c98ce13549dccd7xe0a83f8537eb834d9cfc3d12bf3503: -336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3505 - -# 10: Invalid hash section (hash does not include 3 variants) -a:h:0:10000: -756c41b90ac9aa23a6c98ce13549dccd75e0a83f8537eb834d9cfc3d12bf3503: -336c41b90ac9aa23a6c98ce13549dccd72e0a83f8537eb834d9cfc3d12bf3505 - -# 11: Invalid dump section (size includes non hex character) -a:d:10:10x - -ABCD - -a:d:10:10 diff --git a/extra/usb_updater/servo_micro.json b/extra/usb_updater/servo_micro.json deleted file mode 100644 index 71b1fd25dc..0000000000 --- a/extra/usb_updater/servo_micro.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Comment": "This file describes the updateable sections of the flash.", - "board": "servo_micro", - "vid": "0x18d1", - "pid": "0x501a", - "console": "3", - "Comment on flash": "This is the base address of writeable flash", - "flash": "0x8000000", - "Comment on region format": "name: [baseoffset, length]", - "regions": { - "RW": ["0x10000", "0x10000"], - "PSTATE": ["0xf000", "0x1000"], - "RO": ["0x0000", "0xf000"] - } -} diff --git a/extra/usb_updater/servo_updater.py b/extra/usb_updater/servo_updater.py deleted file mode 100755 index 4dff264182..0000000000 --- a/extra/usb_updater/servo_updater.py +++ /dev/null @@ -1,456 +0,0 @@ -#!/usr/bin/env python -# Copyright 2016 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. -# -# Ignore indention messages, since legacy scripts use 2 spaces instead of 4. -# pylint: disable=bad-indentation,docstring-section-indent -# pylint: disable=docstring-trailing-quotes - -# Note: This is a py2/3 compatible file. - -from __future__ import print_function - -import argparse -import errno -import os -import re -import subprocess -import time -import tempfile - -import json - -import fw_update -import ecusb.tiny_servo_common as c -from ecusb import tiny_servod - -class ServoUpdaterException(Exception): - """Raised on exceptions generated by servo_updater.""" - -BOARD_C2D2 = 'c2d2' -BOARD_SERVO_MICRO = 'servo_micro' -BOARD_SERVO_V4 = 'servo_v4' -BOARD_SERVO_V4P1 = 'servo_v4p1' -BOARD_SWEETBERRY = 'sweetberry' - -DEFAULT_BOARD = BOARD_SERVO_V4 - -# These lists are to facilitate exposing choices in the command-line tool -# below. -BOARDS = [BOARD_C2D2, BOARD_SERVO_MICRO, BOARD_SERVO_V4, BOARD_SERVO_V4P1, - BOARD_SWEETBERRY] - -# Servo firmware bundles four channels of firmware. We need to make sure the -# user does not request a non-existing channel, so keep the lists around to -# guard on command-line usage. - -DEFAULT_CHANNEL = STABLE_CHANNEL = 'stable' - -PREV_CHANNEL = 'prev' - -# The ordering here matters. From left to right it's the channel that the user -# is most likely to be running. This is used to inform and warn the user if -# there are issues. e.g. if the all channels are the same, we want to let the -# user know they are running the 'stable' version before letting them know they -# are running 'dev' or even 'alpah' which (while true) might cause confusion. - -CHANNELS = [DEFAULT_CHANNEL, PREV_CHANNEL, 'dev', 'alpha'] - -DEFAULT_BASE_PATH = '/usr/' -TEST_IMAGE_BASE_PATH = '/usr/local/' - -COMMON_PATH = 'share/servo_updater' - -FIRMWARE_DIR = "firmware/" -CONFIGS_DIR = "configs/" - -RETRIES_COUNT = 10 -RETRIES_DELAY = 1 - -def do_with_retries(func, *args): - """ - Call function passed as argument and check if no error happened. - If exception was raised by function, - it will be retried up to RETRIES_COUNT times. - - Args: - func: function that will be called - args: arguments passed to 'func' - - Returns: - If call to function was successful, its result will be returned. - If retries count was exceeded, exception will be raised. - """ - - retry = 0 - while retry < RETRIES_COUNT: - try: - return func(*args) - except Exception as e: - print("Retrying function %s: %s" % (func.__name__, e)) - retry = retry + 1 - time.sleep(RETRIES_DELAY) - continue - - raise Exception("'{}' failed after {} retries".format(func.__name__, RETRIES_COUNT)) - -def flash(brdfile, serialno, binfile): - """ - Call fw_update to upload to updater USB endpoint. - - Args: - brdfile: path to board configuration file - serialno: device serial number - binfile: firmware file - """ - - p = fw_update.Supdate() - p.load_board(brdfile) - p.connect_usb(serialname=serialno) - p.load_file(binfile) - - # Start transfer and erase. - p.start() - # Upload the bin file - print("Uploading %s" % binfile) - p.write_file() - - # Finalize - print("Done. Finalizing.") - p.stop() - -def flash2(vidpid, serialno, binfile): - """ - Call fw update via usb_updater2 commandline. - - Args: - vidpid: vendor id and product id of device - serialno: device serial number (optional) - binfile: firmware file - """ - - tool = 'usb_updater2' - cmd = "%s -d %s" % (tool, vidpid) - if serialno: - cmd += " -S %s" % serialno - cmd += " -n" - cmd += " %s" % binfile - - print(cmd) - help_cmd = '%s --help' % tool - with open('/dev/null') as devnull: - valid_check = subprocess.call(help_cmd.split(), stdout=devnull, - stderr=devnull) - if valid_check: - raise ServoUpdaterException('%s exit with res = %d. Make sure the tool ' - 'is available on the device.' % (help_cmd, - valid_check)) - res = subprocess.call(cmd.split()) - - if res in (0, 1, 2): - return res - else: - raise ServoUpdaterException("%s exit with res = %d" % (cmd, res)) - -def select(tinys, region): - """ - Ensure the servo is in the expected ro/rw region. - This function jumps to the required region and verify if jump was - successful by executing 'sysinfo' command and reading current region. - If response was not received or region is invalid, exception is raised. - - Args: - tinys: TinyServod object - region: region to jump to, only "rw" and "ro" is allowed - """ - - if region not in ["rw", "ro"]: - raise Exception("Region must be ro or rw") - - if region is "ro": - cmd = "reboot" - else: - cmd = "sysjump %s" % region - - tinys.pty._issue_cmd(cmd) - - tinys.close() - time.sleep(2) - tinys.reinitialize() - - res = tinys.pty._issue_cmd_get_results("sysinfo", ["Copy:[\s]+(RO|RW)"]) - current_region = res[0][1].lower() - if current_region != region: - raise Exception("Invalid region: %s/%s" % (current_region, region)) - -def do_version(tinys): - """Check version via ec console 'pty'. - - Args: - tinys: TinyServod object - - Returns: - detected version number - - Commands are: - # > version - # ... - # Build: tigertail_v1.1.6749-74d1a312e - """ - cmd = '\r\nversion\r\n' - regex = 'Build:\s+(\S+)[\r\n]+' - - results = tinys.pty._issue_cmd_get_results(cmd, [regex])[0] - - return results[1].strip(' \t\r\n\0') - -def do_updater_version(tinys): - """Check whether this uses python updater or c++ updater - - Args: - tinys: TinyServod object - - Returns: - updater version number. 2 or 6. - """ - vers = do_version(tinys) - - # Servo versions below 58 are from servo-9040.B. Versions starting with _v2 - # are newer than anything _v1, no need to check the exact number. Updater - # version is not directly queryable. - if re.search('_v[2-9]\.\d', vers): - return 6 - m = re.search('_v1\.1\.(\d\d\d\d)', vers) - if m: - version_number = int(m.group(1)) - if version_number < 5800: - return 2 - else: - return 6 - raise ServoUpdaterException( - "Can't determine updater target from vers: [%s]" % vers) - -def _extract_version(boardname, binfile): - """Find the version string from |binfile|. - - Args: - boardname: the name of the board, eg. "servo_micro" - binfile: path to the binary to search - - Returns: - the version string. - """ - if boardname is None: - # cannot extract the version if the name is None - return None - rawstrings = subprocess.check_output( - ['cbfstool', binfile, 'read', '-r', 'RO_FRID', '-f', '/dev/stdout'], - **c.get_subprocess_args()) - m = re.match(r'%s_v\S+' % boardname, rawstrings) - if m: - newvers = m.group(0).strip(' \t\r\n\0') - else: - raise ServoUpdaterException("Can't find version from file: %s." % binfile) - - return newvers - -def get_firmware_channel(bname, version): - """Find out which channel |version| for |bname| came from. - - Args: - bname: board name - version: current version string - - Returns: - one of the channel names if |version| came from one of those, or None - """ - for channel in CHANNELS: - # Pass |bname| as cname to find the board specific file, and pass None as - # fname to ensure the default directory is searched - _, _, vers = get_files_and_version(bname, None, channel=channel) - if version == vers: - return channel - # None of the channels matched. This firmware is currently unknown. - return None - -def get_files_and_version(cname, fname=None, channel=DEFAULT_CHANNEL): - """Select config and firmware binary files. - - This checks default file names and paths. - In: /usr/share/servo_updater/[firmware|configs] - check for board.json, board.bin - - Args: - cname: board name, or config name. eg. "servo_v4" or "servo_v4.json" - fname: firmware binary name. Can be None to try default. - channel: the channel requested for servo firmware. See |CHANNELS| above. - - Returns: - cname, fname, version: validated filenames selected from the path. - """ - for p in (DEFAULT_BASE_PATH, TEST_IMAGE_BASE_PATH): - updater_path = os.path.join(p, COMMON_PATH) - if os.path.exists(updater_path): - break - else: - raise ServoUpdaterException('servo_updater/ dir not found in known spots.') - - firmware_path = os.path.join(updater_path, FIRMWARE_DIR) - configs_path = os.path.join(updater_path, CONFIGS_DIR) - - for p in (firmware_path, configs_path): - if not os.path.exists(p): - raise ServoUpdaterException('Could not find required path %r' % p) - - if not os.path.isfile(cname): - # If not an existing file, try checking on the default path. - newname = os.path.join(configs_path, cname) - if os.path.isfile(newname): - cname = newname - else: - # Try appending ".json" to convert board name to config file. - cname = newname + ".json" - if not os.path.isfile(cname): - raise ServoUpdaterException("Can't find config file: %s." % cname) - - # Always retrieve the boardname - with open(cname) as data_file: - data = json.load(data_file) - boardname = data['board'] - - if not fname: - # If no |fname| supplied, look for the default locations with the board - # and channel requested. - binary_file = '%s.%s.bin' % (boardname, channel) - newname = os.path.join(firmware_path, binary_file) - if os.path.isfile(newname): - fname = newname - else: - raise ServoUpdaterException("Can't find firmware binary: %s." % - binary_file) - elif not os.path.isfile(fname): - # If a name is specified but not found, try the default path. - newname = os.path.join(firmware_path, fname) - if os.path.isfile(newname): - fname = newname - else: - raise ServoUpdaterException("Can't find file: %s." % fname) - - # Lastly, retrieve the version as well for decision making, debug, and - # informational purposes. - binvers = _extract_version(boardname, fname) - - return cname, fname, binvers - -def main(): - parser = argparse.ArgumentParser(description="Image a servo device") - parser.add_argument('-p', '--print', dest='print_only', action='store_true', - default=False, - help='only print available firmware for board/channel') - parser.add_argument('-s', '--serialno', type=str, - help="serial number to program", default=None) - parser.add_argument('-b', '--board', type=str, - help="Board configuration json file", - default=DEFAULT_BOARD, choices=BOARDS) - parser.add_argument('-c', '--channel', type=str, - help="Firmware channel to use", - default=DEFAULT_CHANNEL, choices=CHANNELS) - parser.add_argument('-f', '--file', type=str, - help="Complete ec.bin file", default=None) - parser.add_argument('--force', action="store_true", - help="Update even if version match", default=False) - parser.add_argument('-v', '--verbose', action="store_true", - help="Chatty output") - parser.add_argument('-r', '--reboot', action="store_true", - help="Always reboot, even after probe.") - - args = parser.parse_args() - - brdfile, binfile, newvers = get_files_and_version(args.board, args.file, - args.channel) - - # If the user only cares about the information then just print it here, - # and exit. - if args.print_only: - output = ('board: %s\n' - 'channel: %s\n' - 'firmware: %s') % (args.board, args.channel, newvers) - print(output) - return - - serialno = args.serialno - - with open(brdfile) as data_file: - data = json.load(data_file) - vid, pid = int(data['vid'], 0), int(data['pid'], 0) - vidpid = "%04x:%04x" % (vid, pid) - iface = int(data['console'], 0) - boardname = data['board'] - - # Make sure device is up. - print("===== Waiting for USB device =====") - c.wait_for_usb(vidpid, serialname=serialno) - # We need a tiny_servod to query some information. Set it up first. - tinys = tiny_servod.TinyServod(vid, pid, iface, serialno, args.verbose) - - if not args.force: - vers = do_version(tinys) - print("Current %s version is %s" % (boardname, vers)) - print("Available %s version is %s" % (boardname, newvers)) - - if newvers == vers: - print("No version update needed") - if args.reboot: - select(tinys, 'ro') - return - else: - print("Updating to recommended version.") - - # Make sure the servo MCU is in RO - print("===== Jumping to RO =====") - do_with_retries(select, tinys, 'ro') - - print("===== Flashing RW =====") - vers = do_with_retries(do_updater_version, tinys) - # To make sure that the tiny_servod here does not interfere with other - # processes, close it out. - tinys.close() - - if vers == 2: - flash(brdfile, serialno, binfile) - elif vers == 6: - flash2(vidpid, serialno, binfile) - else: - raise ServoUpdaterException("Can't detect updater version") - - # Make sure device is up. - c.wait_for_usb(vidpid, serialname=serialno) - # After we have made sure that it's back/available, reconnect the tiny servod. - tinys.reinitialize() - - # Make sure the servo MCU is in RW - print("===== Jumping to RW =====") - do_with_retries(select, tinys, 'rw') - - print("===== Flashing RO =====") - vers = do_with_retries(do_updater_version, tinys) - - if vers == 2: - flash(brdfile, serialno, binfile) - elif vers == 6: - flash2(vidpid, serialno, binfile) - else: - raise ServoUpdaterException("Can't detect updater version") - - # Make sure the servo MCU is in RO - print("===== Rebooting =====") - do_with_retries(select, tinys, 'ro') - # Perform additional reboot to free USB/UART resources, taken by tiny servod. - # See https://issuetracker.google.com/196021317 for background. - tinys.pty._issue_cmd("reboot") - - print("===== Finished =====") - -if __name__ == "__main__": - main() diff --git a/extra/usb_updater/servo_v4.json b/extra/usb_updater/servo_v4.json deleted file mode 100644 index e041f56b68..0000000000 --- a/extra/usb_updater/servo_v4.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Comment": "This file describes the updateable sections of the flash.", - "board": "servo_v4", - "vid": "0x18d1", - "pid": "0x501b", - "console": "0", - "Comment on flash": "This is the base address of writeable flash", - "flash": "0x8000000", - "Comment on region format": "name: [baseoffset, length]", - "regions": { - "RW": ["0x10000", "0x10000"], - "PSTATE": ["0xf000", "0x1000"], - "RO": ["0x0000", "0xf000"] - } -} diff --git a/extra/usb_updater/servo_v4p1.json b/extra/usb_updater/servo_v4p1.json deleted file mode 100644 index 46efbf24ad..0000000000 --- a/extra/usb_updater/servo_v4p1.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Comment": "This file describes the updateable sections of the flash.", - "board": "servo_v4p1", - "vid": "0x18d1", - "pid": "0x520d", - "console": "0", - "Comment on flash": "This is the base address of writeable flash", - "flash": "0x8000000", - "Comment on region format": "name: [baseoffset, length]", - "regions": { - "RW": ["0x10000", "0x10000"], - "PSTATE": ["0xf000", "0x1000"], - "RO": ["0x0000", "0xf000"] - } -} diff --git a/extra/usb_updater/sweetberry.json b/extra/usb_updater/sweetberry.json deleted file mode 100644 index 6b70d19fad..0000000000 --- a/extra/usb_updater/sweetberry.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "Comment": "This file describes the updateable sections of the flash.", - "board": "sweetberry", - "vid": "0x18d1", - "pid": "0x5020", - "console": "0", - "Comment on flash": "This is the base address of writeable flash", - "flash": "0x8000000", - "Comment on region format": "name: [baseoffset, length]", - "regions": { - "RW": ["0x40000", "0x40000"], - "RO": ["0x0000", "0x40000"] - } -} diff --git a/extra/usb_updater/usb_updater2.c b/extra/usb_updater/usb_updater2.c deleted file mode 100644 index 12ee1615fc..0000000000 --- a/extra/usb_updater/usb_updater2.c +++ /dev/null @@ -1,1244 +0,0 @@ -/* - * Copyright 2017 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 <asm/byteorder.h> -#include <endian.h> -#include <fcntl.h> -#include <getopt.h> -#include <libusb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -#include <fmap.h> - -#ifndef __packed -#define __packed __attribute__((packed)) -#endif - -#include "compile_time_macros.h" -#include "misc_util.h" -#include "usb_descriptor.h" -#include "update_fw.h" -#include "vb21_struct.h" - -#ifdef DEBUG -#define debug printf -#else -#define debug(fmt, args...) -#endif - -/* - * This file contains the source code of a Linux application used to update - * EC device firmware (common code only, gsctool takes care of cr50). - */ - -#define VID USB_VID_GOOGLE -#define PID 0x5022 -#define SUBCLASS USB_SUBCLASS_GOOGLE_UPDATE -#define PROTOCOL USB_PROTOCOL_GOOGLE_UPDATE - -enum exit_values { - noop = 0, /* All up to date, no update needed. */ - all_updated = 1, /* Update completed, reboot required. */ - rw_updated = 2, /* RO was not updated, reboot required. */ - update_error = 3 /* Something went wrong. */ -}; - -struct usb_endpoint { - struct libusb_device_handle *devh; - uint8_t ep_num; - int chunk_len; -}; - -struct transfer_descriptor { - /* - * offsets of section available for update (not currently active). - */ - uint32_t offset; - - struct usb_endpoint uep; -}; - -/* Information about the target */ -static struct first_response_pdu targ; - -static uint16_t protocol_version; -static uint16_t header_type; -static char *progname; -static char *short_opts = "bd:efg:hjlnp:rsS:tuw"; -static const struct option long_opts[] = { - /* name hasarg *flag val */ - {"binvers", 1, NULL, 'b'}, - {"device", 1, NULL, 'd'}, - {"entropy", 0, NULL, 'e'}, - {"fwver", 0, NULL, 'f'}, - {"tp_debug", 1, NULL, 'g'}, - {"help", 0, NULL, 'h'}, - {"jump_to_rw", 0, NULL, 'j'}, - {"follow_log", 0, NULL, 'l'}, - {"no_reset", 0, NULL, 'n'}, - {"tp_update", 1, NULL, 'p'}, - {"reboot", 0, NULL, 'r'}, - {"stay_in_ro", 0, NULL, 's'}, - {"serial", 1, NULL, 'S'}, - {"tp_info", 0, NULL, 't'}, - {"unlock_rollback", 0, NULL, 'u'}, - {"unlock_rw", 0, NULL, 'w'}, - {}, -}; - -/* Release USB device and return error to the OS. */ -static void shut_down(struct usb_endpoint *uep) -{ - libusb_close(uep->devh); - libusb_exit(NULL); - exit(update_error); -} - -static void usage(int errs) -{ - printf("\nUsage: %s [options] <binary image>\n" - "\n" - "This updates EC firmware over USB (common code EC, no cr50).\n" - "The required argument is the full RO+RW image.\n" - "\n" - "Options:\n" - "\n" - " -b,--binvers Report versions of image's " - "RW and RO, do not update\n" - " -d,--device VID:PID USB device (default %04x:%04x)\n" - " -e,--entropy Add entropy to device secret\n" - " -f,--fwver Report running firmware versions.\n" - " -g,--tp_debug <hex data> Touchpad debug command\n" - " -h,--help Show this message\n" - " -j,--jump_to_rw Tell EC to jump to RW\n" - " -l,--follow_log Get console log\n" - " -p,--tp_update file Update touchpad FW\n" - " -r,--reboot Tell EC to reboot\n" - " -s,--stay_in_ro Tell EC to stay in RO\n" - " -S,--serial Device serial number\n" - " -t,--tp_info Get touchpad information\n" - " -u,--unlock_rollback Tell EC to unlock the rollback region\n" - " -w,--unlock_rw Tell EC to unlock the RW region\n" - "\n", progname, VID, PID); - - exit(errs ? update_error : noop); -} - -static void str2hex(const char *str, uint8_t *data, int *len) -{ - int i; - int slen = strlen(str); - - if (slen/2 > *len) { - fprintf(stderr, "Hex string too long.\n"); - exit(update_error); - } - - if (slen % 2 != 0) { - fprintf(stderr, "Hex string length not a multiple of 2.\n"); - exit(update_error); - } - - for (i = 0, *len = 0; i < slen; i += 2, (*len)++) { - char *end; - char tmp[3]; - - tmp[0] = str[i]; - tmp[1] = str[i+1]; - tmp[2] = 0; - - data[*len] = strtol(tmp, &end, 16); - - if (*end != 0) { - fprintf(stderr, "Invalid hex string.\n"); - exit(update_error); - } - } -} - -static void hexdump(const uint8_t *data, int len) -{ - int i; - - for (i = 0; i < len; i++) { - printf("%02x", data[i]); - if ((i % 16) == 15) - printf("\n"); - } - - if ((len % 16) != 0) - printf("\n"); -} - -static void dump_touchpad_info(const uint8_t *data, int len) -{ - const struct touchpad_info *info = (const struct touchpad_info *)data; - - if (len != sizeof(struct touchpad_info)) { - fprintf(stderr, "Hex string length is not %zu", - sizeof(struct touchpad_info)); - hexdump(data, len); - return; - } - - printf("\n"); - printf("status: 0x%02x\n", info->status); - printf("vendor: 0x%04x\n", info->vendor); - printf("fw_address: 0x%08x\n", info->fw_address); - printf("fw_size: 0x%08x\n", info->fw_size); - - printf("allowed_fw_hash:\n"); - hexdump(info->allowed_fw_hash, sizeof(info->allowed_fw_hash)); - - switch (info->vendor) { - case 0x04f3: /* ELAN */ - case 0x0483: /* ST */ - printf("id: 0x%04x\n", info->elan.id); - printf("fw_version: 0x%04x\n", info->elan.fw_version); - printf("fw_fw_checksum: 0x%04x\n", info->elan.fw_checksum); - break; - default: - fprintf(stderr, "Unknown vendor, vendor specific data:\n"); - hexdump((const uint8_t *)&info->elan, sizeof(info->elan)); - break; - } -} - -/* Read file into buffer */ -static uint8_t *get_file_or_die(const char *filename, size_t *len_ptr) -{ - FILE *fp; - struct stat st; - uint8_t *data; - size_t len; - - fp = fopen(filename, "rb"); - if (!fp) { - perror(filename); - exit(update_error); - } - if (fstat(fileno(fp), &st)) { - perror("stat"); - exit(update_error); - } - - len = st.st_size; - - data = malloc(len); - if (!data) { - perror("malloc"); - exit(update_error); - } - - if (fread(data, st.st_size, 1, fp) != 1) { - perror("fread"); - exit(update_error); - } - - 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)) - -/* - * Actual USB transfer function, the 'allow_less' flag indicates that the - * valid response could be shortef than allotted memory, the 'rxed_count' - * pointer, if provided along with 'allow_less' lets the caller know how mavy - * bytes were received. - */ -static void do_xfer(struct usb_endpoint *uep, void *outbuf, int outlen, - void *inbuf, int inlen, int allow_less, - size_t *rxed_count) -{ - - int r, actual; - - /* Send data out */ - if (outbuf && outlen) { - actual = 0; - r = libusb_bulk_transfer(uep->devh, uep->ep_num, - outbuf, outlen, - &actual, 2000); - if (r < 0) { - USB_ERROR("libusb_bulk_transfer", r); - exit(update_error); - } - if (actual != outlen) { - fprintf(stderr, "%s:%d, only sent %d/%d bytes\n", - __FILE__, __LINE__, actual, outlen); - shut_down(uep); - } - } - - /* Read reply back */ - if (inbuf && inlen) { - - actual = 0; - r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80, - inbuf, inlen, - &actual, 5000); - if (r < 0) { - USB_ERROR("libusb_bulk_transfer", r); - exit(update_error); - } - if ((actual != inlen) && !allow_less) { - fprintf(stderr, "%s:%d, only received %d/%d bytes\n", - __FILE__, __LINE__, actual, inlen); - hexdump(inbuf, actual); - shut_down(uep); - } - - if (rxed_count) - *rxed_count = actual; - } -} - -static void xfer(struct usb_endpoint *uep, void *outbuf, - size_t outlen, void *inbuf, size_t inlen, int allow_less) -{ - do_xfer(uep, outbuf, outlen, inbuf, inlen, allow_less, NULL); -} - -/* Return 0 on error, since it's never gonna be EP 0 */ -static int find_endpoint(const struct libusb_interface_descriptor *iface, - struct usb_endpoint *uep) -{ - const struct libusb_endpoint_descriptor *ep; - - if (iface->bInterfaceClass == 255 && - iface->bInterfaceSubClass == SUBCLASS && - iface->bInterfaceProtocol == PROTOCOL && - iface->bNumEndpoints) { - ep = &iface->endpoint[0]; - uep->ep_num = ep->bEndpointAddress & 0x7f; - uep->chunk_len = ep->wMaxPacketSize; - return 1; - } - - return 0; -} - -/* Return -1 on error */ -static int find_interface(struct usb_endpoint *uep) -{ - 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(uep->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, uep)) { - 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) strtoull(copy, &e, 16); - if (!*optarg || (e && *e)) - return 0; - - *pid_ptr = (uint16_t) strtoull(s, &e, 16); - if (!*optarg || (e && *e)) - return 0; - - return 1; -} - -static libusb_device_handle *check_device(libusb_device *dev, - uint16_t vid, uint16_t pid, char *serialno) -{ - struct libusb_device_descriptor desc; - libusb_device_handle *handle = NULL; - char sn[256]; - int ret; - int match = 1; - int snvalid = 0; - - ret = libusb_get_device_descriptor(dev, &desc); - if (ret < 0) - return NULL; - - ret = libusb_open(dev, &handle); - - if (ret != LIBUSB_SUCCESS) - return NULL; - - if (desc.iSerialNumber) { - ret = libusb_get_string_descriptor_ascii(handle, - desc.iSerialNumber, (unsigned char *)sn, sizeof(sn)); - if (ret > 0) - snvalid = 1; - } - - if (vid != 0 && vid != desc.idVendor) - match = 0; - if (pid != 0 && pid != desc.idProduct) - match = 0; - if (serialno != NULL && (!snvalid || strstr(sn, serialno) == NULL)) - match = 0; - - if (match) - return handle; - - libusb_close(handle); - return NULL; -} - -static void usb_findit(uint16_t vid, uint16_t pid, - char *serialno, struct usb_endpoint *uep) -{ - int iface_num, r, i; - libusb_device **devs; - libusb_device_handle *devh = NULL; - ssize_t count; - - memset(uep, 0, sizeof(*uep)); - - r = libusb_init(NULL); - if (r < 0) { - USB_ERROR("libusb_init", r); - exit(update_error); - } - - count = libusb_get_device_list(NULL, &devs); - if (count < 0) - return; - - for (i = 0; devs[i]; i++) { - devh = check_device(devs[i], vid, pid, serialno); - if (devh) { - printf("Found device.\n"); - break; - } - } - - libusb_free_device_list(devs, 1); - - if (!devh) { - fprintf(stderr, "Can't find device\n"); - exit(update_error); - } - - uep->devh = devh; - - iface_num = find_interface(uep); - if (iface_num < 0) { - fprintf(stderr, "USB FW update not supported by that device\n"); - shut_down(uep); - } - if (!uep->chunk_len) { - fprintf(stderr, "wMaxPacketSize isn't valid\n"); - shut_down(uep); - } - - printf("found interface %d endpoint %d, chunk_len %d\n", - iface_num, uep->ep_num, uep->chunk_len); - - libusb_set_auto_detach_kernel_driver(uep->devh, 1); - r = libusb_claim_interface(uep->devh, iface_num); - if (r < 0) { - USB_ERROR("libusb_claim_interface", r); - shut_down(uep); - } - - printf("READY\n-------\n"); -} - -static int transfer_block(struct usb_endpoint *uep, - struct update_frame_header *ufh, - uint8_t *transfer_data_ptr, size_t payload_size) -{ - size_t transfer_size; - uint32_t reply; - int actual; - int r; - - /* First send the header. */ - xfer(uep, ufh, sizeof(*ufh), NULL, 0, 0); - - /* Now send the block, chunk by chunk. */ - for (transfer_size = 0; transfer_size < payload_size;) { - int chunk_size; - - chunk_size = MIN(uep->chunk_len, payload_size - transfer_size); - xfer(uep, transfer_data_ptr, chunk_size, NULL, 0, 0); - transfer_data_ptr += chunk_size; - transfer_size += chunk_size; - } - - /* Now get the reply. */ - r = libusb_bulk_transfer(uep->devh, uep->ep_num | 0x80, - (void *) &reply, sizeof(reply), - &actual, 5000); - if (r) { - if (r == -7) { - fprintf(stderr, "Timeout!\n"); - return r; - } - USB_ERROR("libusb_bulk_transfer", r); - shut_down(uep); - } - - reply = *((uint8_t *)&reply); - if (reply) { - fprintf(stderr, "Error: status %#x\n", reply); - exit(update_error); - } - - return 0; -} - -/** - * Transfer an image section (typically RW or RO). - * - * td - transfer descriptor to use to communicate with the target - * data_ptr - pointer at the section base in the image - * section_addr - address of the section in the target memory space - * data_len - section size - * smart_update - non-zero to enable the smart trailing of 0xff. - */ -static void transfer_section(struct transfer_descriptor *td, - uint8_t *data_ptr, - uint32_t section_addr, - size_t data_len, - uint8_t smart_update) -{ - /* - * Actually, we can skip trailing chunks of 0xff, as the entire - * section space must be erased before the update is attempted. - * - * FIXME: We can be smarter than this and skip blocks within the image. - */ - if (smart_update) - while (data_len && (data_ptr[data_len - 1] == 0xff)) - data_len--; - - printf("sending 0x%zx bytes to %#x\n", data_len, section_addr); - while (data_len) { - size_t payload_size; - uint32_t block_base; - int max_retries; - - /* prepare the header to prepend to the block. */ - payload_size = MIN(data_len, targ.common.maximum_pdu_size); - - block_base = htobe32(section_addr); - - struct update_frame_header ufh; - - ufh.block_size = htobe32(payload_size + - sizeof(struct update_frame_header)); - ufh.cmd.block_base = block_base; - ufh.cmd.block_digest = 0; - for (max_retries = 10; max_retries; max_retries--) - if (!transfer_block(&td->uep, &ufh, - data_ptr, payload_size)) - break; - - if (!max_retries) { - fprintf(stderr, - "Failed to transfer block, %zd to go\n", - data_len); - exit(update_error); - } - data_len -= payload_size; - data_ptr += payload_size; - section_addr += payload_size; - } -} - -/* - * Each RO or RW section of the new image can be in one of the following - * states. - */ -enum upgrade_status { - not_needed = 0, /* Version below or equal that on the target. */ - not_possible, /* - * RO is newer, but can't be transferred due to - * target RW shortcomings. - */ - needed /* - * This section needs to be transferred to the - * target. - */ -}; - -/* This array describes all sections of the new image. */ -static struct { - const char *name; - uint32_t offset; - uint32_t size; - enum upgrade_status ustatus; - char version[32]; - int32_t rollback; - uint32_t key_version; -} sections[] = { - {"RO"}, - {"RW"} -}; - -static const struct fmap_area *fmap_find_area_or_die(const struct fmap *fmap, - const char *name) -{ - const struct fmap_area *fmaparea; - - fmaparea = fmap_find_area(fmap, name); - if (!fmaparea) { - fprintf(stderr, "Cannot find FMAP area %s\n", name); - exit(update_error); - } - - return fmaparea; -} - -/* - * Scan the new image and retrieve versions of all sections. - */ -static void fetch_header_versions(const uint8_t *image, size_t len) -{ - const struct fmap *fmap; - const struct fmap_area *fmaparea; - long int offset; - size_t i; - - offset = fmap_find(image, len); - if (offset < 0) { - fprintf(stderr, "Cannot find FMAP in image\n"); - exit(update_error); - } - fmap = (const struct fmap *)(image+offset); - - /* FIXME: validate fmap struct more than this? */ - if (fmap->size != len) { - fprintf(stderr, "Mismatch between FMAP size and image size\n"); - exit(update_error); - } - - for (i = 0; i < ARRAY_SIZE(sections); i++) { - const char *fmap_name; - const char *fmap_fwid_name; - const char *fmap_rollback_name = NULL; - const char *fmap_key_name = NULL; - - if (!strcmp(sections[i].name, "RO")) { - fmap_name = "EC_RO"; - fmap_fwid_name = "RO_FRID"; - } else if (!strcmp(sections[i].name, "RW")) { - fmap_name = "EC_RW"; - fmap_fwid_name = "RW_FWID"; - fmap_rollback_name = "RW_RBVER"; - /* - * Key version comes from key RO (RW signature does not - * contain the key version. - */ - fmap_key_name = "KEY_RO"; - } else { - fprintf(stderr, "Invalid section name\n"); - exit(update_error); - } - - fmaparea = fmap_find_area_or_die(fmap, fmap_name); - - /* FIXME: endianness? */ - sections[i].offset = fmaparea->offset; - sections[i].size = fmaparea->size; - - fmaparea = fmap_find_area_or_die(fmap, fmap_fwid_name); - - if (fmaparea->size != sizeof(sections[i].version)) { - fprintf(stderr, "Invalid fwid size\n"); - exit(update_error); - } - memcpy(sections[i].version, image+fmaparea->offset, - fmaparea->size); - - sections[i].rollback = -1; - if (fmap_rollback_name) { - fmaparea = fmap_find_area(fmap, fmap_rollback_name); - if (fmaparea) - memcpy(§ions[i].rollback, - image+fmaparea->offset, - sizeof(sections[i].rollback)); - } - - sections[i].key_version = -1; - if (fmap_key_name) { - fmaparea = fmap_find_area(fmap, fmap_key_name); - if (fmaparea) { - const struct vb21_packed_key *key = - (const void *)(image+fmaparea->offset); - sections[i].key_version = key->key_version; - } - } - } -} - -static int show_headers_versions(const void *image) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(sections); i++) { - printf("%s off=%08x/%08x v=%.32s rb=%d kv=%d\n", - sections[i].name, sections[i].offset, sections[i].size, - sections[i].version, sections[i].rollback, - sections[i].key_version); - } - return 0; -} - -/* - * Pick sections to transfer based on information retrieved from the target, - * the new image, and the protocol version the target is running. - */ -static void pick_sections(struct transfer_descriptor *td) -{ - size_t i; - - for (i = 0; i < ARRAY_SIZE(sections); i++) { - uint32_t offset = sections[i].offset; - - /* Skip currently active section. */ - if (offset != td->offset) - continue; - - sections[i].ustatus = needed; - } -} - -static void setup_connection(struct transfer_descriptor *td) -{ - size_t rxed_size; - size_t i; - uint32_t error_code; - - /* - * Need to be backwards compatible, communicate with targets running - * different protocol versions. - */ - union { - struct first_response_pdu rpdu; - uint32_t legacy_resp; - } start_resp; - - /* Send start request. */ - printf("start\n"); - - struct update_frame_header ufh; - uint8_t inbuf[td->uep.chunk_len]; - int actual = 0; - - /* Flush all data from endpoint to recover in case of error. */ - while (!libusb_bulk_transfer(td->uep.devh, - td->uep.ep_num | 0x80, - (void *)&inbuf, td->uep.chunk_len, - &actual, 10)) { - printf("flush\n"); - } - - memset(&ufh, 0, sizeof(ufh)); - ufh.block_size = htobe32(sizeof(ufh)); - do_xfer(&td->uep, &ufh, sizeof(ufh), &start_resp, - sizeof(start_resp), 1, &rxed_size); - - /* We got something. Check for errors in response */ - if (rxed_size < 8) { - fprintf(stderr, "Unexpected response size %zd: ", rxed_size); - for (i = 0; i < rxed_size; i++) - fprintf(stderr, " %02x", ((uint8_t *)&start_resp)[i]); - fprintf(stderr, "\n"); - exit(update_error); - } - - protocol_version = be16toh(start_resp.rpdu.protocol_version); - if (protocol_version < 5 || protocol_version > 6) { - fprintf(stderr, "Unsupported protocol version %d\n", - protocol_version); - exit(update_error); - } - - header_type = be16toh(start_resp.rpdu.header_type); - - printf("target running protocol version %d (type %d)\n", - protocol_version, header_type); - if (header_type != UPDATE_HEADER_TYPE_COMMON) { - fprintf(stderr, "Unsupported header type %d\n", - header_type); - exit(update_error); - } - - error_code = be32toh(start_resp.rpdu.return_value); - - if (error_code) { - fprintf(stderr, "Target reporting error %d\n", error_code); - shut_down(&td->uep); - exit(update_error); - } - - td->offset = be32toh(start_resp.rpdu.common.offset); - memcpy(targ.common.version, start_resp.rpdu.common.version, - sizeof(start_resp.rpdu.common.version)); - targ.common.maximum_pdu_size = - be32toh(start_resp.rpdu.common.maximum_pdu_size); - targ.common.flash_protection = - be32toh(start_resp.rpdu.common.flash_protection); - targ.common.min_rollback = be32toh(start_resp.rpdu.common.min_rollback); - targ.common.key_version = be32toh(start_resp.rpdu.common.key_version); - - printf("maximum PDU size: %d\n", targ.common.maximum_pdu_size); - printf("Flash protection status: %04x\n", targ.common.flash_protection); - printf("version: %32s\n", targ.common.version); - printf("key_version: %d\n", targ.common.key_version); - printf("min_rollback: %d\n", targ.common.min_rollback); - printf("offset: writable at %#x\n", td->offset); - - pick_sections(td); -} - -/* - * Channel TPM extension/vendor command over USB. The payload of the USB frame - * in this case consists of the 2 byte subcommand code concatenated with the - * command body. The caller needs to indicate if a response is expected, and - * if it is - of what maximum size. - */ -static int ext_cmd_over_usb(struct usb_endpoint *uep, uint16_t subcommand, - void *cmd_body, size_t body_size, - void *resp, size_t *resp_size, - int allow_less) -{ - struct update_frame_header *ufh; - uint16_t *frame_ptr; - size_t usb_msg_size; - - usb_msg_size = sizeof(struct update_frame_header) + - sizeof(subcommand) + body_size; - - ufh = malloc(usb_msg_size); - if (!ufh) { - printf("%s: failed to allocate %zd bytes\n", - __func__, usb_msg_size); - return -1; - } - - ufh->block_size = htobe32(usb_msg_size); - ufh->cmd.block_digest = 0; - ufh->cmd.block_base = htobe32(UPDATE_EXTRA_CMD); - frame_ptr = (uint16_t *)(ufh + 1); - *frame_ptr = htobe16(subcommand); - - if (body_size) - memcpy(frame_ptr + 1, cmd_body, body_size); - - xfer(uep, ufh, usb_msg_size, resp, resp_size ? *resp_size : 0, - allow_less); - - free(ufh); - return 0; -} - -/* - * Indicate to the target that update image transfer has been completed. Upon - * receiveing of this message the target state machine transitions into the - * 'rx_idle' state. The host may send an extension command to reset the target - * after this. - */ -static void send_done(struct usb_endpoint *uep) -{ - uint32_t out; - - /* Send stop request, ignoring reply. */ - out = htobe32(UPDATE_DONE); - xfer(uep, &out, sizeof(out), &out, 1, 0); -} - -static void send_subcommand(struct transfer_descriptor *td, uint16_t subcommand, - void *cmd_body, size_t body_size, - uint8_t *response, size_t response_size) -{ - send_done(&td->uep); - - ext_cmd_over_usb(&td->uep, subcommand, - cmd_body, body_size, - response, &response_size, 0); - printf("sent command %x, resp %x\n", subcommand, response[0]); -} - -/* Returns number of successfully transmitted image sections. */ -static int transfer_image(struct transfer_descriptor *td, - uint8_t *data, size_t data_len) -{ - size_t i; - int num_txed_sections = 0; - - for (i = 0; i < ARRAY_SIZE(sections); i++) - if (sections[i].ustatus == needed) { - transfer_section(td, - data + sections[i].offset, - sections[i].offset, - sections[i].size, 1); - num_txed_sections++; - } - - /* - * Move USB receiver sate machine to idle state so that vendor - * commands can be processed later, if any. - */ - send_done(&td->uep); - - if (!num_txed_sections) - printf("nothing to do\n"); - else - printf("-------\nupdate complete\n"); - return num_txed_sections; -} - -static void generate_reset_request(struct transfer_descriptor *td) -{ - size_t response_size; - uint8_t response; - uint16_t subcommand; - uint8_t command_body[2]; /* Max command body size. */ - size_t command_body_size; - - if (protocol_version < 6) { - /* - * Send a second stop request, which should reboot - * without replying. - */ - send_done(&td->uep); - /* Nothing we can do over /dev/tpm0 running versions below 6. */ - return; - } - - /* - * If the user explicitly wants it, request post reset instead of - * immediate reset. In this case next time the target reboots, the h1 - * will reboot as well, and will consider running the uploaded code. - * - * In case target RW version is 19 or above, to reset the target the - * host is supposed to send the command to enable the uploaded image - * disabled by default. - * - * Otherwise the immediate reset command would suffice. - */ - /* Most common case. */ - command_body_size = 0; - response_size = 1; - subcommand = UPDATE_EXTRA_CMD_IMMEDIATE_RESET; - ext_cmd_over_usb(&td->uep, subcommand, - command_body, command_body_size, - &response, &response_size, 0); - - printf("reboot not triggered\n"); -} - -static void get_random(uint8_t *data, int len) -{ - FILE *fp; - int i = 0; - - fp = fopen("/dev/random", "rb"); - if (!fp) { - perror("Can't open /dev/random"); - exit(update_error); - } - - while (i < len) { - int ret = fread(data+i, len-i, 1, fp); - - if (ret < 0) { - perror("fread"); - exit(update_error); - } - - i += ret; - } - - fclose(fp); -} - -static void read_console(struct transfer_descriptor *td) -{ - uint8_t payload[] = { 0x1 }; - uint8_t response[64]; - size_t response_size = 64; - struct timespec sleep_duration = { /* 100 ms */ - .tv_sec = 0, - .tv_nsec = 100l * 1000l * 1000l, - }; - - send_done(&td->uep); - - printf("\n"); - while (1) { - response_size = 1; - ext_cmd_over_usb(&td->uep, - UPDATE_EXTRA_CMD_CONSOLE_READ_INIT, - NULL, 0, - response, &response_size, 0); - - while (1) { - response_size = 64; - ext_cmd_over_usb(&td->uep, - UPDATE_EXTRA_CMD_CONSOLE_READ_NEXT, - payload, sizeof(payload), - response, &response_size, 1); - if (response[0] == 0) - break; - /* make sure it's null-terminated. */ - response[response_size - 1] = 0; - printf("%s", (const char *)response); - } - nanosleep(&sleep_duration, NULL); - } -} - -int main(int argc, char *argv[]) -{ - struct transfer_descriptor td; - int errorcnt; - uint8_t *data = 0; - size_t data_len = 0; - uint16_t vid = VID, pid = PID; - char *serialno = NULL; - int i; - size_t j; - int transferred_sections = 0; - int binary_vers = 0; - int show_fw_ver = 0; - int no_reset_request = 0; - int touchpad_update = 0; - int extra_command = -1; - uint8_t extra_command_data[50]; - int extra_command_data_len = 0; - uint8_t extra_command_answer[64]; - int extra_command_answer_len = 1; - - progname = strrchr(argv[0], '/'); - if (progname) - progname++; - else - progname = argv[0]; - - /* Usb transfer - default mode. */ - memset(&td, 0, sizeof(td)); - - errorcnt = 0; - opterr = 0; /* quiet, you */ - while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) { - switch (i) { - case 'b': - binary_vers = 1; - break; - case 'd': - if (!parse_vidpid(optarg, &vid, &pid)) { - printf("Invalid argument: \"%s\"\n", optarg); - errorcnt++; - } - break; - case 'e': - get_random(extra_command_data, 32); - extra_command_data_len = 32; - extra_command = UPDATE_EXTRA_CMD_INJECT_ENTROPY; - break; - case 'f': - show_fw_ver = 1; - break; - case 'g': - extra_command = UPDATE_EXTRA_CMD_TOUCHPAD_DEBUG; - /* Maximum length. */ - extra_command_data_len = 50; - str2hex(optarg, - extra_command_data, &extra_command_data_len); - hexdump(extra_command_data, extra_command_data_len); - extra_command_answer_len = 64; - break; - case 'h': - usage(errorcnt); - break; - case 'j': - extra_command = UPDATE_EXTRA_CMD_JUMP_TO_RW; - break; - case 'l': - extra_command = UPDATE_EXTRA_CMD_CONSOLE_READ_INIT; - break; - case 'n': - no_reset_request = 1; - break; - case 'p': - touchpad_update = 1; - - data = get_file_or_die(optarg, &data_len); - printf("read %zd(%#zx) bytes from %s\n", - data_len, data_len, argv[optind - 1]); - - break; - case 'r': - extra_command = UPDATE_EXTRA_CMD_IMMEDIATE_RESET; - break; - case 's': - extra_command = UPDATE_EXTRA_CMD_STAY_IN_RO; - break; - case 'S': - serialno = optarg; - break; - case 't': - extra_command = UPDATE_EXTRA_CMD_TOUCHPAD_INFO; - extra_command_answer_len = - sizeof(struct touchpad_info); - break; - case 'u': - extra_command = UPDATE_EXTRA_CMD_UNLOCK_ROLLBACK; - break; - case 'w': - extra_command = UPDATE_EXTRA_CMD_UNLOCK_RW; - 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(update_error); - } - } - - if (errorcnt) - usage(errorcnt); - - if (!show_fw_ver && extra_command == -1 && !touchpad_update) { - if (optind >= argc) { - fprintf(stderr, - "\nERROR: Missing required <binary image>\n\n"); - usage(1); - } - - data = get_file_or_die(argv[optind], &data_len); - printf("read %zd(%#zx) bytes from %s\n", - data_len, data_len, argv[optind]); - - fetch_header_versions(data, data_len); - - if (binary_vers) - exit(show_headers_versions(data)); - } else { - if (optind < argc) - printf("Ignoring binary image %s\n", argv[optind]); - } - - usb_findit(vid, pid, serialno, &td.uep); - - setup_connection(&td); - - if (show_fw_ver) { - printf("Current versions:\n"); - printf("Writable %32s\n", targ.common.version); - } - - if (data) { - if (touchpad_update) { - transfer_section(&td, - data, - 0x80000000, - data_len, 0); - free(data); - - send_done(&td.uep); - } else { - transferred_sections = transfer_image(&td, - data, data_len); - free(data); - - if (transferred_sections && !no_reset_request) - generate_reset_request(&td); - } - } else if (extra_command == UPDATE_EXTRA_CMD_CONSOLE_READ_INIT) { - read_console(&td); - } else if (extra_command > -1) { - send_subcommand(&td, extra_command, - extra_command_data, extra_command_data_len, - extra_command_answer, extra_command_answer_len); - - switch (extra_command) { - case UPDATE_EXTRA_CMD_TOUCHPAD_INFO: - dump_touchpad_info(extra_command_answer, - extra_command_answer_len); - break; - case UPDATE_EXTRA_CMD_TOUCHPAD_DEBUG: - hexdump(extra_command_answer, extra_command_answer_len); - break; - } - } - - libusb_close(td.uep.devh); - libusb_exit(NULL); - - if (!transferred_sections) - return noop; - /* - * We should indicate if RO update was not done because of the - * insufficient RW version. - */ - for (j = 0; j < ARRAY_SIZE(sections); j++) - if (sections[j].ustatus == not_possible) { - /* This will allow scripting repeat attempts. */ - printf("Failed to update RO, run the command again\n"); - return rw_updated; - } - - printf("image updated\n"); - return all_updated; -} |