[bootloader/storage] new bootloader and fix python issue

This commit is contained in:
devdl11
2022-04-07 19:56:53 +02:00
parent 5365718761
commit 3dfc8d749c
133 changed files with 5442 additions and 1127 deletions

View File

@@ -147,7 +147,8 @@ bool AppsContainer::dispatchEvent(Ion::Events::Event event) {
* pictogram. */
updateBatteryState();
if (switchTo(usbConnectedAppSnapshot())) {
Ion::USB::DFU(true, GlobalPreferences::sharedGlobalPreferences()->dfuUnlocked(), GlobalPreferences::sharedGlobalPreferences()->dfuLevel());
Ion::USB::DFU(true
);
// Update LED when exiting DFU mode
Ion::LED::updateColorWithPlugAndCharge();
bool switched = switchTo(activeSnapshot);

View File

@@ -18,7 +18,7 @@ constexpr SettingsMessageTree s_modelMenu[] =
#endif
SettingsMessageTree(I18n::Message::BetaPopUp),
SettingsMessageTree(I18n::Message::About, s_modelAboutChildren),
SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
// SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren)};
constexpr SettingsMessageTree s_model = SettingsMessageTree(I18n::Message::SettingsApp, s_modelMenu);

View File

@@ -17,7 +17,7 @@ constexpr SettingsMessageTree s_modelMenu[] =
#ifdef HAS_CODE
SettingsMessageTree(I18n::Message::CodeApp, s_codeChildren),
#endif
SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
// SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren),
SettingsMessageTree(I18n::Message::About, s_modelAboutChildren)};

View File

@@ -18,7 +18,7 @@ constexpr SettingsMessageTree s_modelMenu[] =
#endif
SettingsMessageTree(I18n::Message::UpdatePopUp),
SettingsMessageTree(I18n::Message::Accessibility, s_accessibilityChildren),
SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
// SettingsMessageTree(I18n::Message::UsbSetting, s_usbProtectionChildren),
SettingsMessageTree(I18n::Message::About, s_modelAboutChildren)};
constexpr SettingsMessageTree s_model = SettingsMessageTree(I18n::Message::SettingsApp, s_modelMenu);

View File

@@ -1,5 +1,6 @@
bootloader_src += $(addprefix bootloader/,\
slot_exam_mode.cpp \
boot.cpp \
main.cpp \
kernel_header.cpp \
@@ -8,14 +9,15 @@ bootloader_src += $(addprefix bootloader/,\
interface.cpp \
jump_to_firmware.s \
trampoline.cpp \
usb_desc.cpp \
recovery.cpp \
usb_data.cpp \
itoa.cpp \
)
bootloader_images = $(addprefix bootloader/, \
cable.png \
computer.png \
)
bootloader_src += $(filter-out ion/src/device/shared/drivers/usb_desc.cpp,$(ion_src)) $(simple_kandinsky_src) $(liba_src) $(libaxx_src) $(bootloader_images)
bootloader_src += $(ion_src) $(simple_kandinsky_src) $(liba_src) $(libaxx_src) $(bootloader_images)
$(eval $(call depends_on_image,bootloader/interface.cpp,$(bootloader_images)))

View File

@@ -3,64 +3,156 @@
#include <ion.h>
#include <ion/src/device/shared/drivers/reset.h>
#include <bootloader/interface.h>
#include <ion/src/device/n0110/drivers/power.h>
#include <bootloader/recovery.h>
#include <bootloader/usb_data.h>
#include <ion/src/device/shared/drivers/flash.h>
#include <assert.h>
namespace Bootloader {
BootMode Boot::mode() {
// We use the exam mode driver as storage for the boot mode
uint8_t mode = Ion::ExamMode::FetchExamMode();
if (mode > 3)
return Unknown;
return (BootMode) mode;
return BootMode::SlotA;
}
void Boot::setMode(BootMode mode) {
BootMode currentMode = Boot::mode();
if (currentMode == mode)
return;
// We dont use the exam mode driver as storage for the boot mode because we need the 16k of storage x)
}
assert(mode != BootMode::Unknown);
int8_t deltaMode = (int8_t)mode - (int8_t)currentMode;
deltaMode = deltaMode < 0 ? deltaMode + 4 : deltaMode;
assert(deltaMode > 0);
Ion::ExamMode::IncrementExamMode(deltaMode);
void Boot::bootSlot(Bootloader::Slot s) {
if (!s.userlandHeader()->isOmega() && !s.userlandHeader()->isUpsilon()) {
// We are trying to boot epsilon, so we check the version and show an advertisement if needed
const char * version = s.userlandHeader()->version();
const char * min = "18.2.4";
int vsum = 0;
for (int i = 0; i < strlen(version); i++) {
vsum += version[i] * (100-i*15);
}
int minsum = 0;
for (int i = 0; i < strlen(min); i++) {
minsum += min[i] * (100-i*15);
}
if (vsum >= minsum) {
Interface::drawEpsilonAdvertisement();
uint64_t scan = 0;
while (scan != Ion::Keyboard::State(Ion::Keyboard::Key::Back)) {
scan = Ion::Keyboard::scan();
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::EXE) || scan == Ion::Keyboard::State(Ion::Keyboard::Key::OK)) {
scan = Ion::Keyboard::State(Ion::Keyboard::Key::Back);
s.boot();
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) {
Ion::Power::standby(); // Force a core reset to exit
}
}
Interface::drawMenu();
return;
}
}
s.boot();
Interface::drawMenu();
}
__attribute__((noreturn)) void Boot::boot() {
assert(mode() != BootMode::Unknown);
if (!Slot::A().kernelHeader()->isValid() && !Slot::B().kernelHeader()->isValid()) {
// Bootloader if both invalid
bootloader();
} else if (!Slot::A().kernelHeader()->isValid()) {
// If slot A is invalid and B valid, boot B
setMode(BootMode::SlotB);
Slot::B().boot();
} else if (!Slot::B().kernelHeader()->isValid()) {
// If slot B is invalid and A valid, boot A
setMode(BootMode::SlotA);
Slot::A().boot();
} else {
// Both valid, boot the selected one
if (mode() == BootMode::SlotA) {
Slot::A().boot();
} else if (mode() == BootMode::SlotB) {
Slot::B().boot();
bool isSlotA = Slot::A().kernelHeader()->isValid();
bool isSlotB = Slot::B().kernelHeader()->isValid();
bool isSlotKhi = Slot::Khi().kernelHeader()->isValid();
Interface::drawMenu();
while (true) {
uint64_t scan = Ion::Keyboard::scan();
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::One) && isSlotA) {
Boot::bootSlot(Slot::A());
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Two) && isSlotKhi) {
Boot::bootSlot(Slot::Khi());
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Three) && isSlotB) {
Boot::bootSlot(Slot::B());
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Four)) {
installerMenu();
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Five)) {
aboutMenu();
}
// else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Six)) {
// Ion::Device::Reset::core();
// }
else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) {
Ion::Power::standby(); // Force a core reset to exit
}
}
// Achivement unlocked: How Did We Get Here?
// Achievement unlocked: How Did We Get Here?
bootloader();
}
__attribute__ ((noreturn)) void Boot::bootloader() {
void Boot::installerMenu() {
Interface::drawInstallerSelection();
uint64_t scan = 0;
while (scan != Ion::Keyboard::State(Ion::Keyboard::Key::Back)) {
scan = Ion::Keyboard::scan();
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::One)) {
scan = Ion::Keyboard::State(Ion::Keyboard::Key::Back);
bootloader();
continue;
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Two)) {
scan = Ion::Keyboard::State(Ion::Keyboard::Key::Back);
blupdate();
continue;
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) {
Ion::Power::standby(); // Force a core reset to exit
}
}
Interface::drawMenu();
}
void Boot::aboutMenu() {
// Draw the about menu
Interface::drawAbout();
// Wait for the user to press OK, EXE or Back button
while (true) {
uint64_t scan = Ion::Keyboard::scan();
if ((scan == Ion::Keyboard::State(Ion::Keyboard::Key::OK)) ||
(scan == Ion::Keyboard::State(Ion::Keyboard::Key::EXE)) ||
(scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back))) {
// Redraw the menu and return
Interface::drawMenu();
return;
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) {
Ion::Power::standby(); // Force a core reset to exit
}
}
}
void Boot::blupdate() {
USBData data = USBData::BLUPDATE();
for (;;) {
Bootloader::Interface::drawBLUpdate();
Ion::USB::enable();
do {
uint64_t scan = Ion::Keyboard::scan();
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back)) {
Ion::USB::disable();
Interface::drawMenu();
return;
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) {
Ion::Power::standby();
}
} while (!Ion::USB::isEnumerated());
Ion::USB::DFU(true, &data);
}
}
void Boot::bootloader() {
USBData data = USBData::DEFAULT();
for(;;) {
// Draw the interfaces and infos
Bootloader::Interface::draw();
Bootloader::Interface::drawFlasher();
// Enable USB
Ion::USB::enable();
@@ -70,13 +162,27 @@ __attribute__ ((noreturn)) void Boot::bootloader() {
// If we pressed back while waiting, reset.
uint64_t scan = Ion::Keyboard::scan();
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back)) {
Ion::Device::Reset::core();
// Disable USB, redraw the menu and return
Ion::USB::disable();
Interface::drawMenu();
return;
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) {
Ion::Power::standby(); // Force a core reset to exit
}
} while (!Ion::USB::isEnumerated());
// Launch the DFU stack, allowing to press Back to quit and reset
Ion::USB::DFU(true);
Ion::USB::DFU(true, &data);
}
}
void Boot::lockInternal() {
Ion::Device::Flash::DisableInternalProtection();
Ion::Device::Flash::SetInternalSectorProtection(0, true);
Ion::Device::Flash::SetInternalSectorProtection(1, true);
Ion::Device::Flash::SetInternalSectorProtection(2, true);
Ion::Device::Flash::SetInternalSectorProtection(3, true);
Ion::Device::Flash::EnableInternalProtection();
}
}

View File

@@ -2,6 +2,7 @@
#define BOOTLOADER_BOOT_H
#include <stdint.h>
#include <bootloader/slot.h>
namespace Bootloader {
@@ -20,7 +21,12 @@ public:
static BootMode mode();
static void setMode(BootMode mode);
__attribute__ ((noreturn)) static void boot();
__attribute__ ((noreturn)) static void bootloader();
static void bootloader();
static void aboutMenu();
static void installerMenu();
static void blupdate();
static void bootSlot(Bootloader::Slot slot);
static void lockInternal();
};
}

129
bootloader/boot/isr.c Normal file
View File

@@ -0,0 +1,129 @@
#include "isr.h"
extern const void * _stack_start;
/* Interrupt Service Routines are void->void functions */
typedef void(*ISR)(void);
/* Notice: The Cortex-M4 expects all jumps to be made at an odd address when
* jumping to Thumb code. For example, if you want to execute Thumb code at
* address 0x100, you'll have to jump to 0x101. Luckily, this idiosyncrasy is
* properly handled by the C compiler that will generate proper addresses when
* using function pointers. */
#define INITIALISATION_VECTOR_SIZE 0x71
ISR InitialisationVector[INITIALISATION_VECTOR_SIZE]
__attribute__((section(".isr_vector_table")))
__attribute__((used))
= {
(ISR)&_stack_start, // Stack start
start, // Reset service routine,
0, // NMI service routine,
hard_fault_handler, // HardFault service routine,
mem_fault_handler, // MemManage service routine,
bus_fault_handler, // BusFault service routine,
usage_fault_handler, // UsageFault service routine,
0, 0, 0, 0, // Reserved
0, // SVCall service routine,
0, // DebugMonitor service routine,
0, // Reserved
0, // PendSV service routine,
isr_systick, // SysTick service routine
0, // WWDG service routine
0, // PVD service routine
0, // TampStamp service routine
0, // RtcWakeup service routine
0, // Flash service routine
0, // RCC service routine
0, // EXTI0 service routine
0, // EXTI1 service routine
0, // EXTI2 service routine
0, // EXTI3 service routine
0, // EXTI4 service routine
0, // DMA1Stream0 service routine
0, // DMA1Stream1 service routine
0, // DMA1Stream2 service routine
0, // DMA1Stream3 service routine
0, // DMA1Stream4 service routine
0, // DMA1Stream5 service routine
0, // DMA1Stream6 service routine
0, // ADC1 global interrupt
0, // CAN1 TX interrupt
0, // CAN1 RX0 interrupt
0, // CAN1 RX1 interrupt
0, // CAN1 SCE interrupt
0, // EXTI Line[9:5] interrupts
0, // TIM1 Break interrupt and TIM9 global interrupt
0, // TIM1 update interrupt and TIM10 global interrupt
0, // TIM1 Trigger & Commutation interrupts and TIM11 global interrupt
0, // TIM1 Capture Compare interrupt
0, // TIM2 global interrupt
0, // TIM3 global interrupt
0, // TIM4 global interrupt
0, // I2C1 global event interrupt
0, // I2C1 global error interrupt
0, // I2C2 global event interrupt
0, // I2C2 global error interrupt
0, // SPI1 global interrupt
0, // SPI2 global interrupt
0, // USART1 global interrupt
0, // USART2 global interrupt
0, // USART3 global interrupt
0, // EXTI Line[15:10] interrupts
0, // EXTI Line 17 interrupt RTC Alarms (A and B) through EXTI line interrupt
0, // EXTI Line 18 interrupt / USB On-The-Go FS Wakeup through EXTI line interrupt
0, // TIM8 Break interrupt TIM12 global interrupt
0, // TIM8 Update interrupt TIM13 global interrupt
0, // TIM8 Trigger & Commutation interrupt TIM14 global interrupt
0, // TIM8 Cap/Com interrupt
0, // DMA1 global interrupt Channel 7
0, // FSMC global interrupt
0, // SDIO global interrupt
0, // TIM5 global interrupt
0, // SPI3 global interrupt
0, // ?
0, // ?
0, // TIM6 global interrupt
0, // TIM7 global interrupt
0, // DMA2 Stream0 global interrupt
0, // DMA2 Stream1 global interrupt
0, // DMA2 Stream2 global interrupt
0, // DMA2 Stream3 global interrupt
0, // DMA2 Stream4 global interrupt
0, // SD filter0 global interrupt
0, // SD filter1 global interrupt
0, // CAN2 TX interrupt
0, // BXCAN2 RX0 interrupt
0, // BXCAN2 RX1 interrupt
0, // CAN2 SCE interrupt
0, // USB On The Go FS global interrupt
0, // DMA2 Stream5 global interrupts
0, // DMA2 Stream6 global interrupt
0, // DMA2 Stream7 global interrupt
0, // USART6 global interrupt
0, // I2C3 event interrupt
0, // I2C3 error interrupt
0, // ?
0, // ?
0, // ?
0, // ?
0, // ?
0, // ?
0, // RNG global interrupt
0, // FPU global interrupt
0, // ?
0, // ?
0, // SPI4 global interrupt
0, // SPI5 global interrupt
0, // ?
0, // ?
0, // ?
0, // ?
0, // ?
0, // ?
0, // Quad-SPI global interrupt
0, // ?
0, // ?
0, // I2CFMP1 event interrupt
0 // I2CFMP1 error interrupt
};

23
bootloader/boot/isr.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef ION_DEVICE_BOOT_ISR_H
#define ION_DEVICE_BOOT_ISR_H
#ifdef __cplusplus
extern "C" {
#endif
void start();
void abort();
void isr_systick();
// Fault handlers
void hard_fault_handler();
void mem_fault_handler();
void usage_fault_handler();
void bus_fault_handler();
#ifdef __cplusplus
}
#endif
#endif

146
bootloader/boot/rt0.cpp Normal file
View File

@@ -0,0 +1,146 @@
#include <stdint.h>
#include <string.h>
#include <ion.h>
#include <bootloader/boot/isr.h>
#include <drivers/board.h>
#include <drivers/rtc.h>
#include <drivers/reset.h>
#include <drivers/timing.h>
#include <bootloader/recovery.h>
typedef void (*cxx_constructor)();
extern "C" {
extern char _data_section_start_flash;
extern char _data_section_start_ram;
extern char _data_section_end_ram;
extern char _bss_section_start_ram;
extern char _bss_section_end_ram;
extern cxx_constructor _init_array_start;
extern cxx_constructor _init_array_end;
}
void __attribute__((noinline)) abort() {
#ifdef NDEBUG
Ion::Device::Reset::core();
#else
while (1) {
}
#endif
}
void __attribute__((interrupt, noinline)) isr_systick() {
auto t = Ion::Device::Timing::MillisElapsed;
t++;
Ion::Device::Timing::MillisElapsed = t;
}
void __attribute__((noinline)) hard_fault_handler() {
Bootloader::Recovery::crash_handler("HardFault");
}
void __attribute__((noinline)) mem_fault_handler() {
Bootloader::Recovery::crash_handler("MemoryFault");
}
void __attribute__((noinline)) usage_fault_handler() {
Bootloader::Recovery::crash_handler("UsageFault");
}
void __attribute__((noinline)) bus_fault_handler() {
Bootloader::Recovery::crash_handler("BusFault");
}
/* In order to ensure that this method is execute from the external flash, we
* forbid inlining it.*/
static void __attribute__((noinline)) external_flash_start() {
/* Init the peripherals. We do not initialize the backlight in case there is
* an on boarding app: indeed, we don't want the user to see the LCD tests
* happening during the on boarding app. The backlight will be initialized
* after the Power-On Self-Test if there is one or before switching to the
* home app otherwise. */
Ion::Device::Board::initPeripherals(false);
return ion_main(0, nullptr);
}
/* This additional function call 'jump_to_external_flash' serves two purposes:
* - By default, the compiler is free to inline any function call he wants. If
* the compiler decides to inline some functions that make use of VFP
* registers, it will need to push VFP them onto the stack in calling
* function's prologue.
* Problem: in start()'s prologue, we would never had a chance to enable the
* FPU since this function is the first thing called after reset.
* We can safely assume that neither memcpy, memset, nor any Ion::Device::init*
* method will use floating-point numbers, but ion_main very well can.
* To make sure ion_main's potential usage of VFP registers doesn't bubble-up to
* start(), we isolate it in its very own non-inlined function call.
* - To avoid jumping on the external flash when it is shut down, we ensure
* there is no symbol references from the internal flash to the external
* flash except this jump. In order to do that, we isolate this
* jump in a symbol that we link in a special section separated from the
* internal flash section. We can than forbid cross references from the
* internal flash to the external flash. */
static void __attribute__((noinline)) jump_to_external_flash() {
external_flash_start();
}
/* When 'start' is executed, the external flash is supposed to be shutdown. We
* thus forbid inlining to prevent executing this code from external flash
* (just in case 'start' was to be called from the external flash). */
void __attribute__((noinline)) start() {
/* This is where execution starts after reset.
* Many things are not initialized yet so the code here has to pay attention. */
/* Initialize the FPU as early as possible.
* For example, static C++ objects are very likely to manipulate float values */
Ion::Device::Board::initFPU();
/* Copy data section to RAM
* The data section is R/W but its initialization value matters. It's stored
* in Flash, but linked as if it were in RAM. Now's our opportunity to copy
* it. Note that until then the data section (e.g. global variables) contains
* garbage values and should not be used. */
size_t dataSectionLength = (&_data_section_end_ram - &_data_section_start_ram);
memcpy(&_data_section_start_ram, &_data_section_start_flash, dataSectionLength);
/* Zero-out the bss section in RAM
* Until we do, any uninitialized global variable will be unusable. */
size_t bssSectionLength = (&_bss_section_end_ram - &_bss_section_start_ram);
memset(&_bss_section_start_ram, 0, bssSectionLength);
/* Call static C++ object constructors
* The C++ compiler creates an initialization function for each static object.
* The linker then stores the address of each of those functions consecutively
* between _init_array_start and _init_array_end. So to initialize all C++
* static objects we just have to iterate between theses two addresses and
* call the pointed function. */
#define SUPPORT_CPP_GLOBAL_CONSTRUCTORS 0
#if SUPPORT_CPP_GLOBAL_CONSTRUCTORS
for (cxx_constructor * c = &_init_array_start; c<&_init_array_end; c++) {
(*c)();
}
#else
/* In practice, static initialized objects are a terrible idea. Since the init
* order is not specified, most often than not this yields the dreaded static
* init order fiasco. How about bypassing the issue altogether? */
if (&_init_array_start != &_init_array_end) {
abort();
}
#endif
/* At this point, we initialized clocks and the external flash but no other
* peripherals. */
Ion::Device::Board::init();
jump_to_external_flash();
abort();
}

View File

