strip out dfu

This commit is contained in:
Matthew Kennedy 2023-08-25 22:34:36 -07:00 committed by rusefillc
parent 2fa2f2d522
commit cf82019b91
4 changed files with 5 additions and 419 deletions

View File

@ -193,11 +193,11 @@ CPPSRC = $(ALLCPPSRC) \
$(PROJECT_DIR)/controllers/algo/engine_configuration.cpp \
$(PROJECT_DIR)/controllers/persistent_store.cpp \
$(PROJECT_DIR)/hw_layer/io_pins.cpp \
$(PROJECT_DIR)/hw_layer/serial_over_usb/usbcfg.cpp \
$(PROJECT_DIR)/util/efilib.cpp \
$(PROJECT_DIR)/hw_layer/pin_repository.cpp \
$(RUSEFI_LIB_CPP) \
src/rusefi_stubs.cpp \
src/dfu.cpp \
src/main.cpp
# C sources to be compiled in ARM mode regardless of the global setting.

View File

@ -1,319 +0,0 @@
#include "pch.h"
#include "hardware.h"
#include "flash_int.h"
#include "dfu.h"
// Communication vars
static UartTsChannel blTsChannel(TS_PRIMARY_UxART_PORT);
static uint8_t buffer[DFU_BUFFER_SIZE];
// Use short timeout for the first data packet, and normal timeout for the rest
static int sr5Timeout = DFU_SR5_TIMEOUT_FIRST;
// This big buffer is used for temporary storing of the bootloader flash page
static uint8_t bootloaderVirtualPageBuffer[BOOTLOADER_SIZE];
// needed by DFU protocol (DFU_DEVICE_ID_CMD)
static uint32_t getMcuRevision() {
// =0x413 for F407
// =0x419 for F42xxx and F43xxx
// =0x434 for F469
return DBGMCU->IDCODE & MCU_REVISION_MASK;
}
static bool getByte(uint8_t *b) {
return blTsChannel.readTimeout(b, 1, sr5Timeout) == 1;
}
static void sendByte(uint8_t b) {
blTsChannel.write(&b, 1, true);
}
static uint8_t dfuCalcChecksum(const uint8_t *buf, uint8_t size) {
uint8_t checksum = buf[0];
for (uint8_t i = 1; i < size; i++) {
checksum ^= buf[i];
}
return checksum;
}
// Used to detect writing of the current flash sector
static bool isBootloaderAddress(uint32_t addr) {
return addr >= BOOTLOADER_ADDR && addr < (BOOTLOADER_ADDR + BOOTLOADER_SIZE);
}
static bool isInVirtualPageBuffer(uint32_t addr) {
return addr >= (uint32_t)bootloaderVirtualPageBuffer && addr < (uint32_t)bootloaderVirtualPageBuffer + sizeof(bootloaderVirtualPageBuffer);
}
// Read 32-bit address and 8-bit checksum.
// Returns true if all 5 bytes are received and checksum is correct, and false otherwise.
static bool readAddress(uint32_t *addr) {
uint8_t buf[5]; // 4 bytes+checksum
if (blTsChannel.readTimeout(buf, 5, sr5Timeout) != 5)
return false;
if (dfuCalcChecksum(buf, 4) != buf[4])
return false;
*addr = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
// for bootloader flash, return a virtual buffer instead
if (isBootloaderAddress(*addr)) {
*addr = (uint32_t)bootloaderVirtualPageBuffer + (*addr - BOOTLOADER_ADDR);
}
return true;
}
// needed by DFU protocol to validate received bytes
static uint8_t complementByte(uint8_t c) {
return c ^ 0xff;
}
static uint16_t bufToInt16(uint8_t *buf) {
return (buf[0] << 8) | buf[1];
}
static void prepareInterruptsForJump() {
#ifdef STM32F4
// interrupt control
SCB->ICSR &= ~SCB_ICSR_PENDSVSET_Msk;
// set interrupt vectors
for(int i = 0; i < 8; i++)
NVIC->ICER[i] = NVIC->IABR[i];
__set_CONTROL(0);
#else
// todo: add support for other MCUs
#error "Unsupported MCU configuration!"
#endif
}
// some weird STM32 magic...
void dfuJumpToApp(uint32_t addr) {
typedef void (*pFunction)(void);
// goodbye ChibiOS, we're leaving...
chSysDisable();
// get jump addr
uint32_t jumpAddress = *((uint32_t *)(addr + 4));
pFunction jump = (pFunction) jumpAddress;
prepareInterruptsForJump();
// set stack pointer
__set_MSP(*(uint32_t *)addr);
// call
jump();
// we shouldn't get here
chSysHalt("dfuJumpToApp FAIL");
}
static void dfuHandleGetList() {
static const uint8_t cmdsInfo[] = { DFU_VERSION_NUMBER, DFU_GET_LIST_CMD, DFU_DEVICE_ID_CMD, DFU_READ_CMD, DFU_GO_CMD,
DFU_WRITE_CMD, DFU_ERASE_CMD };
size_t numBytes = sizeof(cmdsInfo);
sendByte(numBytes - 1); // number of commands
for (size_t i = 0; i < numBytes; i++)
sendByte(cmdsInfo[i]);
sendByte(DFU_ACK_BYTE);
}
static void dfuHandleDeviceId() {
uint32_t mcuRev = getMcuRevision();
sendByte(0x01); // the number of bytes to be send - 1
// send 12 bit MCU revision
sendByte((uint8_t)((mcuRev >> 8) & 0xf));
sendByte((uint8_t)(mcuRev & 0xff));
sendByte(DFU_ACK_BYTE);
}
static void dfuHandleGo() {
uint32_t addr;
if (!readAddress(&addr)) {
sendByte(DFU_NACK_BYTE);
return;
}
// todo: check if the address is valid
sendByte(DFU_ACK_BYTE);
dfuJumpToApp(addr);
}
static void dfuHandleRead() {
uint32_t addr;
if (!readAddress(&addr)) {
sendByte(DFU_NACK_BYTE);
return;
}
sendByte(DFU_ACK_BYTE);
uint8_t byte, complement;
if (!getByte(&byte))
return;
if (!getByte(&complement))
return;
// check if we have a correct byte received
if (complement != complementByte(byte)) {
sendByte(DFU_NACK_BYTE);
return;
}
int numBytes = (int)byte + 1;
sendByte(DFU_ACK_BYTE);
// read flash or virtual RAM buffer (don't transmit directly from flash)
if (isInVirtualPageBuffer(addr))
memcpy(buffer, (uint8_t *)addr, numBytes);
else
intFlashRead(addr, (char *)buffer, numBytes);
// transmit data
blTsChannel.write(buffer, numBytes, true);
}
static void dfuHandleWrite() {
uint32_t addr;
if (!readAddress(&addr)) {
sendByte(DFU_NACK_BYTE);
return;
}
sendByte(DFU_ACK_BYTE);
if (!getByte(buffer))
return;
int numBytes = buffer[0] + 1;
int numBytesAndChecksum = numBytes + 1; // +1 byte of checkSum
// receive data
if (blTsChannel.readTimeout(buffer + 1, numBytesAndChecksum, sr5Timeout) != numBytesAndChecksum)
return;
// don't write corrupted data!
if (dfuCalcChecksum(buffer, numBytesAndChecksum) != buffer[numBytesAndChecksum]) {
sendByte(DFU_NACK_BYTE);
return;
}
// now write to flash (or to the virtual RAM buffer)
if (isInVirtualPageBuffer(addr))
memcpy((uint8_t *)addr, (buffer + 1), numBytes);
else
intFlashWrite(addr, (const char *)(buffer + 1), numBytes);
// we're done!
sendByte(DFU_ACK_BYTE);
}
static void dfuHandleErase() {
int numSectors;
if (!getByte(buffer))
return;
if (!getByte(buffer + 1))
return;
numSectors = bufToInt16(buffer);
int numSectorData;
if (numSectors == 0xffff) // erase all chip
numSectorData = 1;
else
numSectorData = (numSectors + 1) * 2 + 1;
uint8_t *sectorList = buffer + 2;
// read sector data & checksum
if (blTsChannel.readTimeout(sectorList, numSectorData, sr5Timeout) != numSectorData)
return;
// verify checksum
if (dfuCalcChecksum(buffer, 2 + numSectorData - 1) != buffer[2 + numSectorData - 1]) {
sendByte(DFU_NACK_BYTE);
return;
}
// Erase the chosen sectors, sector by sector
for (int i = 0; i < numSectorData - 1; i += 2) {
int sectorIdx = bufToInt16(sectorList + i);
if (sectorIdx < BOOTLOADER_NUM_SECTORS) { // skip first sectors where our bootloader is
// imitate flash erase by writing '0xff'
memset(bootloaderVirtualPageBuffer, 0xff, BOOTLOADER_SIZE);
continue;
}
// erase sector
intFlashSectorErase(sectorIdx);
}
sendByte(DFU_ACK_BYTE);
}
bool dfuStartLoop(void) {
bool wasCommand = false;
uint8_t command, complement;
sr5Timeout = DFU_SR5_TIMEOUT_FIRST;
// We cannot afford waiting for the first handshake byte, so we have to send an answer in advance!
sendByte(DFU_ACK_BYTE);
// Fill the temporary buffer from the real flash memory
memcpy(bootloaderVirtualPageBuffer, (void *)BOOTLOADER_ADDR, BOOTLOADER_SIZE);
while (true) {
// read command & complement bytes
if (!getByte(&command)) {
// timeout, but wait more if we're in bootloader mode
if (wasCommand)
continue;
// exit if no data was received
break;
}
if (!getByte(&complement)) {
if (wasCommand) {
// something is wrong, but keep the connection
sendByte(DFU_NACK_BYTE);
continue;
}
break;
}
// check if we have a correct command received
if (complement != complementByte(command)) {
sendByte(DFU_NACK_BYTE);
continue;
}
// confirm that we've got the command
sendByte(DFU_ACK_BYTE);
wasCommand = true;
// set normal (longer) timeout, we're not in a hurry anymore
sr5Timeout = DFU_SR5_TIMEOUT_NORMAL;
// now execute it (see ST appnote "AN3155")
switch (command) {
case DFU_UART_CHECK:
break;
case DFU_GET_LIST_CMD:
dfuHandleGetList();
break;
case DFU_DEVICE_ID_CMD:
dfuHandleDeviceId();
break;
case DFU_GO_CMD:
dfuHandleGo();
break;
case DFU_READ_CMD:
dfuHandleRead();
break;
case DFU_WRITE_CMD:
dfuHandleWrite();
break;
case DFU_ERASE_CMD:
dfuHandleErase();
break;
default:
break;
} /* End switch */
}
return wasCommand;
}
SerialTsChannelBase *getTsChannel() {
return &blTsChannel;
}

