diff options
-rw-r--r-- | board/zinger/board.c | 59 | ||||
-rw-r--r-- | board/zinger/board.h | 11 | ||||
-rw-r--r-- | board/zinger/hardware.c | 150 | ||||
-rw-r--r-- | board/zinger/runtime.c | 11 | ||||
-rw-r--r-- | board/zinger/usb_pd_policy.c | 58 | ||||
-rw-r--r-- | common/usb_pd_protocol.c | 19 | ||||
-rw-r--r-- | include/usb_pd.h | 39 |
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; |