@@ -0,0 +1,444 @@
#include <drivers/board.h>
#include <drivers/cache.h>
#include <drivers/internal_flash.h>
#include <drivers/config/clocks.h>
#include <drivers/config/internal_flash.h>
#include <drivers/external_flash.h>
#include <regs/regs.h>
#include <ion.h>
typedef void(*ISR)(void);
extern ISR InitialisationVector[];
// Public Ion methods
const char * Ion::fccId() {
return "2ALWP-N0110";
}
// Private Ion::Device methods
namespace Ion {
namespace Device {
namespace Board {
using namespace Regs;
void bootloaderMPU() {
// 1. Disable the MPU
// 1.1 Memory barrier
Cache::dmb();
// 1.3 Disable the MPU and clear the control register
MPU.CTRL()->setENABLE(false);
MPU.RNR()->setREGION(7);
MPU.RBAR()->setADDR(0x90000000);
MPU.RASR()->setXN(false);
MPU.RASR()->setENABLE(true);
// 2.3 Enable MPU
MPU.CTRL()->setENABLE(true);
// 3. Data/instruction synchronisation barriers to ensure that the new MPU configuration is used by subsequent instructions.
Cache::disable();
Cache::dsb();
Cache::isb();
}
void initMPU() {
// 1. Disable the MPU
// 1.1 Memory barrier
Cache::dmb();
// 1.2 Disable fault exceptions
CORTEX.SHCRS()->setMEMFAULTENA(false);
// 1.3 Disable the MPU and clear the control register
MPU.CTRL()->setENABLE(false);
// 2. MPU settings
// 2.1 Configure a MPU region for the FMC memory area
/* This is needed for interfacing with the LCD
* We define the whole FMC memory bank 1 as strongly ordered, non-executable
* and not accessible. We define the FMC command and data addresses as
* writeable non-cachable, non-buffereable and non shareable. */
int sector = 0;
MPU.RNR()->setREGION(sector++);
MPU.RBAR()->setADDR(0x60000000);
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_256MB);
MPU.RASR()->setAP(MPU::RASR::AccessPermission::NoAccess);
MPU.RASR()->setXN(true);
MPU.RASR()->setTEX(2);
MPU.RASR()->setS(0);
MPU.RASR()->setC(0);
MPU.RASR()->setB(0);
MPU.RASR()->setENABLE(true);
MPU.RNR()->setREGION(sector++);
MPU.RBAR()->setADDR(0x60000000);
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_32B);
MPU.RASR()->setXN(true);
MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW);
MPU.RASR()->setTEX(2);
MPU.RASR()->setS(0);
MPU.RASR()->setC(0);
MPU.RASR()->setB(0);
MPU.RASR()->setENABLE(true);
MPU.RNR()->setREGION(sector++);
MPU.RBAR()->setADDR(0x60000000+0x20000);
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_32B);
MPU.RASR()->setXN(true);
MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW);
MPU.RASR()->setTEX(2);
MPU.RASR()->setS(0);
MPU.RASR()->setC(0);
MPU.RASR()->setB(0);
MPU.RASR()->setENABLE(true);
// 2.2 Configure MPU regions for the QUADSPI peripheral
/* L1 Cache can issue speculative reads to any memory address. But, when the
* Quad-SPI is in memory-mapped mode, if an access is made to an address
* outside of the range defined by FSIZE but still within the 256Mbytes range,
* then an AHB error is given (AN4760). To prevent this to happen, we
* configure the MPU to define the whole Quad-SPI addressable space as
* strongly ordered, non-executable and not accessible. Plus, we define the
* Quad-SPI region corresponding to the Expternal Chip as executable and
* fully accessible (AN4861). */
MPU.RNR()->setREGION(sector++);
MPU.RBAR()->setADDR(0x90000000);
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_256MB);
MPU.RASR()->setAP(MPU::RASR::AccessPermission::NoAccess);
MPU.RASR()->setXN(true);
MPU.RASR()->setTEX(0);
MPU.RASR()->setS(0);
MPU.RASR()->setC(0);
MPU.RASR()->setB(0);
MPU.RASR()->setENABLE(true);
MPU.RNR()->setREGION(sector++);
MPU.RBAR()->setADDR(0x90000000);
MPU.RASR()->setSIZE(MPU::RASR::RegionSize::_8MB);
MPU.RASR()->setAP(MPU::RASR::AccessPermission::RW);
MPU.RASR()->setXN(false);
MPU.RASR()->setTEX(0);
MPU.RASR()->setS(0);
MPU.RASR()->setC(1);
MPU.RASR()->setB(0);
MPU.RASR()->setENABLE(true);
// 2.3 Enable MPU
MPU.CTRL()->setPRIVDEFENA(true);
MPU.CTRL()->setENABLE(true);
//2.4 Enable fault exceptions
CORTEX.SHCRS()->setMEMFAULTENA(true);
CORTEX.SHCRS()->setBUSFAULTENA(true);
CORTEX.SHCRS()->setUSGFAULTENA(true);
// 3. Data/instruction synchronisation barriers to ensure that the new MPU configuration is used by subsequent instructions.
Cache::dsb();
Cache::isb();
}
void init() {
initFPU();
initMPU();
initClocks();
// The bootloader leaves its own after flashing
//SYSCFG.MEMRMP()->setMEM_MODE(SYSCFG::MEMRMP::MemMode::MainFlashmemory);
// Ensure right location of interrupt vectors
CORTEX.VTOR()->setVTOR((void*)&InitialisationVector);
// Put all inputs as Analog Input, No pull-up nor pull-down
// Except for the SWD port (PB3, PA13, PA14)
GPIOA.MODER()->set(0xEBFFFFFF);
GPIOA.PUPDR()->set(0x24000000);
GPIOB.MODER()->set(0xFFFFFFBF);
GPIOB.PUPDR()->set(0x00000000);
for (int g=2; g<5; g++) {
GPIO(g).MODER()->set(0xFFFFFFFF); // All to "Analog"
GPIO(g).PUPDR()->set(0x00000000); // All to "None"
}
ExternalFlash::init();
// Initiate L1 cache after initiating the external flash
Cache::enable();
}
void initClocks() {
/* System clock
* Configure the CPU at 192 MHz and USB at 48 MHz. */
/* After reset, the device is using the high-speed internal oscillator (HSI)
* as a clock source, which runs at a fixed 16 MHz frequency. The HSI is not
* accurate enough for reliable USB operation, so we need to use the external
* high-speed oscillator (HSE). */
// Enable the HSI and wait for it to be ready
RCC.CR()->setHSION(true);
while(!RCC.CR()->getHSIRDY()) {
}
// Enable the HSE and wait for it to be ready
RCC.CR()->setHSEON(true);
while(!RCC.CR()->getHSERDY()) {
}
// Enable PWR peripheral clock
RCC.APB1ENR()->setPWREN(true);
/* To pass electromagnetic compatibility tests, we activate the Spread
* Spectrum clock generation, which adds jitter to the PLL clock in order to
* "lower peak-energy on the central frequency" and its harmonics.
* It must be done before enabling the PLL. */
class RCC::SSCGR sscgr(0); // Reset value
sscgr.setMODPER(Clocks::Config::SSCG_MODPER);
sscgr.setINCSTEP(Clocks::Config::SSCG_INCSTEP);
sscgr.setSPREADSEL(RCC::SSCGR::SPREADSEL::CenterSpread);
sscgr.setSSCGEN(true);
RCC.SSCGR()->set(sscgr);
/* Given the crystal used on our device, the HSE will oscillate at 8 MHz. By
* piping it through a phase-locked loop (PLL) we can derive other frequencies
* for use in different parts of the system. */
// Configure the PLL ratios and use HSE as a PLL input
RCC.PLLCFGR()->setPLLM(Clocks::Config::PLL_M);
RCC.PLLCFGR()->setPLLN(Clocks::Config::PLL_N);
RCC.PLLCFGR()->setPLLQ(Clocks::Config::PLL_Q);
RCC.PLLCFGR()->setPLLSRC(RCC::PLLCFGR::PLLSRC::HSE);
// Enable the PLL and wait for it to be ready
RCC.CR()->setPLLON(true);
// Enable Over-drive
PWR.CR()->setODEN(true);
while(!PWR.CSR()->getODRDY()) {
}
PWR.CR()->setODSWEN(true);
while(!PWR.CSR()->getODSWRDY()) {
}
// Choose Voltage scale 1
PWR.CR()->setVOS(PWR::CR::Voltage::Scale1);
while (!PWR.CSR()->getVOSRDY()) {}
/* After reset the Flash runs as fast as the CPU. When we clock the CPU faster
* the flash memory cannot follow and therefore flash memory accesses need to
* wait a little bit.
* The spec tells us that at 2.8V and over 210MHz the flash expects 7 WS. */
FLASH.ACR()->setLATENCY(7);
/* Enable prefetching flash instructions */
/* Fetching instructions increases slightly the power consumption but the
* increase is negligible compared to the screen consumption. */
FLASH.ACR()->setPRFTEN(true);
/* Enable the ART */
FLASH.ACR()->setARTEN(true);
// 192 MHz is too fast for APB1. Divide it by four to reach 48 MHz
RCC.CFGR()->setPPRE1(Clocks::Config::APB1PrescalerReg);
// 192 MHz is too fast for APB2. Divide it by two to reach 96 MHz
RCC.CFGR()->setPPRE2(Clocks::Config::APB2PrescalerReg);
while(!RCC.CR()->getPLLRDY()) {
}
// Use the PLL output as a SYSCLK source
RCC.CFGR()->setSW(RCC::CFGR::SW::PLL);
while (RCC.CFGR()->getSWS() != RCC::CFGR::SW::PLL) {
}
// Now that we don't need use it anymore, turn the HSI off
RCC.CR()->setHSION(false);
// Peripheral clocks
// AHB1 bus
// Our peripherals are using GPIO A, B, C, D and E.
// We're not using the CRC nor DMA engines.
class RCC::AHB1ENR ahb1enr(0x00100000); // Reset value
ahb1enr.setGPIOAEN(true);
ahb1enr.setGPIOBEN(true);
ahb1enr.setGPIOCEN(true);
ahb1enr.setGPIODEN(true);
ahb1enr.setGPIOEEN(true);
ahb1enr.setDMA2EN(true);
RCC.AHB1ENR()->set(ahb1enr);
// AHB2 bus
RCC.AHB2ENR()->setOTGFSEN(true);
// AHB3 bus
RCC.AHB3ENR()->setFSMCEN(true);
// APB1 bus
// We're using TIM3 for the LEDs
RCC.APB1ENR()->setTIM3EN(true);
RCC.APB1ENR()->setPWREN(true);
RCC.APB1ENR()->setRTCAPB(true);
// APB2 bus
class RCC::APB2ENR apb2enr(0); // Reset value
apb2enr.setADC1EN(true);
apb2enr.setSYSCFGEN(true);
apb2enr.setUSART6EN(true); // TODO required if building bench target only?
RCC.APB2ENR()->set(apb2enr);
// Configure clocks in sleep mode
// AHB1 peripheral clock enable in low-power mode register
class RCC::AHB1LPENR ahb1lpenr(0x7EF7B7FF); // Reset value
ahb1lpenr.setGPIOALPEN(true); // Enable IO port A for Charging/USB plug/Keyboard pins
ahb1lpenr.setGPIOBLPEN(true); // Enable IO port B for LED pins
ahb1lpenr.setGPIOCLPEN(true); // Enable IO port C for LED/Keyboard pins
ahb1lpenr.setGPIODLPEN(false); // Disable IO port D (LCD...)
ahb1lpenr.setGPIOELPEN(true); // Enable IO port E for Keyboard/Battery pins
ahb1lpenr.setGPIOFLPEN(false); // Disable IO port F
ahb1lpenr.setGPIOGLPEN(false); // Disable IO port G
ahb1lpenr.setGPIOHLPEN(false); // Disable IO port H
ahb1lpenr.setGPIOILPEN(false); // Disable IO port I
ahb1lpenr.setCRCLPEN(false);
ahb1lpenr.setFLITFLPEN(false);
ahb1lpenr.setSRAM1LPEN(false);
ahb1lpenr.setDMA1LPEN(false);
ahb1lpenr.setDMA2LPEN(false);
ahb1lpenr.setAXILPEN(false);
ahb1lpenr.setSRAM2LPEN(false);
ahb1lpenr.setBKPSRAMLPEN(false);
ahb1lpenr.setDTCMLPEN(false);
ahb1lpenr.setOTGHSLPEN(false);
ahb1lpenr.setOTGHSULPILPEN(false);
RCC.AHB1LPENR()->set(ahb1lpenr);
// AHB2 peripheral clock enable in low-power mode register
class RCC::AHB2LPENR ahb2lpenr(0x000000F1); // Reset value
ahb2lpenr.setOTGFSLPEN(false);
ahb2lpenr.setRNGLPEN(false);
ahb2lpenr.setAESLPEN(false);
RCC.AHB2LPENR()->set(ahb2lpenr);
// AHB3 peripheral clock enable in low-power mode register
class RCC::AHB3LPENR ahb3lpenr(0x00000003); // Reset value
ahb3lpenr.setFMCLPEN(false);
ahb3lpenr.setQSPILPEN(false);
RCC.AHB3LPENR()->set(ahb3lpenr);
// APB1 peripheral clock enable in low-power mode register
class RCC::APB1LPENR apb1lpenr(0xFFFFCBFF); // Reset value
apb1lpenr.setTIM2LPEN(false);
apb1lpenr.setTIM3LPEN(true); // Enable TIM3 in sleep mode for LEDs
apb1lpenr.setTIM4LPEN(false);
apb1lpenr.setTIM5LPEN(false);
apb1lpenr.setTIM6LPEN(false);
apb1lpenr.setTIM7LPEN(false);
apb1lpenr.setTIM12LPEN(false);
apb1lpenr.setTIM13LPEN(false);
apb1lpenr.setTIM14LPEN(false);
apb1lpenr.setRTCAPBLPEN(false);
apb1lpenr.setWWDGLPEN(false);
apb1lpenr.setSPI2LPEN(false);
apb1lpenr.setSPI3LPEN(false);
apb1lpenr.setUSART2LPEN(false);
apb1lpenr.setUSART3LPEN(false);
apb1lpenr.setI2C1LPEN(false);
apb1lpenr.setI2C2LPEN(false);
apb1lpenr.setI2C3LPEN(false);
apb1lpenr.setCAN1LPEN(false);
apb1lpenr.setPWRLPEN(false);
apb1lpenr.setLPTIM1LPEN(false);
apb1lpenr.setUSART4LPEN(false);
apb1lpenr.setUSART5LPEN(false);
apb1lpenr.setOTGHSLPEN(false);
apb1lpenr.setOTGHSULPILPEN(false);
RCC.APB1LPENR()->set(apb1lpenr);
// APB2 peripheral clock enable in low-power mode register
class RCC::APB2LPENR apb2lpenr(0x04F77F33); // Reset value
apb2lpenr.setTIM1LPEN(false);
apb2lpenr.setTIM8LPEN(false);
apb2lpenr.setUSART1LPEN(false);
apb2lpenr.setUSART6LPEN(false);
apb2lpenr.setADC1LPEN(false);
apb2lpenr.setSPI1LPEN(false);
apb2lpenr.setSPI4LPEN(false);
apb2lpenr.setSYSCFGLPEN(false);
apb2lpenr.setTIM9LPEN(false);
apb2lpenr.setTIM10LPEN(false);
apb2lpenr.setTIM11LPEN(false);
apb2lpenr.setSPI5LPEN(false);
apb2lpenr.setSDMMC2LPEN(false);
apb2lpenr.setADC2LPEN(false);
apb2lpenr.setADC3LPEN(false);
apb2lpenr.setSAI1LPEN(false);
apb2lpenr.setSAI2LPEN(false);
RCC.APB2LPENR()->set(apb2lpenr);
}
void shutdownClocks(bool keepLEDAwake) {
// APB2 bus
RCC.APB2ENR()->set(0); // Reset value
// AHB2 bus
RCC.AHB2ENR()->set(0); // Reset value
// AHB3 bus
RCC.AHB3ENR()->set(0); // Reset value
// APB1
class RCC::APB1ENR apb1enr(0); // Reset value
// AHB1 bus
class RCC::AHB1ENR ahb1enr(0x00100000); // Reset value
if (keepLEDAwake) {
apb1enr.setTIM3EN(true);
ahb1enr.setGPIOBEN(true);
}
RCC.APB1ENR()->set(apb1enr);
RCC.AHB1ENR()->set(ahb1enr);
}
constexpr int k_pcbVersionOTPIndex = 0;
/* As we want the PCB versions to be in ascending order chronologically, and
* because the OTP are initialized with 1s, we store the bitwise-not of the
* version number. This way, devices with blank OTP are considered version 0. */
PCBVersion pcbVersion() {
#if IN_FACTORY
/* When flashing for the first time, we want all systems that depend on the
* PCB version to function correctly before flashing the PCB version. This
* way, flashing the PCB version can be done last. */
return PCB_LATEST;
#else
PCBVersion version = readPCBVersionInMemory();
return (version == k_alternateBlankVersion ? 0 : version);
#endif
}
PCBVersion readPCBVersionInMemory() {
return ~(*reinterpret_cast<const PCBVersion *>(InternalFlash::Config::OTPAddress(k_pcbVersionOTPIndex)));
}
void writePCBVersion(PCBVersion version) {
uint8_t * destination = reinterpret_cast<uint8_t *>(InternalFlash::Config::OTPAddress(k_pcbVersionOTPIndex));
PCBVersion formattedVersion = ~version;
InternalFlash::WriteMemory(destination, reinterpret_cast<uint8_t *>(&formattedVersion), sizeof(formattedVersion));
}
void lockPCBVersion() {
uint8_t * destination = reinterpret_cast<uint8_t *>(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex));
uint8_t zero = 0;
InternalFlash::WriteMemory(destination, &zero, sizeof(zero));
}
bool pcbVersionIsLocked() {
return *reinterpret_cast<const uint8_t *>(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex)) == 0;
}
}
}
}

View File

