diff options
author | Nick Sanders <nsanders@chromium.org> | 2016-04-06 14:25:45 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-05-26 16:17:26 -0700 |
commit | 56ee8aefc33505a7df4e4148001a11ac461907a3 (patch) | |
tree | 9aa84b4f26d7396878757bb7ed79bebaa18f59a2 | |
parent | 5cc3cac589d3e869266c18ed7e538a769496478f (diff) | |
download | chrome-ec-56ee8aefc33505a7df4e4148001a11ac461907a3.tar.gz |
servo_micro: add programmable serial number
This change provides a console command for setting,
and loading a usb serial number from flash. This
feature adds CONFIG_USB_SERIALNO, and currently only
has a useful implementation when PSTATE is present.
BUG=chromium:571477
TEST=serialno set abcdef; serialno load; reboot
BRANCH=none
Change-Id: I3b24cfa2d52d54118bc3fd54b276e3d95412d245
Signed-off-by: Nick Sanders <nsanders@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/337359
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | board/servo_micro/board.c | 4 | ||||
-rw-r--r-- | board/servo_micro/board.h | 3 | ||||
-rw-r--r-- | chip/stm32/usb.c | 112 | ||||
-rw-r--r-- | common/flash.c | 170 | ||||
-rw-r--r-- | include/config.h | 3 | ||||
-rw-r--r-- | include/flash.h | 16 | ||||
-rw-r--r-- | include/usb_descriptor.h | 18 |
7 files changed, 297 insertions, 29 deletions
diff --git a/board/servo_micro/board.c b/board/servo_micro/board.c index f22bda4342..5ee94f032b 100644 --- a/board/servo_micro/board.c +++ b/board/servo_micro/board.c @@ -154,12 +154,11 @@ USB_STREAM_CONFIG(usart4_usb, /****************************************************************************** * Define the strings used in our USB descriptors. */ - const void *const usb_strings[] = { [USB_STR_DESC] = usb_string_desc, [USB_STR_VENDOR] = USB_STRING_DESC("Google Inc."), [USB_STR_PRODUCT] = USB_STRING_DESC("Servo Micro"), - [USB_STR_SERIALNO] = USB_STRING_DESC("1234-a"), + [USB_STR_SERIALNO] = 0, [USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32), [USB_STR_USART4_STREAM_NAME] = USB_STRING_DESC("Servo UART3"), [USB_STR_CONSOLE_NAME] = USB_STRING_DESC("Servo EC Shell"), @@ -169,7 +168,6 @@ const void *const usb_strings[] = { BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT); - /****************************************************************************** * Support SPI bridging over USB, this requires usb_spi_board_enable and * usb_spi_board_disable to be defined to enable and disable the SPI bridge. diff --git a/board/servo_micro/board.h b/board/servo_micro/board.h index 48f9368436..7efb2fc3dd 100644 --- a/board/servo_micro/board.h +++ b/board/servo_micro/board.h @@ -87,6 +87,9 @@ #include "gpio_signal.h" +#define CONFIG_USB_SERIALNO +#define DEFAULT_SERIALNO "Uninitialized" + /* USB string indexes */ enum usb_strings { USB_STR_DESC = 0, diff --git a/chip/stm32/usb.c b/chip/stm32/usb.c index ca82fbd4a4..85e2320642 100644 --- a/chip/stm32/usb.c +++ b/chip/stm32/usb.c @@ -7,6 +7,7 @@ #include "common.h" #include "config.h" #include "console.h" +#include "flash.h" #include "gpio.h" #include "hooks.h" #include "link_defs.h" @@ -34,6 +35,12 @@ #define CONFIG_USB_BCD_DEV 0x0100 /* 1.00 */ #endif +#ifndef CONFIG_USB_SERIALNO +#define USB_STR_SERIALNO 0 +#else +static int usb_load_serial(void); +#endif + /* USB Standard Device Descriptor */ static const struct usb_device_descriptor dev_desc = { .bLength = USB_DT_DEVICE_SIZE, @@ -48,7 +55,7 @@ static const struct usb_device_descriptor dev_desc = { .bcdDevice = CONFIG_USB_BCD_DEV, .iManufacturer = USB_STR_VENDOR, .iProduct = USB_STR_PRODUCT, - .iSerialNumber = 0, + .iSerialNumber = USB_STR_SERIALNO, .bNumConfigurations = 1 }; @@ -85,6 +92,8 @@ static int desc_left; /* pointer to descriptor data if any */ static const uint8_t *desc_ptr; + + void usb_read_setup_packet(usb_uint *buffer, struct usb_setup_packet *packet) { packet->bmRequestType = buffer[0] & 0xff; @@ -137,7 +146,12 @@ static void ep0_rx(void) if (idx >= USB_STR_COUNT) /* The string does not exist : STALL */ goto unknown_req; - desc = usb_strings[idx]; +#ifdef CONFIG_USB_SERIALNO + if (idx == USB_STR_SERIALNO) + desc = (uint8_t *)usb_serialno_desc; + else +#endif + desc = usb_strings[idx]; len = desc[0]; break; case USB_DT_DEVICE_QUALIFIER: /* Get device qualifier desc */ @@ -307,6 +321,9 @@ void usb_init(void) /* set interrupts mask : reset/correct tranfer/errors */ STM32_USB_CNTR = 0xe400; +#ifdef CONFIG_USB_SERIALNO + usb_load_serial(); +#endif #ifndef CONFIG_USB_INHIBIT_CONNECT usb_connect(); #endif @@ -402,3 +419,94 @@ void *memcpy_from_usbram(void *dest, const void *src, size_t n) return dest; } + +#ifdef CONFIG_USB_SERIALNO +/* This will be subbed into USB_STR_SERIALNO. */ +struct usb_string_desc *usb_serialno_desc = + USB_WR_STRING_DESC(DEFAULT_SERIALNO); + +/* Update serial number */ +static int usb_set_serial(const char *serialno) +{ + struct usb_string_desc *sd = usb_serialno_desc; + int i; + + if (!serialno) + return EC_ERROR_INVAL; + + /* Convert into unicode usb string desc. */ + for (i = 0; i < USB_STRING_LEN; i++) { + sd->_data[i] = serialno[i]; + if (serialno[i] == 0) + break; + } + /* Count wchars (w/o null terminator) plus size & type bytes. */ + sd->_len = (i * 2) + 2; + sd->_type = USB_DT_STRING; + + return EC_SUCCESS; +} + +/* Retrieve serial number from pstate flash. */ +static int usb_load_serial(void) +{ + const char *serialno; + int rv; + + serialno = flash_read_serial(); + if (!serialno) + return EC_ERROR_ACCESS_DENIED; + + rv = usb_set_serial(serialno); + return rv; +} + +/* Save serial number into pstate region. */ +static int usb_save_serial(const char *serialno) +{ + int rv; + + if (!serialno) + return EC_ERROR_INVAL; + + /* Save this new serial number to flash. */ + rv = flash_write_serial(serialno); + if (rv) + return rv; + + /* Load this new serial number to memory. */ + rv = usb_load_serial(); + return rv; +} + +static int command_serialno(int argc, char **argv) +{ + struct usb_string_desc *sd = usb_serialno_desc; + char buf[USB_STRING_LEN]; + int rv = EC_SUCCESS; + int i; + + if (argc != 1) { + if ((strcasecmp(argv[1], "set") == 0) && + (argc == 3)) { + ccprintf("Saving serial number\n"); + rv = usb_save_serial(argv[2]); + } else if ((strcasecmp(argv[1], "load") == 0) && + (argc == 2)) { + ccprintf("Loading serial number\n"); + rv = usb_load_serial(); + } else + return EC_ERROR_INVAL; + } + + for (i = 0; i < USB_STRING_LEN; i++) + buf[i] = sd->_data[i]; + ccprintf("Serial number: %s\n", buf); + return rv; +} + +DECLARE_CONSOLE_COMMAND(serialno, command_serialno, + "load/set [value]", + "Read and write USB serial number", + NULL); +#endif diff --git a/common/flash.c b/common/flash.c index 7cd977d9e0..2bb1679197 100644 --- a/common/flash.c +++ b/common/flash.c @@ -35,17 +35,24 @@ #ifdef CONFIG_FLASH_PSTATE_BANK /* Persistent protection state - emulates a SPI status register for flashrom */ -struct persist_state { - uint8_t version; /* Version of this struct */ - uint8_t flags; /* Lock flags (PERSIST_FLAG_*) */ - uint8_t reserved[2]; /* Reserved; set 0 */ -}; - -#define PERSIST_STATE_VERSION 2 /* Expected persist_state.version */ +/* NOTE: It's not expected that RO and RW will support + * differing PSTATE versions. */ +#define PERSIST_STATE_VERSION 3 /* Expected persist_state.version */ +#define SERIALNO_MAX 30 /* Flags for persist_state.flags */ /* Protect persist state and RO firmware at boot */ #define PERSIST_FLAG_PROTECT_RO 0x02 +#define PSTATE_VALID_FLAGS (1 << 0) +#define PSTATE_VALID_SERIALNO (1 << 1) + +struct persist_state { + uint8_t version; /* Version of this struct */ + uint8_t flags; /* Lock flags (PERSIST_FLAG_*) */ + uint8_t valid_fields; /* Flags for valid data. */ + uint8_t reserved; /* Reserved; set 0 */ + uint8_t serialno[SERIALNO_MAX]; /* Serial number. */ +}; #else /* !CONFIG_FLASH_PSTATE_BANK */ @@ -142,6 +149,7 @@ static uint32_t flash_read_pstate(void) flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); if ((pstate->version == PERSIST_STATE_VERSION) && + (pstate->valid_fields & PSTATE_VALID_FLAGS) && (pstate->flags & PERSIST_FLAG_PROTECT_RO)) { /* Lock flag is known to be set */ return EC_FLASH_PROTECT_RO_AT_BOOT; @@ -155,23 +163,33 @@ static uint32_t flash_read_pstate(void) } /** - * Write persistent state from pstate, erasing if necessary. + * Read and return persistent serial number. + */ +static const char *flash_read_pstate_serial(void) +{ + const struct persist_state *pstate = + (const struct persist_state *) + flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); + + if ((pstate->version == PERSIST_STATE_VERSION) && + (pstate->valid_fields & PSTATE_VALID_SERIALNO)) { + return (const char *)(pstate->serialno); + } + + return 0; +} + +/** + * Write persistent state after erasing. * - * @param flags New flash write protect flags to set in pstate. + * @param pstate New data to set in pstate. NOT memory mapped + * old pstate as it will be erased. * @return EC_SUCCESS, or nonzero if error. */ -static int flash_write_pstate(uint32_t flags) +static int flash_write_pstate_data(struct persist_state *newpstate) { - struct persist_state pstate; int rv; - /* Only check the flags we write to pstate */ - flags &= EC_FLASH_PROTECT_RO_AT_BOOT; - - /* Check if pstate has actually changed */ - if (flags == flash_read_pstate()) - return EC_SUCCESS; - /* Erase pstate */ rv = flash_physical_erase(CONFIG_FW_PSTATE_OFF, CONFIG_FW_PSTATE_SIZE); @@ -184,15 +202,101 @@ static int flash_write_pstate(uint32_t flags) * it's protected. */ - /* Write a new pstate */ - memset(&pstate, 0, sizeof(pstate)); - pstate.version = PERSIST_STATE_VERSION; + /* Write the updated pstate */ + return flash_physical_write(CONFIG_FW_PSTATE_OFF, sizeof(*newpstate), + (const char *)newpstate); +} + + + +/** + * Validate and Init persistent state datastructure. + * + * @param pstate A pstate data structure. Will be valid at complete. + * @return EC_SUCCESS, or nonzero if error. + */ +static int validate_pstate_struct(struct persist_state *pstate) +{ + if (pstate->version != PERSIST_STATE_VERSION) { + memset(pstate, 0, sizeof(*pstate)); + pstate->version = PERSIST_STATE_VERSION; + pstate->valid_fields = 0; + } + + return EC_SUCCESS; +} + +/** + * Write persistent state from pstate, erasing if necessary. + * + * @param flags New flash write protect flags to set in pstate. + * @return EC_SUCCESS, or nonzero if error. + */ +static int flash_write_pstate(uint32_t flags) +{ + struct persist_state newpstate; + const struct persist_state *pstate = + (const struct persist_state *) + flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); + + /* Only check the flags we write to pstate */ + flags &= EC_FLASH_PROTECT_RO_AT_BOOT; + + /* Check if pstate has actually changed */ + if (flags == flash_read_pstate()) + return EC_SUCCESS; + + /* Cache the old copy for read/modify/write. */ + memcpy(&newpstate, pstate, sizeof(newpstate)); + validate_pstate_struct(&newpstate); + if (flags & EC_FLASH_PROTECT_RO_AT_BOOT) - pstate.flags |= PERSIST_FLAG_PROTECT_RO; - return flash_physical_write(CONFIG_FW_PSTATE_OFF, sizeof(pstate), - (const char *)&pstate); + newpstate.flags |= PERSIST_FLAG_PROTECT_RO; + else + newpstate.flags &= ~PERSIST_FLAG_PROTECT_RO; + newpstate.valid_fields |= PSTATE_VALID_FLAGS; + + return flash_write_pstate_data(&newpstate); } +/** + * Write persistent serial number to pstate, erasing if necessary. + * + * @param serialno New iascii serial number to set in pstate. + * @return EC_SUCCESS, or nonzero if error. + */ +static int flash_write_pstate_serial(const char *serialno) +{ + int i; + struct persist_state newpstate; + const struct persist_state *pstate = + (const struct persist_state *) + flash_physical_dataptr(CONFIG_FW_PSTATE_OFF); + + /* Check that this is OK */ + if (!serialno) + return EC_ERROR_INVAL; + + /* Cache the old copy for read/modify/write. */ + memcpy(&newpstate, pstate, sizeof(newpstate)); + validate_pstate_struct(&newpstate); + + /* Copy in serialno. */ + for (i = 0; i < SERIALNO_MAX - 1; i++) { + newpstate.serialno[i] = serialno[i]; + if (serialno[i] == 0) + break; + } + for (; i < SERIALNO_MAX; i++) + newpstate.serialno[i] = 0; + newpstate.valid_fields |= PSTATE_VALID_SERIALNO; + + return flash_write_pstate_data(&newpstate); +} + + + + #else /* !CONFIG_FLASH_PSTATE_BANK */ /** @@ -349,6 +453,24 @@ int flash_erase(int offset, int size) return flash_physical_erase(offset, size); } +const char *flash_read_serial(void) +{ +#if defined(CONFIG_FLASH_PSTATE) && defined(CONFIG_FLASH_PSTATE_BANK) + return flash_read_pstate_serial(); +#else + return 0; +#endif +} + +int flash_write_serial(const char *serialno) +{ +#if defined(CONFIG_FLASH_PSTATE) && defined(CONFIG_FLASH_PSTATE_BANK) + return flash_write_pstate_serial(serialno); +#else + return EC_ERROR_UNIMPLEMENTED; +#endif +} + int flash_protect_at_boot(enum flash_wp_range range) { #ifdef CONFIG_FLASH_PSTATE diff --git a/include/config.h b/include/config.h index 591af79c5f..3ecf920140 100644 --- a/include/config.h +++ b/include/config.h @@ -2011,6 +2011,9 @@ */ #undef CONFIG_USB_PORT_POWER_SMART_INVERTED +/* Support programmable USB device iSerial field. */ +#undef CONFIG_USB_SERIALNO + /******************************************************************************/ /* USB port switch */ diff --git a/include/flash.h b/include/flash.h index b065e7bb1b..03a6e89c25 100644 --- a/include/flash.h +++ b/include/flash.h @@ -245,4 +245,20 @@ uint32_t flash_get_protect(void); */ int flash_set_protect(uint32_t mask, uint32_t flags); +/** + * Get the serial number from flash. + * + * @return char * ascii serial number string. + */ +const char *flash_read_serial(void); + +/** + * Set the serial number in flash. + * + * @param serialno ascii serial number string < 30 char. + * + * @return success status. + */ +int flash_write_serial(const char *serialno); + #endif /* __CROS_EC_FLASH_H */ diff --git a/include/usb_descriptor.h b/include/usb_descriptor.h index baa6e6421b..a29b9eda76 100644 --- a/include/usb_descriptor.h +++ b/include/usb_descriptor.h @@ -233,6 +233,24 @@ struct usb_setup_packet { WIDESTR(str) \ } +#ifdef CONFIG_USB_SERIALNO +/* String Descriptor for USB, for editable strings. */ +#define USB_STRING_LEN 30 +struct usb_string_desc { + uint8_t _len; + uint8_t _type; + wchar_t _data[USB_STRING_LEN]; +}; +#define USB_WR_STRING_DESC(str) \ + (&(struct usb_string_desc) { \ + /* As above, two bytes metadata, no null terminator. */ \ + sizeof(WIDESTR(str)) + 2 - 2, \ + USB_DT_STRING, \ + WIDESTR(str) \ +}) +extern struct usb_string_desc *usb_serialno_desc; +#endif + /* Use these macros for declaring descriptors, to order them properly */ #define USB_CONF_DESC(name) CONCAT2(usb_desc_, name) \ __attribute__((section(".rodata.usb_desc_" STRINGIFY(name)))) |