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>
This commit is contained in:
Nick Sanders
2016-04-06 14:25:45 -07:00
committed by chrome-bot
parent 5cc3cac589
commit 56ee8aefc3
7 changed files with 298 additions and 30 deletions

View File

@@ -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.

View File

@@ -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,

View File

@@ -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

View File

@@ -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;
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);
/* 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)
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

View File

@@ -2011,6 +2011,9 @@
*/
#undef CONFIG_USB_PORT_POWER_SMART_INVERTED
/* Support programmable USB device iSerial field. */
#undef CONFIG_USB_SERIALNO
/******************************************************************************/
/* USB port switch */

View File

@@ -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 */

View File

@@ -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))))