@@ -1,20 +1,22 @@
#include <assert.h>
#include <ion.h>
#include <ion/timing.h>
#include <bootloader/interface.h>
#include <bootloader/messages.h>
#include <bootloader/slot.h>
#include <bootloader/boot.h>
#include "computer.h"
#include "cable.h"
namespace Bootloader {
void Interface::drawImage(KDContext* ctx, const Image* image, int offset) {
const uint8_t* data;
void Interface::drawImage(KDContext * ctx, const Image * image, int offset) {
const uint8_t * data;
size_t size;
size_t pixelBufferSize;
if (image != nullptr) {
data = image->compressedPixelData();
size = image->compressedPixelDataSize();
@@ -39,44 +41,281 @@ void Interface::drawImage(KDContext* ctx, const Image* image, int offset) {
ctx->fillRectWithPixels(bounds, pixelBuffer, nullptr);
}
void Interface::draw() {
void Interface::drawHeader() {
KDContext * ctx = KDIonContext::sharedContext();
ctx->fillRect(KDRect(0,0,320,240), KDColorBlack);
drawImage(ctx, ImageStore::Computer, 70);
drawImage(ctx, ImageStore::Cable, 172);
ctx->fillRect(KDRect(0, 0, 320, 240), KDColorWhite);
drawImage(ctx, ImageStore::Computer, 25);
KDSize fontSize = KDFont::LargeFont->glyphSize();
int initPos = (320 - fontSize.width() * strlen(Messages::mainTitle)) / 2;
ctx->drawString(Messages::mainTitle, KDPoint(initPos, ImageStore::Computer->height() + fontSize.height() + 10), KDFont::LargeFont, KDColorBlack, KDColorWhite);
}
ctx->drawString("Slot A:", KDPoint(0, 0), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString("Slot B:", KDPoint(0, 13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString("Current:", KDPoint(0, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack);
void Interface::drawMenu() {
// Get the context
KDContext * ctx = KDIonContext::sharedContext();
// Clear the screen
ctx->fillRect(KDRect(0, 0, 320, 240), KDColorWhite);
// Draw the image
drawImage(ctx, ImageStore::Computer, 25);
// Get the font size
KDSize largeSize = KDFont::LargeFont->glyphSize();
KDSize smallSize = KDFont::SmallFont->glyphSize();
// Draw the title
int initPos = (320 - largeSize.width() * strlen(Messages::mainMenuTitle)) / 2;
ctx->drawString(Messages::mainMenuTitle, KDPoint(initPos, ImageStore::Computer->height() + largeSize.height() + 10), KDFont::LargeFont, KDColorBlack, KDColorWhite);
if (Boot::mode() == BootMode::SlotA) {
ctx->drawString("Slot A", KDPoint(63, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack);
} else if (Boot::mode() == BootMode::SlotB) {
ctx->drawString("Slot B", KDPoint(63, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack);
}
// Initialize the slot list
Slot slots[3] = {Slot::A(), Slot::Khi(), Slot::B()};
char indexes[3] = {'1', '2', '3'};
Slot slots[2] = {Slot::A(), Slot::B()};
// Set the khi slot index, improve this when Khi will have a dedicated header
const uint8_t khiIndex = 2 - 1;
for(uint8_t i = 0; i < 2; i++) {
// Get the start y position
int y = ImageStore::Computer->height() + (largeSize.height() + 10) + (smallSize.height() + 10);
// Iterate over the slot list
for (uint8_t i = 0; i < sizeof(indexes) / sizeof(indexes[0]); i++) {
// Get the slot from the list
Slot slot = slots[i];
// Get the "X - " string
char converted[] = {indexes[i], ' ', '-', ' ', '\0'};
// Setup the margin
int x = 10;
// If the slot is valid, draw the slot
if (slot.kernelHeader()->isValid() && slot.userlandHeader()->isValid()) {
// Draw the slot number
ctx->drawString(converted, KDPoint(x, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Increment the x position
x += strlen(converted) * smallSize.width();
// Draw the slot version
ctx->drawString(slot.userlandHeader()->version(), KDPoint(x, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Increment the x position
x += strlen(slot.userlandHeader()->version()) * smallSize.width() + smallSize.width() * 2;
// Draw the slot commit
ctx->drawString(slot.kernelHeader()->patchLevel(), KDPoint(x, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Increment the x position
x += strlen(slot.kernelHeader()->patchLevel()) * smallSize.width() + smallSize.width();
const char * OSName = "";
const char * OSVersion = "";
// If the slot is Upsilon, draw the slot name
if (slot.userlandHeader()->isOmega() && slot.userlandHeader()->isUpsilon()) {
ctx->drawString("Upsilon", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString(slot.userlandHeader()->upsilonVersion(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
// Set the OS name
OSName = Messages::upsilon;
// Set the OS version
OSVersion = slot.userlandHeader()->upsilonVersion();
} else if (slot.userlandHeader()->isOmega()) {
ctx->drawString("Omega", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString(slot.userlandHeader()->omegaVersion(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
// Get if slot is Khi
bool isKhi = (i == khiIndex);
// If the slot is Khi, draw the slot name (Khi)
if (isKhi) {
// Set the OS name
OSName = Messages::khi;
} else {
// Set the OS name
OSName = Messages::omega;
}
// Set the OS version
OSVersion = slot.userlandHeader()->omegaVersion();
} else {
ctx->drawString("Epsilon", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString(slot.userlandHeader()->version(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
// Set the OS name
OSName = Messages::epsilon;
}
ctx->drawString(slot.kernelHeader()->patchLevel(), KDPoint(168, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
// Draw the OS name
ctx->drawString(OSName, KDPoint(x, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Increment the x position
x += strlen(OSName) * smallSize.width() + smallSize.width();
// Draw the OS version
ctx->drawString(OSVersion, KDPoint(x, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Else, the slot is invalid
} else {
ctx->drawString("Invalid", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString(Messages::invalidSlot, KDPoint(10, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
}
// Increment the y position
y += smallSize.height() + 10;
}
// Draw the DFU message
ctx->drawString(Messages::dfuText, KDPoint(10, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Increment the y position
y += smallSize.height() + 10;
// Draw the about message
ctx->drawString(Messages::aboutText, KDPoint(10, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Reboot is disabled for now
// y += smallSize.height() + 10;
// ctx->drawString(Messages::rebootText, KDPoint(10, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Draw the footer
initPos = (320 - smallSize.width() * strlen(Messages::mainTitle)) / 2;
ctx->drawString(Messages::mainTitle, KDPoint(initPos, 240 - smallSize.height() - 10), KDFont::SmallFont, KDColorBlack, KDColorWhite);
}
void Interface::drawFlasher() {
Interface::drawHeader();
KDContext * ctx = KDIonContext::sharedContext();
int y = ImageStore::Computer->height() + (KDFont::LargeFont->glyphSize().height() + 10) + (KDFont::SmallFont->glyphSize().height() + 10);
int initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::dfuSubtitle)) / 2;
ctx->drawString(Messages::dfuSubtitle, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
}
void Interface::drawLoading() {
KDContext * ctx = KDIonContext::sharedContext();
ctx->fillRect(KDRect(0, 0, 320, 240), KDColorWhite);
drawImage(ctx, ImageStore::Computer, 25);
Ion::Timing::msleep(250);
KDSize fontSize = KDFont::LargeFont->glyphSize();
int initPos = (320 - fontSize.width() * strlen(Messages::mainTitle)) / 2;
for (uint8_t i = 0; i < strlen(Messages::mainTitle); i++) {
char tmp[2] = {Messages::mainTitle[i], '\0'};
ctx->drawString(tmp, KDPoint(initPos + i * (fontSize.width()), ImageStore::Computer->height() + fontSize.height() + 10), KDFont::LargeFont, KDColorBlack, KDColorWhite);
Ion::Timing::msleep(50);
}
Ion::Timing::msleep(500);
}
void Interface::drawAbout() {
drawHeader();
// Create the list of about messages
// TODO: Move it to messages.h
char aboutMessages[][38] = {
"This is the bootloader of",
"the Upsilon Calculator.",
"It is used to install",
"and select the OS",
"to boot."
};
// Get the context
KDContext * ctx = KDIonContext::sharedContext();
// Get the start Y position
KDSize largeSize = KDFont::LargeFont->glyphSize();
KDSize smallSize = KDFont::SmallFont->glyphSize();
int y = ImageStore::Computer->height() + (largeSize.height() + 10) + (smallSize.height() + 10);
// Iterate over the list and draw each message
for (uint8_t i = 0; i < sizeof(aboutMessages) / sizeof(aboutMessages[0]); i++) {
// Get the message
char * actualMessage = aboutMessages[i];
// Get the start X position
int initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(actualMessage)) / 2;
// Draw the message
ctx->drawString(actualMessage, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Increment the Y position
y += smallSize.height() + 10;
}
ctx->drawString(Messages::bootloaderVersion, KDPoint((320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::bootloaderVersion)) / 2, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
}
void Interface::drawCrash(const char * error) {
KDContext * ctx = KDIonContext::sharedContext();
ctx->fillRect(KDRect(0, 0, 320, 240), KDColorWhite);
drawImage(ctx, ImageStore::Computer, 25);
KDSize fontSize = KDFont::LargeFont->glyphSize();
int initPos = (320 - fontSize.width() * strlen(Messages::crashTitle)) / 2;
ctx->drawString(Messages::crashTitle, KDPoint(initPos, ImageStore::Computer->height() + fontSize.height() + 10), KDFont::LargeFont, KDColorBlack, KDColorWhite);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(error)) / 2;
ctx->drawString(error, KDPoint(initPos, ImageStore::Computer->height() + fontSize.height() + 10 + KDFont::SmallFont->glyphSize().height() + 10), KDFont::SmallFont, KDColorBlack, KDColorWhite);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::crashText1)) / 2;
ctx->drawString(Messages::crashText1, KDPoint(initPos, ImageStore::Computer->height() + fontSize.height() + 10 + KDFont::SmallFont->glyphSize().height() + 10 + KDFont::SmallFont->glyphSize().height() + 10 + 20), KDFont::SmallFont, KDColorBlack, KDColorWhite);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::crashText2)) / 2;
ctx->drawString(Messages::crashText2, KDPoint(initPos, ImageStore::Computer->height() + fontSize.height() + 10 + KDFont::SmallFont->glyphSize().height() + 10 + KDFont::SmallFont->glyphSize().height() + 10 + 20 + KDFont::SmallFont->glyphSize().height() + 10), KDFont::SmallFont, KDColorBlack, KDColorWhite);
}
void Interface::drawRecovery() {
KDContext * ctx = KDIonContext::sharedContext();
ctx->fillRect(KDRect(0, 0, 320, 240), KDColorWhite);
drawImage(ctx, ImageStore::Computer, 25);
KDSize fontSize = KDFont::LargeFont->glyphSize();
int initPos = (320 - fontSize.width() * strlen(Messages::recoveryTitle)) / 2;
int y = ImageStore::Computer->height() + fontSize.height() + 5;
ctx->drawString(Messages::recoveryTitle, KDPoint(initPos, y), KDFont::LargeFont, KDColorBlack, KDColorWhite);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::recoveryText1)) / 2;
y += fontSize.height() + 5;
ctx->drawString(Messages::recoveryText1, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::recoveryText2)) / 2;
y += fontSize.height() + 5;
ctx->drawString(Messages::recoveryText2, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::recoveryText3)) / 2;
y += fontSize.height() + 5;
ctx->drawString(Messages::recoveryText3, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::recoveryText4)) / 2;
y += fontSize.height() + 5;
ctx->drawString(Messages::recoveryText4, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::recoveryText5)) / 2;
y += fontSize.height() + 5;
ctx->drawString(Messages::recoveryText5, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
}
void Interface::drawInstallerSelection() {
// Get the context
KDContext * ctx = KDIonContext::sharedContext();
// Clear the screen
ctx->fillRect(KDRect(0, 0, 320, 240), KDColorWhite);
// Draw the image
drawImage(ctx, ImageStore::Computer, 25);
// Get the font size
KDSize largeSize = KDFont::LargeFont->glyphSize();
KDSize smallSize = KDFont::SmallFont->glyphSize();
// Get the start x position
int initPos = (320 - largeSize.width() * strlen(Messages::installerSelectionTitle)) / 2;
// Get the start y position
int y = ImageStore::Computer->height() + largeSize.height() + 10;
// Draw the title
ctx->drawString(Messages::installerSelectionTitle, KDPoint(initPos, y), KDFont::LargeFont, KDColorBlack, KDColorWhite);
// Increment the y position
y += largeSize.height() + 5;
// Get the y position of the subtitle
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::installerText1)) / 2;
// Draw the subtitle
ctx->drawString(Messages::installerText1, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Increment the y position
y += smallSize.height() + 10;
// Set the start x position
initPos = 10;
// Draw the first button (slot flash)
ctx->drawString(Messages::installerText2, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
// Increment the y position
y += smallSize.height() + 10;
// Draw the second button (bootloader flash)
ctx->drawString(Messages::installerText3, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
}
void Interface::drawBLUpdate() {
Interface::drawHeader();
KDContext * ctx = KDIonContext::sharedContext();
int y = ImageStore::Computer->height() + (KDFont::LargeFont->glyphSize().height() + 10) + (KDFont::SmallFont->glyphSize().height() + 10);
int initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::bootloaderSubtitle)) / 2;
ctx->drawString(Messages::bootloaderSubtitle, KDPoint(initPos, y), KDFont::SmallFont, KDColorBlack, KDColorWhite);
}
void Interface::drawEpsilonAdvertisement() {
KDContext * ctx = KDIonContext::sharedContext();
ctx->fillRect(KDRect(0, 0, 320, 240), KDColorRed);
drawImage(ctx, ImageStore::Computer, 25);
KDSize fontSize = KDFont::LargeFont->glyphSize();
int initPos = (320 - fontSize.width() * strlen(Messages::epsilonWarningTitle)) / 2;
int y = ImageStore::Computer->height() + fontSize.height() + 15;
ctx->drawString(Messages::epsilonWarningTitle, KDPoint(initPos, y), KDFont::LargeFont, KDColorWhite, KDColorRed);
initPos = (320 - fontSize.width() * strlen(Messages::epsilonWarningText1)) / 2;
y += fontSize.height() + 5;
ctx->drawString(Messages::epsilonWarningText1, KDPoint(initPos, y), KDFont::LargeFont, KDColorWhite, KDColorRed);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::epsilonWarningText2)) / 2;
y += fontSize.height() + 2;
ctx->drawString(Messages::epsilonWarningText2, KDPoint(initPos, y), KDFont::SmallFont, KDColorWhite, KDColorRed);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::epsilonWarningText3)) / 2;
y += KDFont::SmallFont->glyphSize().height() + 5;
ctx->drawString(Messages::epsilonWarningText3, KDPoint(initPos, y), KDFont::SmallFont, KDColorWhite, KDColorRed);
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::epsilonWarningText4)) / 2;
y += KDFont::SmallFont->glyphSize().height() + 10;
ctx->drawString(Messages::epsilonWarningText4, KDPoint(initPos, y), KDFont::SmallFont, KDColorWhite, KDColorRed);
y += KDFont::SmallFont->glyphSize().height() + 10;
initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::epsilonWarningText5)) / 2;
ctx->drawString(Messages::epsilonWarningText5, KDPoint(initPos, y), KDFont::SmallFont, KDColorWhite, KDColorRed);
y += KDFont::SmallFont->glyphSize().height() + 5;
ctx->drawString(Messages::epsilonWarningText6, KDPoint(initPos, y), KDFont::SmallFont, KDColorWhite, KDColorRed);
}
}

View File

@@ -10,10 +10,19 @@ namespace Bootloader {
class Interface {
private:
static void drawImage(KDContext* ctx, const Image* image, int offset);
static void drawImage(KDContext * ctx, const Image * image, int offset);
public:
static void draw();
static void drawLoading();
static void drawHeader();
static void drawMenu();
static void drawFlasher();
static void drawAbout();
static void drawCrash(const char * error);
static void drawRecovery();
static void drawInstallerSelection();
static void drawBLUpdate();
static void drawEpsilonAdvertisement();
};

64
bootloader/itoa.cpp Normal file
View File

@@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include <bootloader/itoa.h>
// https://www.techiedelight.com/implement-itoa-function-in-c/
// Function to swap two numbers
void swap(char *x, char *y) {
char t = *x; *x = *y; *y = t;
}
// Function to reverse `buffer[i…j]`
char* reverse(char *buffer, int i, int j)
{
while (i < j) {
swap(&buffer[i++], &buffer[j--]);
}
return buffer;
}
// Iterative function to implement `itoa()` function in C
char* Bootloader::Utility::itoa(int value, char* buffer, int base)
{
// invalid input
if (base < 2 || base > 32) {
return buffer;
}
// consider the absolute value of the number
int n = abs(value);
int i = 0;
while (n)
{
int r = n % base;
if (r >= 10) {
buffer[i++] = 65 + (r - 10);
}
else {
buffer[i++] = 48 + r;
}
n = n / base;
}
// if the number is 0
if (i == 0) {
buffer[i++] = '0';
}
// If the base is 10 and the value is negative, the resulting string
// is preceded with a minus sign (-)
// With any other base, value is always considered unsigned
if (value < 0 && base == 10) {
buffer[i++] = '-';
}
buffer[i] = '\0'; // null terminate string
// reverse the string and return it
return reverse(buffer, 0, i - 1);
}

11
bootloader/itoa.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef _BOOTLOADER_ITOA_H_
#define _BOOTLOADER_ITOA_H_
namespace Bootloader {
class Utility {
public:
static char * itoa(int value, char * result, int base);
};
}
#endif // _BOOTLOADER_ITOA_H_

View File

@@ -22,4 +22,18 @@ const void(*KernelHeader::startPointer() const)() {
return m_startPointer;
}
const bool KernelHeader::isNewVersion() const {
int sum = 0;
for (int i = 0; i < 2; i++) {
sum += m_version[i] * (5 - i);
}
char newVersion[] = "16";
int min = 0;
for (int i = 0; i < 2; i++) {
min += newVersion[i] * (5 - i);
}
return sum >= min;
}
}

View File

@@ -10,6 +10,7 @@ public:
const char * version() const;
const char * patchLevel() const;
const bool isValid() const;
const bool isNewVersion() const;
const uint32_t* stackPointer() const;
const void(*startPointer() const)();

View File

@@ -3,40 +3,47 @@
#include <assert.h>
#include <bootloader/boot.h>
#include <bootloader/interface.h>
#include <bootloader/slot.h>
#include <bootloader/slot_exam_mode.h>
#include <bootloader/recovery.h>
#include <ion/src/device/shared/drivers/flash.h>
__attribute__ ((noreturn)) void ion_main(int argc, const char * const argv[]) {
// Clear the screen
Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorBlack);
Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorWhite);
// Initialize the backlight
Ion::Backlight::init();
// Set the mode to slot A if undefined
if (Bootloader::Boot::mode() == Bootloader::BootMode::Unknown) {
Bootloader::Boot::setMode(Bootloader::BootMode::SlotA);
// We check if there is a slot in exam_mode
bool isSlotA = Bootloader::Slot::A().kernelHeader()->isValid();
if (isSlotA) {
Bootloader::ExamMode::ExamMode SlotAExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotAExamMode(Bootloader::Slot::A().kernelHeader()->isNewVersion());
if (SlotAExamMode != Bootloader::ExamMode::ExamMode::Off && SlotAExamMode != Bootloader::ExamMode::ExamMode::Unknown) {
// We boot the slot in exam_mode
Bootloader::Slot::A().boot();
}
}
// Handle rebooting to bootloader
if (Bootloader::Boot::mode() == Bootloader::BootMode::SlotABootloader) {
Bootloader::Boot::setMode(Bootloader::BootMode::SlotA);
Bootloader::Boot::bootloader();
} else if (Bootloader::Boot::mode() == Bootloader::BootMode::SlotBBootloader) {
Bootloader::Boot::setMode(Bootloader::BootMode::SlotB);
Bootloader::Boot::bootloader();
bool isSlotB = Bootloader::Slot::B().kernelHeader()->isValid();
if (isSlotB) {
Bootloader::ExamMode::ExamMode SlotBExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotBExamMode(Bootloader::Slot::B().kernelHeader()->isNewVersion());
if (SlotBExamMode != Bootloader::ExamMode::ExamMode::Off && SlotBExamMode != Bootloader::ExamMode::ExamMode::Unknown && isSlotB) {
// We boot the slot in exam_mode
Bootloader::Slot::B().boot();
}
}
uint64_t scan = Ion::Keyboard::scan();
// Reset+4 => Launch bootloader
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Four)) {
Bootloader::Boot::bootloader();
// Reset+1 => Launch slot A
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::One)) {
Bootloader::Boot::setMode(Bootloader::BootMode::SlotA);
// Reset+2 => Launch slot B
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Two)) {
Bootloader::Boot::setMode(Bootloader::BootMode::SlotB);
if (Bootloader::Recovery::has_crashed()) {
Bootloader::Recovery::recover_data();
}
Bootloader::Interface::drawLoading();
// Boot the firmware
Bootloader::Boot::boot();
}

60
bootloader/messages.h Normal file
View File

@@ -0,0 +1,60 @@
#ifndef BOOTLOADER_INTERFACE_MESSAGES_H
#define BOOTLOADER_INTERFACE_MESSAGES_H
namespace Bootloader {
class Messages {
public:
// TODO: Remove it when this fork will be updated
#define UPSILON_VERSION "1.0.0-dev"
#ifdef UPSILON_VERSION
constexpr static const char * mainTitle = "Upsilon Calculator";
#elif defined OMEGA_VERSION
constexpr static const char * mainTitle = "Omega Calculator";
#else
constexpr static const char * mainTitle = "NumWorks Calculator";
#endif
constexpr static const char * mainMenuTitle = "Select a slot";
constexpr static const char * upsilon = "-- Upsilon ";
constexpr static const char * khi = "-- Khi ";
constexpr static const char * omega = "-- Omega ";
constexpr static const char * epsilon = "-- Epsilon ";
constexpr static const char * invalidSlot = "X - Invalid slot";
constexpr static const char * dfuText = "4 - Installer Mode";
constexpr static const char * aboutText = "5 - About";
constexpr static const char * rebootText = "6 - Reboot";
constexpr static const char * dfuSubtitle = "Waiting for Slots...";
constexpr static const char * crashTitle = "BOOTLOADER CRASH";
constexpr static const char * crashText1 = "The bootloader has crashed.";
constexpr static const char * crashText2 = "Please restart the calculator.";
constexpr static const char * recoveryTitle = "Recovery mode";
constexpr static const char * recoveryText1 = "The bootloader has detected a crash.";
constexpr static const char * recoveryText2 = "Plug the calculator to a device capable of";
constexpr static const char * recoveryText3 = "accessing the internal storage.";
constexpr static const char * recoveryText4 = "Press Back to continue.";
constexpr static const char * recoveryText5 = "(you will not be able to recover your data !)";
constexpr static const char * installerSelectionTitle = "Installer Mode";
constexpr static const char * installerText1 = "Please select a mode:";
constexpr static const char * installerText2 = "1. Flash slots";
constexpr static const char * installerText3 = "2. Flash the bootloader";
constexpr static const char * bootloaderSubtitle = "Waiting for the bootloader...";
constexpr static const char * epsilonWarningTitle = "Epsilon Slot";
constexpr static const char * epsilonWarningText1 = "!! WARNING !! ";
constexpr static const char * epsilonWarningText2 = "This version of epsilon";
constexpr static const char * epsilonWarningText3 = "can lock the calculator.";
constexpr static const char * epsilonWarningText4 = "Proceed the boot ?";
constexpr static const char * epsilonWarningText5 = "EXE - Yes";
constexpr static const char * epsilonWarningText6 = "BACK - No";
constexpr static const char * bootloaderVersion = "Version 1.0.0 - FREEDOM";
//USB NAMES
constexpr static const char * upsilonBootloader = "Upsilon Bootloader";
constexpr static const char * upsilonRecovery = "Upsilon Recovery";
constexpr static const char * bootloaderUpdate = "Bootloader Update";
};
};
#endif

130
bootloader/recovery.cpp Normal file
View File

@@ -0,0 +1,130 @@
#include <bootloader/recovery.h>
#include <ion.h>
#include <ion/src/device/n0110/drivers/power.h>
#include <ion/src/device/shared/drivers/reset.h>
#include <ion/src/device/shared/drivers/board.h>
#include <assert.h>
#include <bootloader/interface.h>
#include <bootloader/slot.h>
#include <bootloader/usb_data.h>
constexpr static uint32_t MagicStorage = 0xEE0BDDBA;
void Bootloader::Recovery::crash_handler(const char *error) {
Ion::Device::Board::shutdownPeripherals(true);
Ion::Device::Board::initPeripherals(false);
Ion::Timing::msleep(100);
Ion::Backlight::init();
Ion::Backlight::setBrightness(180);
Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorWhite);
Interface::drawCrash(error);
while (true) {
}
}
bool Bootloader::Recovery::has_crashed() {
bool isA = Bootloader::Slot::A().kernelHeader()->isValid() && Bootloader::Slot::A().userlandHeader()->isValid();
bool isB = Bootloader::Slot::B().kernelHeader()->isValid() && Bootloader::Slot::B().userlandHeader()->isValid();
bool isACrashed = false;
bool isBCrashed = false;
if (isA) {
const uint32_t * storage = (uint32_t *)Bootloader::Slot::A().userlandHeader()->storageAddress();
isACrashed = *storage == MagicStorage;
}
if (isB) {
const uint32_t * storage = (uint32_t *)Bootloader::Slot::B().userlandHeader()->storageAddress();
isBCrashed = *storage == MagicStorage;
}
return (isACrashed || isBCrashed);
}
Bootloader::Recovery::CrashedSlot Bootloader::Recovery::getSlotConcerned() {
bool isA = Bootloader::Slot::A().kernelHeader()->isValid() && Bootloader::Slot::A().userlandHeader()->isValid();
bool isB = Bootloader::Slot::B().kernelHeader()->isValid() && Bootloader::Slot::B().userlandHeader()->isValid();
bool isACrashed = false;
bool isBCrashed = false;
if (isA) {
const uint32_t * storage = (uint32_t *)Bootloader::Slot::A().userlandHeader()->storageAddress();
isACrashed = *storage == MagicStorage;
}
if (isB) {
const uint32_t * storage = (uint32_t *)Bootloader::Slot::B().userlandHeader()->storageAddress();
isBCrashed = *storage == MagicStorage;
}
assert(isACrashed || isBCrashed);
if (isACrashed) {
return CrashedSlot(Bootloader::Slot::A().userlandHeader()->storageSize(), Bootloader::Slot::A().userlandHeader()->storageAddress());
} else {
return CrashedSlot(Bootloader::Slot::B().userlandHeader()->storageSize(), Bootloader::Slot::B().userlandHeader()->storageAddress());
}
}
void Bootloader::Recovery::recover_data() {
Ion::Device::Board::initPeripherals(false);
Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorWhite);
Ion::Backlight::init();
Interface::drawRecovery();
uint64_t scan = 0;
USBData udata = USBData::Recovery((uint32_t)getSlotConcerned().getStorageAddress(), (uint32_t)getSlotConcerned().getStorageSize());
while (scan != Ion::Keyboard::State(Ion::Keyboard::Key::Back)) {
Ion::USB::enable();
do {
scan = Ion::Keyboard::scan();
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back)) {
Ion::USB::disable();
}
} while (!Ion::USB::isEnumerated() && scan != Ion::Keyboard::State(Ion::Keyboard::Key::Back));
if (scan != Ion::Keyboard::State(Ion::Keyboard::Key::Back)) {
Ion::USB::DFU(true, &udata);
}
}
// Invalidate storage header
*(uint32_t *)(getSlotConcerned().getStorageAddress()) = (uint32_t)0x0;
}
void Bootloader::Recovery::debug() {
bool isA = Bootloader::Slot::A().kernelHeader()->isValid() && Bootloader::Slot::A().userlandHeader()->isValid();
bool isB = Bootloader::Slot::B().kernelHeader()->isValid() && Bootloader::Slot::B().userlandHeader()->isValid();
if (isA) {
const uint32_t * storage = (uint32_t *)Bootloader::Slot::A().userlandHeader()->storageAddress();
*(uint32_t *)(0x20000070) = *storage;
if (*storage == MagicStorage) {
*(uint32_t *)(0x20000070 + (sizeof(uint32_t))) = 0xDEADBEEF;
}
}
if (isB) {
const uint32_t * storage = (uint32_t *)Bootloader::Slot::B().userlandHeader()->storageAddress();
*(uint32_t *)(0x20000070 + 2*(sizeof(uint32_t))) = *storage;
if (*storage == MagicStorage) {
*(uint32_t *)(0x20000070 + 3*(sizeof(uint32_t))) = 0xDEADBEEF;
}
}
if (has_crashed()) {
*(uint32_t *)(0x20000070 + 4*(sizeof(uint32_t))) = 0xDEADBEEF;
} else {
*(uint32_t *)(0x20000070 + 4*(sizeof(uint32_t))) = 0xBEEFDEAD;
}
}

32
bootloader/recovery.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef BOOTLOADER_RECOVERY_H_
#define BOOTLOADER_RECOVERY_H_
#include <stdint.h>
#include <stddef.h>
namespace Bootloader {
class Recovery {
public:
class CrashedSlot {
public:
CrashedSlot(const size_t size, const void * address) : m_storageAddress(address), m_storageSize(size) {}
const size_t getStorageSize() const { return m_storageSize; }
const void * getStorageAddress() const { return m_storageAddress; }
private:
const void * m_storageAddress;
const size_t m_storageSize;
};
static CrashedSlot getSlotConcerned();
static void crash_handler(const char * error);
static void recover_data();
static bool has_crashed();
static void debug();
};
};
#endif //BOOTLOADER_RECOVERY_H_

View File

@@ -1,5 +1,7 @@
#include <bootloader/slot.h>
#include <ion/src/device/shared/drivers/board.h>
#include <ion/src/device/shared/drivers/flash.h>
#include <bootloader/boot.h>
extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void));
@@ -13,6 +15,10 @@ const Slot Slot::B() {
return Slot(0x90400000);
}
const Slot Slot::Khi() {
return Slot(0x90180000);
}
const KernelHeader* Slot::kernelHeader() const {
return m_kernelHeader;
}
@@ -22,6 +28,17 @@ const UserlandHeader* Slot::userlandHeader() const {
}
[[ noreturn ]] void Slot::boot() const {
if (m_address == 0x90000000) {
// If we are booting from slot A, we need to lock the slot B
Ion::Device::Flash::LockSlotB();
} else {
// If we are booting from slot B, we need to lock the slot A (and Khi)
Ion::Device::Flash::LockSlotA();
}
Boot::lockInternal();
// Configure the MPU for the booted firmware
Ion::Device::Board::bootloaderMPU();

View File

@@ -13,7 +13,8 @@ class Slot {
public:
Slot(uint32_t address) :
m_kernelHeader(reinterpret_cast<KernelHeader*>(address)),
m_userlandHeader(reinterpret_cast<UserlandHeader*>(address + 64 * 1024)) { }
m_userlandHeader(reinterpret_cast<UserlandHeader*>(address + 64 * 1024)),
m_address(address) { }
const KernelHeader* kernelHeader() const;
const UserlandHeader* userlandHeader() const;
@@ -21,10 +22,12 @@ public:
static const Slot A();
static const Slot B();
static const Slot Khi();
private:
const KernelHeader* m_kernelHeader;
const UserlandHeader* m_userlandHeader;
const uint32_t m_address;
};

View File

@@ -0,0 +1,166 @@
#include <bootloader/slot_exam_mode.h>
#include <assert.h>
#include <ion.h>
#include <ion/src/device/shared/drivers/flash.h>
namespace Bootloader {
namespace ExamMode {
/* The exam mode is written in flash so that it is resilient to resets.
* We erase the dedicated flash sector (all bits written to 1) and, upon
* deactivating or activating standard, nosym or Dutch exam mode we write one, two or tree
* bits to 0. To determine in which exam mode we are, we count the number of
* leading 0 bits. If it is equal to:
* - 0[3]: the exam mode is off;
* - 1[3]: the standard exam mode is activated;
* - 2[3]: the NoSym exam mode is activated;
* - 3[3]: the Dutch exam mode is activated;
* - 4[3]: the NoSymNoText exam mode is activated. */
/* significantExamModeAddress returns the first uint32_t * in the exam mode
* flash sector that does not point to 0. If this flash sector has only 0s or
* if it has only one 1, it is erased (to 1) and significantExamModeAddress
* returns the start of the sector. */
constexpr static size_t numberOfBitsInByte = 8;
// if i = 0b000011101, firstOneBitInByte(i) returns 5
size_t numberOfBitsAfterLeadingZeroes(int i) {
int minShift = 0;
int maxShift = numberOfBitsInByte;
while (maxShift > minShift+1) {
int shift = (minShift + maxShift)/2;
int shifted = i >> shift;
if (shifted == 0) {
maxShift = shift;
} else {
minShift = shift;
}
}
return maxShift;
}
uint8_t * SignificantSlotAExamModeAddress(bool newVersion) {
uint32_t * persitence_start_32 = (uint32_t *)SlotsExamMode::getSlotAStartExamAddress(newVersion);
uint32_t * persitence_end_32 = (uint32_t *)SlotsExamMode::getSlotAEndExamAddress(newVersion);
if (!newVersion) {
assert((persitence_end_32 - persitence_start_32) % 4 == 0);
while (persitence_start_32 < persitence_end_32 && *persitence_start_32 == 0x0) {
// Scan by groups of 32 bits to reach first non-zero bit
persitence_start_32++;
}
uint8_t * persitence_start_8 = (uint8_t *)persitence_start_32;
uint8_t * persitence_end_8 = (uint8_t *)persitence_end_32;
while (persitence_start_8 < persitence_end_8 && *persitence_start_8 == 0x0) {
// Scan by groups of 8 bits to reach first non-zero bit
persitence_start_8++;
}
if (persitence_start_8 == persitence_end_8
// we can't toggle from 0[3] to 2[3] when there is only one 1 bit in the whole sector
|| (persitence_start_8 + 1 == persitence_end_8 && *persitence_start_8 == 1)) {
assert(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotAStartExamAddress(newVersion)) >= 0);
Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotAStartExamAddress(newVersion)));
return (uint8_t *)SlotsExamMode::getSlotAStartExamAddress(newVersion);
}
return persitence_start_8;
} else {
persitence_end_32 = persitence_end_32 - 1;
while (persitence_end_32 - (uint32_t)(10 / 8) >= persitence_end_32 && *persitence_end_32 == 0xFFFFFFFF) {
persitence_end_32 -= 1;
}
uint8_t * start = reinterpret_cast<uint8_t *>(persitence_start_32);
uint8_t * end = reinterpret_cast<uint8_t *>(persitence_end_32 + 1) - 1;
while (end >= start + 2 && *end == 0xFF) {
end -= 1;
}
return end - 1;
}
}
uint8_t * SignificantSlotBExamModeAddress(bool newVersion) {
uint32_t * persitence_start_32 = (uint32_t *)SlotsExamMode::getSlotBStartExamAddress(newVersion);
uint32_t * persitence_end_32 = (uint32_t *)SlotsExamMode::getSlotBEndExamAddress(newVersion);
if (!newVersion) {
assert((persitence_end_32 - persitence_start_32) % 4 == 0);
while (persitence_start_32 < persitence_end_32 && *persitence_start_32 == 0x0) {
// Scan by groups of 32 bits to reach first non-zero bit
persitence_start_32++;
}
uint8_t * persitence_start_8 = (uint8_t *)persitence_start_32;
uint8_t * persitence_end_8 = (uint8_t *)persitence_end_32;
while (persitence_start_8 < persitence_end_8 && *persitence_start_8 == 0x0) {
// Scan by groups of 8 bits to reach first non-zero bit
persitence_start_8++;
}
if (persitence_start_8 == persitence_end_8
// we can't toggle from 0[3] to 2[3] when there is only one 1 bit in the whole sector
|| (persitence_start_8 + 1 == persitence_end_8 && *persitence_start_8 == 1)) {
assert(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotBStartExamAddress(newVersion)) >= 0);
Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotBStartExamAddress(newVersion)));
return (uint8_t *)SlotsExamMode::getSlotBStartExamAddress(newVersion);
}
return persitence_start_8;
} else {
persitence_end_32 = persitence_end_32 - 1;
while (persitence_end_32 - (uint32_t)(10 / 8) >= persitence_end_32 && *persitence_end_32 == 0xFFFFFFFF) {
persitence_end_32 -= 1;
}
uint8_t * start = reinterpret_cast<uint8_t *>(persitence_start_32);
uint8_t * end = reinterpret_cast<uint8_t *>(persitence_end_32 + 1) - 1;
while (end >= start + 2 && *end == 0xFF) {
end -= 1;
}
return end - 1;
}
}
uint8_t SlotsExamMode::FetchSlotAExamMode(bool newVersion) {
uint8_t * readingAddress = SignificantSlotAExamModeAddress(newVersion);
if (!newVersion) {
// Count the number of 0[3] before reading address
uint32_t nbOfZerosBefore = ((readingAddress - (uint8_t *)getSlotAStartExamAddress(newVersion)) * numberOfBitsInByte) % 4;
// Count the number of 0[3] at reading address
size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*readingAddress)) % 4;
return (nbOfZerosBefore + numberOfLeading0) % 4;
} else {
return *((uint8_t *)readingAddress);
}
}
uint8_t SlotsExamMode::FetchSlotBExamMode(bool newVersion) {
uint8_t * readingAddress = SignificantSlotBExamModeAddress(newVersion);
if (!newVersion) {
// Count the number of 0[3] before reading address
uint32_t nbOfZerosBefore = ((readingAddress - (uint8_t *)getSlotBStartExamAddress(newVersion)) * numberOfBitsInByte) % 4;
// Count the number of 0[3] at reading address
size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*readingAddress)) % 4;
return (nbOfZerosBefore + numberOfLeading0) % 4;
} else {
return *((uint8_t *)readingAddress);
}
}
uint32_t SlotsExamMode::getSlotAStartExamAddress(bool newVersion) {
return newVersion ? SlotAExamModeBufferStartNewVersions : SlotAExamModeBufferStartOldVersions;
}
uint32_t SlotsExamMode::getSlotAEndExamAddress(bool newVersion) {
return newVersion ? SlotAExamModeBufferEndNewVersions : SlotAExamModeBufferEndOldVersions;
}
uint32_t SlotsExamMode::getSlotBStartExamAddress(bool newVersion) {
return newVersion ? SlotBExamModeBufferStartNewVersions : SlotBExamModeBufferStartOldVersions;
}
uint32_t SlotsExamMode::getSlotBEndExamAddress(bool newVersion) {
return newVersion ? SlotBExamModeBufferEndNewVersions : SlotBExamModeBufferEndOldVersions;
}
}
}

View File

@@ -0,0 +1,47 @@
#ifndef BOOTLOADER_EXAM_MODE_H
#define BOOTLOADER_EXAM_MODE_H
extern "C" {
#include <stdint.h>
}
namespace Bootloader {
namespace ExamMode {
static const uint32_t SlotAExamModeBufferStartOldVersions = 0x90001000;
static const uint32_t SlotAExamModeBufferEndOldVersions = 0x90003000;
static const uint32_t SlotAExamModeBufferStartNewVersions = 0x903f0000;
static const uint32_t SlotAExamModeBufferEndNewVersions = 0x90400000;
static const uint32_t SlotBExamModeBufferStartOldVersions = 0x90401000;
static const uint32_t SlotBExamModeBufferEndOldVersions = 0x90403000;
static const uint32_t SlotBExamModeBufferStartNewVersions = 0x907f0000;
static const uint32_t SlotBExamModeBufferEndNewVersions = 0x90800000;
class SlotsExamMode{
public:
static uint8_t FetchSlotAExamMode(bool newVersion);
static uint8_t FetchSlotBExamMode(bool newVerion);
static uint32_t getSlotAStartExamAddress(bool newVersion);
static uint32_t getSlotAEndExamAddress(bool newVersion);
static uint32_t getSlotBStartExamAddress(bool newVersion);
static uint32_t getSlotBEndExamAddress(bool newVersion);
};
enum class ExamMode : int8_t {
Unknown = -1,
Off = 0,
Standard = 1,
NoSym = 2,
NoSymNoText = 3,
Dutch = 4,
};
}
}
#endif

View File

@@ -36,6 +36,7 @@ void* CustomTrampolines[CUSTOM_TRAMPOLINES_COUNT]
__attribute__((used))
= {
(void*) Bootloader::Boot::mode,
// This function doesn't do anything ...
(void*) Bootloader::Boot::setMode
};

39
bootloader/usb_data.cpp Normal file
View File

@@ -0,0 +1,39 @@
#include <bootloader/usb_data.h>
#include <stdint.h>
#include <stddef.h>
#include <bootloader/itoa.h>
#include <stdlib.h>
#include <string.h>
#include <bootloader/messages.h>
extern "C" {
}
static char data[255];
const char * Bootloader::USBData::buildStringDescriptor(StringHeader header, uint32_t startAddress, uint32_t size) {
strlcpy(data, header.getString(), sizeof(data));
Bootloader::Utility::itoa((int32_t)startAddress, &data[strlen(header.getString())], 16);
data[strlen(header.getString()) + 8] = '/';
data[strlen(header.getString()) + 8 + 1] = '0';
data[strlen(header.getString()) + 8 + 2] = '1';
data[strlen(header.getString()) + 8 + 3] = '*';
data[strlen(header.getString()) + 8 + 4] = '0';
Bootloader::Utility::itoa((int32_t)size/1024, &data[strlen(header.getString()) + 8 + 5], 10);
data[strlen(header.getString()) + 8 + 5 + 2] = 'K';
data[strlen(header.getString()) + 8 + 5 + 2 + 1] = 'g';
data[strlen(header.getString()) + 8 + 5 + 2 + 2] = '\0';
return &data[0];
}
const Bootloader::USBData Bootloader::USBData::DEFAULT() {
return USBData("@Flash/0x90000000/08*004Kg,01*032Kg,63*064Kg,64*064Kg", Messages::upsilonBootloader, DFUData());
}
const Bootloader::USBData Bootloader::USBData::BLUPDATE() {
return USBData("@Flash/0x08000000/04*016Kg", Messages::bootloaderUpdate, DFUData(true, false));
}
Bootloader::USBData Bootloader::USBData::Recovery(uint32_t startAddress, uint32_t size) {
return USBData(buildStringDescriptor(StringHeader::SRAM(), startAddress, size), Messages::upsilonRecovery, DFUData(false, false));
}

56
bootloader/usb_data.h Normal file
View File

@@ -0,0 +1,56 @@
#ifndef BOOTLOADER_USB_DATA_H_
#define BOOTLOADER_USB_DATA_H_
#include <stdint.h>
#include <stddef.h>
namespace Bootloader {
class DFUData {
public:
DFUData(bool unlockInternal = false, bool unlockExternal = true) : m_protectInternal(!unlockInternal), m_protectExternal(!unlockExternal) {};
bool isProtectedInternal() const { return m_protectInternal; }
bool isProtectedExternal() const { return m_protectExternal; }
private:
bool m_protectInternal;
bool m_protectExternal;
};
class USBData {
public:
class StringHeader{
public:
StringHeader(const char * string) : m_string(string) {};
const char * getString() const { return m_string; }
static const StringHeader Flash() { return StringHeader("@Flash/0x"); }
static const StringHeader SRAM() { return StringHeader("@SRAM/0x"); }
private:
const char * m_string;
};
USBData(const char * desc, const char * name, DFUData data = DFUData()) : m_stringDescriptor(desc), m_name(name), m_data(&data) {};
const char * stringDescriptor() const { return m_stringDescriptor; }
const char * getName() const { return m_name; }
DFUData * getData() const { return m_data; }
static const char * buildStringDescriptor(StringHeader header, uint32_t startAddress, uint32_t size);
static const USBData DEFAULT();
static const USBData BLUPDATE();
static USBData Recovery(uint32_t startAddress, uint32_t size);
private:
const char * m_stringDescriptor;
const char * m_name;
DFUData * m_data;
};
}
#endif //BOOTLOADER_USB_DATA_H_

View File

@@ -14,7 +14,7 @@ const bool UserlandHeader::isValid() const {
}
const bool UserlandHeader::isOmega() const {
return m_omegaMagicHeader == OmegaMagic && m_omegaMagicFooter == OmegaMagic;
return m_ohm_header == OmegaMagic && m_ohm_footer == OmegaMagic;
}
@@ -23,11 +23,19 @@ const char * UserlandHeader::omegaVersion() const {
}
const bool UserlandHeader::isUpsilon() const {
return m_upsilonMagicHeader == UpsilonMagic && m_upsilonMagicFooter == UpsilonMagic;
return m_ups_header == UpsilonMagic && m_ups_footer == UpsilonMagic;
}
const char * UserlandHeader::upsilonVersion() const {
return m_UpsilonVersion;
}
const void * UserlandHeader::storageAddress() const {
return m_storageAddressRAM;
}
const size_t UserlandHeader::storageSize() const {
return m_storageSizeRAM;
}
}

View File

@@ -15,6 +15,8 @@ public:
const char * omegaVersion() const;
const bool isUpsilon() const;
const char * upsilonVersion() const;
const void * storageAddress() const;
const size_t storageSize() const;
private:
UserlandHeader();
@@ -32,14 +34,14 @@ private:
uint32_t m_externalAppsRAMStart;
uint32_t m_externalAppsRAMEnd;
uint32_t m_footer;
uint32_t m_omegaMagicHeader;
uint32_t m_ohm_header;
const char m_omegaVersion[16];
const volatile char m_username[16];
uint32_t m_omegaMagicFooter;
uint32_t m_upsilonMagicHeader;
uint32_t m_ohm_footer;
uint32_t m_ups_header;
const char m_UpsilonVersion[16];
uint32_t m_osType;
uint32_t m_upsilonMagicFooter;
uint32_t m_ups_footer;
};
extern const UserlandHeader* s_userlandHeaderA;

View File