View File

@ -1,51 +0,0 @@
#pragma once
#include "tunerstudio_io.h"
// This is where the bootloader starts
#define BOOTLOADER_ADDR 0x08000000
// Bootloader code max. size, in bytes
#define BOOTLOADER_SIZE 0x8000
// Number of sectors for the bootloader
#define BOOTLOADER_NUM_SECTORS (BOOTLOADER_SIZE/0x4000)
// This is where the application starts
#define APPLICATION_ADDR 0x08008000
#define DFU_BUFFER_SIZE 258 // Max. 256 bytes at a time plus 2 bytes (numBytes+checksum)
// DFU/USART Protocol is described in AN3155 document "Application note. USART protocol used in the STM32 bootloader"
// http://www.st.com/resource/en/application_note/cd00264342.pdf
#define DFU_UART_CHECK 0x7F // "UART handshake" escape byte
#define DFU_GET_LIST_CMD 0x00 // "Get supported commands list" command
#define DFU_DEVICE_ID_CMD 0x02 // "Get device ID" command
#define DFU_READ_CMD 0x11 // "Read memory" command
#define DFU_GO_CMD 0x21 // "Go" command
#define DFU_WRITE_CMD 0x31 // "Write memory" command
#define DFU_ERASE_CMD 0x44 // "Erase memory" command
#define DFU_VERSION_NUMBER 0x31 // The DFU protocol version number
#define DFU_ACK_BYTE 0x79 // Acknowledge byte ID
#define DFU_NACK_BYTE 0x1F // Not-Acknowledge byte ID
#define DFU_SR5_TIMEOUT_FIRST TIME_MS2I(200)
#define DFU_SR5_TIMEOUT_NORMAL TIME_MS2I(1000)
#define MCU_REVISION_MASK 0xfff // MCU Revision ID is needed by DFU protocol
// The address in MCU system memory where the bootloader version number is stored (2 bytes)
#define DFU_BOOTLOADER_VERSION_ADDRESS 0x1FFF76DE
/**
* @brief This function waits for the command to apply (write, read etc...)
*/
bool dfuStartLoop(void);
/**
* @brief Jump to the application
*/
void dfuJumpToApp(uint32_t addr);
SerialTsChannelBase* getTsChannel();

