mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-09 00:51:29 +00:00
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>
This commit is contained in:
committed by
chrome-internal-fetch
parent
00cf957832
commit
5fd3341077
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 */ }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user