@@ -3,6 +3,8 @@ import os
MAGIK_CODE = [0x32, 0x30, 0x30, 0x36]
MAGIK_POS = 0x44F
# Disable Script
sys.exit(0)
if len(sys.argv) > 1:
ext_path = os.path.join(os.getcwd(), sys.argv[1])

View File

@@ -1,14 +1,14 @@
HANDY_TARGETS += test.external_flash.write test.external_flash.read bootloader
$(BUILD_DIR)/test.external_flash.%.$(EXE): LDSCRIPT = ion/test/device/n0110/external_flash_tests.ld
test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(default_kandinsky_src) $(poincare_src) $(ion_device_dfu_relegated_src) $(runner_src)
test_external_flash_src = $(ion_src) $(liba_src) $(libaxx_src) $(default_kandinsky_src) $(poincare_src) $(ion_device_dfu_relogated_src) $(runner_src)
$(BUILD_DIR)/test.external_flash.read.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_read_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_read_src))
$(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_external_flash_write_symbols.o $(call object_for,$(test_external_flash_src) $(test_ion_external_flash_write_src))
.PHONY: bootloader
bootloader: $(BUILD_DIR)/bootloader.bin
$(BUILD_DIR)/bootloader.$(EXE): $(call flavored_object_for,$(bootloader_src),usbxip)
$(BUILD_DIR)/bootloader.$(EXE): LDSCRIPT = ion/src/device/n0110/internal_flash.ld
$(BUILD_DIR)/bootloader.$(EXE): LDSCRIPT = ion/src/device/bootloader/internal_flash.ld
.PHONY: %_flash
%_flash: $(BUILD_DIR)/%.dfu
@@ -36,4 +36,3 @@ binpack: $(BUILD_DIR)/flasher.light.bin $(BUILD_DIR)/epsilon.onboarding.two_bina
cp $(BUILD_DIR)/epsilon.onboarding.internal.bin $(BUILD_DIR)/epsilon.onboarding.external.bin $(BUILD_DIR)/binpack
cd $(BUILD_DIR) && for binary in flasher.light.bin epsilon.onboarding.internal.bin epsilon.onboarding.external.bin; do shasum -a 256 -b binpack/$${binary} > binpack/$${binary}.sha256;done
cd $(BUILD_DIR) && tar cvfz binpack-$(MODEL)-`git rev-parse HEAD | head -c 7`.tgz binpack/*
$(PYTHON) build/device/secure_ext.py $(BUILD_DIR)/epsilon.onboarding.external.bin

View File

@@ -140,8 +140,6 @@ protected:
};
RecordIterator end() const { return RecordIterator(nullptr); }
mutable Record m_lastRecordRetrieved;
mutable char * m_lastRecordRetrievedPointer;
private:
constexpr static uint32_t Magic = 0xEE0BDDBA;
constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8);
@@ -174,6 +172,9 @@ private:
char m_buffer[k_storageSize];
uint32_t m_magicFooter;
StorageDelegate * m_delegate;
public:
mutable Record m_lastRecordRetrieved;
mutable char * m_lastRecordRetrievedPointer;
};
/* Some apps memoize records and need to be notified when a record might have

View File

@@ -8,7 +8,7 @@ bool isPlugged();
bool isEnumerated(); // Speed-enumerated, to be accurate
void clearEnumerationInterrupt();
void DFU(bool exitWithKeyboard = true, bool unlocked = false, int level = 0);
void DFU(bool exitWithKeyboard = true, void * data = nullptr);
void enable();
void disable();

View File

@@ -10,7 +10,7 @@ ifeq ($(EPSILON_TELEMETRY),1)
ion_src += ion/src/shared/telemetry_console.cpp
endif
ion_device_src += ion/src/shared/collect_registers.cpp
ion_src += ion/src/shared/collect_registers.cpp
IN_FACTORY ?= 0

View File

@@ -6,15 +6,6 @@
#include <drivers/rtc.h>
#include <drivers/reset.h>
#include <drivers/timing.h>
#include <drivers/power.h>
#include <drivers/wakeup.h>
#include <drivers/battery.h>
#include <drivers/usb.h>
#include <drivers/led.h>
#include <kandinsky.h>
#include <regs/config/pwr.h>
#include <regs/config/rcc.h>
#include <regs/regs.h>
typedef void (*cxx_constructor)();
@@ -32,6 +23,15 @@ extern "C" {
extern char _isr_vector_table_end_ram;
}
void __attribute__((noinline)) abort() {
#ifdef NDEBUG
Ion::Device::Reset::core();
#else
while (1) {
}
#endif
}
/* In order to ensure that this method is execute from the external flash, we
* forbid inlining it.*/
@@ -68,153 +68,6 @@ static void __attribute__((noinline)) jump_to_external_flash() {
external_flash_start();
}
void __attribute__((noinline)) abort_init() {
Ion::Device::Board::shutdownPeripherals(true);
Ion::Device::Board::initPeripherals(false);
Ion::Timing::msleep(100);
Ion::Backlight::init();
Ion::LED::setColor(KDColorRed);
Ion::Backlight::setBrightness(180);
}
void __attribute__((noinline)) abort_economy() {
int brightness = Ion::Backlight::brightness();
bool plugged = Ion::USB::isPlugged();
while (brightness > 0) {
brightness--;
Ion::Backlight::setBrightness(brightness);
Ion::Timing::msleep(50);
if(plugged || (!plugged && Ion::USB::isPlugged())){
Ion::Backlight::setBrightness(180);
return;
}
}
Ion::Backlight::shutdown();
while (1) {
Ion::Device::Power::sleepConfiguration();
Ion::Device::WakeUp::onUSBPlugging();
Ion::Device::WakeUp::onChargingEvent();
Ion::Device::Power::internalFlashSuspend(true);
if (!plugged && Ion::USB::isPlugged()) {
break;
}
plugged = Ion::USB::isPlugged();
};
Ion::Device::Board::setStandardFrequency(Ion::Device::Board::Frequency::High);
Ion::Backlight::init();
Ion::Backlight::setBrightness(180);
}
void __attribute__((noinline)) abort_sleeping() {
if (Ion::Battery::level() != Ion::Battery::Charge::EMPTY) {
return;
}
// we don't use Ion::Power::suspend because we don't want to move the exam buffer into the internal
Ion::Device::Board::shutdownPeripherals(true);
bool plugged = Ion::USB::isPlugged();
while (1) {
Ion::Device::Battery::initGPIO();
Ion::Device::USB::initGPIO();
Ion::Device::LED::init();
Ion::Device::Power::sleepConfiguration();
Ion::Device::Board::shutdownPeripherals(true);
Ion::Device::WakeUp::onUSBPlugging();
Ion::Device::WakeUp::onChargingEvent();
Ion::Device::Power::internalFlashSuspend(true);
Ion::Device::USB::initGPIO();
if (!plugged && Ion::USB::isPlugged()) {
break;
}
plugged = Ion::USB::isPlugged();
}
Ion::Device::Board::setStandardFrequency(Ion::Device::Board::Frequency::High);
abort_init();
}
void __attribute__((noinline)) abort_core(const char * text) {
Ion::Timing::msleep(100);
int counting;
while (true) {
counting = 0;
if (Ion::Battery::level() == Ion::Battery::Charge::EMPTY) {
abort_sleeping();
abort_screen(text);
}
Ion::USB::enable();
Ion::Battery::Charge previous_state = Ion::Battery::level();
while (!Ion::USB::isEnumerated()) {
if (Ion::Battery::level() == Ion::Battery::Charge::LOW) {
if (previous_state != Ion::Battery::Charge::LOW) {
previous_state = Ion::Battery::Charge::LOW;
counting = 0;
}
Ion::Timing::msleep(500);
if (counting >= 20) {
abort_sleeping();
abort_screen(text);
counting = -1;
}
counting++;
} else {
if (previous_state == Ion::Battery::Charge::LOW) {
previous_state = Ion::Battery::level();
counting = 0;
}
Ion::Timing::msleep(100);
if (counting >= 300) {
abort_economy();
counting = -1;
}
counting++;
}
}
Ion::USB::DFU(false, false, 0);
}
}
void __attribute__((noinline)) abort_screen(const char * text){
KDRect screen = KDRect(0, 0, Ion::Display::Width, Ion::Display::Height);
Ion::Display::pushRectUniform(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height), KDColor::RGB24(0xffffff));
KDContext* ctx = KDIonContext::sharedContext();
ctx->setOrigin(KDPointZero);
ctx->setClippingRect(screen);
ctx->drawString("UPSILON CRASH", KDPoint(90, 10), KDFont::LargeFont, KDColorRed, KDColor::RGB24(0xffffff));
ctx->drawString("An error occurred", KDPoint(10, 30), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("If you have some important data, please", KDPoint(10, 45), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("use bit.ly/upsiBackup to backup them.", KDPoint(10, 60), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("YOU WILL LOSE ALL YOUR DATA", KDPoint(10, 85), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("→ You can try to reboot by presssing the", KDPoint(10, 110), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("reset button at the back of the calculator", KDPoint(10, 125), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("→ If Upsilon keeps crashing, you can connect", KDPoint(10, 140), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("the calculator to a computer or a phone", KDPoint(10, 160), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("and try to reinstall Upsilon", KDPoint(10, 175), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString(text, KDPoint(220, 200), KDFont::SmallFont, KDColorRed, KDColor::RGB24(0xffffff));
}
void __attribute__((noinline)) abort() {
abort_init();
abort_screen("HARDFAULT");
abort_core("HARDFAULT");
}
void __attribute__((noinline)) nmi_abort() {
abort_init();
abort_screen("NMIFAULT");
abort_core("NMIFAULT");
}
void __attribute__((noinline)) bf_abort() {
abort_init();
abort_screen("BUSFAULT");
abort_core("BUSFAULT");
}
void __attribute__((noinline)) uf_abort() {
abort_init();
abort_screen("USAGEFAULT");
abort_core("USAGEFAULT");
}
/* When 'start' is executed, the external flash is supposed to be shutdown. We
* thus forbid inlining to prevent executing this code from external flash
* (just in case 'start' was to be called from the external flash). */
@@ -225,6 +78,8 @@ void __attribute__((noinline)) start() {
/* Initialize the FPU as early as possible.
* For example, static C++ objects are very likely to manipulate float values */
Ion::Device::Board::initFPU();
/* Copy data section to RAM
* The data section is R/W but its initialization value matters. It's stored
* in Flash, but linked as if it were in RAM. Now's our opportunity to copy

View File

@@ -89,10 +89,15 @@ public:
public:
using Register8::Register8;
REGS_BOOL_FIELD_R(BUSY, 0);
REGS_BOOL_FIELD(BP, 2);
REGS_BOOL_FIELD(BP1, 3);
REGS_BOOL_FIELD(BP2, 4);
REGS_BOOL_FIELD(TB, 5);
};
class StatusRegister2 : public Register8 {
public:
using Register8::Register8;
REGS_BOOL_FIELD(SRP1, 0);
REGS_BOOL_FIELD(QE, 1);
};
};
@@ -428,6 +433,46 @@ void unlockFlash() {
wait();
}
void LockSlotA() {
unset_memory_mapped_mode();
unlockFlash();
send_command(Command::WriteEnable);
wait();
ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0);
ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0);
ExternalFlashStatusRegister::StatusRegister2 currentStatusRegister2(0);
send_read_command(Command::ReadStatusRegister2, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(&currentStatusRegister2), sizeof(currentStatusRegister2));
statusRegister2.setQE(currentStatusRegister2.getQE());
statusRegister2.setSRP1(true);
statusRegister1.setTB(true);
statusRegister1.setBP2(true);
statusRegister1.setBP1(true);
uint8_t registers[] = {statusRegister1.get(), statusRegister2.get()};
send_write_command(Command::WriteStatusRegister, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(registers), sizeof(registers), sOperatingModes101);
wait();
set_as_memory_mapped();
}
void LockSlotB() {
unset_memory_mapped_mode();
unlockFlash();
send_command(Command::WriteEnable);
wait();
ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0);
ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0);
ExternalFlashStatusRegister::StatusRegister2 currentStatusRegister2(0);
send_read_command(Command::ReadStatusRegister2, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(&currentStatusRegister2), sizeof(currentStatusRegister2));
statusRegister2.setQE(currentStatusRegister2.getQE());
statusRegister2.setSRP1(true);
statusRegister1.setTB(false);
statusRegister1.setBP2(true);
statusRegister1.setBP1(true);
uint8_t registers[] = {statusRegister1.get(), statusRegister2.get()};
send_write_command(Command::WriteStatusRegister, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(registers), sizeof(registers), sOperatingModes101);
wait();
set_as_memory_mapped();
}
void MassErase() {
if (Config::NumberOfSectors == 0) {
return;

View File

@@ -378,6 +378,13 @@ void WriteMemory(uint8_t * destination, const uint8_t * source, size_t length) {
void EraseSector(int i) {
asm("cpsid if");
(*reinterpret_cast<void(**)(int)>(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashEraseSector)))(i);
asm("cpsie if");
}
void LockSlotA() {
}
void LockSlotB() {
}
}

View File

@@ -0,0 +1,12 @@
namespace Ion {
namespace Device {
namespace USB {
const char* stringDescriptor() {
return "@Flash/0x90000000/08*004Kg,01*032Kg,63*064Kg,64*064Kg";
}
}
}
}

View File

@@ -0,0 +1,123 @@
/* Same as flash.ld but everything is linked in internal flash */
MEMORY {
INTERNAL_FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K
}
STACK_SIZE = 32K;
TRAMPOLINES_OFFSET = 0xE000;
CUSTOM_TRAMPOLINES_OFFSET = 64K - 64;
SECTIONS {
.isr_vector_table ORIGIN(INTERNAL_FLASH) : {
/* When booting, the STM32F412 fetches the content of address 0x0, and
* extracts from it various key infos: the initial value of the PC register
* (program counter), the initial value of the stack pointer, and various
* entry points to interrupt service routines. This data is called the ISR
* vector table.
*
* Note that address 0x0 is always an alias. It points to the beginning of
* Flash, SRAM, or integrated bootloader depending on the boot mode chosen.
* (This mode is chosen by setting the BOOTn pins on the chip).
*
* We're generating the ISR vector table in code because it's very
* convenient: using function pointers, we can easily point to the service
* routine for each interrupt. */
KEEP(*(.isr_vector_table))
} >INTERNAL_FLASH
.header : {
KEEP(*(.header))
} >INTERNAL_FLASH
.rodata : {
. = ALIGN(4);
*(.rodata)
*(.rodata.*)
} >INTERNAL_FLASH
.exam_mode_buffer : {
_exam_mode_buffer_start = .;
KEEP(*(.exam_mode_buffer))
/* We don't set it because we will not use it */
/* . = ORIGIN(INTERNAL_FLASH) + FLASH_SECOND_SECTOR_OFFSET + FLASH_SECOND_SECTOR_SIZE; */
_exam_mode_buffer_end = .;
} >INTERNAL_FLASH
.text : {
. = ALIGN(4);
*(.text)
*(.text.*)
} >INTERNAL_FLASH
.init_array : {
. = ALIGN(4);
_init_array_start = .;
KEEP (*(.init_array*))
_init_array_end = .;
} >INTERNAL_FLASH
.data : {
/* The data section is written to Flash but linked as if it were in RAM.
*
* This is required because its initial value matters (so it has to be in
* persistant memory in the first place), but it is a R/W area of memory
* so it will have to live in RAM upon execution (in linker lingo, that
* translates to the data section having a LMA in Flash and a VMA in RAM).
*
* This means we'll have to copy it from Flash to RAM on initialization.
* To do this, we'll need to know the source location of the data section
* (in Flash), the target location (in RAM), and the size of the section.
* That's why we're defining three symbols that we'll use in the initial-
* -ization routine. */
. = ALIGN(4);
_data_section_start_flash = LOADADDR(.data);
_data_section_start_ram = .;
*(.data)
*(.data.*)
_data_section_end_ram = .;
} >SRAM AT> INTERNAL_FLASH
.trampolines_table : {
. = ORIGIN(INTERNAL_FLASH) + TRAMPOLINES_OFFSET;
KEEP(*(.trampolines_table));
} > INTERNAL_FLASH
.custom_trampolines_table : {
. = ORIGIN(INTERNAL_FLASH) + CUSTOM_TRAMPOLINES_OFFSET;
KEEP(*(.custom_trampolines_table));
} > INTERNAL_FLASH
.bss : {
/* The bss section contains data for all uninitialized variables
* So like the .data section, it will go in RAM, but unlike the data section
* we don't care at all about an initial value.
*
* Before execution, crt0 will erase that section of memory though, so we'll
* need pointers to the beginning and end of this section. */
. = ALIGN(4);
_bss_section_start_ram = .;
*(.bss)
*(.bss.*)
/* The compiler may choose to allocate uninitialized global variables as
* COMMON blocks. This can be disabled with -fno-common if needed. */
*(COMMON)
_bss_section_end_ram = .;
} >SRAM
.heap : {
_heap_start = .;
/* Note: We don't increment "." here, we set it. */
. = (ORIGIN(SRAM) + LENGTH(SRAM) - STACK_SIZE);
_heap_end = .;
} >SRAM
.stack : {
. = ALIGN(8);
_stack_end = .;
. += (STACK_SIZE - 8);
. = ALIGN(8);
_stack_start = .;
} >SRAM
}

View File

@@ -0,0 +1,99 @@
# USB code
ion_device_usb_src += $(addprefix ion/src/device/bootloader/usb/, \
calculator.cpp \
dfu_interface.cpp\
)
ion_device_usb_src += $(addprefix ion/src/device/bootloader/usb/stack/, \
device.cpp\
endpoint0.cpp \
interface.cpp\
request_recipient.cpp\
setup_packet.cpp\
streamable.cpp\
)
ion_device_usb_src += $(addprefix ion/src/device/bootloader/usb/stack/descriptor/, \
bos_descriptor.cpp\
configuration_descriptor.cpp \
descriptor.cpp\
device_descriptor.cpp\
device_capability_descriptor.cpp\
dfu_functional_descriptor.cpp\
extended_compat_id_descriptor.cpp \
interface_descriptor.cpp\
language_id_string_descriptor.cpp \
microsoft_os_string_descriptor.cpp\
platform_device_capability_descriptor.cpp\
string_descriptor.cpp\
url_descriptor.cpp\
webusb_platform_descriptor.cpp\
)
# DFU code
ion_device_dfu_src += liba/src/abs.c
ion_device_dfu_src += liba/src/assert.c
ion_device_dfu_src += liba/src/strlen.c
ion_device_dfu_src += liba/src/strlcpy.c
ion_device_dfu_src += liba/src/memset.c
ion_device_dfu_src += liba/src/memcpy.c
ion_device_dfu_src += libaxx/src/cxxabi/pure_virtual.cpp
ion_device_dfu_src += ion/src/device/bootloader/usb/boot.cpp
ion_device_dfu_src += ion/src/device/n0110/drivers/board.cpp
ion_device_dfu_src += ion/src/device/n0110/drivers/cache.cpp
ion_device_dfu_src += ion/src/device/n0110/drivers/external_flash.cpp
ion_device_dfu_src += ion/src/device/n0110/drivers/reset.cpp
ion_device_dfu_src += ion/src/device/n0110/drivers/usb.cpp
ion_device_dfu_src += $(addprefix ion/src/device/shared/drivers/, \
backlight.cpp \
battery.cpp \
base64.cpp \
board.cpp \
console_uart.cpp \
crc32.cpp \
display.cpp \
events_keyboard_platform.cpp \
flash.cpp \
internal_flash.cpp \
keyboard.cpp \
led.cpp \
power.cpp\
random.cpp\
reset.cpp \
serial_number.cpp \
swd.cpp \
timing.cpp \
usb.cpp \
usb_desc.cpp \
wakeup.cpp \
)
# Sources required to execute DFU in place
ion_device_src += ion/src/device/bootloader/usb/dfu_xip.cpp:+usbxip
ion_device_src += $(addsuffix :+usbxip,$(ion_device_usb_src))
# Sources required to execute DFU in RAM
$(BUILD_DIR)/ion/src/device/bootloader/usb/dfu.elf: LDSCRIPT = ion/src/device/bootloader/usb/dfu.ld
$(BUILD_DIR)/ion/src/device/bootloader/usb/dfu.elf: $(call object_for,$(ion_device_usb_src) $(ion_device_dfu_src))
# In order to link the dfu bootloader inside the epsilon firmware, we need to
# turn the dfu binary (dfu.bin) into an elf object.
# By default, 'objcpy' generates a section 'data' and two symbols to the
# start and the end of the binary input respectively named:
# - '_binary_[file name]_[file extension]_start'
# - '_binary_[file name]_[file extension]_end'.
# For our purpose, dfu.o can go in rodata section and we rename the start and
# end of binary symbols: _dfu_bootloader_flash_[start/end]
$(BUILD_DIR)/ion/src/device/bootloader/usb/dfu.o: $(BUILD_DIR)/ion/src/device/bootloader/usb/dfu.bin
$(call rule_label,OBJCOPY)
$(Q) cd $(dir $<) ; $(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata.dfu_bootloader --redefine-sym _binary_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_dfu_bin_end=_dfu_bootloader_flash_end $(notdir $<) $(notdir $@)
ion_device_src += ion/src/device/bootloader/usb/dfu.cpp:-usbxip
ion_device_src += ion/src/device/bootloader/usb/dfu_relocated.cpp:-usbxip
ion_device_src += $(addprefix ion/src/device/bootloader/drivers/, \
usb_desc.cpp \
)

View File

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

View File

@@ -0,0 +1,94 @@
#include "calculator.h"
#include <ion/usb.h>
#include <drivers/keyboard.h>
#include <drivers/serial_number.h>
#include <bootloader/usb_data.h>
namespace Ion {
namespace Device {
namespace USB {
void Calculator::PollAndReset(bool exitWithKeyboard, void * data) {
char serialNumber[Ion::Device::SerialNumber::Length+1];
Ion::Device::SerialNumber::copy(serialNumber);
Calculator c(serialNumber, data == nullptr ? stringDescriptor() : static_cast<Bootloader::USBData *>(data)->stringDescriptor(), data == nullptr ? "Upsilon Bootloader" : static_cast<Bootloader::USBData *>(data)->getName());
if (data != nullptr) {
c.setConfigData(static_cast<Bootloader::USBData *>(data));
}
/* Leave DFU mode if the Back key is pressed, the calculator unplugged or the
* USB core soft-disconnected. */
Ion::Keyboard::Key exitKey = Ion::Keyboard::Key::Back;
uint8_t exitKeyRow = Ion::Device::Keyboard::rowForKey(exitKey);
uint8_t exitKeyColumn = Ion::Device::Keyboard::columnForKey(exitKey);
Ion::Device::Keyboard::activateRow(exitKeyRow);
while (!(exitWithKeyboard && !c.isErasingAndWriting() && Ion::Device::Keyboard::columnIsActive(exitKeyColumn)) &&
Ion::USB::isPlugged() &&
!c.isSoftDisconnected()) {
c.poll();
}
if (!c.isSoftDisconnected()) {
c.detach();
}
if (c.resetOnDisconnect()) {
c.leave(c.addressPointer());
}
}
Descriptor * Calculator::descriptor(uint8_t type, uint8_t index) {
/* Special case: Microsoft OS String Descriptor should be returned when
* searching for string descriptor at index 0xEE. */
if (type == m_microsoftOSStringDescriptor.type() && index == 0xEE) {
return &m_microsoftOSStringDescriptor;
}
int typeCount = 0;
for (size_t i=0; i<sizeof(m_descriptors)/sizeof(m_descriptors[0]); i++) {
Descriptor * descriptor = m_descriptors[i];
if (descriptor->type() != type) {
continue;
}
if (typeCount == index) {
return descriptor;
} else {
typeCount++;
}
}
return nullptr;
}
bool Calculator::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
if (Device::processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) {
return true;
}
if (request->requestType() == SetupPacket::RequestType::Vendor) {
if (request->bRequest() == k_webUSBVendorCode && request->wIndex() == 2) {
// This is a WebUSB, GET_URL request
assert(request->wValue() == k_webUSBLandingPageIndex);
return getURLCommand(transferBuffer, transferBufferLength, transferBufferMaxLength);
}
if (request->bRequest() == k_microsoftOSVendorCode && request->wIndex() == 0x0004) {
// This is a Microsoft OS descriptor, Extended Compat ID request
assert(request->wValue() == 0);
return getExtendedCompatIDCommand(transferBuffer, transferBufferLength, transferBufferMaxLength);
}
}
return false;
}
bool Calculator::getURLCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
*transferBufferLength = m_workshopURLDescriptor.copy(transferBuffer, transferBufferMaxLength);
return true;
}
bool Calculator::getExtendedCompatIDCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
*transferBufferLength = m_extendedCompatIdDescriptor.copy(transferBuffer, transferBufferMaxLength);
return true;
}
}
}
}

View File

@@ -0,0 +1,176 @@
#ifndef ION_DEVICE_SHARED_USB_CALCULATOR_H
#define ION_DEVICE_SHARED_USB_CALCULATOR_H
#include <stddef.h>
#include <assert.h>
#include <drivers/usb.h>
#include <drivers/config/usb.h>
#include "dfu_interface.h"
#include "stack/device.h"
#include "stack/descriptor/bos_descriptor.h"
#include "stack/descriptor/configuration_descriptor.h"
#include "stack/descriptor/descriptor.h"
#include "stack/descriptor/device_descriptor.h"
#include "stack/descriptor/dfu_functional_descriptor.h"
#include "stack/descriptor/extended_compat_id_descriptor.h"
#include "stack/descriptor/interface_descriptor.h"
#include "stack/descriptor/language_id_string_descriptor.h"
#include "stack/descriptor/microsoft_os_string_descriptor.h"
#include "stack/descriptor/string_descriptor.h"
#include "stack/descriptor/url_descriptor.h"
#include "stack/descriptor/webusb_platform_descriptor.h"
#include <bootloader/usb_data.h>
namespace Ion {
namespace Device {
namespace USB {
class Calculator : public Device {
public:
static void PollAndReset(bool exitWithKeyboard, void * data)
__attribute__((section(".dfu_entry_point"))) // Needed to pinpoint this symbol in the linker script
__attribute__((used)) // Make sure this symbol is not discarded at link time
; // Return true if reset is needed
Calculator(const char * serialNumber, const char * desc, const char * product) :
Device(&m_dfuInterface),
m_usbConfig(nullptr),
m_deviceDescriptor(
0x0210, /* bcdUSB: USB Specification Number which the device complies
* to. Must be greater than 0x0200 to use the BOS. */
0, // bDeviceClass: The class is defined by the interface.
0, // bDeviceSUBClass: The subclass is defined by the interface.
0, // bDeviceProtocol: The protocol is defined by the interface.
64, // bMaxPacketSize0: Maximum packet size for endpoint 0
0x0483, // idVendor
0xA291, // idProduct
0x0100, // bcdDevice: Device Release Number
1, // iManufacturer: Index of the manufacturer name string, see m_descriptor
2, // iProduct: Index of the product name string, see m_descriptor
3, // iSerialNumber: Index of the SerialNumber string, see m_descriptor
1), // bNumConfigurations
m_dfuFunctionalDescriptor(
0b0011, /* bmAttributes:
* - bitWillDetach: If true, the device will perform a bus
* detach-attach sequence when it receives a DFU_DETACH
* request. The host must not issue a USB Reset.
* - bitManifestationTolerant: if true, the device is able to
* communicate via USB after Manifestation phase. The
* manifestation phase implies a reset in the calculator, so,
* even if the device is still plugged, it needs to be
* re-enumerated to communicate.
* - bitCanUpload
* - bitCanDnload */
0, /* wDetachTimeOut: Time, in milliseconds, that the device in APP
* mode will wait after receipt of the DFU_DETACH request before
* switching to DFU mode. It does not apply to the calculator.*/
2048, // wTransferSize: Maximum number of bytes that the device can accept per control-write transaction
0x0100),// bcdDFUVersion
m_interfaceDescriptor(
0, // bInterfaceNumber
k_dfuInterfaceAlternateSetting, // bAlternateSetting
0, // bNumEndpoints: Other than endpoint 0
0xFE, // bInterfaceClass: DFU (https://www.usb.org/defined-class-codes)
1, // bInterfaceSubClass: DFU
2, // bInterfaceProtocol: DFU Mode (not DFU Runtime, which would be 1)
4, // iInterface: Index of the Interface string, see m_descriptor
&m_dfuFunctionalDescriptor),
m_configurationDescriptor(
9 + 9 + 9, // wTotalLength: configuration descriptor + interface descriptor + dfu functional descriptor lengths
1, // bNumInterfaces
k_bConfigurationValue, // bConfigurationValue
0, // iConfiguration: No string descriptor for the configuration
0x80, /* bmAttributes:
* Bit 7: Reserved, set to 1
* Bit 6: Self Powered
* Bit 5: Remote Wakeup (allows the device to wake up the host when the host is in suspend)
* Bit 4..0: Reserved, set to 0 */
0x32, // bMaxPower: half of the Maximum Power Consumption
&m_interfaceDescriptor),
m_webUSBPlatformDescriptor(
k_webUSBVendorCode,
k_webUSBLandingPageIndex),
m_bosDescriptor(
5 + 24, // wTotalLength: BOS descriptor + webusb platform descriptor lengths
1, // bNumDeviceCapabilities
&m_webUSBPlatformDescriptor),
m_languageStringDescriptor(),
m_manufacturerStringDescriptor("NumWorks"),
m_productStringDescriptor(product),
m_serialNumberStringDescriptor(serialNumber),
m_interfaceStringDescriptor(desc),
//m_interfaceStringDescriptor("@SRAM/0x20000000/01*256Ke"),
/* Switch to this descriptor to use dfu-util to write in the SRAM.
* FIXME Should be an alternate Interface. */
m_microsoftOSStringDescriptor(k_microsoftOSVendorCode),
m_workshopURLDescriptor(URLDescriptor::Scheme::HTTPS, "getupsilon.web.app"),
m_extendedCompatIdDescriptor("WINUSB"),
m_descriptors{
&m_deviceDescriptor, // Type = Device, Index = 0
&m_configurationDescriptor, // Type = Configuration, Index = 0
&m_languageStringDescriptor, // Type = String, Index = 0
&m_manufacturerStringDescriptor, // Type = String, Index = 1
&m_productStringDescriptor, // Type = String, Index = 2
&m_serialNumberStringDescriptor, // Type = String, Index = 3
&m_interfaceStringDescriptor, // Type = String, Index = 4
&m_bosDescriptor // Type = BOS, Index = 0
},
m_dfuInterface(this, &m_ep0, k_dfuInterfaceAlternateSetting)
{
}
uint32_t addressPointer() const { return m_dfuInterface.addressPointer(); }
bool isErasingAndWriting() const { return m_dfuInterface.isErasingAndWriting(); }
void setConfigData(Bootloader::USBData * data) { m_usbConfig = data; m_dfuInterface.setDfuConfig(data->getData()); }
Bootloader::USBData * getConfigData() const { return m_usbConfig; }
protected:
Descriptor * descriptor(uint8_t type, uint8_t index) override;
void setActiveConfiguration(uint8_t configurationIndex) override {
assert(configurationIndex == k_bConfigurationValue);
}
uint8_t getActiveConfiguration() override {
return k_bConfigurationValue;
}
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
private:
static constexpr uint8_t k_bConfigurationValue = 1;
static constexpr uint8_t k_dfuInterfaceAlternateSetting = 0;
static constexpr uint8_t k_webUSBVendorCode = 1;
static constexpr uint8_t k_webUSBLandingPageIndex = 1;
static constexpr uint8_t k_microsoftOSVendorCode = 2;
// WebUSB and MicrosoftOSDescriptor commands
bool getURLCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
bool getExtendedCompatIDCommand(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
// Descriptors
Bootloader::USBData * m_usbConfig;
DeviceDescriptor m_deviceDescriptor;
DFUFunctionalDescriptor m_dfuFunctionalDescriptor;
InterfaceDescriptor m_interfaceDescriptor;
ConfigurationDescriptor m_configurationDescriptor;
WebUSBPlatformDescriptor m_webUSBPlatformDescriptor;
BOSDescriptor m_bosDescriptor;
LanguageIDStringDescriptor m_languageStringDescriptor;
StringDescriptor m_manufacturerStringDescriptor;
StringDescriptor m_productStringDescriptor;
StringDescriptor m_serialNumberStringDescriptor;
StringDescriptor m_interfaceStringDescriptor;
MicrosoftOSStringDescriptor m_microsoftOSStringDescriptor;
URLDescriptor m_workshopURLDescriptor;
ExtendedCompatIDDescriptor m_extendedCompatIdDescriptor;
Descriptor * m_descriptors[8];
/* m_descriptors contains only descriptors that sould be returned via the
* method descriptor(uint8_t type, uint8_t index), so do not count descriptors
* included in other descriptors or returned by other functions. */
// Interface
DFUInterface m_dfuInterface;
};
}
}
}
#endif

View File

@@ -0,0 +1,57 @@
/* 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 = 9K
}
SECTIONS {
.text : {
. = ALIGN(4);
KEEP(*(.dfu_entry_point))
*(.text)
*(.text.*)
} >RAM_BUFFER
.rodata : {
*(.rodata)
*(.rodata.*)
} >RAM_BUFFER
.data : {
/* We need to keep these symbols. */
*(.data._ZN3Ion6Device13ExternalFlashL14sOperatingModeE)
*(.data._ZN3Ion6Device5BoardL18sStandardFrequencyE)
} >RAM_BUFFER
/DISCARD/ : {
/* For now, we do not need .bss and .data sections. This allows us to simply
* skip any rt0-style initialization and jump straight into the PollAndReset
* routine. */
*(.bss)
*(.bss.*)
*(.data)
*(.data.*)
}
}

View File

@@ -0,0 +1,314 @@
#include "dfu_interface.h"
#include <string.h>
#include <drivers/flash.h>
#include <ion/timing.h>
namespace Ion {
namespace Device {
namespace USB {
static inline uint32_t minUint32T(uint32_t x, uint32_t y) { return x < y ? x : y; }
void DFUInterface::StatusData::push(Channel * c) const {
c->push(m_bStatus);
c->push(m_bwPollTimeout[2]);
c->push(m_bwPollTimeout[1]);
c->push(m_bwPollTimeout[0]);
c->push(m_bState);
c->push(m_iString);
}
void DFUInterface::StateData::push(Channel * c) const {
c->push(m_bState);
}
void DFUInterface::wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {
if (request->bRequest() == (uint8_t) DFURequest::Download) {
// Handle a download request
if (request->wValue() == 0) {
// The request is a special command
switch (transferBuffer[0]) {
case (uint8_t) DFUDownloadCommand::SetAddressPointer:
setAddressPointerCommand(request, transferBuffer, *transferBufferLength);
return;
case (uint8_t) DFUDownloadCommand::Erase:
eraseCommand(transferBuffer, *transferBufferLength);
return;
default:
m_state = State::dfuERROR;
m_status = Status::errSTALLEDPKT;
return;
}
}
if (request->wValue() == 1) {
m_ep0->stallTransaction();
return;
}
if (request->wLength() > 0) {
// The request is a "real" download. Compute the writing address.
m_writeAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer;
// Store the received data until we copy it on the flash.
memcpy(m_largeBuffer, transferBuffer, *transferBufferLength);
m_largeBufferLength = *transferBufferLength;
m_state = State::dfuDNLOADSYNC;
}
}
}
void DFUInterface::wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {
if (request->bRequest() == (uint8_t) DFURequest::GetStatus) {
// Do any needed action after the GetStatus request.
if (m_state == State::dfuMANIFEST) {
/* If we leave the DFU and reset immediately, dfu-util outputs an error:
* "File downloaded successfully
* dfu-util: Error during download get_status"
* If we sleep 1us here, there is no error. We put 1ms for security.
* This error might be due to the USB connection being cut too soon after
* the last USB exchange, so the host does not have time to process the
* answer received for the last GetStatus request. */
Ion::Timing::msleep(1);
// Leave DFU routine: Leave DFU, reset device, jump to application code
leaveDFUAndReset();
} else if (m_state == State::dfuDNBUSY) {
if (m_largeBufferLength != 0) {
// Here, copy the data from the transfer buffer to the flash memory
writeOnMemory();
}
changeAddressPointerIfNeeded();
eraseMemoryIfNeeded();
m_state = State::dfuDNLOADIDLE;
}
}
}
bool DFUInterface::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
if (Interface::processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) {
return true;
}
switch (request->bRequest()) {
case (uint8_t) DFURequest::Detach:
m_device->detach();
return true;
case (uint8_t) DFURequest::Download:
return processDownloadRequest(request->wLength(), transferBufferLength);
case (uint8_t) DFURequest::Upload:
return processUploadRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
case (uint8_t) DFURequest::GetStatus:
return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
case (uint8_t) DFURequest::ClearStatus:
return clearStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
case (uint8_t) DFURequest::GetState:
return getState(transferBuffer, transferBufferLength, transferBufferMaxLength);
case (uint8_t) DFURequest::Abort:
return dfuAbort(transferBufferLength);
}
return false;
}
bool DFUInterface::processDownloadRequest(uint16_t wLength, uint16_t * transferBufferLength) {
if (m_state != State::dfuIDLE && m_state != State::dfuDNLOADIDLE) {
m_state = State::dfuERROR;
m_status = Status::errNOTDONE;
m_ep0->stallTransaction();
return false;
}
if (wLength == 0) {
// Leave DFU routine: Reset the device and jump to application code
m_state = State::dfuMANIFESTSYNC;
} else {
// Prepare to receive the download data
m_ep0->clearForOutTransactions(wLength);
m_state = State::dfuDNLOADSYNC;
}
return true;
}
bool DFUInterface::processUploadRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
if (m_state != State::dfuIDLE && m_state != State::dfuUPLOADIDLE) {
m_ep0->stallTransaction();
return false;
}
if (request->wValue() == 0) {
/* The host requests to read the commands supported by the bootloader. After
* receiving this command, the device should returns N bytes representing
* the command codes for :
* Get command / Set Address Pointer / Erase / Read Unprotect
* We no not need it for now. */
return false;
} else if (request->wValue() == 1) {
m_ep0->stallTransaction();
return false;
} else {
/* We decided to never protect Read operation. Else we would have to check
* here it is not protected before reading. */
// Compute the reading address
uint32_t readAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer;
// Copy the requested memory zone into the transfer buffer.
uint16_t copySize = minUint32T(transferBufferMaxLength, request->wLength());
memcpy(transferBuffer, (void *)readAddress, copySize);
*transferBufferLength = copySize;
}
m_state = State::dfuUPLOADIDLE;
return true;
}
void DFUInterface::setAddressPointerCommand(SetupPacket * request, uint8_t * transferBuffer, uint16_t transferBufferLength) {
assert(transferBufferLength == 5);
// Compute the new address but change it after the next getStatus request.
m_potentialNewAddressPointer = transferBuffer[1]
+ (transferBuffer[2] << 8)
+ (transferBuffer[3] << 16)
+ (transferBuffer[4] << 24);
m_state = State::dfuDNLOADSYNC;
}
void DFUInterface::changeAddressPointerIfNeeded() {
if (m_potentialNewAddressPointer == 0) {
// There was no address change waiting.
return;
}
// If there is a new address pointer waiting, change the pointer address.
m_addressPointer = m_potentialNewAddressPointer;
m_potentialNewAddressPointer = 0;
m_state = State::dfuDNLOADIDLE;
m_status = Status::OK;
}
void DFUInterface::eraseCommand(uint8_t * transferBuffer, uint16_t transferBufferLength) {
/* We determine whether the commands asks for a mass erase or which sector to
* erase. The erase must be done after the next getStatus request. */
m_state = State::dfuDNLOADSYNC;
if (transferBufferLength == 1) {
// Mass erase
m_erasePage = Flash::TotalNumberOfSectors();
return;
}
// Sector erase
assert(transferBufferLength == 5);
m_eraseAddress = transferBuffer[1]
+ (transferBuffer[2] << 8)
+ (transferBuffer[3] << 16)
+ (transferBuffer[4] << 24);
m_erasePage = Flash::SectorAtAddress(m_eraseAddress);
if (m_erasePage < 0) {
// Unrecognized sector
m_state = State::dfuERROR;
m_status = Status::errTARGET;
}
}
void DFUInterface::eraseMemoryIfNeeded() {
if (m_erasePage < 0) {
// There was no erase waiting.
return;
}
willErase();
Bootloader::DFUData * config = getDfuConfig();
if (config != nullptr) {
// More simple to read
if ((0x08000000 <= m_eraseAddress && m_eraseAddress <= 0x08010000)&& !m_dfuData.isProtectedInternal()) {
Flash::EraseSector(m_erasePage);
} else if ((0x90000000 <= m_eraseAddress && m_eraseAddress <= 0x90800000)&& !m_dfuData.isProtectedExternal()) {
Flash::EraseSector(m_erasePage);
}
} else {
if (m_erasePage == Flash::TotalNumberOfSectors()) {
Flash::MassErase();
} else {
Flash::EraseSector(m_erasePage);
}
}
/* Put an out of range value in m_erasePage to indicate that no erase is
* waiting. */
m_erasePage = -1;
m_state = State::dfuDNLOADIDLE;
m_status = Status::OK;
}
void DFUInterface::writeOnMemory() {
if (m_writeAddress >= k_sramStartAddress && m_writeAddress <= k_sramEndAddress) {
// Write on SRAM
// FIXME We should check that we are not overriding the current instructions.
memcpy((void *)m_writeAddress, m_largeBuffer, m_largeBufferLength);
} else if (Flash::SectorAtAddress(m_writeAddress) >= 0) {
Bootloader::DFUData * config = getDfuConfig();
if (config != nullptr) {
if (m_writeAddress >= 0x08000000 && m_writeAddress <= 0x08010000 && !m_dfuData.isProtectedInternal()) {
Flash::WriteMemory(reinterpret_cast<uint8_t *>(m_writeAddress), m_largeBuffer, m_largeBufferLength);
} else if (m_writeAddress >= 0x90000000 && m_writeAddress <= 0x90800000 && !m_dfuData.isProtectedExternal()) {
Flash::WriteMemory(reinterpret_cast<uint8_t *>(m_writeAddress), m_largeBuffer, m_largeBufferLength);
}
} else {
Flash::WriteMemory(reinterpret_cast<uint8_t *>(m_writeAddress), m_largeBuffer, m_largeBufferLength);
}
} else {
// Invalid write address
m_largeBufferLength = 0;
m_state = State::dfuERROR;
m_status = Status::errTARGET;
return;
}
// Reset the buffer length
m_largeBufferLength = 0;
// Change the interface state and status
m_state = State::dfuDNLOADIDLE;
m_status = Status::OK;
}
bool DFUInterface::getStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
// Change the status if needed
if (m_state == State::dfuMANIFESTSYNC) {
m_state = State::dfuMANIFEST;
} else if (m_state == State::dfuDNLOADSYNC) {
m_state = State::dfuDNBUSY;
}
// Copy the status on the TxFifo
*transferBufferLength = StatusData(m_status, m_state).copy(transferBuffer, transferBufferMaxLength);
return true;
}
bool DFUInterface::clearStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
m_status = Status::OK;
m_state = State::dfuIDLE;
return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
}
bool DFUInterface::getState(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t maxSize) {
*transferBufferLength = StateData(m_state).copy(transferBuffer, maxSize);
return true;
}
bool DFUInterface::dfuAbort(uint16_t * transferBufferLength) {
m_status = Status::OK;
m_state = State::dfuIDLE;
*transferBufferLength = 0;
return true;
}
void DFUInterface::leaveDFUAndReset() {
m_device->setResetOnDisconnect(true);
m_device->detach();
}
void DFUInterface::copyDfuData() {
m_dfuData = Bootloader::DFUData(!m_dfuConfig->isProtectedInternal(), !m_dfuConfig->isProtectedExternal());
}
}
}
}

View File

@@ -0,0 +1,192 @@
#ifndef ION_DEVICE_SHARED_USB_DFU_INTERFACE_H
#define ION_DEVICE_SHARED_USB_DFU_INTERFACE_H
#include <assert.h>
#include <stddef.h>
#include "stack/device.h"
#include "stack/interface.h"
#include "stack/endpoint0.h"
#include "stack/setup_packet.h"
#include "stack/streamable.h"
#include <bootloader/usb_data.h>
namespace Ion {
namespace Device {
namespace USB {
class DFUInterface : public Interface {
public:
DFUInterface(Device * device, Endpoint0 * ep0, uint8_t bInterfaceAlternateSetting) :
Interface(ep0),
m_device(device),
m_status(Status::OK),
m_state(State::dfuIDLE),
m_addressPointer(0),
m_potentialNewAddressPointer(0),
m_erasePage(-1),
m_largeBuffer{0},
m_largeBufferLength(0),
m_writeAddress(0),
m_bInterfaceAlternateSetting(bInterfaceAlternateSetting),
m_isErasingAndWriting(false),
m_dfuConfig(nullptr),
m_eraseAddress(0),
m_dfuData()
{
}
uint32_t addressPointer() const { return m_addressPointer; }
void wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) override;
void wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) override;
bool isErasingAndWriting() const { return m_isErasingAndWriting; }
void setDfuConfig(Bootloader::DFUData * data) { m_dfuConfig = data; copyDfuData(); }
Bootloader::DFUData * getDfuConfig() const { return m_dfuConfig; }
protected:
void setActiveInterfaceAlternative(uint8_t interfaceAlternativeIndex) override {
assert(interfaceAlternativeIndex == m_bInterfaceAlternateSetting);
}
uint8_t getActiveInterfaceAlternative() override {
return m_bInterfaceAlternateSetting;
}
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
private:
// DFU Request Codes
enum class DFURequest {
Detach = 0,
Download = 1,
Upload = 2,
GetStatus = 3,
ClearStatus = 4,
GetState = 5,
Abort = 6
};
// DFU Download Commmand Codes
enum class DFUDownloadCommand {
GetCommand = 0x00,
SetAddressPointer = 0x21,
Erase = 0x41,
ReadUnprotect = 0x92
};
enum class Status : uint8_t {
OK = 0x00,
errTARGET = 0x01,
errFILE = 0x02,
errWRITE = 0x03,
errERASE = 0x04,
errCHECK_ERASED = 0x05,
errPROG = 0x06,
errVERIFY = 0x07,
errADDRESS = 0x08,
errNOTDONE = 0x09,
errFIRMWARE = 0x0A,
errVENDOR = 0x0B,
errUSBR = 0x0C,
errPOR = 0x0D,
errUNKNOWN = 0x0E,
errSTALLEDPKT = 0x0F
};
enum class State : uint8_t {
appIDLE = 0,
appDETACH = 1,
dfuIDLE = 2,
dfuDNLOADSYNC = 3,
dfuDNBUSY = 4,
dfuDNLOADIDLE = 5,
dfuMANIFESTSYNC = 6,
dfuMANIFEST = 7,
dfuMANIFESTWAITRESET = 8,
dfuUPLOADIDLE = 9,
dfuERROR = 10
};
class StatusData : public Streamable {
public:
StatusData(Status status, State state, uint32_t pollTimeout = 1) :
/* We put a default pollTimeout value of 1ms: if the device is busy, the
* host has to wait 1ms before sending a getStatus Request. */
m_bStatus((uint8_t)status),
m_bwPollTimeout{uint8_t((pollTimeout>>16) & 0xFF), uint8_t((pollTimeout>>8) & 0xFF), uint8_t(pollTimeout & 0xFF)},
m_bState((uint8_t)state),
m_iString(0)
{
}
protected:
void push(Channel * c) const override;
private:
uint8_t m_bStatus; // Status resulting from the execution of the most recent request
uint8_t m_bwPollTimeout[3]; // m_bwPollTimeout is 24 bits
uint8_t m_bState; // State of the device immediately following transmission of this response
uint8_t m_iString;
};
class StateData : public Streamable {
public:
StateData(State state) : m_bState((uint8_t)state) {}
protected:
void push(Channel * c) const override;
private:
uint8_t m_bState; // Current state of the device
};
/* The Flash and SRAM addresses are in flash.ld. However, dfu_interface is
* linked with dfu.ld, so we cannot access the values. */
constexpr static uint32_t k_sramStartAddress = 0x20000000;
constexpr static uint32_t k_sramEndAddress = 0x20040000;
// Download and upload
bool processDownloadRequest(uint16_t wLength, uint16_t * transferBufferLength);
bool processUploadRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
// Address pointer
void setAddressPointerCommand(SetupPacket * request, uint8_t * transferBuffer, uint16_t transferBufferLength);
void changeAddressPointerIfNeeded();
// Access memory
void eraseCommand(uint8_t * transferBuffer, uint16_t transferBufferLength);
void eraseMemoryIfNeeded();
void writeOnMemory();
void unlockFlashMemory();
void lockFlashMemoryAndPurgeCaches();
// Status
bool getStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
bool clearStatus(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
// State
bool getState(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t maxSize);
// Abort
bool dfuAbort(uint16_t * transferBufferLength);
// Leave DFU
void leaveDFUAndReset();
/* Erase and Write state. After starting the erase of flash memory, the user
* can no longer leave DFU mode by pressing the Back key of the keyboard. This
* way, we prevent the user from interrupting a software download. After every
* software download, the calculator resets, which unlocks the "exit on
* pressing back". */
void willErase() { m_isErasingAndWriting = true; }
void copyDfuData();
Device * m_device;
Status m_status;
State m_state;
uint32_t m_addressPointer;
uint32_t m_potentialNewAddressPointer;
int32_t m_erasePage;
uint8_t m_largeBuffer[Endpoint0::MaxTransferSize];
uint16_t m_largeBufferLength;
uint32_t m_writeAddress;
uint8_t m_bInterfaceAlternateSetting;
bool m_isErasingAndWriting;
Bootloader::DFUData * m_dfuConfig;
uint32_t m_eraseAddress;
Bootloader::DFUData m_dfuData;
};
}
}
}
#endif

View File

@@ -0,0 +1,89 @@
#include <ion.h>
#include <ion/usb.h>
#include <string.h>
#include <assert.h>
#include <drivers/cache.h>
#include "../drivers/timing.h"
extern const void * _stack_end;
extern char _dfu_bootloader_flash_start;
extern char _dfu_bootloader_flash_end;
namespace Ion {
namespace USB {
typedef void (*PollFunctionPointer)(bool exitWithKeyboard, void * data);
void DFU(bool exitWithKeyboard, void * data) {
Ion::updateSlotInfo();
/* DFU transfers can serve two purposes:
* - Transfering RAM data between the machine and a 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.
*
* The new DFU address in RAM 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. */
/* 1- The stack being in reverse order, the end of the stack will be the
* beginning of the DFU bootloader copied in RAM. */
size_t dfu_bootloader_size = &_dfu_bootloader_flash_end - &_dfu_bootloader_flash_start;
char * dfu_bootloader_ram_start = reinterpret_cast<char *>(&_stack_end);
assert(&_stack_end == (void *)(0x20000000 + 256*1024 - 32*1024));
/* 2- Verify there is enough free space on the stack to copy the DFU code. */
char foo;
char * stackPointer = &foo;
if (dfu_bootloader_ram_start + dfu_bootloader_size > stackPointer) {
// There is not enough room on the stack to copy the DFU bootloader.
return;
}
/* 3- Copy the DFU bootloader from Flash to RAM. */
memcpy(dfu_bootloader_ram_start, &_dfu_bootloader_flash_start, dfu_bootloader_size);
/* The DFU bootloader might have been copied in the DCache. However, when we
* run the instructions from the DFU bootloader, the CPU looks for
* instructions in the ICache and then in the RAM. We thus need to flush the
* DCache to update the RAM. */
// Flush data cache
Device::Cache::cleanDCache();
/* 4- Disable all interrupts
* The interrupt service routines live in the Flash and could be overwritten
* by garbage during a firmware upgrade opration, so we disable them. */
Device::Timing::shutdown();
/* 5- Jump to DFU bootloader code. We made sure in the linker script that the
* first function we want to call is at the beginning of the DFU code. */
PollFunctionPointer dfu_bootloader_entry = reinterpret_cast<PollFunctionPointer>(dfu_bootloader_ram_start);
/* To have the right debug symbols for the reallocated code, break here and:
* - Get the address of the new .text section
* In a terminal: arm-none-eabi-readelf -a ion/src/device/usb/dfu.elf
* - Delete the current symbol table
* symbol-file
* - Add the new symbol table, with the address of the new .text section
* add-symbol-file ion/src/device/usb/dfu.elf 0x20038000
*/
dfu_bootloader_entry(exitWithKeyboard, data);
/* 5- Restore interrupts */
Device::Timing::init();
/* 6- That's all. The DFU bootloader on the stack is now dead code that will
* be overwritten when the stack grows. */
}
}
}

View File

@@ -0,0 +1,13 @@
#include <ion.h>
#include "calculator.h"
namespace Ion {
namespace USB {
void DFU(bool exitWithKeyboard, void * data) {
Ion::updateSlotInfo();
Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard, data);
}
}
}

View File

@@ -0,0 +1,22 @@
#include "bos_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
void BOSDescriptor::push(Channel * c) const {
Descriptor::push(c);
c->push(m_wTotalLength);
c->push(m_bNumDeviceCaps);
for (uint8_t i = 0; i < m_bNumDeviceCaps; i++) {
m_deviceCapabilities[i].push(c);
}
}
uint8_t BOSDescriptor::bLength() const {
return Descriptor::bLength() + sizeof(uint16_t) + sizeof(uint8_t);
}
}
}
}

