[usb] Separate bin file for DFU code, copied in RAM for execution.

Change-Id: I1b2147ecd2f3d4a5c3d7db5e7a07b66258666fca
This commit is contained in:
Léa Saviot
2018-03-20 18:14:27 +01:00
parent 4b4a8307e1
commit e0e26a3607
17 changed files with 194 additions and 158 deletions

View File

@@ -46,8 +46,11 @@ products += $(dependencies)
$(all_objs): $(generated_headers)
epsilon.$(EXE): $(objs)
test.$(EXE): $(objs)
.SECONDARY: $(objs)
%.$(EXE): $(objs)
%.$(EXE):
@echo "LD $@"
$(Q) $(LD) $^ $(LDFLAGS) -o $@

View File

@@ -1,5 +1,6 @@
include ion/src/device/boot/Makefile
include ion/src/device/bench/Makefile
include ion/src/device/usb/Makefile
ion/src/shared/platform_info.o: SFLAGS += -DHEADER_SECTION="__attribute__((section(\".header\")))" -DFORCE_LINK="__attribute__((used))"
@@ -29,31 +30,6 @@ objs += $(addprefix ion/src/device/, \
wakeup.o \
)
usb_objs += $(addprefix ion/src/device/usb/, \
calculator.o \
device.o\
dfu_interface.o\
endpoint0.o \
interface.o\
request_recipient.o\
setup_packet.o\
)
$(usb_objs): SFLAGS += -fPIC
objs += $(usb_objs)
objs += $(addprefix ion/src/device/usb/stack/, \
configuration_descriptor.o \
descriptor.o\
device_descriptor.o\
dfu_functional_descriptor.o\
interface_descriptor.o\
language_id_string_descriptor.o \
streamable.o\
string_descriptor.o\
)
# When using the register.h C++ file in production mode, we expect the compiler
# to completely inline all bit manipulations. For some reason, if we build using
# the -Os optimization flag, GCC doesn't inline everything and and ends up

View File

@@ -0,0 +1,45 @@
/* DFU transfers can serve two purposes:
* - Transfering RAM data between the machine and the host, e.g. Python scripts
* - Upgrading the flash memory to perform a software update
*
* The second case raises a huge issue: code cannot be executed from memory that
* is being modified. We're solving this issue by copying the DFU code in RAM.
*
* This linker script will generate some code that expects to be executed from a
* fixed address in RAM. The corresponding instructions will be embedded in the
* main Epsilon ELF file, and copied to that address before execution.
*
* This address needs to live in RAM, and needs to be temporarily overwriteable
* when the program is being run. Epsilon has a large stack to allow deeply
* recursive code to run. But when doing DFU transfers it is safe to assume we
* will need very little stack space. We're therefore using the topmost 8K of
* the stack reserved by Epsilon.
*
* Last but not least, we'll want to jump to a known entry point when running
* the DFU code (namely, Ion::USB::Device::Calculator::Poll). We're simply
* making sure this is the first symbol output. */
EPSILON_STACK_END = 0x20000000 + 256K - 32K;
MEMORY {
RAM_BUFFER (rw) : ORIGIN = EPSILON_STACK_END, LENGTH = 8K
}
SECTIONS {
.text : {
. = ALIGN(4);
KEEP(*(.text._ZN3Ion3USB6Device10Calculator4PollEv))
*(.text)
*(.text.*)
} >RAM_BUFFER
.rodata : {
*(.rodata)
*(.rodata.*)
} >RAM_BUFFER
.data : {
*(.data)
*(.data.*)
} >RAM_BUFFER
}

View File

