write soft flasher
parent
a4191a553a
commit
9a1c1b692f
|
@ -1,5 +1,8 @@
|
|||
#define BOOTSTUB
|
||||
|
||||
#include "config.h"
|
||||
#include "obj/gitversion.h"
|
||||
|
||||
#ifdef STM32F4
|
||||
#define PANDA
|
||||
#include "stm32f4xx.h"
|
||||
|
@ -14,6 +17,7 @@
|
|||
|
||||
#include "drivers/drivers.h"
|
||||
#include "drivers/spi.h"
|
||||
#include "drivers/usb.h"
|
||||
|
||||
#include "crypto/rsa.h"
|
||||
#include "crypto/sha.h"
|
||||
|
@ -22,35 +26,30 @@
|
|||
|
||||
#include "spi_flasher.h"
|
||||
|
||||
int puts(const char *a) { return 0; }
|
||||
void puth(unsigned int i) {}
|
||||
|
||||
void __initialize_hardware_early() {
|
||||
early();
|
||||
|
||||
if (is_entering_bootmode) {
|
||||
spi_flasher();
|
||||
}
|
||||
}
|
||||
|
||||
void fail() {
|
||||
#ifdef PANDA
|
||||
// detect usb host
|
||||
int no_usb = detect_with_pull(GPIOA, 11, PULL_UP);
|
||||
|
||||
if (no_usb) {
|
||||
// no usb host, go to SPI flasher
|
||||
spi_flasher();
|
||||
}
|
||||
#else
|
||||
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC;
|
||||
NVIC_SystemReset();
|
||||
#endif
|
||||
soft_flasher_start();
|
||||
}
|
||||
|
||||
// know where to sig check
|
||||
extern void *_app_start[];
|
||||
|
||||
int main() {
|
||||
__disable_irq();
|
||||
clock_init();
|
||||
|
||||
if (enter_bootloader_mode == ENTER_SOFTLOADER_MAGIC) {
|
||||
enter_bootloader_mode = 0;
|
||||
soft_flasher_start();
|
||||
}
|
||||
|
||||
// validate length
|
||||
int len = (int)_app_start[0];
|
||||
if ((len < 8) || (((uint32_t)&_app_start[0] + RSANUMBYTES) >= 0x8100000)) fail();
|
||||
|
|
|
@ -38,6 +38,8 @@ recover: obj/bootstub.$(PROJ_NAME).bin obj/$(PROJ_NAME).bin
|
|||
ota: obj/$(PROJ_NAME).bin
|
||||
curl http://192.168.0.10/stupdate --upload-file $<
|
||||
|
||||
bin: obj/$(PROJ_NAME).bin
|
||||
|
||||
ifneq ($(wildcard ../.git/HEAD),)
|
||||
obj/gitversion.h: ../.git/HEAD ../.git/index
|
||||
echo "const uint8_t gitversion[] = \"$(shell git rev-parse HEAD)\";" > $@
|
||||
|
|
|
@ -13,8 +13,14 @@
|
|||
#endif
|
||||
|
||||
#define USB_VID 0xbbaa
|
||||
#define USB_PID 0xddcc
|
||||
|
||||
#ifdef BOOTSTUB
|
||||
#define USB_PID 0xddee
|
||||
#else
|
||||
#define USB_PID 0xddcc
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#define NULL ((void*)0)
|
||||
#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;}
|
||||
|
||||
|
@ -28,4 +34,7 @@
|
|||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; })
|
||||
|
||||
#define MAX_RESP_LEN 0x40
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ USB_OTG_GlobalTypeDef *USBx = USB_OTG_FS;
|
|||
|
||||
#define USB_OTG_SPEED_FULL 3
|
||||
|
||||
#define MAX_RESP_LEN 0x40
|
||||
uint8_t resp[MAX_RESP_LEN];
|
||||
|
||||
// descriptor types
|
||||
|
|
|
@ -369,6 +369,7 @@ void gpio_init() {
|
|||
// ********************* early bringup *********************
|
||||
|
||||
#define ENTER_BOOTLOADER_MAGIC 0xdeadbeef
|
||||
#define ENTER_SOFTLOADER_MAGIC 0xdeadc0de
|
||||
#define POST_BOOTLOADER_MAGIC 0xdeadb111
|
||||
|
||||
extern void *g_pfnVectors;
|
||||
|
@ -406,6 +407,9 @@ void early() {
|
|||
|
||||
// early GPIOs float everything
|
||||
RCC->AHB1ENR = RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;
|
||||
RCC->APB2ENR = 0;
|
||||
RCC->AHB2ENR = 0;
|
||||
|
||||
GPIOA->MODER = 0; GPIOB->MODER = 0; GPIOC->MODER = 0;
|
||||
GPIOA->ODR = 0; GPIOB->ODR = 0; GPIOC->ODR = 0;
|
||||
GPIOA->PUPDR = 0; GPIOB->PUPDR = 0; GPIOC->PUPDR = 0;
|
||||
|
@ -429,5 +433,9 @@ void early() {
|
|||
|
||||
jump_to_bootloader();
|
||||
}
|
||||
|
||||
if (is_entering_bootmode) {
|
||||
enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
18
board/main.c
18
board/main.c
|
@ -1,8 +1,4 @@
|
|||
#include "config.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
// *** end config ***
|
||||
|
||||
#include "obj/gitversion.h"
|
||||
|
||||
// ********************* includes *********************
|
||||
|
@ -178,9 +174,17 @@ int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, int hardwired) {
|
|||
case 0xd1:
|
||||
// this allows reflashing of the bootstub
|
||||
// so it's blocked over wifi
|
||||
if (hardwired) {
|
||||
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC;
|
||||
NVIC_SystemReset();
|
||||
switch (setup->b.wValue.w) {
|
||||
case 0:
|
||||
if (hardwired) {
|
||||
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC;
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC;
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// **** 0xd2: get health packet
|
||||
|
|
|
@ -1,100 +1,152 @@
|
|||
// can't go on the stack cause it's DMAed
|
||||
uint8_t spi_tx_buf[0x44];
|
||||
|
||||
uint32_t *prog_ptr;
|
||||
// flasher state variables
|
||||
uint32_t *prog_ptr = NULL;
|
||||
int unlocked = 0;
|
||||
|
||||
int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) {
|
||||
// get serial number even in bootstub mode
|
||||
if (memcmp("\x00\x00\x00\x00\x40\xD0\x00\x00\x00\x00\x20\x00", data, 0xC) == 0) {
|
||||
memcpy(data_out, (void *)0x1fff79e0, 0x20);
|
||||
return 0x20;
|
||||
}
|
||||
int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, int hardwired) {
|
||||
int resp_len = 0;
|
||||
|
||||
// flasher mode
|
||||
if (data[0] == (0xff^data[1]) &&
|
||||
data[2] == (0xff^data[3])) {
|
||||
int sec;
|
||||
memset(data_out, 0, 4);
|
||||
memcpy(data_out+4, "\xde\xad\xd0\x0d", 4);
|
||||
data_out[0] = 0xff;
|
||||
data_out[2] = data[0];
|
||||
data_out[3] = data[1];
|
||||
switch (data[0]) {
|
||||
case 0xf:
|
||||
// echo
|
||||
data_out[1] = 0xff;
|
||||
break;
|
||||
case 0x10:
|
||||
// unlock flash
|
||||
if (FLASH->CR & FLASH_CR_LOCK) {
|
||||
FLASH->KEYR = 0x45670123;
|
||||
FLASH->KEYR = 0xCDEF89AB;
|
||||
data_out[1] = 0xff;
|
||||
}
|
||||
set_led(LED_GREEN, 1);
|
||||
unlocked = 1;
|
||||
prog_ptr = (uint32_t *)0x8004000;
|
||||
break;
|
||||
case 0x11:
|
||||
// erase
|
||||
sec = data[2] & 0xF;
|
||||
// don't erase the bootloader
|
||||
if (sec != 0 && sec < 12 && unlocked) {
|
||||
FLASH->CR = (sec << 3) | FLASH_CR_SER;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
data_out[1] = 0xff;
|
||||
}
|
||||
break;
|
||||
case 0x12:
|
||||
if (data[2] <= 4 && unlocked) {
|
||||
set_led(LED_RED, 0);
|
||||
for (int i = 0; i < data[2]; i++) {
|
||||
// program byte 1
|
||||
FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG;
|
||||
// flasher machine
|
||||
memset(resp, 0, 4);
|
||||
memcpy(resp+4, "\xde\xad\xd0\x0d", 4);
|
||||
resp[0] = 0xff;
|
||||
resp[2] = setup->b.bRequest;
|
||||
resp[3] = ~setup->b.bRequest;
|
||||
*((uint32_t **)&resp[8]) = prog_ptr;
|
||||
resp_len = 0xc;
|
||||
|
||||
*prog_ptr = *(uint32_t*)(data+4+(i*4));
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
|
||||
//*(uint64_t*)(&spi_tx_buf[0x30+(i*4)]) = *prog_ptr;
|
||||
prog_ptr++;
|
||||
}
|
||||
set_led(LED_RED, 1);
|
||||
data_out[1] = 0xff;
|
||||
int sec;
|
||||
switch (setup->b.bRequest) {
|
||||
// **** 0xb0: flasher echo
|
||||
case 0xb0:
|
||||
resp[1] = 0xff;
|
||||
break;
|
||||
// **** 0xb1: unlock flash
|
||||
case 0xb1:
|
||||
if (FLASH->CR & FLASH_CR_LOCK) {
|
||||
FLASH->KEYR = 0x45670123;
|
||||
FLASH->KEYR = 0xCDEF89AB;
|
||||
resp[1] = 0xff;
|
||||
}
|
||||
set_led(LED_GREEN, 1);
|
||||
unlocked = 1;
|
||||
prog_ptr = (uint32_t *)0x8004000;
|
||||
break;
|
||||
// **** 0xb2: erase sector
|
||||
case 0xb2:
|
||||
sec = setup->b.wValue.w;
|
||||
// don't erase the bootloader
|
||||
if (sec != 0 && sec < 12 && unlocked) {
|
||||
FLASH->CR = (sec << 3) | FLASH_CR_SER;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
resp[1] = 0xff;
|
||||
}
|
||||
break;
|
||||
// **** 0xd0: fetch serial number
|
||||
case 0xd0:
|
||||
#ifdef PANDA
|
||||
// addresses are OTP
|
||||
if (setup->b.wValue.w == 1) {
|
||||
memcpy(resp, (void *)0x1fff79c0, 0x10);
|
||||
resp_len = 0x10;
|
||||
} else {
|
||||
memcpy(resp, (void *)0x1fff79e0, 0x20);
|
||||
resp_len = 0x20;
|
||||
}
|
||||
break;
|
||||
case 0x13:
|
||||
// reset
|
||||
#endif
|
||||
break;
|
||||
// **** 0xd1: enter bootloader mode
|
||||
case 0xd1:
|
||||
// this allows reflashing of the bootstub
|
||||
// so it's blocked over wifi
|
||||
if (hardwired) {
|
||||
enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC;
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// **** 0xd6: get version
|
||||
case 0xd6:
|
||||
COMPILE_TIME_ASSERT(sizeof(gitversion) <= MAX_RESP_LEN)
|
||||
memcpy(resp, gitversion, sizeof(gitversion));
|
||||
resp_len = sizeof(gitversion);
|
||||
break;
|
||||
// **** 0xd8: reset ST
|
||||
case 0xd8:
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
}
|
||||
|
||||
return 8;
|
||||
return resp_len;
|
||||
}
|
||||
|
||||
void spi_flasher() {
|
||||
__disable_irq();
|
||||
|
||||
int usb_cb_ep1_in(uint8_t *usbdata, int len, int hardwired) { return 0; }
|
||||
void usb_cb_ep3_out(uint8_t *usbdata, int len, int hardwired) { }
|
||||
void usb_cb_enumeration_complete() { }
|
||||
|
||||
void usb_cb_ep2_out(uint8_t *usbdata, int len, int hardwired) {
|
||||
set_led(LED_RED, 0);
|
||||
for (int i = 0; i < len/4; i++) {
|
||||
// program byte 1
|
||||
FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG;
|
||||
|
||||
*prog_ptr = *(uint32_t*)(usbdata+(i*4));
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
|
||||
//*(uint64_t*)(&spi_tx_buf[0x30+(i*4)]) = *prog_ptr;
|
||||
prog_ptr++;
|
||||
}
|
||||
set_led(LED_RED, 1);
|
||||
}
|
||||
|
||||
|
||||
int spi_cb_rx(uint8_t *data, int len, uint8_t *data_out) {
|
||||
int resp_len = 0;
|
||||
switch (data[0]) {
|
||||
case 0:
|
||||
// control transfer
|
||||
resp_len = usb_cb_control_msg((USB_Setup_TypeDef *)(data+4), data_out, 0);
|
||||
break;
|
||||
case 2:
|
||||
// ep 2, flash!
|
||||
usb_cb_ep2_out(data+4, data[2], 0);
|
||||
break;
|
||||
}
|
||||
return resp_len;
|
||||
}
|
||||
|
||||
void soft_flasher_start() {
|
||||
// safe to call twice?
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
|
||||
RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN;
|
||||
|
||||
// setup SPI
|
||||
GPIOA->MODER = GPIO_MODER_MODER4_1 | GPIO_MODER_MODER5_1 |
|
||||
GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1;
|
||||
GPIOA->AFR[0] = GPIO_AF5_SPI1 << (4*4) | GPIO_AF5_SPI1 << (5*4) |
|
||||
GPIO_AF5_SPI1 << (6*4) | GPIO_AF5_SPI1 << (7*4);
|
||||
// A4,A5,A6,A7: setup SPI
|
||||
set_gpio_alternate(GPIOA, 4, GPIO_AF5_SPI1);
|
||||
set_gpio_alternate(GPIOA, 5, GPIO_AF5_SPI1);
|
||||
set_gpio_alternate(GPIOA, 6, GPIO_AF5_SPI1);
|
||||
set_gpio_alternate(GPIOA, 7, GPIO_AF5_SPI1);
|
||||
|
||||
// A11,A12: USB
|
||||
set_gpio_alternate(GPIOA, 11, GPIO_AF10_OTG_FS);
|
||||
set_gpio_alternate(GPIOA, 12, GPIO_AF10_OTG_FS);
|
||||
GPIOA->OSPEEDR = GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12;
|
||||
|
||||
// flasher
|
||||
spi_init();
|
||||
|
||||
// blue LED on for flashing
|
||||
set_led(LED_BLUE, 1);
|
||||
|
||||
// flasher
|
||||
spi_init();
|
||||
// enable USB
|
||||
usb_init();
|
||||
|
||||
__enable_irq();
|
||||
|
||||
while (1) { }
|
||||
while (1) {
|
||||
// blink the blue LED fast
|
||||
set_led(LED_BLUE, 0);
|
||||
delay(500000);
|
||||
set_led(LED_BLUE, 1);
|
||||
delay(500000);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ import time
|
|||
|
||||
__version__ = '0.0.3'
|
||||
|
||||
BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")
|
||||
|
||||
def parse_can_buffer(dat):
|
||||
ret = []
|
||||
for j in range(0, len(dat), 0x10):
|
||||
|
@ -91,7 +93,18 @@ class Panda(object):
|
|||
|
||||
def __init__(self, serial=None, claim=True):
|
||||
self._serial = serial
|
||||
if serial == "WIFI":
|
||||
self._handle = None
|
||||
self.connect(claim)
|
||||
|
||||
def close(self):
|
||||
self._handle.close()
|
||||
self._handle = None
|
||||
|
||||
def connect(self, claim=True):
|
||||
if self._handle != None:
|
||||
self.close()
|
||||
|
||||
if self._serial == "WIFI":
|
||||
self._handle = WifiHandle()
|
||||
print("opening WIFI device")
|
||||
else:
|
||||
|
@ -99,23 +112,67 @@ class Panda(object):
|
|||
|
||||
self._handle = None
|
||||
for device in context.getDeviceList(skip_on_error=True):
|
||||
if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddcc:
|
||||
if serial is None or device.getSerialNumber() == serial:
|
||||
if device.getVendorID() == 0xbbaa and device.getProductID() in [0xddcc, 0xddee]:
|
||||
if self._serial is None or device.getSerialNumber() == self._serial:
|
||||
print("opening device", device.getSerialNumber())
|
||||
self.bootstub = device.getProductID() == 0xddee
|
||||
self.legacy = (device.getbcdDevice() != 0x2300)
|
||||
self._handle = device.open()
|
||||
if claim:
|
||||
self._handle.claimInterface(0)
|
||||
#self._handle.setInterfaceAltSetting(0, 0) #Issue in USB stack
|
||||
break
|
||||
|
||||
assert self._handle != None
|
||||
assert(self._handle != None)
|
||||
|
||||
def reset(self, enter_bootstub=False):
|
||||
# reset
|
||||
try:
|
||||
if enter_bootstub:
|
||||
self._handle.controlWrite(Panda.REQUEST_IN, 0xd1, 1, 0, b'')
|
||||
else:
|
||||
self._handle.controlWrite(Panda.REQUEST_IN, 0xd8, 0, 0, b'')
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(1.0)
|
||||
self.connect()
|
||||
|
||||
def flash(self):
|
||||
if not self.bootstub:
|
||||
self.reset(True)
|
||||
|
||||
assert(self.bootstub)
|
||||
ret = os.system("cd %s && make clean && make -f %s bin" % (os.path.join(BASEDIR, "board"), "Makefile.legacy" if self.legacy else "Makefile"))
|
||||
|
||||
# TODO: build and detect legacy
|
||||
with open(os.path.join(BASEDIR, "board", "obj", "code.bin" if self.legacy else "panda.bin")) as f:
|
||||
dat = f.read()
|
||||
|
||||
fr = self._handle.controlRead(Panda.REQUEST_IN, 0xb0, 0, 0, 0xc)
|
||||
from hexdump import hexdump
|
||||
hexdump(str(fr))
|
||||
|
||||
# unlock flash
|
||||
print("flash: unlocking")
|
||||
self._handle.controlWrite(Panda.REQUEST_IN, 0xb1, 0, 0, b'')
|
||||
|
||||
# erase sectors 1 and 2
|
||||
print("flash: erasing")
|
||||
self._handle.controlWrite(Panda.REQUEST_IN, 0xb2, 1, 0, b'')
|
||||
self._handle.controlWrite(Panda.REQUEST_IN, 0xb2, 2, 0, b'')
|
||||
|
||||
# flash over EP2
|
||||
print("flash: flashing")
|
||||
for i in range(0, len(dat), 0x40):
|
||||
self._handle.bulkWrite(2, dat[i:i+0x40])
|
||||
|
||||
# reset
|
||||
print("flash: resetting")
|
||||
self.reset()
|
||||
|
||||
def close(self):
|
||||
self._handle.close()
|
||||
|
||||
@staticmethod
|
||||
def program(clean=False, legacy=False):
|
||||
BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")
|
||||
# TODO: check for legacy board
|
||||
if clean:
|
||||
cmd = "make clean"
|
||||
|
|
Loading…
Reference in New Issue