View File

@@ -0,0 +1,36 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_BOS_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_BOS_DESCRIPTOR_H
#include "descriptor.h"
#include "device_capability_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class BOSDescriptor : public Descriptor {
public:
constexpr BOSDescriptor(
uint16_t wTotalLength,
uint8_t bNumDeviceCapabilities,
const DeviceCapabilityDescriptor * deviceCapabilities) :
Descriptor(0x0F),
m_wTotalLength(wTotalLength),
m_bNumDeviceCaps(bNumDeviceCapabilities),
m_deviceCapabilities(deviceCapabilities)
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
uint16_t m_wTotalLength;
uint8_t m_bNumDeviceCaps;
const DeviceCapabilityDescriptor * m_deviceCapabilities;
};
}
}
}
#endif

View File

@@ -0,0 +1,26 @@
#include "configuration_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
void ConfigurationDescriptor::push(Channel * c) const {
Descriptor::push(c);
c->push(m_wTotalLength);
c->push(m_bNumInterfaces);
c->push(m_bConfigurationValue);
c->push(m_iConfiguration);
c->push(m_bmAttributes);
c->push(m_bMaxPower);
for (uint8_t i = 0; i < m_bNumInterfaces; i++) {
m_interfaces[i].push(c);
}
}
uint8_t ConfigurationDescriptor::bLength() const {
return Descriptor::bLength() + sizeof(uint16_t) + 5*sizeof(uint8_t);
}
}
}
}

View File

@@ -0,0 +1,48 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_CONFIGURATION_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_CONFIGURATION_DESCRIPTOR_H
#include "descriptor.h"
#include "interface_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class ConfigurationDescriptor : public Descriptor {
public:
constexpr ConfigurationDescriptor(
uint16_t wTotalLength,
uint8_t bNumInterfaces,
uint8_t bConfigurationValue,
uint8_t iConfiguration,
uint8_t bmAttributes,
uint8_t bMaxPower,
const InterfaceDescriptor * interfaces) :
Descriptor(0x02),
m_wTotalLength(wTotalLength),
m_bNumInterfaces(bNumInterfaces),
m_bConfigurationValue(bConfigurationValue),
m_iConfiguration(iConfiguration),
m_bmAttributes(bmAttributes),
m_bMaxPower(bMaxPower),
m_interfaces(interfaces)
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
uint16_t m_wTotalLength;
uint8_t m_bNumInterfaces;
uint8_t m_bConfigurationValue;
uint8_t m_iConfiguration;
uint8_t m_bmAttributes;
uint8_t m_bMaxPower;
const InterfaceDescriptor * m_interfaces;
};
}
}
}
#endif

View File

@@ -0,0 +1,15 @@
#include "descriptor.h"
#include <string.h>
namespace Ion {
namespace Device {
namespace USB {
void Descriptor::push(Channel * c) const {
c->push(bLength());
c->push(m_bDescriptorType);
}
}
}
}

View File