@@ -38,16 +38,6 @@ SECTIONS {
KEEP(*(.header))
} >FLASH
.usb : {
. = ALIGN(4);
_flash_usb_stack_start = .;
*(.text.*3Ion3USB6Device*)
*(.text.memcpy)
*(.text.strlen)
*(.text.*Register*)
_flash_usb_stack_end = .;
} >FLASH
.text : {
. = ALIGN(4);
*(.text)

View File

@@ -5,10 +5,6 @@
#include "display.h"
#include "regs/regs.h"
#include <stdlib.h>
#include <new>
extern char _flash_usb_stack_start;
extern char _flash_usb_stack_end;
namespace Ion {
namespace USB {
@@ -17,43 +13,6 @@ bool isPlugged() {
return Device::VbusPin.group().IDR()->get(Device::VbusPin.pin());
}
typedef void (*PollFunctionPointer)(Device::Calculator *);
void DFU() {
size_t usb_stack_size = &_flash_usb_stack_end - &_flash_usb_stack_start;
/* 1 - Allocate a buffer in RAM that's large enough to contain all the code
* and objects of our USB stack. */
size_t ram_buffer_size = usb_stack_size + sizeof(Device::Calculator);
char * ram_buffer = static_cast<char *>(malloc(ram_buffer_size));
if (ram_buffer == nullptr) {
// Allocation failure
return;
}
// 2 - Copy the USB stack code from Flash to RAM
char * ram_usb_stack_start = ram_buffer;
memcpy(ram_usb_stack_start, &_flash_usb_stack_start, usb_stack_size);
// 3 - Initialize data in RAM buffer
Device::Calculator * calculator = new (ram_buffer + usb_stack_size) Device::Calculator();
// 4 - Figure out the address of Ion::USB::Device::poll() in RAM
char * flash_poll_function_address = reinterpret_cast<char *>(&Ion::USB::Device::poll);
assert(flash_poll_function_address >= &_flash_usb_stack_start);
assert(flash_poll_function_address < &_flash_usb_stack_end);
char * ram_poll_function_address = flash_poll_function_address - &_flash_usb_stack_start + ram_usb_stack_start;
PollFunctionPointer ram_poll_function = reinterpret_cast<PollFunctionPointer>(ram_poll_function_address);
// 5 - Execute Ion::USB::Device::poll() from RAM
ram_poll_function(calculator);
// 6 - Upon return, delete data and free the buffer in RAM
calculator->~Calculator();
free(ram_buffer);
}
}
}
@@ -61,16 +20,6 @@ namespace Ion {
namespace USB {
namespace Device {
void poll(Calculator * calculator) {
// Wait for speed enumeration done
while (!OTG.GINTSTS()->getENUMDNE()) {
}
while (true) {
calculator->poll();
}
}
void init() {
initGPIO();
initOTG();

View File

@@ -27,9 +27,6 @@ void shutdownGPIO();
void initOTG();
void shutdownOTG();
// Enters DFU mode, returns when detached.
void poll(Calculator * calculator);
}
}
}

View File

@@ -0,0 +1,48 @@
usb_objs += $(addprefix ion/src/device/usb/, \
calculator.o \
device.o\
dfu_interface.o\
endpoint0.o \
interface.o\
request_recipient.o\
setup_packet.o\
)
usb_objs += $(addprefix ion/src/device/usb/stack/, \
configuration_descriptor.o \
descriptor.o\
device_descriptor.o\
dfu_functional_descriptor.o\
interface_descriptor.o\
language_id_string_descriptor.o \
streamable.o\
string_descriptor.o\
)
ifeq ($(USB_DFU_XIP_FLASH),1)
objs += ion/src/device/usb_dfu_xip_flash.o
objs += $(usb_objs)
else
usb_objs += liba/src/assert.o
usb_objs += liba/src/strlen.o
usb_objs += liba/src/strlcpy.o
usb_objs += liba/src/memset.o
usb_objs += liba/src/memcpy.o
usb_objs += libaxx/src/cxxabi/pure_virtual.o
usb_objs += ion/src/device/usb/boot.o
ion/src/device/usb/dfu.elf: LDFLAGS = --gc-sections -T ion/src/device/boot/dfu.ld
ion/src/device/usb/dfu.elf: $(usb_objs)
ion/src/device/usb/dfu.o: ion/src/device/usb/dfu.bin
$(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata --redefine-sym _binary_ion_src_device_usb_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_ion_src_device_usb_dfu_bin_end=_dfu_bootloader_flash_end $< $@
objs += ion/src/device/usb/dfu.o
objs += ion/src/device/usb_dfu_relocated_ram.o
products += $(usb_objs) $(addprefix ion/src/device/usb/dfu, .elf .bin)
endif

View File

@@ -0,0 +1,2 @@
extern "C" void abort() {
}

View File

@@ -1,9 +1,21 @@
#include "calculator.h"
#include "../regs/regs.h"
namespace Ion {
namespace USB {
namespace Device {
void Calculator::Poll() {
// Wait for speed enumeration done
while (!OTG.GINTSTS()->getENUMDNE()) {
}
Calculator c;
while (true) {
c.poll();
}
}
Descriptor * Calculator::descriptor(uint8_t type, uint8_t index) {
int typeCount = 0;
for (size_t i=0; i<sizeof(m_descriptors)/sizeof(m_descriptors[0]); i++) {

View File

@@ -9,7 +9,7 @@
#include "stack/dfu_functional_descriptor.h"
#include "stack/interface_descriptor.h"
#include "stack/language_id_string_descriptor.h"
#include "stack/relocatable_string_descriptor.h"
#include "stack/string_descriptor.h"
#include <stddef.h>
#include <assert.h>
@@ -19,6 +19,7 @@ namespace Device {
class Calculator : public Device {
public:
static void Poll();
Calculator() :
Device(&m_dfuInterface),
m_deviceDescriptor(
@@ -90,10 +91,10 @@ private:
InterfaceDescriptor m_interfaceDescriptor;
ConfigurationDescriptor m_configurationDescriptor;
LanguageIDStringDescriptor m_languageStringDescriptor;
RelocatableStringDescriptor<9> m_manufacturerStringDescriptor;
RelocatableStringDescriptor<11> m_productStringDescriptor;
RelocatableStringDescriptor<6> m_serialNumberStringDescriptor;
RelocatableStringDescriptor<47> m_interfaceStringDescriptor;
StringDescriptor m_manufacturerStringDescriptor;
StringDescriptor m_productStringDescriptor;
StringDescriptor m_serialNumberStringDescriptor;
StringDescriptor m_interfaceStringDescriptor;
Descriptor * m_descriptors[7]; // We do not need to include m_interfaceDescriptor nor m_dfuFunctionalDescriptor, because they are inluded in other descriptors.

View File

@@ -1,7 +1,6 @@
#include "dfu_interface.h"
#include "../regs/flash.h"
#include <ion.h> //TODO REMOVE
#include <kandinsky.h>//TODO REMOVE
#include <string.h>
namespace Ion {
namespace USB {
@@ -11,26 +10,6 @@ static inline uint32_t min(uint32_t x, uint32_t y) { return (x<y ? x : y); }
//TODO vérifier qu'on ne change pas d'état si on est dans dfuError, sauf en cas de clear status
// DEBUG functions
void whiteScreen() {
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorWhite);
}
void redScreen() {
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorRed);
}
void blueScreen() {
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorBlue);
}
void greenScreen() {
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorGreen);
}
void yellowScreen() {
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorYellow);
}
void blackScreen() {
Ion::Display::pushRectUniform(KDRect(KDPointZero, 320,240), KDColorWhite);
}
void DFUInterface::StatusData::push(Channel * c) const {
c->push(m_bStatus);
c->push(m_bwPollTimeout[2]);
@@ -50,22 +29,16 @@ bool DFUInterface::processSetupInRequest(SetupPacket * request, uint8_t * transf
}
switch (request->bRequest()) {
case (uint8_t) DFURequest::GetStatus:
whiteScreen();
return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
case (uint8_t) DFURequest::ClearStatus:
greenScreen();
return clearStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
case (uint8_t) DFURequest::Abort:
redScreen();
return dfuAbort(transferBufferLength);
case (uint8_t) DFURequest::GetState:
blueScreen();
return getState(transferBuffer, transferBufferLength, request->wValue());
case (uint8_t) DFURequest::Download:
yellowScreen();
return processDownloadRequest(request->wLength(), transferBufferLength);
case (uint8_t) DFURequest::Upload:
blackScreen();
return processUploadRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
}
return false;

View File

@@ -86,9 +86,21 @@ void Endpoint0::readAndDispatchSetupPacket() {
m_request = SetupPacket(m_largeBuffer);
uint16_t maxBufferLength = MIN(m_request.wLength(), k_largeBufferLength);
#if 0
// Requests are only sent to the device or the interface for now.
assert(((uint8_t)m_request.recipientType() == 0) || ((uint8_t)m_request.recipientType() == 1));
m_requestRecipients[(uint8_t)(m_request.recipientType())]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
#else
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
if (type == 0) {
m_requestRecipients[0]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
} else {
for (volatile int i=0;i<10; i++) {
i = i+1;
}
m_requestRecipients[1]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
}
#endif
}
void Endpoint0::processINpacket() {

View File

@@ -1,31 +0,0 @@
#ifndef ION_DEVICE_USB_STACK_RELOCATABLE_STRING_DESCRIPTOR_H
#define ION_DEVICE_USB_STACK_RELOCATABLE_STRING_DESCRIPTOR_H
#include "string_descriptor.h"
#include <string.h>
namespace Ion {
namespace USB {
namespace Device {
template <int T>
class RelocatableStringDescriptor : public StringDescriptor {
public:
RelocatableStringDescriptor(const char * string) :
StringDescriptor()
{
strlcpy(m_string, string, T);
}
protected:
const char * string() const override {
return m_string;
}
private:
char m_string[T];
};
}
}
}
#endif

View File

@@ -7,7 +7,7 @@ namespace Device {
void StringDescriptor::push(Channel * c) const {
Descriptor::push(c);
const char * stringPointer = string();
const char * stringPointer = m_string;
while (*stringPointer != 0) {
uint16_t stringAsUTF16CodePoint = *stringPointer;
c->push(stringAsUTF16CodePoint);
@@ -17,7 +17,7 @@ void StringDescriptor::push(Channel * c) const {
uint8_t StringDescriptor::bLength() const {
// The script is returned in UTF-16, hence the multiplication.
return Descriptor::bLength() + 2*strlen(string());
return Descriptor::bLength() + 2*strlen(m_string);
}
}

View File

@@ -9,11 +9,16 @@ namespace Device {
class StringDescriptor : public Descriptor {
public:
StringDescriptor() : Descriptor(0x03) { }
constexpr StringDescriptor(const char * string) :
Descriptor(0x03),
m_string(string)
{
}
protected:
void push(Channel * c) const override;
virtual uint8_t bLength() const override;
virtual const char * string() const = 0;
private:
const char * m_string;
};
}

View File

@@ -0,0 +1,43 @@
#include <ion/usb.h>
#include "usb.h"
#include "device.h"
#include <stdlib.h>
extern char _dfu_bootloader_flash_start;
extern char _dfu_bootloader_flash_end;
namespace Ion {
namespace USB {
typedef void (*FunctionPointer)();
void DFU() {
//TODO: Explain, in steps
size_t dfu_bootloader_size = &_dfu_bootloader_flash_end - &_dfu_bootloader_flash_start;
char * dfu_bootloader_ram_start = reinterpret_cast<char *>(0x20000000 + 256*1024 - 32*1024);
char * ram_backup_buffer = static_cast<char *>(malloc(dfu_bootloader_size));
if (ram_backup_buffer == nullptr) {
return;
}
memcpy(ram_backup_buffer, dfu_bootloader_ram_start, dfu_bootloader_size);
memcpy(dfu_bootloader_ram_start, &_dfu_bootloader_flash_start, dfu_bootloader_size);
// Cortex-M expects jumps to be made to odd addresses when jumping to Thumb code
// In general this is handled by the compiler, but here we're jumping to an arbitrary address
// TODO: Check this is needed, maybe the compiler is super smart :)
FunctionPointer dfu_bootloader_entry = reinterpret_cast<FunctionPointer>(dfu_bootloader_ram_start+1);
dfu_bootloader_entry();
memcpy(dfu_bootloader_ram_start, ram_backup_buffer, dfu_bootloader_size);
free(ram_backup_buffer);
}
}
}

View File

@@ -0,0 +1,11 @@
#include
namespace Ion {
namespace USB {
void DFU() {
Ion::USB::Device::Calculator::Poll();
}
}
}