summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2014-05-13 13:02:26 -0700
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-05-17 20:14:23 +0000
commit5fd33410770fbc9b29432fe6ca81a763e6b47ec4 (patch)
treeed81c35caa3cf5f5dd39f9c5eb245dbbd8b9c8d6
parent00cf95783210c5b94565b0e0d480271dcef490ca (diff)
downloadchrome-ec-5fd33410770fbc9b29432fe6ca81a763e6b47ec4.tar.gz
zinger: add support to flash RW firmware
Allow flashing the RW firmware by sending Vendor-Defined Messages over the USB-PD link. This is not the secure update whose design is still under discussion, it's a simple update with integrity check. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=chrome-os-partner:28330 TEST=using the following CLs, ./util/flash_pd.py ./build/zinger/ec.RW.flat and see Zinger booting on RW, repeat the operations with different builds of the RW firmware. Change-Id: Icd90eb92f7321ccd66341a50b9dabd73c59c68c1 Reviewed-on: https://chromium-review.googlesource.com/197948 Reviewed-by: Alec Berg <alecaberg@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--board/zinger/board.c59
-rw-r--r--board/zinger/board.h11
-rw-r--r--board/zinger/hardware.c150
-rw-r--r--board/zinger/runtime.c11
-rw-r--r--board/zinger/usb_pd_policy.c58
-rw-r--r--common/usb_pd_protocol.c19
-rw-r--r--include/usb_pd.h39
7 files changed, 337 insertions, 10 deletions
diff --git a/board/zinger/board.c b/board/zinger/board.c
index 9a830e7e14..869a6afc30 100644
--- a/board/zinger/board.c
+++ b/board/zinger/board.c
@@ -8,11 +8,16 @@
#include "debug.h"
#include "irq_handler.h"
#include "registers.h"
+#include "sha1.h"
#include "usb_pd.h"
#include "util.h"
extern void pd_rx_handler(void);
+/* RW firmware reset vector */
+static uint32_t * const rw_rst =
+ (uint32_t *)(CONFIG_FLASH_BASE+CONFIG_FW_RW_OFF+4);
+
/* External interrupt EXTINT7 for external comparator on PA7 */
void IRQ_HANDLER(STM32_IRQ_EXTI4_15)(void)
{
@@ -22,16 +27,64 @@ void IRQ_HANDLER(STM32_IRQ_EXTI4_15)(void)
pd_rx_handler();
}
+static void jump_to_rw(void)
+{
+ void (*jump_rw_rst)(void) = (void *)*rw_rst;
+
+ debug_printf("Jump to RW\n");
+ /* Disable interrupts */
+ asm volatile("cpsid i");
+ /* Call RW firmware reset vector */
+ jump_rw_rst();
+}
+
+int is_ro_mode(void)
+{
+ return (uint32_t)&jump_to_rw < *rw_rst;
+}
+
+static int check_rw_valid(void)
+{
+ uint32_t *hash;
+ uint32_t *fw_hash = (uint32_t *)
+ (CONFIG_FLASH_BASE + CONFIG_FLASH_SIZE - 32);
+
+ /* Check if we have a RW firmware flashed */
+ if (*rw_rst == 0xffffffff)
+ return 0;
+
+ hash = (uint32_t *)flash_hash_rw();
+ /* TODO(crosbug.com/p/28336) use secret key to check RW */
+ if (memcmp(hash, fw_hash, SHA1_DIGEST_SIZE) != 0) {
+ /* Firmware doesn't match the recorded hash */
+ debug_printf("SHA-1 %08x %08x %08x %08x %08x\n",
+ hash[0], hash[1], hash[2], hash[3], hash[4]);
+ debug_printf("FW SHA-1 %08x %08x %08x %08x %08x\n",
+ fw_hash[0], fw_hash[1], fw_hash[2],
+ fw_hash[3], fw_hash[4]);
+ return 0;
+ }
+
+ return 1;
+}
+
extern void pd_task(void);
int main(void)
{
hardware_init();
- debug_printf("Power supply started ...\n");
+ debug_printf("Power supply started ... %s\n",
+ is_ro_mode() ? "RO" : "RW");
+
+ /* Verify RW firmware and use it if valid */
+ if (is_ro_mode() && check_rw_valid())
+ jump_to_rw();
/* background loop for PD events */
pd_task();
- while (1)
- ;
+ debug_printf("background loop exited !\n");
+ /* we should never reach that point */
+ cpu_reset();
+ return 0;
}
diff --git a/board/zinger/board.h b/board/zinger/board.h
index 90b492871b..610fedf7c6 100644
--- a/board/zinger/board.h
+++ b/board/zinger/board.h
@@ -18,7 +18,9 @@
#define CONFIG_USB_POWER_DELIVERY
#undef CONFIG_USB_PD_DUAL_ROLE
#undef CONFIG_USB_PD_INTERNAL_COMP
+#define CONFIG_USB_PD_CUSTOM_VDM
#define CONFIG_HW_CRC
+#define CONFIG_SHA1
#undef CONFIG_WATCHDOG_HELP
#undef CONFIG_LID_SWITCH
#undef CONFIG_TASK_PROFILING
@@ -63,6 +65,15 @@ void hardware_init(void);
/* last interrupt event */
extern volatile uint32_t last_event;
+/* RW section flashing */
+int flash_erase_rw(void);
+int flash_write_rw(int offset, int size, const char *data);
+uint8_t *flash_hash_rw(void);
+int is_ro_mode(void);
+
+/* Reboot the CPU */
+void cpu_reset(void);
+
#endif /* !__ASSEMBLER__ */
#endif /* __BOARD_H */
diff --git a/board/zinger/hardware.c b/board/zinger/hardware.c
index 1f9851c707..cf71c3ffad 100644
--- a/board/zinger/hardware.c
+++ b/board/zinger/hardware.c
@@ -9,6 +9,7 @@
#include "common.h"
#include "cpu.h"
#include "registers.h"
+#include "sha1.h"
#include "task.h"
#include "timer.h"
#include "util.h"
@@ -99,11 +100,14 @@ static void pins_init(void)
static void adc_init(void)
{
- /* ADC calibration (done with ADEN = 0) */
- STM32_ADC_CR = 1 << 31; /* set ADCAL = 1, ADC off */
- /* wait for the end of calibration */
- while (STM32_ADC_CR & (1 << 31))
- ;
+ /* Only do the calibration if the ADC is off */
+ if (!(STM32_ADC_CR & 1)) {
+ /* ADC calibration */
+ STM32_ADC_CR = 1 << 31; /* set ADCAL = 1, ADC off */
+ /* wait for the end of calibration */
+ while (STM32_ADC_CR & (1 << 31))
+ ;
+ }
/* ADC enabled */
STM32_ADC_CR = 1 << 0;
/* Single conversion, right aligned, 12-bit */
@@ -178,3 +182,139 @@ int adc_read_channel(enum adc_channel ch)
return value;
}
+
+/* ---- flash handling ---- */
+
+/*
+ * Approximate number of CPU cycles per iteration of the loop when polling
+ * the flash status
+ */
+#define CYCLE_PER_FLASH_LOOP 10
+
+/* Flash page programming timeout. This is 2x the datasheet max. */
+#define FLASH_TIMEOUT_US 16000
+#define FLASH_TIMEOUT_LOOP \
+ (FLASH_TIMEOUT_US * (CPU_CLOCK / SECOND) / CYCLE_PER_FLASH_LOOP)
+
+/* Flash unlocking keys */
+#define KEY1 0x45670123
+#define KEY2 0xCDEF89AB
+
+/* Lock bits for FLASH_CR register */
+#define PG (1<<0)
+#define PER (1<<1)
+#define STRT (1<<6)
+#define CR_LOCK (1<<7)
+
+int flash_write_rw(int offset, int size, const char *data)
+{
+ uint16_t *address = (uint16_t *)
+ (CONFIG_FLASH_BASE + CONFIG_FW_RW_OFF + offset);
+ int res = EC_SUCCESS;
+ int i;
+
+ if ((uint32_t)address > CONFIG_FLASH_BASE + CONFIG_FLASH_SIZE)
+ return EC_ERROR_INVAL;
+
+ /* unlock CR if needed */
+ if (STM32_FLASH_CR & CR_LOCK) {
+ STM32_FLASH_KEYR = KEY1;
+ STM32_FLASH_KEYR = KEY2;
+ }
+
+ /* Clear previous error status */
+ STM32_FLASH_SR = 0x34;
+ /* set the ProGram bit */
+ STM32_FLASH_CR |= PG;
+
+ for (; size > 0; size -= sizeof(uint16_t)) {
+ /* wait to be ready */
+ for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
+ i++)
+ ;
+ /* write the half word */
+ *address++ = data[0] + (data[1] << 8);
+ data += 2;
+ /* Wait for writes to complete */
+ for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
+ i++)
+ ;
+ if (i == FLASH_TIMEOUT_LOOP) {
+ res = EC_ERROR_TIMEOUT;
+ goto exit_wr;
+ }
+ /* Check for error conditions - erase failed, voltage error,
+ * protection error */
+ if (STM32_FLASH_SR & 0x14) {
+ res = EC_ERROR_UNKNOWN;
+ goto exit_wr;
+ }
+ }
+
+exit_wr:
+ STM32_FLASH_CR &= ~PG;
+ STM32_FLASH_CR = CR_LOCK;
+
+ return res;
+}
+
+int flash_erase_rw(void)
+{
+ int res = EC_SUCCESS;
+ int offset = CONFIG_FW_RW_OFF;
+ int size = CONFIG_FW_RW_SIZE;
+
+ /* unlock CR if needed */
+ if (STM32_FLASH_CR & CR_LOCK) {
+ STM32_FLASH_KEYR = KEY1;
+ STM32_FLASH_KEYR = KEY2;
+ }
+
+ /* Clear previous error status */
+ STM32_FLASH_SR = 0x34;
+ /* set PER bit */
+ STM32_FLASH_CR |= PER;
+
+ for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE,
+ offset += CONFIG_FLASH_ERASE_SIZE) {
+ int i;
+ /* select page to erase */
+ STM32_FLASH_AR = CONFIG_FLASH_BASE + offset;
+ /* set STRT bit : start erase */
+ STM32_FLASH_CR |= STRT;
+
+
+ /* Wait for erase to complete */
+ for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
+ i++)
+ ;
+ if (i == FLASH_TIMEOUT_LOOP) {
+ res = EC_ERROR_TIMEOUT;
+ goto exit_er;
+ }
+
+ /*
+ * Check for error conditions - erase failed, voltage error,
+ * protection error
+ */
+ if (STM32_FLASH_SR & 0x14) {
+ res = EC_ERROR_UNKNOWN;
+ goto exit_er;
+ }
+ }
+
+exit_er:
+ STM32_FLASH_CR &= ~PER;
+ STM32_FLASH_CR = CR_LOCK;
+
+ return res;
+}
+
+static struct sha1_ctx ctx;
+uint8_t *flash_hash_rw(void)
+{
+ sha1_init(&ctx);
+ sha1_update(&ctx, (void *)CONFIG_FLASH_BASE + CONFIG_FW_RW_OFF,
+ CONFIG_FW_RW_SIZE - 32);
+ return sha1_final(&ctx);
+}
diff --git a/board/zinger/runtime.c b/board/zinger/runtime.c
index 9393c8fe50..0598bd93fd 100644
--- a/board/zinger/runtime.c
+++ b/board/zinger/runtime.c
@@ -91,6 +91,17 @@ uint32_t task_wait_event(int timeout_us)
return evt;
}
+void cpu_reset(void)
+{
+ /* Disable interrupts */
+ asm volatile("cpsid i");
+ /* reboot the CPU */
+ CPU_NVIC_APINT = 0x05fa0004;
+ /* Spin and wait for reboot; should never return */
+ while (1)
+ ;
+}
+
/* --- stubs --- */
void __hw_timer_enable_clock(int n, int enable)
{ /* Done in hardware init */ }
diff --git a/board/zinger/usb_pd_policy.c b/board/zinger/usb_pd_policy.c
index 1e416f51a0..c55f33561c 100644
--- a/board/zinger/usb_pd_policy.c
+++ b/board/zinger/usb_pd_policy.c
@@ -10,9 +10,11 @@
#include "debug.h"
#include "hooks.h"
#include "registers.h"
+#include "sha1.h"
#include "timer.h"
#include "util.h"
#include "usb_pd.h"
+#include "version.h"
/* ------------------------- Power supply control ------------------------ */
@@ -205,3 +207,59 @@ int pd_board_checks(void)
return EC_SUCCESS;
}
+
+/* ----------------- Vendor Defined Messages ------------------ */
+int pd_custom_vdm(void *ctxt, int cnt, uint32_t *payload, uint32_t **rpayload)
+{
+ static int flash_offset;
+ void *hash;
+ int cmd = PD_VDO_CMD(payload[0]);
+ int rsize = 1;
+ debug_printf("%T] VDM/%d [%d] %08x\n", cnt, cmd, payload[0]);
+
+ *rpayload = payload;
+ switch (cmd) {
+ case VDO_CMD_VERSION:
+ memcpy(payload + 1, &version_data.version, 24);
+ rsize = 7;
+ break;
+ case VDO_CMD_REBOOT:
+ /* ensure the power supply is in a safe state */
+ pd_power_supply_reset();
+ cpu_reset();
+ break;
+ case VDO_CMD_RW_HASH:
+ hash = flash_hash_rw();
+ memcpy(payload + 1, hash, SHA1_DIGEST_SIZE);
+ rsize = 6;
+ break;
+ case VDO_CMD_FLASH_ERASE:
+ /* do not kill the code under our feet */
+ if (!is_ro_mode())
+ break;
+ flash_offset = 0;
+ flash_erase_rw();
+ break;
+ case VDO_CMD_FLASH_WRITE:
+ /* do not kill the code under our feet */
+ if (!is_ro_mode())
+ break;
+ flash_write_rw(flash_offset, 4*(cnt - 1),
+ (const char *)(payload+1));
+ flash_offset += 4*(cnt - 1);
+ break;
+ case VDO_CMD_FLASH_HASH:
+ /* this is not touching the code area */
+ flash_write_rw(CONFIG_FW_RW_SIZE - 32, 4*cnt,
+ (const char *)(payload+1));
+ break;
+ default:
+ /* Unknown : do not answer */
+ return 0;
+ }
+ debug_printf("%T] DONE\n");
+ /* respond (positively) to the request */
+ payload[0] |= VDO_SRC_RESPONDER;
+
+ return rsize;
+}
diff --git a/common/usb_pd_protocol.c b/common/usb_pd_protocol.c
index d9eb6259d6..0fc93539c0 100644
--- a/common/usb_pd_protocol.c
+++ b/common/usb_pd_protocol.c
@@ -395,8 +395,23 @@ static int send_bist(void *ctxt)
static void handle_vdm_request(void *ctxt, int cnt, uint32_t *payload)
{
- CPRINTF("[%T PD Unhandled VDM VID %04x CMD %04x]\n", payload[0] >> 16,
- payload[0] & 0xFFFF);
+ uint16_t vid = PD_VDO_VID(payload[0]);
+#ifdef CONFIG_USB_PD_CUSTOM_VDM
+ int rlen;
+ uint32_t *rdata;
+
+ if (vid == USB_VID_GOOGLE) {
+ rlen = pd_custom_vdm(ctxt, cnt, payload, &rdata);
+ if (rlen > 0) {
+ uint16_t header = PD_HEADER(PD_DATA_VENDOR_DEF,
+ pd_role, pd_message_id, rlen);
+ send_validate_message(ctxt, header, rlen, rdata);
+ }
+ return;
+ }
+#endif
+ CPRINTF("[%T PD Unhandled VDM VID %04x CMD %04x]\n",
+ vid, payload[0] & 0xFFFF);
}
static void handle_data_request(void *ctxt, uint16_t head, uint32_t *payload)
diff --git a/include/usb_pd.h b/include/usb_pd.h
index 659fc5cd54..56c669373d 100644
--- a/include/usb_pd.h
+++ b/include/usb_pd.h
@@ -104,6 +104,34 @@ enum pd_errors {
/* VDO : Vendor Defined Message Object */
#define VDO(vid, custom) (((vid) << 16) | ((custom) & 0xFFFF))
+#define VDO_ACK (0 << 6)
+#define VDO_NAK (1 << 6)
+#define VDO_PENDING (2 << 6)
+
+#define VDO_SRC_INITIATOR (0 << 5)
+#define VDO_SRC_RESPONDER (1 << 5)
+
+#define VDO_CMD_DISCOVER_VID (1 << 0)
+#define VDO_CMD_DISCOVER_ALT (2 << 0)
+#define VDO_CMD_AUTHENTICATE (3 << 0)
+#define VDO_CMD_ENTER_ALT (4 << 0)
+#define VDO_CMD_EXIT_ALT (5 << 0)
+#define VDO_CMD_VENDOR(x) (((10 + (x)) & 0x1f))
+
+/* ChromeOS specific commands */
+#define VDO_CMD_VERSION VDO_CMD_VENDOR(0)
+#define VDO_CMD_RW_HASH VDO_CMD_VENDOR(2)
+#define VDO_CMD_REBOOT VDO_CMD_VENDOR(5)
+#define VDO_CMD_FLASH_ERASE VDO_CMD_VENDOR(6)
+#define VDO_CMD_FLASH_WRITE VDO_CMD_VENDOR(7)
+#define VDO_CMD_FLASH_HASH VDO_CMD_VENDOR(8)
+
+#define PD_VDO_VID(vdo) ((vdo) >> 16)
+#define PD_VDO_CMD(vdo) ((vdo) & 0x1f)
+
+/* USB Vendor ID assigned to Google Inc. */
+#define USB_VID_GOOGLE 0x18d1
+
/* --- Policy layer functions --- */
/**
@@ -157,6 +185,17 @@ void pd_request_source_voltage(int mv);
*/
int pd_board_checks(void);
+/*
+ * Handle Vendor Defined Message with our vendor ID.
+ *
+ * @param ctxt opaque context for PD communication.
+ * @param cnt number of data objects in the payload.
+ * @param payload payload data.
+ * @param rpayload pointer to the data to send back.
+ * @return if >0, number of VDOs to send back.
+ */
+int pd_custom_vdm(void *ctxt, int cnt, uint32_t *payload, uint32_t **rpayload);
+
/* Power Data Objects for the source and the sink */
extern const uint32_t pd_src_pdo[];
extern const int pd_src_pdo_cnt;