@@ -0,0 +1,32 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_DESCRIPTOR_H
#include "../streamable.h"
namespace Ion {
namespace Device {
namespace USB {
class InterfaceDescriptor;
class Descriptor : public Streamable {
friend class InterfaceDescriptor;
public:
constexpr Descriptor(uint8_t bDescriptorType) :
m_bDescriptorType(bDescriptorType)
{
}
uint8_t type() const { return m_bDescriptorType; }
protected:
void push(Channel * c) const override;
virtual uint8_t bLength() const { return 2*sizeof(uint8_t); }
private:
uint8_t m_bDescriptorType;
};
}
}
}
#endif

View File

@@ -0,0 +1,18 @@
#include "device_capability_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
void DeviceCapabilityDescriptor::push(Channel * c) const {
Descriptor::push(c);
c->push(m_bDeviceCapabilityType);
}
uint8_t DeviceCapabilityDescriptor::bLength() const {
return Descriptor::bLength() + sizeof(uint8_t);
}
}
}
}

View File

@@ -0,0 +1,31 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABLITY_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABLITY_DESCRIPTOR_H
#include "descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class BOSDescriptor;
class DeviceCapabilityDescriptor : public Descriptor {
friend class BOSDescriptor;
public:
constexpr DeviceCapabilityDescriptor(uint8_t bDeviceCapabilityType) :
Descriptor(0x10),
m_bDeviceCapabilityType(bDeviceCapabilityType)
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
uint8_t m_bDeviceCapabilityType;
};
}
}
}
#endif

View File

@@ -0,0 +1,29 @@
#include "device_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
void DeviceDescriptor::push(Channel * c) const {
Descriptor::push(c);
c->push(m_bcdUSB);
c->push(m_bDeviceClass);
c->push(m_bDeviceSubClass);
c->push(m_bDeviceProtocol);
c->push(m_bMaxPacketSize0);
c->push(m_idVendor);
c->push(m_idProduct);
c->push(m_bcdDevice);
c->push(m_iManufacturer);
c->push(m_iProduct);
c->push(m_iSerialNumber);
c->push(m_bNumConfigurations);
}
uint8_t DeviceDescriptor::bLength() const {
return Descriptor::bLength() + sizeof(uint16_t) + 4*sizeof(uint8_t) + 3*sizeof(uint16_t) + 4*sizeof(uint8_t);
}
}
}
}

View File

@@ -0,0 +1,62 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_DEVICE_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_DEVICE_DESCRIPTOR_H
#include "descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class DeviceDescriptor : public Descriptor {
public:
constexpr DeviceDescriptor(
uint16_t bcdUSB,
uint8_t bDeviceClass,
uint8_t bDeviceSubClass,
uint8_t bDeviceProtocol,
uint8_t bMaxPacketSize0,
uint16_t idVendor,
uint16_t idProduct,
uint16_t bcdDevice,
uint8_t iManufacturer,
uint8_t iProduct,
uint8_t iSerialNumber,
uint8_t bNumConfigurations) :
Descriptor(0x01),
m_bcdUSB(bcdUSB),
m_bDeviceClass(bDeviceClass),
m_bDeviceSubClass(bDeviceSubClass),
m_bDeviceProtocol(bDeviceProtocol),
m_bMaxPacketSize0(bMaxPacketSize0),
m_idVendor(idVendor),
m_idProduct(idProduct),
m_bcdDevice(bcdDevice),
m_iManufacturer(iManufacturer),
m_iProduct(iProduct),
m_iSerialNumber(iSerialNumber),
m_bNumConfigurations(bNumConfigurations)
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
uint16_t m_bcdUSB;
uint8_t m_bDeviceClass;
uint8_t m_bDeviceSubClass;
uint8_t m_bDeviceProtocol;
uint8_t m_bMaxPacketSize0;
uint16_t m_idVendor;
uint16_t m_idProduct;
uint16_t m_bcdDevice;
uint8_t m_iManufacturer;
uint8_t m_iProduct;
uint8_t m_iSerialNumber;
uint8_t m_bNumConfigurations;
};
}
}
}
#endif

View File

@@ -0,0 +1,21 @@
#include "dfu_functional_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
void DFUFunctionalDescriptor::push(Channel * c) const {
Descriptor::push(c);
c->push(m_bmAttributes);
c->push(m_wDetachTimeOut);
c->push(m_wTransferSize);
c->push(m_bcdDFUVersion);
}
uint8_t DFUFunctionalDescriptor::bLength() const {
return Descriptor::bLength() + sizeof(uint8_t) + 3*sizeof(uint16_t);
}
}
}
}

View File

@@ -0,0 +1,38 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_DFU_FUNCTIONAL_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_DFU_FUNCTIONAL_DESCRIPTOR_H
#include "descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class DFUFunctionalDescriptor : public Descriptor {
public:
constexpr DFUFunctionalDescriptor(
uint8_t bmAttributes,
uint16_t wDetachTimeOut,
uint16_t wTransferSize,
uint16_t bcdDFUVersion) :
Descriptor(0x21),
m_bmAttributes(bmAttributes),
m_wDetachTimeOut(wDetachTimeOut),
m_wTransferSize(wTransferSize),
m_bcdDFUVersion(bcdDFUVersion)
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
uint8_t m_bmAttributes;
uint16_t m_wDetachTimeOut;
uint16_t m_wTransferSize;
uint16_t m_bcdDFUVersion;
};
}
}
}
#endif

View File

@@ -0,0 +1,61 @@
#include "extended_compat_id_descriptor.h"
#include <string.h>
namespace Ion {
namespace Device {
namespace USB {
ExtendedCompatIDDescriptor::ExtendedCompatIDDescriptor(const char * compatibleID) :
m_dwLength(sizeof(uint32_t)
+ 2*sizeof(uint16_t)
+ sizeof(uint8_t)
+ k_reserved1Size * sizeof(uint8_t)
+ 2*sizeof(uint8_t)
+ k_compatibleIDSize * sizeof(uint8_t)
+ k_compatibleIDSize * sizeof(uint8_t)
+ k_reserved2Size * sizeof(uint8_t)),
m_bcdVersion(0x0100), // Microsoft OS Descriptors version 1
m_wIndex(Index),
m_bCount(1), // We assume one function only.
m_reserved1{0, 0, 0, 0, 0, 0, 0},
m_bFirstInterfaceNumber(0),
m_bReserved(1),
m_subCompatibleID{0, 0, 0, 0, 0, 0, 0, 0},
m_reserved2{0, 0, 0, 0, 0, 0}
{
/* Compatible ID has size k_compatibleIDSize, and any unused bytes should be
* filled with 0. */
size_t compatibleIDSize = strlen(compatibleID);
size_t compatibleIDCopySize = k_compatibleIDSize < compatibleIDSize ? k_compatibleIDSize : compatibleIDSize;
for (size_t i = 0; i < compatibleIDCopySize; i++) {
m_compatibleID[i] = compatibleID[i];
}
for (size_t i = compatibleIDCopySize; i < k_compatibleIDSize; i++) {
m_compatibleID[i] = 0;
}
}
void ExtendedCompatIDDescriptor::push(Channel * c) const {
c->push(m_dwLength);
c->push(m_bcdVersion);
c->push(m_wIndex);
c->push(m_bCount);
for (uint8_t i = 0; i < k_reserved1Size; i++) {
c->push(m_reserved1[i]);
}
c->push(m_bFirstInterfaceNumber);
c->push(m_bReserved);
for (uint8_t i = 0; i < k_compatibleIDSize; i++) {
c->push(m_compatibleID[i]);
}
for (uint8_t i = 0; i < k_compatibleIDSize; i++) {
c->push(m_subCompatibleID[i]);
}
for (uint8_t i = 0; i < k_reserved2Size; i++) {
c->push(m_reserved2[i]);
}
}
}
}
}

View File

@@ -0,0 +1,43 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_EXTENDED_COMPAT_ID_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_EXTENDED_COMPAT_ID_DESCRIPTOR_H
#include "../streamable.h"
namespace Ion {
namespace Device {
namespace USB {
/* We use this descriptor to tell the Windows OS that the device should be
* treated as a WinUSB device. The Extended Compat ID Descriptor can set
* differents compat IDs according to the interface and function of the device,
* but we assume there is only one. */
class ExtendedCompatIDDescriptor : public Streamable {
public:
static constexpr uint8_t Index = 0x0004;
ExtendedCompatIDDescriptor(const char * compatibleID);
protected:
void push(Channel * c) const override;
private:
constexpr static uint8_t k_reserved1Size = 7;
constexpr static uint8_t k_compatibleIDSize = 8;
constexpr static uint8_t k_reserved2Size = 6;
// Header
uint32_t m_dwLength; // The length, in bytes, of the complete extended compat ID descriptor
uint16_t m_bcdVersion; // The descriptors version number, in binary coded decimal format
uint16_t m_wIndex; // An index that identifies the particular OS feature descriptor
uint8_t m_bCount; // The number of function sections
uint8_t m_reserved1[k_reserved1Size];
// Function
uint8_t m_bFirstInterfaceNumber; // The interface or function number
uint8_t m_bReserved;
uint8_t m_compatibleID[k_compatibleIDSize];
uint8_t m_subCompatibleID[k_compatibleIDSize];
uint8_t m_reserved2[k_reserved2Size];
};
}
}
}
#endif

View File

@@ -0,0 +1,27 @@
#include "interface_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
void InterfaceDescriptor::push(Channel * c) const {
Descriptor::push(c);
c->push(m_bInterfaceNumber);
c->push(m_bAlternateSetting);
c->push(m_bNumEndpoints);
c->push(m_bInterfaceClass);
c->push(m_bInterfaceSubClass);
c->push(m_bInterfaceProtocol);
c->push(m_iInterface);
if (m_additionalDescriptor != nullptr) {
m_additionalDescriptor->push(c);
}
}
uint8_t InterfaceDescriptor::bLength() const {
return Descriptor::bLength() + 7*sizeof(uint8_t);
}
}
}
}

View File

@@ -0,0 +1,55 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_INTERFACE_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_INTERFACE_DESCRIPTOR_H
#include "descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class ConfigurationDescriptor;
class InterfaceDescriptor : public Descriptor {
friend class ConfigurationDescriptor;
public:
constexpr InterfaceDescriptor(
uint8_t bInterfaceNumber,
uint8_t bAlternateSetting,
uint8_t bNumEndpoints,
uint8_t bInterfaceClass,
uint8_t bInterfaceSubClass,
uint8_t bInterfaceProtocol,
uint8_t iInterface,
Descriptor * additionalDescriptor) :
Descriptor(0x04),
m_bInterfaceNumber(bInterfaceNumber),
m_bAlternateSetting(bAlternateSetting),
m_bNumEndpoints(bNumEndpoints),
m_bInterfaceClass(bInterfaceClass),
m_bInterfaceSubClass(bInterfaceSubClass),
m_bInterfaceProtocol(bInterfaceProtocol),
m_iInterface(iInterface),
m_additionalDescriptor(additionalDescriptor)
/* There could be more than one additional descriptor, but we do not need
* this for now. */
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
uint8_t m_bInterfaceNumber;
uint8_t m_bAlternateSetting;
uint8_t m_bNumEndpoints;
uint8_t m_bInterfaceClass;
uint8_t m_bInterfaceSubClass;
uint8_t m_bInterfaceProtocol;
uint8_t m_iInterface;
const Descriptor * m_additionalDescriptor;
};
}
}
}
#endif

View File

@@ -0,0 +1,19 @@
#include "language_id_string_descriptor.h"
#include <string.h>
namespace Ion {
namespace Device {
namespace USB {
void LanguageIDStringDescriptor::push(Channel * c) const {
Descriptor::push(c);
c->push((uint16_t)(0x0409));
}
uint8_t LanguageIDStringDescriptor::bLength() const {
return Descriptor::bLength() + sizeof(uint16_t);
}
}
}
}

View File

@@ -0,0 +1,25 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_LANGUAGE_ID_STRING_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_LANGUAGE_ID_STRING_DESCRIPTOR_H
#include "descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
// For now this LanguageIDStringDescriptor only ever returns American English
class LanguageIDStringDescriptor : public Descriptor {
public:
constexpr LanguageIDStringDescriptor() :
Descriptor(0x03) { }
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
};
}
}
}
#endif

View File

@@ -0,0 +1,19 @@
#include "microsoft_os_string_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
void MicrosoftOSStringDescriptor::push(Channel * c) const {
StringDescriptor::push(c);
c->push(m_bMSVendorCode);
c->push(m_bPad);
}
uint8_t MicrosoftOSStringDescriptor::bLength() const {
return StringDescriptor::bLength() + 2 * sizeof(uint8_t);
}
}
}
}

View File

@@ -0,0 +1,30 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_MICROSOFT_OS_STRING_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_MICROSOFT_OS_STRING_DESCRIPTOR_H
#include "string_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class MicrosoftOSStringDescriptor : public StringDescriptor {
public:
constexpr MicrosoftOSStringDescriptor(uint8_t bMSVendorCode) :
StringDescriptor("MSFT100"),
m_bMSVendorCode(bMSVendorCode),
m_bPad(0)
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
uint8_t m_bMSVendorCode;
uint8_t m_bPad;
};
}
}
}
#endif

View File

@@ -0,0 +1,21 @@
#include "platform_device_capability_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
void PlatformDeviceCapabilityDescriptor::push(Channel * c) const {
DeviceCapabilityDescriptor::push(c);
c->push(m_bReserved);
for (int i = 0; i < k_platformCapabilityUUIDSize; i++) {
c->push(m_platformCapabilityUUID[i]);
}
}
uint8_t PlatformDeviceCapabilityDescriptor::bLength() const {
return DeviceCapabilityDescriptor::bLength() + sizeof(uint8_t) + k_platformCapabilityUUIDSize*sizeof(uint8_t);
}
}
}
}

View File

@@ -0,0 +1,47 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABLITY_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABLITY_DESCRIPTOR_H
#include "device_capability_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class PlatformDeviceCapabilityDescriptor : public DeviceCapabilityDescriptor {
public:
constexpr PlatformDeviceCapabilityDescriptor(const uint8_t platformCapabilityUUID[]) :
DeviceCapabilityDescriptor(0x05),
m_bReserved(0),
m_platformCapabilityUUID{
platformCapabilityUUID[0],
platformCapabilityUUID[1],
platformCapabilityUUID[2],
platformCapabilityUUID[3],
platformCapabilityUUID[4],
platformCapabilityUUID[5],
platformCapabilityUUID[6],
platformCapabilityUUID[7],
platformCapabilityUUID[8],
platformCapabilityUUID[9],
platformCapabilityUUID[10],
platformCapabilityUUID[11],
platformCapabilityUUID[12],
platformCapabilityUUID[13],
platformCapabilityUUID[14],
platformCapabilityUUID[15]}
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
constexpr static uint8_t k_platformCapabilityUUIDSize = 16;
uint8_t m_bReserved;
uint8_t m_platformCapabilityUUID[k_platformCapabilityUUIDSize];
};
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,28 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_STRING_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_STRING_DESCRIPTOR_H
#include "descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class StringDescriptor : public Descriptor {
public:
constexpr StringDescriptor(const char * string) :
Descriptor(0x03),
m_string(string)
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
const char * m_string;
};
}
}
}
#endif

View File

@@ -0,0 +1,25 @@
#include "url_descriptor.h"
#include <string.h>
namespace Ion {
namespace Device {
namespace USB {
void URLDescriptor::push(Channel * c) const {
Descriptor::push(c);
c->push(m_bScheme);
const char * stringPointer = m_string;
while (*stringPointer != 0) {
c->push(*stringPointer);
stringPointer++;
}
}
uint8_t URLDescriptor::bLength() const {
// The script is returned in UTF-8.
return Descriptor::bLength() + sizeof(uint8_t) + strlen(m_string);
}
}
}
}

View File

@@ -0,0 +1,36 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_URL_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_URL_DESCRIPTOR_H
#include "descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class URLDescriptor : public Descriptor {
public:
enum class Scheme {
HTTP = 0,
HTTPS = 1,
IncludedInURL = 255
};
constexpr URLDescriptor(Scheme scheme, const char * url) :
Descriptor(0x03),
m_bScheme((uint8_t)scheme),
m_string(url)
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
uint8_t m_bScheme;
const char * m_string;
};
}
}
}
#endif

View File

@@ -0,0 +1,22 @@
#include "webusb_platform_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
constexpr uint8_t WebUSBPlatformDescriptor::k_webUSBUUID[];
void WebUSBPlatformDescriptor::push(Channel * c) const {
PlatformDeviceCapabilityDescriptor::push(c);
c->push(m_bcdVersion);
c->push(m_bVendorCode);
c->push(m_iLandingPage);
}
uint8_t WebUSBPlatformDescriptor::bLength() const {
return PlatformDeviceCapabilityDescriptor::bLength() + sizeof(uint16_t) + 2*sizeof(uint8_t);
}
}
}
}

View File

@@ -0,0 +1,37 @@
#ifndef ION_DEVICE_SHARED_USB_STACK_WEBUSB_PLATFORM_DESCRIPTOR_H
#define ION_DEVICE_SHARED_USB_STACK_WEBUSB_PLATFORM_DESCRIPTOR_H
#include "platform_device_capability_descriptor.h"
namespace Ion {
namespace Device {
namespace USB {
class WebUSBPlatformDescriptor : public PlatformDeviceCapabilityDescriptor {
public:
constexpr WebUSBPlatformDescriptor(uint8_t bVendorCode, uint8_t iLandingPage) :
PlatformDeviceCapabilityDescriptor(k_webUSBUUID),
m_bcdVersion(0x0100),
m_bVendorCode(bVendorCode),
m_iLandingPage(iLandingPage)
{
}
protected:
void push(Channel * c) const override;
uint8_t bLength() const override;
private:
/* Little-endian encoding of {3408B638-09A9-47A0-8BFD-A0768815B665}.
* See https://wicg.github.io/webusb/#webusb-platform-capability-descriptor */
constexpr static uint8_t k_webUSBUUID[] = {
0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,
0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65};
uint16_t m_bcdVersion;
uint8_t m_bVendorCode;
uint8_t m_iLandingPage;
};
}
}
}
#endif

View File

@@ -0,0 +1,168 @@
#include "device.h"
#include <drivers/config/internal_flash.h>
#include <drivers/reset.h>
#include <regs/regs.h>
namespace Ion {
namespace Device {
namespace USB {
using namespace Regs;
static inline uint16_t minUint16T(uint16_t x, uint16_t y) { return x < y ? x : y; }
void Device::poll() {
// Read the interrupts
class OTG::GINTSTS intsts(OTG.GINTSTS()->get());
/* SETUP or OUT transaction
* If the Rx FIFO is not empty, there is a SETUP or OUT transaction.
* The interrupt is done AFTER THE HANSDHAKE of the transaction. */
if (intsts.getRXFLVL()) {
class OTG::GRXSTSP grxstsp(OTG.GRXSTSP()->get());
// Store the packet status
OTG::GRXSTSP::PKTSTS pktsts = grxstsp.getPKTSTS();
// We only use endpoint 0
assert(grxstsp.getEPNUM() == 0);
if (pktsts == OTG::GRXSTSP::PKTSTS::OutTransferCompleted || pktsts == OTG::GRXSTSP::PKTSTS::SetupTransactionCompleted) {
// There is no data associated with this interrupt.
return;
}
assert(pktsts != OTG::GRXSTSP::PKTSTS::GlobalOutNAK);
/* We did not enable the GONAKEFFM (Global OUT NAK effective mask) bit in
* GINTSTS, so we should never get this interrupt. */
assert(pktsts == OTG::GRXSTSP::PKTSTS::OutReceived || pktsts == OTG::GRXSTSP::PKTSTS::SetupReceived);
TransactionType type = (pktsts == OTG::GRXSTSP::PKTSTS::OutReceived) ? TransactionType::Out : TransactionType::Setup;
if (type == TransactionType::Setup && OTG.DIEPTSIZ0()->getPKTCNT()) {
// SETUP received but there is a packet in the Tx FIFO. Flush it.
m_ep0.flushTxFifo();
}
// Save the received packet byte count
m_ep0.setReceivedPacketSize(grxstsp.getBCNT());
if (type == TransactionType::Setup) {
m_ep0.readAndDispatchSetupPacket();
} else {
assert(type == TransactionType::Out);
m_ep0.processOUTpacket();
}
m_ep0.discardUnreadData();
}
/* IN transactions.
* The interrupt is done AFTER THE HANSDHAKE of the transaction. */
if (OTG.DIEPINT(0)->getXFRC()) { // We only check endpoint 0.
m_ep0.processINpacket();
// Clear the Transfer Completed Interrupt
OTG.DIEPINT(0)->setXFRC(true);
}
// Handle USB RESET. ENUMDNE = **SPEED** Enumeration Done
if (intsts.getENUMDNE()) {
// Clear the ENUMDNE bit
OTG.GINTSTS()->setENUMDNE(true);
/* After a USB reset, the host talks to the device by sending messages to
* address 0; */
setAddress(0);
// Flush the FIFOs
m_ep0.reset();
m_ep0.setup();
/* In setup(), we should set the MPSIZ field in OTG_DIEPCTL0 to the maximum
* packet size depending on the enumeration speed (found in OTG_DSTS). We
* should always get FullSpeed, so we set the packet size accordingly. */
}
}
bool Device::isSoftDisconnected() const {
return OTG.DCTL()->getSDIS();
}
void Device::detach() {
// Get in soft-disconnected state
OTG.DCTL()->setSDIS(true);
}
void Device::leave(uint32_t leaveAddress) {
if (leaveAddress == Ion::Device::InternalFlash::Config::StartAddress) {
Ion::Device::Reset::coreWhilePlugged();
} else {
Ion::Device::Reset::jump(leaveAddress);
}
}
bool Device::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
// Device only handles standard requests.
if (request->requestType() != SetupPacket::RequestType::Standard) {
return false;
}
switch (request->bRequest()) {
case (int) Request::GetStatus:
return getStatus(transferBuffer, transferBufferLength, transferBufferMaxLength);
case (int) Request::SetAddress:
// Make sure the request is adress is valid.
assert(request->wValue() < 128);
/* According to the reference manual, the address should be set after the
* Status stage of the current transaction, but this is not true.
* It should be set here, after the Data stage. */
setAddress(request->wValue());
*transferBufferLength = 0;
return true;
case (int) Request::GetDescriptor:
return getDescriptor(request, transferBuffer, transferBufferLength, transferBufferMaxLength);
case (int) Request::SetConfiguration:
*transferBufferLength = 0;
return setConfiguration(request);
case (int) Request::GetConfiguration:
return getConfiguration(transferBuffer, transferBufferLength);
}
return false;
}
bool Device::getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
*transferBufferLength = minUint16T(2, transferBufferMaxLength);
for (int i = 0; i<*transferBufferLength; i++) {
transferBuffer[i] = 0; // No remote wakeup, not self-powered.
}
return true;
}
void Device::setAddress(uint8_t address) {
OTG.DCFG()->setDAD(address);
}
bool Device::getDescriptor(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
Descriptor * wantedDescriptor = descriptor(request->descriptorType(), request->descriptorIndex());
if (wantedDescriptor == nullptr) {
return false;
}
*transferBufferLength = wantedDescriptor->copy(transferBuffer, transferBufferMaxLength);
return true;
}
bool Device::getConfiguration(uint8_t * transferBuffer, uint16_t * transferBufferLength) {
*transferBufferLength = 1;
transferBuffer[0] = getActiveConfiguration();
return true;
}
bool Device::setConfiguration(SetupPacket * request) {
// We support one configuration only
setActiveConfiguration(request->wValue());
/* There is one configuration only, we no need to set it again, just reset the
* endpoint. */
m_ep0.reset();
return true;
}
}
}
}

View File