View File

@ -3,38 +3,10 @@
#include "hardware.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#include <ch.h>
#include <hal.h>
#include <stm32f4xx.h>
#ifdef __cplusplus
}
#endif /* __cplusplus */
#include "dfu.h"
static bool wasCommand = false;
static THD_WORKING_AREA(waBootloaderSerial, 128);
static THD_FUNCTION(thBootloaderSerial, arg) {
(void)arg;
chRegSetThreadName("BootloaderSerial");
// start our DFU emulator
wasCommand = dfuStartLoop();
chThdExit(MSG_OK);
}
int main(void) {
// run ChibiOS
halInit();
chSysInit();
// set base pin configuration based on the board
setDefaultBasePins();
/* at the moment SD card is not needed by bootloader
@ -42,23 +14,7 @@ int main(void) {
setDefaultSdCardParameters();
*/
// start UART
getTsChannel()->start(38400); // TODO: should bootloader serial speed be configurable?
// start a serial port reader thread
thread_t *thrSerial = chThdCreateStatic(waBootloaderSerial, sizeof(waBootloaderSerial), NORMALPRIO, thBootloaderSerial, NULL);
// wait for the thread to finish
chThdWait(thrSerial);
#if 0
if (wasCommand) // abnormal termination of the bootloader thread
chSysHalt("Bootloader DFU FAIL");
#endif
// Run application
dfuJumpToApp(APPLICATION_ADDR);
return 0;
while (true) {
chThdSleepMilliseconds(1);
}
}