@@ -0,0 +1,67 @@
#ifndef ION_DEVICE_SHARED_USB_DEVICE_H
#define ION_DEVICE_SHARED_USB_DEVICE_H
#include "descriptor/descriptor.h"
#include "endpoint0.h"
#include "interface.h"
#include "request_recipient.h"
#include "setup_packet.h"
namespace Ion {
namespace Device {
namespace USB {
// We only handle control transfers, on EP0.
class Device : public RequestRecipient {
public:
Device(Interface * interface) :
RequestRecipient(&m_ep0),
m_ep0(this, interface),
m_resetOnDisconnect(false)
{
}
void poll();
bool isSoftDisconnected() const;
void detach();
void leave(uint32_t leaveAddress);
bool resetOnDisconnect() { return m_resetOnDisconnect; }
void setResetOnDisconnect(bool reset) { m_resetOnDisconnect = reset; }
protected:
virtual Descriptor * descriptor(uint8_t type, uint8_t index) = 0;
virtual void setActiveConfiguration(uint8_t configurationIndex) = 0;
virtual uint8_t getActiveConfiguration() = 0;
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
Endpoint0 m_ep0;
private:
// USB Standard Device Request Codes
enum class Request {
GetStatus = 0,
ClearFeature = 1,
SetFeature = 3,
SetAddress = 5,
GetDescriptor = 6,
SetDescriptor = 7,
GetConfiguration = 8,
SetConfiguration = 9,
};
enum class TransactionType {
Setup,
In,
Out
};
void setAddress(uint8_t address);
bool getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
bool getDescriptor(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
bool getConfiguration(uint8_t * transferBuffer, uint16_t * transferBufferLength);
bool setConfiguration(SetupPacket * request);
bool m_resetOnDisconnect;
};
}
}
}
#endif

View File

@@ -0,0 +1,348 @@
#include "endpoint0.h"
#include <string.h>
#include <regs/regs.h>
#include "device.h"
#include "interface.h"
#include "request_recipient.h"
#include <algorithm>
namespace Ion {
namespace Device {
namespace USB {
using namespace Regs;
constexpr int Endpoint0::k_maxPacketSize;
constexpr uint16_t Endpoint0::MaxTransferSize;
void Endpoint0::setup() {
// Setup the IN direction
// Reset the device IN endpoint 0 transfer size register
class OTG::DIEPTSIZ0 dieptsiz0(0);
/* Transfer size. The core interrupts the application only after it has
* exhausted the transfer size amount of data. The transfer size is set to the
* maximum packet size, to be interrupted at the end of each packet. */
dieptsiz0.setXFRSIZ(k_maxPacketSize);
OTG.DIEPTSIZ0()->set(dieptsiz0);
// Reset the device IN endpoint 0 control register
class OTG::DIEPCTL0 diepctl0(0); // Reset value
// Set the maximum packet size
diepctl0.setMPSIZ(OTG::DIEPCTL0::MPSIZ::Size64);
// Set the NAK bit: all IN transactions on endpoint 0 receive a NAK answer
diepctl0.setSNAK(true);
// Enable the endpoint
diepctl0.setEPENA(true);
OTG.DIEPCTL0()->set(diepctl0);
// Setup the OUT direction
setupOut();
// Set the NAK bit
OTG.DOEPCTL0()->setSNAK(true);
// Enable the endpoint
enableOut();
// Setup the Tx FIFO
/* Tx FIFO depth
* We process each packet as soon as it arrives, so we only need
* k_maxPacketSize bytes. TX0FD being in terms of 32-bit words, we divide
* k_maxPacketSize by 4. */
OTG.DIEPTXF0()->setTX0FD(k_maxPacketSize/4);
/* Tx FIFO RAM start address. It starts just after the Rx FIFOso the value is
* Rx FIFO start address (0) + Rx FIFO depth. the Rx FIFO depth is set in
* usb.cpp, but because the code is linked separately, we cannot get it. */
OTG.DIEPTXF0()->setTX0FSA(128);
}
void Endpoint0::setupOut() {
class OTG::DOEPTSIZ0 doeptsiz0(0);
// Number of back-to-back SETUP data packets the endpoint can receive
doeptsiz0.setSTUPCNT(1);
// Packet count, false if a packet is written into the Rx FIFO
doeptsiz0.setPKTCNT(true);
/* Transfer size. The core interrupts the application only after it has
* exhausted the transfer size amount of data. The transfer size is set to the
* maximum packet size, to be interrupted at the end of each packet. */
doeptsiz0.setXFRSIZ(64);
OTG.DOEPTSIZ0()->set(doeptsiz0);
}
void Endpoint0::setOutNAK(bool nak) {
m_forceNAK = nak;
/* We need to keep track of the NAK state of the endpoint to use the value
* after a setupOut in poll() of device.cpp. */
if (nak) {
OTG.DOEPCTL0()->setSNAK(true);
} else {
OTG.DOEPCTL0()->setCNAK(true);
}
}
void Endpoint0::enableOut() {
OTG.DOEPCTL0()->setEPENA(true);
}
void Endpoint0::reset() {
flushTxFifo();
flushRxFifo();
}
void Endpoint0::readAndDispatchSetupPacket() {
setOutNAK(true);
// Read the 8-bytes Setup packet
if (readPacket(m_largeBuffer, sizeof(SetupPacket)) != sizeof(SetupPacket)) {
stallTransaction();
return;
};
m_request = SetupPacket(m_largeBuffer);
uint16_t maxBufferLength = std::min(m_request.wLength(), MaxTransferSize);
// Forward the request to the request recipient
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
if (type == 0) {
// Device recipient
m_requestRecipients[0]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
} else {
// Interface recipient
m_requestRecipients[1]->processSetupRequest(&m_request, m_largeBuffer, &m_transferBufferLength, maxBufferLength);
}
}
void Endpoint0::processINpacket() {
switch (m_state) {
case State::DataIn:
sendSomeData();
break;
case State::LastDataIn:
m_state = State::StatusOut;
// Prepare to receive the OUT Data[] transaction.
setOutNAK(false);
break;
case State::StatusIn:
{
m_state = State::Idle;
// All the data has been received. Callback the request recipient.
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
if (type == 0) {
// Device recipient
m_requestRecipients[0]->wholeDataReceivedCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
} else {
// Interface recipient
m_requestRecipients[1]->wholeDataReceivedCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
}
}
break;
default:
stallTransaction();
}
}
void Endpoint0::processOUTpacket() {
switch (m_state) {
case State::DataOut:
if (receiveSomeData() < 0) {
break;
}
if ((m_request.wLength() - m_transferBufferLength) <= k_maxPacketSize) {
m_state = State::LastDataOut;
}
break;
case State::LastDataOut:
if (receiveSomeData() < 0) {
break;
}
// Send the DATA1[] to the host.
writePacket(NULL, 0);
m_state = State::StatusIn;
break;
case State::StatusOut:
{
// Read the DATA1[] sent by the host.
readPacket(NULL, 0);
m_state = State::Idle;
// All the data has been sent. Callback the request recipient.
uint8_t type = static_cast<uint8_t>(m_request.recipientType());
if (type == 0) {
// Device recipient
m_requestRecipients[0]->wholeDataSentCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
} else {
// Interface recipient
m_requestRecipients[1]->wholeDataSentCallback(&m_request, m_largeBuffer, &m_transferBufferLength);
}
}
break;
default:
stallTransaction();
}
}
void Endpoint0::flushTxFifo() {
// Set IN endpoint NAK
OTG.DIEPCTL0()->setSNAK(true);
// Wait for core to respond
while (!OTG.DIEPINT(0)->getINEPNE()) {
}
// Get the Tx FIFO number
uint32_t fifo = OTG.DIEPCTL0()->getTXFNUM();
// Wait for AHB idle
while (!OTG.GRSTCTL()->getAHBIDL()) {
}
// Flush Tx FIFO
OTG.GRSTCTL()->setTXFNUM(fifo);
OTG.GRSTCTL()->setTXFFLSH(true);
// Reset packet counter
OTG.DIEPTSIZ0()->set(0);
// Wait for the flush
while (OTG.GRSTCTL()->getTXFFLSH()) {
}
}
void Endpoint0::flushRxFifo() {
// Set OUT endpoint NAK
OTG.DOEPCTL0()->setSNAK(true);
// Wait for AHB idle
while (!OTG.GRSTCTL()->getAHBIDL()) {
}
// Flush Rx FIFO
OTG.GRSTCTL()->setRXFFLSH(true);
// Reset packet counter
OTG.DOEPTSIZ0()->set(0);
// Wait for the flush
while (OTG.GRSTCTL()->getRXFFLSH()) {
}
}
void Endpoint0::discardUnreadData() {
for (int i = 0; i < m_receivedPacketSize; i += 4) {
OTG.DFIFO0()->get();
}
m_receivedPacketSize = 0;
}
void Endpoint0::sendSomeData() {
if (k_maxPacketSize < m_transferBufferLength) {
// More than one packet needs to be sent
writePacket(m_largeBuffer + m_bufferOffset, k_maxPacketSize);
m_state = State::DataIn;
m_bufferOffset += k_maxPacketSize;
m_transferBufferLength -= k_maxPacketSize;
return;
}
// Last data packet sent
writePacket(m_largeBuffer + m_bufferOffset, m_transferBufferLength);
if (m_zeroLengthPacketNeeded) {
m_state = State::DataIn;
} else {
m_state = State::LastDataIn;
}
m_bufferOffset = 0;
m_zeroLengthPacketNeeded = false;
m_transferBufferLength = 0;
}
void Endpoint0::clearForOutTransactions(uint16_t wLength) {
m_transferBufferLength = 0;
m_state = (wLength > k_maxPacketSize) ? State::DataOut : State::LastDataOut;
setOutNAK(false);
}
int Endpoint0::receiveSomeData() {
// If it is the first chunk of data to be received, m_transferBufferLength is 0.
uint16_t packetSize = std::min(k_maxPacketSize, m_request.wLength() - m_transferBufferLength);
uint16_t sizeOfPacketRead = readPacket(m_largeBuffer + m_transferBufferLength, packetSize);
if (sizeOfPacketRead != packetSize) {
stallTransaction();
return -1;
}
m_transferBufferLength += packetSize;
return packetSize;
}
uint16_t Endpoint0::readPacket(void * buffer, uint16_t length) {
uint32_t * buffer32 = (uint32_t *) buffer;
uint16_t buffer32Length = std::min(length, m_receivedPacketSize);
int i;
// The RX FIFO is read 4 bytes by 4 bytes
for (i = buffer32Length; i >= 4; i -= 4) {
*buffer32++ = OTG.DFIFO0()->get();
m_receivedPacketSize -= 4;
}
if (i) {
/* If there are remaining bytes that should be read, read the next 4 bytes
* and copy only the wanted bytes. */
uint32_t extraData = OTG.DFIFO0()->get();
memcpy(buffer32, &extraData, i);
if (m_receivedPacketSize < 4) {
m_receivedPacketSize = 0;
} else {
m_receivedPacketSize -= 4;
}
}
return buffer32Length;
}
uint16_t Endpoint0::writePacket(const void * buffer, uint16_t length) {
const uint32_t * buffer32 = (uint32_t *) buffer;
// Return if there is already a packet waiting to be read in the TX FIFO
if (OTG.DIEPTSIZ0()->getPKTCNT()) {
return 0;
}
// Enable transmission
class OTG::DIEPTSIZ0 dieptsiz0(0);
// Indicate that the Transfer Size is one packet
dieptsiz0.setPKTCNT(1);
// Indicate the length of the Transfer Size
dieptsiz0.setXFRSIZ(length);
OTG.DIEPTSIZ0()->set(dieptsiz0);
// Enable the endpoint
OTG.DIEPCTL0()->setEPENA(true);
// Clear the NAK bit
OTG.DIEPCTL0()->setCNAK(true);
// Copy the buffer to the TX FIFO by writing data 32bits by 32 bits.
for (int i = length; i > 0; i -= 4) {
OTG.DFIFO0()->set(*buffer32++);
}
return length;
}
void Endpoint0::stallTransaction() {
OTG.DIEPCTL0()->setSTALL(true);
m_state = State::Idle;
}
void Endpoint0::computeZeroLengthPacketNeeded() {
if (m_transferBufferLength
&& m_transferBufferLength < m_request.wLength()
&& m_transferBufferLength % k_maxPacketSize == 0)
{
m_zeroLengthPacketNeeded = true;
return;
}
m_zeroLengthPacketNeeded = false;
}
}
}
}

View File

@@ -0,0 +1,79 @@
#ifndef ION_DEVICE_SHARED_USB_ENDPOINT0_H
#define ION_DEVICE_SHARED_USB_ENDPOINT0_H
#include "setup_packet.h"
namespace Ion {
namespace Device {
namespace USB {
class RequestRecipient;
class Endpoint0 {
public:
enum class State {
Idle,
Stalled,
DataIn,
LastDataIn,
StatusIn,
DataOut,
LastDataOut,
StatusOut,
};
constexpr static int k_maxPacketSize = 64;
constexpr static uint16_t MaxTransferSize = 2048;
constexpr Endpoint0(RequestRecipient * device, RequestRecipient * interface) :
m_forceNAK(false),
m_bufferOffset(0),
m_transferBufferLength(0),
m_receivedPacketSize(0),
m_zeroLengthPacketNeeded(false),
m_request(),
m_requestRecipients{device, interface},
m_state(State::Idle),
m_largeBuffer{0}
{
}
void setup();
void setupOut();
void setOutNAK(bool nak);
void enableOut();
void reset();
bool NAKForced() const { return m_forceNAK; }
void readAndDispatchSetupPacket();
void processINpacket();
void processOUTpacket();
void flushTxFifo();
void flushRxFifo();
void setReceivedPacketSize(uint16_t size) { m_receivedPacketSize = size; }
void discardUnreadData();
void stallTransaction();
void computeZeroLengthPacketNeeded();
void setState(State state) { m_state = state; }
void sendSomeData(); // Writes the next data packet and updates the state.
void clearForOutTransactions(uint16_t wLength);
private:
int receiveSomeData();
uint16_t readPacket(void * buffer, uint16_t length);
uint16_t writePacket(const void * buffer, uint16_t length);
bool m_forceNAK;
int m_bufferOffset; // When sending large data stored in the buffer, the offset keeps tracks of which data packet should be sent next.
uint16_t m_transferBufferLength;
uint16_t m_receivedPacketSize;
bool m_zeroLengthPacketNeeded;
SetupPacket m_request;
RequestRecipient * m_requestRecipients[2];
State m_state;
uint8_t m_largeBuffer[MaxTransferSize];
};
}
}
}
#endif

View File

@@ -0,0 +1,66 @@
#include "interface.h"
namespace Ion {
namespace Device {
namespace USB {
static inline uint16_t minUint16T(uint16_t x, uint16_t y) { return x < y ? x : y; }
bool Interface::processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
if (request->requestType() != SetupPacket::RequestType::Standard) {
return false;
}
switch (request->bRequest()) {
case (int) Request::GetStatus:
return getStatus(transferBuffer, transferBufferLength, transferBufferMaxLength);
case (int) Request::SetInterface:
return setInterface(request, transferBufferLength);
case (int) Request::GetInterface:
return getInterface(transferBuffer, transferBufferLength, transferBufferMaxLength);
case (int) Request::ClearFeature:
return clearFeature(transferBufferLength);
case (int) Request::SetFeature:
return setFeature(transferBufferLength);
}
return false;
}
bool Interface::getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
*transferBufferLength = minUint16T(2, transferBufferMaxLength);
for (int i = 0; i<*transferBufferLength; i++) {
transferBuffer[i] = 0; // Reserved, must be set to 0
}
return true;
}
bool Interface::getInterface(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
*transferBufferLength = minUint16T(1, transferBufferMaxLength);;
if (*transferBufferLength > 0) {
transferBuffer[0] = getActiveInterfaceAlternative();
}
return true;
}
bool Interface::setInterface(SetupPacket * request, uint16_t * transferBufferLength) {
// We support one interface only
setActiveInterfaceAlternative(request->wValue());
// There is one interface alternative only, we no need to set it again.
*transferBufferLength = 0;
return true;
}
bool Interface::clearFeature(uint16_t * transferBufferLength) {
// Not needed for now
*transferBufferLength = 0;
return true;
}
bool Interface::setFeature(uint16_t * transferBufferLength) {
// Not needed for now
*transferBufferLength = 0;
return true;
}
}
}
}

View File

@@ -0,0 +1,42 @@
#ifndef ION_DEVICE_SHARED_USB_INTERFACE_H
#define ION_DEVICE_SHARED_USB_INTERFACE_H
#include "endpoint0.h"
#include "request_recipient.h"
#include "setup_packet.h"
namespace Ion {
namespace Device {
namespace USB {
class Interface : public RequestRecipient {
public:
Interface(Endpoint0 * ep0) :
RequestRecipient(ep0)
{
}
protected:
virtual void setActiveInterfaceAlternative(uint8_t interfaceAlternativeIndex) = 0;
virtual uint8_t getActiveInterfaceAlternative() = 0;
bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override;
private:
// USB Standard Interface Request Codes
enum class Request {
GetStatus = 0,
ClearFeature = 1,
SetFeature = 3,
GetInterface = 10,
SetInterface = 11,
};
bool getStatus(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
bool getInterface(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
bool setInterface(SetupPacket * request, uint16_t * transferBufferLength);
bool clearFeature(uint16_t * transferBufferLength);
bool setFeature(uint16_t * transferBufferLength);
};
}
}
}
#endif

View File

@@ -0,0 +1,32 @@
#include "request_recipient.h"
namespace Ion {
namespace Device {
namespace USB {
bool RequestRecipient::processSetupRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) {
if (request->followingTransaction() == SetupPacket::TransactionType::InTransaction) {
// There is no data stage in this transaction, or the data stage will be in IN direction.
if (!processSetupInRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength)) {
m_ep0->stallTransaction();
return false;
}
if (*transferBufferLength > 0) {
m_ep0->computeZeroLengthPacketNeeded();
m_ep0->sendSomeData();
} else {
m_ep0->sendSomeData();
// On seeing a zero length packet, sendSomeData changed endpoint0 state to
// LastDataIn, but it should be StatusIn as there was no data stage.
m_ep0->setState(Endpoint0::State::StatusIn);
}
} else {
// The following transaction will be an OUT transaction.
m_ep0->clearForOutTransactions(request->wLength());
}
return true;
}
}
}
}

View File

@@ -0,0 +1,29 @@
#ifndef ION_DEVICE_SHARED_USB_REQUEST_RECIPIENT_H
#define ION_DEVICE_SHARED_USB_REQUEST_RECIPIENT_H
#include "endpoint0.h"
#include "setup_packet.h"
namespace Ion {
namespace Device {
namespace USB {
class RequestRecipient {
public:
RequestRecipient(Endpoint0 * ep0):
m_ep0(ep0)
{
}
bool processSetupRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength);
virtual void wholeDataReceivedCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {}
virtual void wholeDataSentCallback(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength) {}
protected:
virtual bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) = 0;
Endpoint0 * m_ep0;
};
}
}
}
#endif

View File

@@ -0,0 +1,38 @@
#include "setup_packet.h"
#include <string.h>
namespace Ion {
namespace Device {
namespace USB {
SetupPacket::SetupPacket(void * buffer) {
memcpy(this, buffer, sizeof(SetupPacket));
}
SetupPacket::TransactionType SetupPacket::followingTransaction() const {
if (m_wLength == 0 || (m_bmRequestType & 0b10000000) != 0) {
return TransactionType::InTransaction;
} else {
return TransactionType::OutTransaction;
}
}
SetupPacket::RequestType SetupPacket::requestType() const {
return (RequestType) ((m_bmRequestType & 0b01100000) >> 5);
}
SetupPacket::RecipientType SetupPacket::recipientType() const {
return (RecipientType) (m_bmRequestType & 0b00001111);
}
int SetupPacket::descriptorIndex() {
return m_wValue & 0xFF;
}
int SetupPacket::descriptorType() {
return m_wValue >> 8;
}
}
}
}

View File

@@ -0,0 +1,65 @@
#ifndef ION_DEVICE_SHARED_USB_SETUP_PACKET_H
#define ION_DEVICE_SHARED_USB_SETUP_PACKET_H
#include <stdint.h>
namespace Ion {
namespace Device {
namespace USB {
class SetupPacket {
public:
enum class TransactionType {
SetupTransaction,
InTransaction,
OutTransaction
};
enum class RequestType {
Standard = 0,
Class = 1,
Vendor = 2
};
enum class RecipientType {
Device = 0,
Interface = 1,
Endpoint = 2,
Other = 3
};
constexpr SetupPacket() :
m_bmRequestType(0),
m_bRequest(0),
m_wValue(0),
m_wIndex(0),
m_wLength(0)
{
}
SetupPacket(void * buffer);
TransactionType followingTransaction() const;
RequestType requestType() const;
RecipientType recipientType() const;
int descriptorIndex();
int descriptorType();
uint8_t bmRequestType() { return m_bmRequestType; }
uint8_t bRequest() { return m_bRequest; }
uint16_t wValue() { return m_wValue; }
uint16_t wIndex() { return m_wIndex; }
uint16_t wLength() { return m_wLength; }
private:
uint8_t m_bmRequestType;
uint8_t m_bRequest;
uint16_t m_wValue;
uint16_t m_wIndex;
uint16_t m_wLength;
};
static_assert(sizeof(SetupPacket) == 8, "SetupData must be packed");
}
}
}
#endif

View File

@@ -0,0 +1,15 @@
#include "streamable.h"
namespace Ion {
namespace Device {
namespace USB {
uint16_t Streamable::copy(void * target, size_t maxSize) const {
Channel c(target, maxSize);
push(&c);
return maxSize - c.sizeLeft();
}
}
}
}

View File

@@ -0,0 +1,44 @@
#ifndef ION_DEVICE_SHARED_USB_STREAMABLE_H
#define ION_DEVICE_SHARED_USB_STREAMABLE_H
#include <stdint.h>
#include <stddef.h>
namespace Ion {
namespace Device {
namespace USB {
class Streamable {
public:
uint16_t copy(void * target, size_t maxSize) const;
protected:
class Channel {
public:
Channel(void * pointer, size_t maxSize) :
m_pointer(pointer),
m_sizeLeft(maxSize)
{
}
template<typename T>
void push(T data) {
if (m_sizeLeft >= sizeof(T)) {
T * typedPointer = static_cast<T *>(m_pointer);
*typedPointer++ = data; // Actually push the data
m_pointer = static_cast<void *>(typedPointer);
m_sizeLeft -= sizeof(T);
}
}
size_t sizeLeft() { return m_sizeLeft; }
private:
void * m_pointer;
size_t m_sizeLeft;
};
virtual void push(Channel * c) const = 0;
};
}
}
}
#endif

View File

@@ -1,16 +1,10 @@
#include <ion.h>
#include <kandinsky.h>
namespace Flasher {
namespace Display {
void init() {
KDRect screen = KDRect(0,0,Ion::Display::Width,Ion::Display::Height);
Ion::Display::pushRectUniform(KDRect(0,0,Ion::Display::Width,Ion::Display::Height), KDColor::RGB24(0x5e81ac));
KDContext * ctx = KDIonContext::sharedContext();
ctx->setOrigin(KDPointZero);
ctx->setClippingRect(screen);
ctx->drawString("RECOVERY MODE", KDPoint(10, 10), KDFont::LargeFont, KDColorWhite, KDColor::RGB24(0x5e81ac));
Ion::Display::pushRectUniform(KDRect(0,0,Ion::Display::Width,Ion::Display::Height), KDColor::RGB24(0xFFFF00));
}
}

View File

@@ -1,55 +1,47 @@
#include <ion.h>
#include <kandinsky.h>
#include "image.h"
namespace Flasher {
namespace Display {
namespace Display {
constexpr static int sNumberOfMessages = 5;
constexpr static int sNumberOfMessages = 5;
constexpr static int sNumberOfLanguages = 2;
constexpr static const char * sMessages[sNumberOfMessages] = {
"RECOVERY MODE",
"Your calculator is waiting",
"for Upsilon to be installed.",
"Follow the instructions",
"on your computer to continue.",
};
void init() {
KDRect screen = KDRect(0,0,Ion::Display::Width,Ion::Display::Height);
Ion::Display::pushRectUniform(screen, KDColor::RGB24(0x2B2B2B));
KDContext * ctx = KDIonContext::sharedContext();
ctx->setOrigin(KDPointZero);
ctx->setClippingRect(screen);
KDCoordinate margin = 30;
KDCoordinate currentHeight = margin;
/* Title */
const char * title = sMessages[0];
KDSize titleSize = KDFont::LargeFont->stringSize(title);
ctx->drawString(title, KDPoint((Ion::Display::Width-titleSize.width())/2, currentHeight),
KDFont::LargeFont, KDColorWhite, KDColor::RGB24(0x2B2B2B));
currentHeight = (uint16_t)((Ion::Display::Height*2)/3);
/* Logo */
for (int i = 0; i < IMAGE_WIDTH; ++i) {
for (int j = 0; j < IMAGE_HEIGHT; ++j) {
ctx->setPixel(KDPoint(i+(uint16_t)((Ion::Display::Width-IMAGE_WIDTH)/2),
j+(titleSize.height()+margin+15)),
KDColor::RGB16(image[i+(j*IMAGE_WIDTH)]));
}
}
/* Messages */
const char * message;
for (int i = 1; i < sNumberOfMessages; ++i) {
message = sMessages[i];
KDSize messageSize = KDFont::SmallFont->stringSize(message);
ctx->drawString(message, KDPoint((Ion::Display::Width-messageSize.width())/2, currentHeight),
KDFont::SmallFont, KDColorWhite, KDColor::RGB24(0x2B2B2B));
currentHeight += messageSize.height();
}
}
constexpr static const char * sMessages[sNumberOfLanguages][sNumberOfMessages] = {
{"RECOVERY MODE",
"Your calculator is waiting",
"for a new software.",
"Follow the instructions",
"on your computer to continue."},
{"MODE RECUPERATION",
"Votre calculatrice attend",
"l'installation d'un nouveau logiciel.",
"Suivez les instructions sur",
"votre ordinateur pour continuer."}
};
void init() {
KDRect screen = KDRect(0,0,Ion::Display::Width,Ion::Display::Height);
Ion::Display::pushRectUniform(screen, KDColorWhite);
KDContext * ctx = KDIonContext::sharedContext();
ctx->setOrigin(KDPointZero);
ctx->setClippingRect(screen);
KDCoordinate margin = 20;
KDCoordinate currentHeight = 0;
for (int i = 0; i < sNumberOfLanguages; i++) {
currentHeight += margin;
const char * title = sMessages[i][0];
KDSize titleSize = KDFont::LargeFont->stringSize(title);
ctx->drawString(title, KDPoint((Ion::Display::Width-titleSize.width())/2, currentHeight), KDFont::LargeFont);
currentHeight += 2*titleSize.height();
for (int j = 1; j < sNumberOfMessages; j++) {
const char * message = sMessages[i][j];
KDSize messageSize = KDFont::SmallFont->stringSize(message);
ctx->drawString(message, KDPoint((Ion::Display::Width-messageSize.width())/2, currentHeight), KDFont::SmallFont);
currentHeight += messageSize.height();
}
}
}
}
}

View File

@@ -11,6 +11,6 @@ void ion_main(int argc, const char * const argv[]) {
Ion::USB::enable();
while (!Ion::USB::isEnumerated()) {
}
Ion::USB::DFU(false, false, 0);
Ion::USB::DFU(false);
}
}

View File

@@ -1,31 +1,31 @@
#include <stdint.h>
#include <string.h>
#include <ion.h>
#include <boot/isr.h>
#include <drivers/board.h>
#include <drivers/rtc.h>
#include <drivers/reset.h>
#include <drivers/timing.h>
#include <drivers/power.h>
#include <drivers/wakeup.h>
#include <drivers/battery.h>
#include <drivers/usb.h>
#include <drivers/led.h>
#include <ion.h>
#include <kandinsky.h>
#include <regs/config/pwr.h>
#include <regs/config/rcc.h>
#include <regs/regs.h>
typedef void (*cxx_constructor)();
extern "C" {
extern char _data_section_start_flash;
extern char _data_section_start_ram;
extern char _data_section_end_ram;
extern char _bss_section_start_ram;
extern char _bss_section_end_ram;
extern cxx_constructor _init_array_start;
extern cxx_constructor _init_array_end;
extern char _data_section_start_flash;
extern char _data_section_start_ram;
extern char _data_section_end_ram;
extern char _bss_section_start_ram;
extern char _bss_section_end_ram;
extern cxx_constructor _init_array_start;
extern cxx_constructor _init_array_end;
}
void __attribute__((noinline)) abort() {
#ifdef NDEBUG
Ion::Device::Reset::core();
#else
while (1) {
}
#endif
}
/* In order to ensure that this method is execute from the external flash, we
@@ -38,6 +38,7 @@ static void __attribute__((noinline)) external_flash_start() {
* after the Power-On Self-Test if there is one or before switching to the
* home app otherwise. */
Ion::Device::Board::initPeripherals(false);
return ion_main(0, nullptr);
}
@@ -63,154 +64,6 @@ static void __attribute__((noinline)) jump_to_external_flash() {
external_flash_start();
}
void __attribute__((noinline)) abort_init() {
Ion::Device::Board::shutdownPeripherals(true);
Ion::Device::Board::initPeripherals(false);
Ion::Timing::msleep(100);
Ion::Backlight::init();
Ion::LED::setColor(KDColorRed);
Ion::Backlight::setBrightness(180);
}
void __attribute__((noinline)) abort_economy() {
int brightness = Ion::Backlight::brightness();
bool plugged = Ion::USB::isPlugged();
while (brightness > 0) {
brightness--;
Ion::Backlight::setBrightness(brightness);
Ion::Timing::msleep(50);
if(plugged || (!plugged && Ion::USB::isPlugged())){
Ion::Backlight::setBrightness(180);
return;
}
}
Ion::Backlight::shutdown();
while (1) {
Ion::Device::Power::sleepConfiguration();
Ion::Device::WakeUp::onUSBPlugging();
Ion::Device::WakeUp::onChargingEvent();
Ion::Device::Power::internalFlashSuspend(true);
if (!plugged && Ion::USB::isPlugged()) {
break;
}
plugged = Ion::USB::isPlugged();
};
Ion::Device::Board::setStandardFrequency(Ion::Device::Board::Frequency::High);
Ion::Backlight::init();
Ion::Backlight::setBrightness(180);
}
void __attribute__((noinline)) abort_sleeping() {
if (Ion::Battery::level() != Ion::Battery::Charge::EMPTY) {
return;
}
// we don't use Ion::Power::suspend because we don't want to move the exam buffer into the internal
Ion::Device::Board::shutdownPeripherals(true);
bool plugged = Ion::USB::isPlugged();
while (1) {
Ion::Device::Battery::initGPIO();
Ion::Device::USB::initGPIO();
Ion::Device::LED::init();
Ion::Device::Power::sleepConfiguration();
Ion::Device::Board::shutdownPeripherals(true);
Ion::Device::WakeUp::onUSBPlugging();
Ion::Device::WakeUp::onChargingEvent();
Ion::Device::Power::internalFlashSuspend(true);
Ion::Device::USB::initGPIO();
if (!plugged && Ion::USB::isPlugged()) {
break;
}
plugged = Ion::USB::isPlugged();
}
Ion::Device::Board::setStandardFrequency(Ion::Device::Board::Frequency::High);
abort_init();
}
void __attribute__((noinline)) abort_core(const char * text) {
Ion::Timing::msleep(100);
int counting;
while (true) {
counting = 0;
if (Ion::Battery::level() == Ion::Battery::Charge::EMPTY) {
abort_sleeping();
abort_screen(text);
}
Ion::USB::enable();
Ion::Battery::Charge previous_state = Ion::Battery::level();
while (!Ion::USB::isEnumerated()) {
if (Ion::Battery::level() == Ion::Battery::Charge::LOW) {
if (previous_state != Ion::Battery::Charge::LOW) {
previous_state = Ion::Battery::Charge::LOW;
counting = 0;
}
Ion::Timing::msleep(500);
if (counting >= 20) {
abort_sleeping();
abort_screen(text);
counting = -1;
}
counting++;
} else {
if (previous_state == Ion::Battery::Charge::LOW) {
previous_state = Ion::Battery::level();
counting = 0;
}
Ion::Timing::msleep(100);
if (counting >= 300) {
abort_economy();
counting = -1;
}
counting++;
}
}
Ion::USB::DFU(false, false, 0);
}
}
void __attribute__((noinline)) abort_screen(const char * text){
KDRect screen = KDRect(0, 0, Ion::Display::Width, Ion::Display::Height);
Ion::Display::pushRectUniform(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height), KDColor::RGB24(0xffffff));
KDContext* ctx = KDIonContext::sharedContext();
ctx->setOrigin(KDPointZero);
ctx->setClippingRect(screen);
ctx->drawString("UPSILON CRASH", KDPoint(90, 10), KDFont::LargeFont, KDColorRed, KDColor::RGB24(0xffffff));
ctx->drawString("An error occurred", KDPoint(10, 30), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("If you have some important data, please", KDPoint(10, 45), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("use bit.ly/upsiBackup to backup them.", KDPoint(10, 60), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("YOU WILL LOSE ALL YOUR DATA", KDPoint(10, 85), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("→ You can try to reboot by presssing the", KDPoint(10, 110), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("reset button at the back of the calculator", KDPoint(10, 125), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("→ If Upsilon keeps crashing, you can connect", KDPoint(10, 140), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("the calculator to a computer or a phone", KDPoint(10, 160), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("and try to reinstall Upsilon", KDPoint(10, 175), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString(text, KDPoint(220, 200), KDFont::SmallFont, KDColorRed, KDColor::RGB24(0xffffff));
}
void __attribute__((noinline)) abort() {
abort_init();
abort_screen("HARDFAULT");
abort_core("HARDFAULT");
}
void __attribute__((noinline)) nmi_abort() {
abort_init();
abort_screen("NMIFAULT");
abort_core("NMIFAULT");
}
void __attribute__((noinline)) bf_abort() {
abort_init();
abort_screen("BUSFAULT");
abort_core("BUSFAULT");
}
void __attribute__((noinline)) uf_abort() {
abort_init();
abort_screen("USAGEFAULT");
abort_core("USAGEFAULT");
}
/* When 'start' is executed, the external flash is supposed to be shutdown. We
* thus forbid inlining to prevent executing this code from external flash
* (just in case 'start' was to be called from the external flash). */
@@ -244,7 +97,7 @@ void __attribute__((noinline)) start() {
* call the pointed function. */
#define SUPPORT_CPP_GLOBAL_CONSTRUCTORS 0
#if SUPPORT_CPP_GLOBAL_CONSTRUCTORS
for (cxx_constructor* c = &_init_array_start; c < &_init_array_end; c++) {
for (cxx_constructor * c = &_init_array_start; c<&_init_array_end; c++) {
(*c)();
}
#else

View File

@@ -71,7 +71,7 @@ SECTIONS {
/* The data section is written to Flash but linked as if it were in RAM.
*
* This is required because its initial value matters (so it has to be in
* persistent memory in the first place), but it is a R/W area of memory
* persistant memory in the first place), but it is a R/W area of memory
* so it will have to live in RAM upon execution (in linker lingo, that
* translates to the data section having a LMA in Flash and a VMA in RAM).
*

View File

@@ -1,5 +1,4 @@
ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \
board.cpp \
cache.cpp \
external_flash.cpp \
led.cpp \
@@ -8,9 +7,21 @@ ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \
usb.cpp \
)
ifeq ($(filter bootloader, ${MAKECMDGOALS}), bootloader)
ion_device_src += $(addprefix bootloader/drivers/, \
board.cpp \
)
ion_device_src += $(addprefix bootloader/boot/, \
rt0.cpp \
)
else
ion_device_src += $(addprefix ion/src/device/n0110/drivers/, \
board.cpp \
)
ion_device_src += $(addprefix ion/src/device/n0110/boot/, \
rt0.cpp \
)
endif
ion_device_src += $(addprefix ion/src/device/n0110/, \
platform_info.cpp \

View File

@@ -1,31 +1,31 @@
#include <stdint.h>
#include <string.h>
#include <ion.h>
#include <boot/isr.h>
#include <drivers/board.h>
#include <drivers/rtc.h>
#include <drivers/reset.h>
#include <drivers/timing.h>
#include <drivers/power.h>
#include <drivers/wakeup.h>
#include <drivers/battery.h>
#include <drivers/usb.h>
#include <drivers/led.h>
#include <ion.h>
#include <kandinsky.h>
#include <regs/config/pwr.h>
#include <regs/config/rcc.h>
#include <regs/regs.h>
typedef void (*cxx_constructor)();
extern "C" {
extern char _data_section_start_flash;
extern char _data_section_start_ram;
extern char _data_section_end_ram;
extern char _bss_section_start_ram;
extern char _bss_section_end_ram;
extern cxx_constructor _init_array_start;
extern cxx_constructor _init_array_end;
extern char _data_section_start_flash;
extern char _data_section_start_ram;
extern char _data_section_end_ram;
extern char _bss_section_start_ram;
extern char _bss_section_end_ram;
extern cxx_constructor _init_array_start;
extern cxx_constructor _init_array_end;
}
void __attribute__((noinline)) abort() {
#ifdef NDEBUG
Ion::Device::Reset::core();
#else
while (1) {
}
#endif
}
/* In order to ensure that this method is execute from the external flash, we
@@ -38,6 +38,7 @@ static void __attribute__((noinline)) external_flash_start() {
* after the Power-On Self-Test if there is one or before switching to the
* home app otherwise. */
Ion::Device::Board::initPeripherals(false);
return ion_main(0, nullptr);
}
@@ -63,154 +64,6 @@ static void __attribute__((noinline)) jump_to_external_flash() {
external_flash_start();
}
void __attribute__((noinline)) abort_init() {
Ion::Device::Board::shutdownPeripherals(true);
Ion::Device::Board::initPeripherals(false);
Ion::Timing::msleep(100);
Ion::Backlight::init();
Ion::LED::setColor(KDColorRed);
Ion::Backlight::setBrightness(180);
}
void __attribute__((noinline)) abort_economy() {
int brightness = Ion::Backlight::brightness();
bool plugged = Ion::USB::isPlugged();
while (brightness > 0) {
brightness--;
Ion::Backlight::setBrightness(brightness);
Ion::Timing::msleep(50);
if(plugged || (!plugged && Ion::USB::isPlugged())){
Ion::Backlight::setBrightness(180);
return;
}
}
Ion::Backlight::shutdown();
while (1) {
Ion::Device::Power::sleepConfiguration();
Ion::Device::WakeUp::onUSBPlugging();
Ion::Device::WakeUp::onChargingEvent();
Ion::Device::Power::internalFlashSuspend(true);
if (!plugged && Ion::USB::isPlugged()) {
break;
}
plugged = Ion::USB::isPlugged();
};
Ion::Device::Board::setStandardFrequency(Ion::Device::Board::Frequency::High);
Ion::Backlight::init();
Ion::Backlight::setBrightness(180);
}
void __attribute__((noinline)) abort_sleeping() {
if (Ion::Battery::level() != Ion::Battery::Charge::EMPTY) {
return;
}
// we don't use Ion::Power::suspend because we don't want to move the exam buffer into the internal
Ion::Device::Board::shutdownPeripherals(true);
bool plugged = Ion::USB::isPlugged();
while (1) {
Ion::Device::Battery::initGPIO();
Ion::Device::USB::initGPIO();
Ion::Device::LED::init();
Ion::Device::Power::sleepConfiguration();
Ion::Device::Board::shutdownPeripherals(true);
Ion::Device::WakeUp::onUSBPlugging();
Ion::Device::WakeUp::onChargingEvent();
Ion::Device::Power::internalFlashSuspend(true);
Ion::Device::USB::initGPIO();
if (!plugged && Ion::USB::isPlugged()) {
break;
}
plugged = Ion::USB::isPlugged();
}
Ion::Device::Board::setStandardFrequency(Ion::Device::Board::Frequency::High);
abort_init();
}
void __attribute__((noinline)) abort_core(const char * text) {
Ion::Timing::msleep(100);
int counting;
while (true) {
counting = 0;
if (Ion::Battery::level() == Ion::Battery::Charge::EMPTY) {
abort_sleeping();
abort_screen(text);
}
Ion::USB::enable();
Ion::Battery::Charge previous_state = Ion::Battery::level();
while (!Ion::USB::isEnumerated()) {
if (Ion::Battery::level() == Ion::Battery::Charge::LOW) {
if (previous_state != Ion::Battery::Charge::LOW) {
previous_state = Ion::Battery::Charge::LOW;
counting = 0;
}
Ion::Timing::msleep(500);
if (counting >= 20) {
abort_sleeping();
abort_screen(text);
counting = -1;
}
counting++;
} else {
if (previous_state == Ion::Battery::Charge::LOW) {
previous_state = Ion::Battery::level();
counting = 0;
}
Ion::Timing::msleep(100);
if (counting >= 300) {
abort_economy();
counting = -1;
}
counting++;
}
}
Ion::USB::DFU(false, false, 0);
}
}
void __attribute__((noinline)) abort_screen(const char * text){
KDRect screen = KDRect(0, 0, Ion::Display::Width, Ion::Display::Height);
Ion::Display::pushRectUniform(KDRect(0, 0, Ion::Display::Width, Ion::Display::Height), KDColor::RGB24(0xffffff));
KDContext* ctx = KDIonContext::sharedContext();
ctx->setOrigin(KDPointZero);
ctx->setClippingRect(screen);
ctx->drawString("UPSILON CRASH", KDPoint(90, 10), KDFont::LargeFont, KDColorRed, KDColor::RGB24(0xffffff));
ctx->drawString("An error occurred", KDPoint(10, 30), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("If you have some important data, please", KDPoint(10, 45), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("use bit.ly/upsiBackup to backup them.", KDPoint(10, 60), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("YOU WILL LOSE ALL YOUR DATA", KDPoint(10, 85), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("→ You can try to reboot by presssing the", KDPoint(10, 110), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("reset button at the back of the calculator", KDPoint(10, 125), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("→ If Upsilon keeps crashing, you can connect", KDPoint(10, 140), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("the calculator to a computer or a phone", KDPoint(10, 160), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString("and try to reinstall Upsilon", KDPoint(10, 175), KDFont::SmallFont, KDColorBlack, KDColor::RGB24(0xffffff));
ctx->drawString(text, KDPoint(220, 200), KDFont::SmallFont, KDColorRed, KDColor::RGB24(0xffffff));
}
void __attribute__((noinline)) abort() {
abort_init();
abort_screen("HARDFAULT");
abort_core("HARDFAULT");
}
void __attribute__((noinline)) nmi_abort() {
abort_init();
abort_screen("NMIFAULT");
abort_core("NMIFAULT");
}
void __attribute__((noinline)) bf_abort() {
abort_init();
abort_screen("BUSFAULT");
abort_core("BUSFAULT");
}
void __attribute__((noinline)) uf_abort() {
abort_init();
abort_screen("USAGEFAULT");
abort_core("USAGEFAULT");
}
/* When 'start' is executed, the external flash is supposed to be shutdown. We
* thus forbid inlining to prevent executing this code from external flash
* (just in case 'start' was to be called from the external flash). */
@@ -244,7 +97,7 @@ void __attribute__((noinline)) start() {
* call the pointed function. */
#define SUPPORT_CPP_GLOBAL_CONSTRUCTORS 0
#if SUPPORT_CPP_GLOBAL_CONSTRUCTORS
for (cxx_constructor* c = &_init_array_start; c < &_init_array_end; c++) {
for (cxx_constructor * c = &_init_array_start; c<&_init_array_end; c++) {
(*c)();
}
#else

View File

@@ -51,10 +51,8 @@ void initMPU() {
// 1.1 Memory barrier
Cache::dmb();
// 1.2 Enable fault exceptions
CORTEX.SHCRS()->setMEMFAULTENA(true);
CORTEX.SHCRS()->setBUSFAULTENA(true);
CORTEX.SHCRS()->setUSGFAULTENA(true);
// 1.2 Disable fault exceptions
CORTEX.SHCRS()->setMEMFAULTENA(false);
// 1.3 Disable the MPU and clear the control register
MPU.CTRL()->setENABLE(false);
@@ -64,7 +62,7 @@ void initMPU() {
/* This is needed for interfacing with the LCD
* We define the whole FMC memory bank 1 as strongly ordered, non-executable
* and not accessible. We define the FMC command and data addresses as
* writeable non-cacheable, non-buffereable and non shareable. */
* writeable non-cachable, non-buffereable and non shareable. */
int sector = 0;
MPU.RNR()->setREGION(sector++);
MPU.RBAR()->setADDR(0x60000000);
@@ -106,7 +104,7 @@ void initMPU() {
* then an AHB error is given (AN4760). To prevent this to happen, we
* configure the MPU to define the whole Quad-SPI addressable space as
* strongly ordered, non-executable and not accessible. Plus, we define the
* Quad-SPI region corresponding to the External Chip as executable and
* Quad-SPI region corresponding to the Expternal Chip as executable and
* fully accessible (AN4861). */
MPU.RNR()->setREGION(sector++);
MPU.RBAR()->setADDR(0x90000000);

View File

@@ -59,7 +59,7 @@ constexpr static double modulationDepth = 0.25; // Must be (0.25% <= md <= 2%)
constexpr static uint32_t SSCG_INCSTEP = (32767*modulationDepth*PLL_N)/(1.0*100*5*SSCG_MODPER);
static_assert(SSCG_MODPER == 250, "SSCG_MODPER changed");
static_assert(SSCG_INCSTEP == 25, "SSCG_INCSTEP changed");
static_assert(SSCG_INCSTEP * SSCG_MODPER < 32767, "Wrong values for the Spread spectrum clock generator");
static_assert(SSCG_INCSTEP * SSCG_MODPER < 32767, "Wrong values for the Spread spectrun clock generator");
}
}
}

View File

@@ -89,10 +89,15 @@ public:
public:
using Register8::Register8;
REGS_BOOL_FIELD_R(BUSY, 0);
REGS_BOOL_FIELD(BP, 2);
REGS_BOOL_FIELD(BP1, 3);
REGS_BOOL_FIELD(BP2, 4);
REGS_BOOL_FIELD(TB, 5);
};
class StatusRegister2 : public Register8 {
public:
using Register8::Register8;
REGS_BOOL_FIELD(SRP1, 0);
REGS_BOOL_FIELD(QE, 1);
};
};
@@ -428,6 +433,46 @@ void unlockFlash() {
wait();
}
void LockSlotA() {
unset_memory_mapped_mode();
unlockFlash();
send_command(Command::WriteEnable);
wait();
ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0);
ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0);
ExternalFlashStatusRegister::StatusRegister2 currentStatusRegister2(0);
send_read_command(Command::ReadStatusRegister2, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(&currentStatusRegister2), sizeof(currentStatusRegister2));
statusRegister2.setQE(currentStatusRegister2.getQE());
statusRegister2.setSRP1(true);
statusRegister1.setTB(true);
statusRegister1.setBP2(true);
statusRegister1.setBP1(true);
uint8_t registers[] = {statusRegister1.get(), statusRegister2.get()};
send_write_command(Command::WriteStatusRegister, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(registers), sizeof(registers), sOperatingModes101);
wait();
set_as_memory_mapped();
}
void LockSlotB() {
unset_memory_mapped_mode();
unlockFlash();
send_command(Command::WriteEnable);
wait();
ExternalFlashStatusRegister::StatusRegister1 statusRegister1(0);
ExternalFlashStatusRegister::StatusRegister2 statusRegister2(0);
ExternalFlashStatusRegister::StatusRegister2 currentStatusRegister2(0);
send_read_command(Command::ReadStatusRegister2, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(&currentStatusRegister2), sizeof(currentStatusRegister2));
statusRegister2.setQE(currentStatusRegister2.getQE());
statusRegister2.setSRP1(true);
statusRegister1.setTB(false);
statusRegister1.setBP2(true);
statusRegister1.setBP1(true);
uint8_t registers[] = {statusRegister1.get(), statusRegister2.get()};
send_write_command(Command::WriteStatusRegister, reinterpret_cast<uint8_t *>(FlashAddressSpaceSize), reinterpret_cast<uint8_t *>(registers), sizeof(registers), sOperatingModes101);
wait();
set_as_memory_mapped();
}
void MassErase() {
if (Config::NumberOfSectors == 0) {
return;

View File

@@ -63,14 +63,10 @@ SECTIONS {
. = ALIGN(4);
*(.text.start)
*(.text.abort)
*(.text.uf_abort)
*(.text.bf_abort)
*(.text.nmi_abort)
*(.text.abort_init)
*(.text.abort_core)
*(.text.abort_sleeping)
*(.text.abort_economy)
*(.text.abort_screen)
*(.text.hard_fault_handler)
*(.text.mem_fault_handler)
*(.text.usage_fault_handler)
*(.text.bus_fault_handler)
*(.text.isr_systick)
*(.text.__assert)
*(.text.memcpy)
@@ -97,123 +93,6 @@ SECTIONS {
/* 'abort' dependencies */
*(.text._ZN3Ion6Device5Reset4coreEv)
*(.text._ZN3Ion3LED8setColorE7KDColor)
*(.text._ZN3Ion3LED11setBlinkingEtf)
*(.text._ZN3Ion6Device3LED*)
*(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4CCMREv)
*(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4BaseEv)
*(.text._ZNK3Ion6Device4Regs3*)
*(.text.___ZN3Ion6Device4Regs8*)
*(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4CCMREv)
*(.text._ZNK3Ion6Device4Regs3TIMINS1_8RegisterItEEE4CCMREv*)
*(.text._ZN3Ion6Device5Board15initPeripheralsEb)
*(.text._ZN3Ion6Device9Backlight4initEv)
*(.text._ZN3Ion6Device6Timing4initEv)
*(.text._ZN3Ion6Timing6msleepEj)
*(.text._ZN3Ion6Device8Keyboard4initEv)
*(.text._ZN3Ion6Device7Battery8initGPIOEv)
*(.text._ZN3Ion6Device3USB8initGPIOEv)
*(.text._ZN3Ion6Device9Backlight8setLevelEh)
*(.text._ZN3Ion6Device6Timing19setSysTickFrequencyEi)
*(.text._ZN3Ion6Device5Board17setClockFrequencyENS1_9FrequencyE)
*(.text._ZN3Ion6Device9Backlight10sendPulsesEi)
*(.text._ZN3Ion6Device5Board19shutdownPeripheralsEb)
*(.text._ZN3Ion6Device6Timing8shutdownEv)
*(.text._ZN3Ion6Device8Keyboard8shutdownEv)
*(.text._ZN9KDContext10drawStringEPKc7KDPointPK6KDFont7KDColorS6_i)
*(.text._ZNK8TextArea11ContentView12drawStringAtEP9KDContextiiPKci7KDColorS5_S4_S4_S5_*)
*(.text._ZL11KDPointZero*)
*(.text._ZGVZN12KDIonContext13sharedContextEvE7context)
*(.text._ZZN12KDIonContext13sharedContextEvE7context)
*(.text._ZN12KDIonContext13sharedContextEv)
*(.text._ZN20KDPostProcessContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN26KDPostProcessInvertContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN12KDIonContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN24KDPostProcessZoomContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN25KDPostProcessGammaContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN16KDRealIonContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN3Ion7Display15pushRectUniformE6KDRect7KDColor)
*(.text._ZNK6KDRect15intersectedWithERKS_)
*(.text._ZN7KDColor6RGB888Ehhh)
*(.text._ZN3Ion6Device7Display14setDrawingAreaE6KDRectNS1_11OrientationE)
*(.text.powf)
*(.text._ZN9KDContext16pushOrPullStringEPKc7KDPointPK6KDFont7KDColorS6_ibPi)
*(.text._ZNK7KDPoint12translatedByES_)
*(.text._ZNK6KDRect12translatedByE7KDPoint)
*(.text._Z7toGammai)
*(.text._ZN3Ion7Display8pullRectE6KDRectP7KDColor)
*(.text._ZN24KDPostProcessZoomContext8pullRectE6KDRectP7KDColor)
*(.text._ZN16KDRealIonContext8pullRectE6KDRectP7KDColor)
*(.text._ZN25KDPostProcessGammaContext8pullRectE6KDRectP7KDColor)
*(.text._ZN12KDIonContext8pullRectE6KDRectP7KDColor)
*(.text._ZN26KDPostProcessInvertContext8pullRectE6KDRectP7KDColor)
*(.text._ZN20KDPostProcessContext8pullRectE6KDRectP7KDColor)
*(.text.sqrtf)
*(.text._ZN11UTF8Decoder13nextCodePointEv)
*(.text._ZN7KDColor5blendES_S_h)
*(.text._ZN9KDContext17blendRectWithMaskE6KDRect7KDColorPKhPS1_)
*(.text.scalbnf)
*(.text._ZNK6KDRect10intersectsERKS_)
*(.text._ZN9KDContext18fillRectWithPixelsE6KDRectPK7KDColorPS1_)
*(.text._ZN9KDContext15setClippingRectE6KDRect)
*(.text._ZN9KDContext9setOriginE7KDPoint)
*(.text._ZN20KDPostProcessContext9setOriginE7KDPoint)
*(.text._ZN20KDPostProcessContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN26KDPostProcessInvertContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN20KDPostProcessContext8pushRectE6KDRectPK7KDColor)
*(.text._ZN12KDIonContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN12KDIonContext8pushRectE6KDRectPK7KDColor)
*(.text._ZN26KDPostProcessInvertContext8pushRectE6KDRectPK7KDColor)
*(.text._ZN24KDPostProcessZoomContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN24KDPostProcessZoomContext8pushRectE6KDRectPK7KDColor)
*(.text._ZN25KDPostProcessGammaContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN25KDPostProcessGammaContext8pushRectE6KDRectPK7KDColor)
*(.text._ZN16KDRealIonContext15pushRectUniformE6KDRect7KDColor)
*(.text._ZN16KDRealIonContext8pushRectE6KDRectPK7KDColor)
*(.text._ZN3Ion7Display8pushRectE6KDRectPK7KDColor)
*(.text._ZN3Ion7Display15pushRectUniformE6KDRect7KDColor)
*(.text._ZNK6KDRect7isEmptyEv)
*(.text._ZN20KDPostProcessContext15setClippingRectE6KDRect)
*(.text._ZNK6KDFont17indexForCodePointE9CodePoint)
*(.text._ZNK6KDFont26fetchGrayscaleGlyphAtIndexEhPh)
*(.text.LZ4_decompress_safe*)
*(.text.LZ4_wildCopy*)
*(.text.*DFU*)
*(.text.*isEnumerated*)
*(.text._ZN3Ion3USB6enableEv)
*(.text._ZN3Ion7Battery5levelEv)
*(.text._ZN3Ion7Battery7voltageEv)
*(.text._ZN3Ion3USB9isPluggedEv)
*(.text.*sleepConfiguration*)
*(.text.*onOnOffKeyDown*)
*(.text.*WakeUp*)
*(.text._ZN3Ion6Device9Backlight*)
*(.text._ZN3Ion9Backlight*)
*(.text._ZNK10Statistics5Store6medianEi)
*(.text._ZNK10Regression5Store12meanOfColumnEiib)
*(.text._ZNK6Shared15DoublePairStore11sumOfColumnEiib)
*(.text._ZNK10Statistics5Store13firstQuartileEi)
*(.text._ZNK10Regression5Store23squaredValueSumOfColumnEiib)
*(.text._ZNK10Regression5Store16varianceOfColumnEiib)
*(.text._ZNK10Statistics5Store8maxValueEi)
*(.text._ZNK10Regression5Store25standardDeviationOfColumnEiib)
*(.text._ZNK10Statistics5Store13thirdQuartileEi)
*(.text._ZNK10Statistics5Store8minValueEi)
*(.text._ZNK6Shared8Interval18IntervalParameters*)
*(.text._ZN6Shared8Interval18IntervalParameters*)
*(.text.sqrt)
*(.text.log)
*(.text._ZN17GlobalPreferences23sharedGlobalPreferencesEv)
*(.text._ZNK10Statistics5Store16sumOfOccurrencesEi)
*(.text.floor)
*(.text.ceil)
*(.text._ZNK10Statistics5Store21frequenciesAreIntegerEi)
*(.text._ZNK10Statistics5Store34sortedElementAtCumulatedPopulationEidb)
*(.text._ZNK10Statistics5Store33sortedElementAtCumulatedFrequencyEidb)
*(.text.round)
*(.text._ZNK10Statistics5Store8minIndexEPdi*)
*(.text.LZ4_decompress_safe*)
/* 'standby' dependencies '*/
*(.text._ZN3Ion6Device5Power20internalFlashStandbyEv)
@@ -236,60 +115,9 @@ SECTIONS {
. = ALIGN(4);
*(.rodata._ZN3Ion6Device13ExternalFlash*)
/* 'start' dependencies */
*(.rodata._ZN3Ion6Device8Keyboard6ConfigL10ColumnGPIOE*)
*(.rodata._ZN3Ion6Device8Keyboard6ConfigL7RowGPIOE*)
*(.rodata._ZN3Ion6Device4RegsL5GPIOAE)
*(.rodata._ZN3Ion6Device4RegsL5GPIOBE)
*(.rodata._ZN3Ion6Device3LED6ConfigL7RGBPinsE)
*(.rodata._ZN3Ion6Device5Board4initEv.str1.4)
*(.rodata._ZL12KDColorWhite*)
*(.rodata._ZL10KDColorRed*)
*(.rodata._ZL12KDColorBlack*)
*(.rodata._ZN4CodeL15BackgroundColorE*)
*(.rodata._ZN3Ion6Device3SWD6ConfigL4PinsE)
*(.rodata._ZN3Ion6Device7Display6ConfigL8FSMCPinsE)
*(.rodata._ZZN3Ion6Device7Display9initPanelEvE11calibration)
*(.rodata._ZN3Ion6Device3USB6ConfigL5DmPinE)
*(.rodata._ZN3Ion6Device3USB6ConfigL5DpPinE)
*(.rodata._ZN3Ion6Device3USB6ConfigL7VbusPinE)
*(.rodata._ZN3Ion6Device8Keyboard6ConfigL10ColumnPinsE)
*(.rodata._ZN3Ion6Device8Keyboard6ConfigL7RowPinsE)
*(.rodata._ZZN3Ion6Device3USB12shutdownGPIOEvE4Pins)
*(.rodata._ZN6KDFont16privateLargeFontE)
*(.rodata.abort.str1.1)
*(.rodata.uf_abort.str1.1)
*(.rodata.bf_abort.str1.1)
*(.rodata.nmi_abort.str1.1)
*(.rodata.abort_screen.str1.1)
*(.rodata._ZL5table*)
*(.rodata._ZL15glyphDataOffset*)
*(.rodata._ZL11KDPointZero*)
*(.rodata._ZL9glyphData*)
*(.rodata._ZN4CodeL14HighlightColorE*)
*(.rodata._ZTV12KDIonContext)
*(.rodata._ZTV16KDRealIonContext)
*(.rodata._ZTV24KDPostProcessZoomContext)
*(.rodata._ZTV25KDPostProcessGammaContext)
*(.rodata._ZTV26KDPostProcessInvertContext)
*(.rodata.bp)
*(.rodata.dp_h)
*(.rodata.dp_l)
*(.rodata._ZN11MicroPython5Color5ParseEPvNS0_4ModeE*)
*(.rodata._ZN9Clipboard10storedTextEv*)
*(.rodata._ZN8Sequence14ListController27toolboxForInputEventHandlerEP17InputEventHandler*)
*(.rodata._ZN8Sequence23TypeParameterController25willDisplayCellAtLocationEP13HighlightCellii*)
*(.rodata._ZN6KDFont16privateSmallFontE)
*(.rodata._ZN4I18nL23CountryPreferencesArrayE)
*(.rodata._ZN3Ion6Device3LED6ConfigL7RGBPinsE*)
*(.rodata._ZN4I18nL23CountryPreferencesArrayE*)
*(.rodata._ZN3Ion6Device3USB6ConfigL7VbusPinE*)
*(.rodata.bp*)
*(.rodata.dp_l*)
*(.rodata.dp_h*)
*(.rodata.abort_sleeping.str1.1)
*(.rodata.abort_core.str1.1)
*(.rodata.dfu_bootloader)
*(.rodata)
} >INTERNAL_FLASH
.exam_mode_buffer ORIGIN(EXTERNAL_FLASH) : {
@@ -323,7 +151,7 @@ SECTIONS {
/* The data section is written to Flash but linked as if it were in RAM.
*
* This is required because its initial value matters (so it has to be in
* persistent memory in the first place), but it is a R/W area of memory
* persistant memory in the first place), but it is a R/W area of memory
* so it will have to live in RAM upon execution (in linker lingo, that
* translates to the data section having a LMA in Flash and a VMA in RAM).
*

Some files were not shown because too many files have changed in this diff Show More