diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index 2e8143c3d..2ed9c7e79 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -147,7 +147,7 @@ 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); diff --git a/apps/settings/main_controller_prompt_beta.cpp b/apps/settings/main_controller_prompt_beta.cpp index 90df4b897..33337d606 100644 --- a/apps/settings/main_controller_prompt_beta.cpp +++ b/apps/settings/main_controller_prompt_beta.cpp @@ -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); diff --git a/apps/settings/main_controller_prompt_none.cpp b/apps/settings/main_controller_prompt_none.cpp index 9dc5d45a6..9f15d0c07 100644 --- a/apps/settings/main_controller_prompt_none.cpp +++ b/apps/settings/main_controller_prompt_none.cpp @@ -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)}; diff --git a/apps/settings/main_controller_prompt_update.cpp b/apps/settings/main_controller_prompt_update.cpp index 48c5b5012..161fc7d5d 100644 --- a/apps/settings/main_controller_prompt_update.cpp +++ b/apps/settings/main_controller_prompt_update.cpp @@ -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); diff --git a/bootloader/Makefile b/bootloader/Makefile index 83f835a36..89308cccd 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -1,21 +1,49 @@ bootloader_src += $(addprefix bootloader/,\ + utility.cpp \ boot.cpp \ main.cpp \ - kernel_header.cpp \ - userland_header.cpp \ - slot.cpp \ - interface.cpp \ jump_to_firmware.s \ trampoline.cpp \ - usb_desc.cpp \ + recovery.cpp \ + usb_data.cpp \ +) + +bootloader_src += $(addprefix bootloader/slots/, \ + slot_exam_mode.cpp \ + slot.cpp \ + userland_header.cpp \ + kernel_header.cpp \ +) + +bootloader_src += $(addprefix bootloader/drivers/, \ + stm32_drivers.cpp \ +) + +bootloader_src += $(addprefix bootloader/interface/static/, \ + interface.cpp \ +) + +bootloader_src += $(addprefix bootloader/interface/src/,\ + menu.cpp \ +) + + +bootloader_src += $(addprefix bootloader/interface/menus/, \ + about.cpp \ + home.cpp \ + dfu.cpp \ + installer.cpp \ + warning.cpp \ + slot_recovery.cpp \ + crash.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))) +$(eval $(call depends_on_image,bootloader/interface/static/interface.cpp,$(bootloader_images))) +$(eval $(call depends_on_image,bootloader/interface/src/menu.cpp,$(bootloader_images))) diff --git a/bootloader/boot.cpp b/bootloader/boot.cpp index fa8d8282e..ae2bbeea5 100644 --- a/bootloader/boot.cpp +++ b/bootloader/boot.cpp @@ -1,66 +1,151 @@ #include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include #include -#include +#include +#include #include +using namespace Utility; + +extern "C" { + extern char _fake_isr_function_start; +} + namespace Bootloader { +BootConfig * Boot::config() { + static BootConfig * bootcfg = new BootConfig(); + return bootcfg; +} + 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::busError() { + Ion::Device::Flash::ClearInternalFlashErrors(); + asm("mov r12, lr"); + if (config()->isBooting()) { + asm("mov lr, r12"); + asm("bx lr"); + } + Bootloader::Recovery::crash_handler("BusFault"); +} + +bool Boot::isKernelPatched(const Slot & s) { + if (s.userlandHeader()->isOmega()) { + // we don't need to patch the kernel + return true; + } + + // It's an epsilon kernel, so we need to check if it's patched + + uint32_t origin_isr = s.address() + sizeof(Bootloader::KernelHeader) - sizeof(uint32_t) * 3; + + if (*(uint32_t *)(origin_isr + sizeof(uint32_t) * 12) == (uint32_t)0x0) { + // fake epsilon + return true; + } + + // return *(uint32_t *)(origin_isr + sizeof(uint32_t) * 4) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 5) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 6) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 7) == ((uint32_t)&_fake_isr_function_start) + 1;*(uint32_t *)(origin_isr + sizeof(uint32_t) * 4) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 5) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 6) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 7) == ((uint32_t)&_fake_isr_function_start) + 1;*(uint32_t *)(origin_isr + sizeof(uint32_t) * 4) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 5) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 6) == ((uint32_t)&_fake_isr_function_start) + 1 && *(uint32_t *)(origin_isr + sizeof(uint32_t) * 7) == ((uint32_t)&_fake_isr_function_start) + 1; + return *(uint32_t *)(origin_isr + sizeof(uint32_t) * 7) == ((uint32_t)&_fake_isr_function_start) + 1; +} + +__attribute((section(".fake_isr_function"))) __attribute__((used)) void Boot::flash_interrupt() { + // a simple function + Ion::Device::Flash::ClearInternalFlashErrors(); + asm("bx lr"); +} + +void Boot::patchKernel(const Slot & s) { + uint32_t origin_isr = s.address() + sizeof(Bootloader::KernelHeader) - sizeof(uint32_t) * 3 - 0x90000000; + // we allocate a big buffer to store the first sector + uint8_t data[1024*4]; + memcpy(data, (void*)0x90000000, 1024*4); + uint32_t dummy_address = (uint32_t)&_fake_isr_function_start + 1; + uint8_t * ptr = (uint8_t *)&dummy_address; + data[origin_isr + sizeof(uint32_t) * 6] = ptr[0]; // BusFault + data[origin_isr + sizeof(uint32_t) * 6 + 1] = ptr[1]; + data[origin_isr + sizeof(uint32_t) * 6 + 2] = ptr[2]; + data[origin_isr + sizeof(uint32_t) * 6 + 3] = ptr[3]; + + // data[origin_isr + sizeof(uint32_t) * 5] = ptr[0]; // MemManage + // data[origin_isr + sizeof(uint32_t) * 5 + 1] = ptr[1]; + // data[origin_isr + sizeof(uint32_t) * 5 + 2] = ptr[2]; + // data[origin_isr + sizeof(uint32_t) * 5 + 3] = ptr[3]; + + data[origin_isr + sizeof(uint32_t) * 7] = ptr[0]; // UsageFault + data[origin_isr + sizeof(uint32_t) * 7 + 1] = ptr[1]; + data[origin_isr + sizeof(uint32_t) * 7 + 2] = ptr[2]; + data[origin_isr + sizeof(uint32_t) * 7 + 3] = ptr[3]; + + // data[origin_isr + sizeof(uint32_t) * 4] = ptr[0];//hardfault + // data[origin_isr + sizeof(uint32_t) * 4 + 1] = ptr[1]; + // data[origin_isr + sizeof(uint32_t) * 4 + 2] = ptr[2]; + // data[origin_isr + sizeof(uint32_t) * 4 + 3] = ptr[3]; + + Ion::Device::ExternalFlash::EraseSector(0); + Ion::Device::ExternalFlash::WriteMemory((uint8_t*)0x90000000, data, 1024*4); +} + +void Boot::bootSlot(Bootloader::Slot s) { + config()->setSlot(&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 versionSum = Utility::versionSum(version, strlen(version)); + int minimalVersionTrigger = Utility::versionSum(min, strlen(min)); + if (versionSum >= minimalVersionTrigger) { + WarningMenu menu = WarningMenu(); + menu.open(); + return; + } + } + bootSelectedSlot(); +} + +void Boot::bootSelectedSlot() { + lockInternal(); + config()->setBooting(true); + Ion::Device::Flash::EnableInternalSessionLock(); + config()->slot()->boot(); } __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(); - } + Boot::config()->clearSlot(); + Boot::config()->setBooting(false); + + while (true) { + HomeMenu menu = HomeMenu(); + menu.open(true); } - // Achivement unlocked: How Did We Get Here? + // Achievement unlocked: How Did We Get Here? bootloader(); } -__attribute__ ((noreturn)) void Boot::bootloader() { +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 +155,34 @@ __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(); + 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::jumpToInternalBootloader() { + Ion::Device::Board::jumpToInternalBootloader(); +} + +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(); +} + +void Boot::enableFlashIntr() { + Ion::Device::Flash::EnableInternalFlashInterrupt(); +} + } diff --git a/bootloader/boot.h b/bootloader/boot.h index bb5df17c1..934d60d66 100644 --- a/bootloader/boot.h +++ b/bootloader/boot.h @@ -2,9 +2,25 @@ #define BOOTLOADER_BOOT_H #include +#include namespace Bootloader { +class BootConfig { + public: + BootConfig() : m_slot(nullptr), m_booting(false) {}; + + void setSlot(Slot * slot) { m_slot = slot; } + Slot * slot() const { return m_slot; } + void clearSlot() { m_slot = nullptr; } + + void setBooting(bool booting) { m_booting = booting; } + bool isBooting() const { return m_booting; } + private: + Bootloader::Slot * m_slot; + bool m_booting; +}; + enum BootMode: uint8_t { SlotA = 0, SlotB = 1, @@ -19,10 +35,26 @@ class Boot { public: static BootMode mode(); static void setMode(BootMode mode); + static BootConfig * config(); + + static bool isKernelPatched(const Slot & slot); + static void patchKernel(const Slot & slot); + + static void busError(); + __attribute__ ((noreturn)) static void boot(); - __attribute__ ((noreturn)) static void bootloader(); + + static void bootSlot(Bootloader::Slot slot); + static void bootSelectedSlot(); + __attribute__ ((noreturn)) static void jumpToInternalBootloader(); + __attribute((section(".fake_isr_function"))) __attribute__((used)) static void flash_interrupt(); + + static void bootloader(); + static void lockInternal(); + static void enableFlashIntr(); }; + } -#endif \ No newline at end of file +#endif diff --git a/bootloader/boot/isr.c b/bootloader/boot/isr.c new file mode 100644 index 000000000..77b112193 --- /dev/null +++ b/bootloader/boot/isr.c @@ -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 +}; diff --git a/bootloader/boot/isr.h b/bootloader/boot/isr.h new file mode 100644 index 000000000..aa980065e --- /dev/null +++ b/bootloader/boot/isr.h @@ -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 diff --git a/bootloader/boot/rt0.cpp b/bootloader/boot/rt0.cpp new file mode 100644 index 000000000..06221b252 --- /dev/null +++ b/bootloader/boot/rt0.cpp @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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::Boot::busError(); +} + +/* 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(); +} diff --git a/bootloader/drivers/board.cpp b/bootloader/drivers/board.cpp new file mode 100644 index 000000000..0a80a89ec --- /dev/null +++ b/bootloader/drivers/board.cpp @@ -0,0 +1,462 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace STM32; +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(InternalFlash::Config::OTPAddress(k_pcbVersionOTPIndex))); +} + +void writePCBVersion(PCBVersion version) { + uint8_t * destination = reinterpret_cast(InternalFlash::Config::OTPAddress(k_pcbVersionOTPIndex)); + PCBVersion formattedVersion = ~version; + InternalFlash::WriteMemory(destination, reinterpret_cast(&formattedVersion), sizeof(formattedVersion)); +} + +void lockPCBVersion() { + uint8_t * destination = reinterpret_cast(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex)); + uint8_t zero = 0; + InternalFlash::WriteMemory(destination, &zero, sizeof(zero)); +} + +bool pcbVersionIsLocked() { + return *reinterpret_cast(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex)) == 0; +} + +void jumpToInternalBootloader() { + asm volatile ("cpsie i" : : : "memory"); + + STM32::rcc_deinit(); + STM32::hal_deinit(); + STM32::systick_deinit(); + + const uint32_t p = (*((uint32_t *) 0x1FF00000)); + asm volatile ("MSR msp, %0" : : "r" (p) : ); + void (*SysMemBootJump)(void); + SysMemBootJump = (void (*)(void)) (*((uint32_t *) 0x1FF00004)); + SysMemBootJump(); +} + +} +} +} diff --git a/bootloader/drivers/stm32_drivers.cpp b/bootloader/drivers/stm32_drivers.cpp new file mode 100644 index 000000000..ac5d09752 --- /dev/null +++ b/bootloader/drivers/stm32_drivers.cpp @@ -0,0 +1,71 @@ +#include "stm32_drivers.h" + +/** + * THIS CODE COMES FROM THE STM32_HAL LIBRARY (LICENSE ABOVE) AND HAVE BEEN MODIFIED + * WE USE ONLY THE HAL_deinit, RCC_deinit and systick_deninit FUNCTIONS AND ONLY COPIED THE CODE NEEDED. + * WE NEEDED THIS CODE TO BE ABLE TO BOOT THE STM32 BOOTLOADER + */ + +/* +This software component is provided to you as part of a software package and +applicable license terms are in the Package_license file. If you received this +software component outside of a package or without applicable license terms, +the terms of the BSD-3-Clause license shall apply. +You may obtain a copy of the BSD-3-Clause at: +https://opensource.org/licenses/BSD-3-Clause +*/ + +void STM32::rcc_deinit() { + SET_BIT(STM_32_RCC->CR, (0x1UL << (0U))); + while (READ_BIT(STM_32_RCC->CR, (0x1UL << (1U))) == 0) {} + SET_BIT(STM_32_RCC->CR, (0x10UL << (3U))); + CLEAR_REG(STM_32_RCC->CFGR); + while (READ_BIT(STM_32_RCC->CFGR, (0x3UL << (2U))) != 0) {} + CLEAR_BIT(STM_32_RCC->CR, (0x1UL << (16U)) | (0x1UL << (18U)) | (0x1UL << (19U))); + while (READ_BIT(STM_32_RCC->CR, (0x1UL << (17U))) != 0) {} + CLEAR_BIT(STM_32_RCC->CR, (0x1UL << (24U))); + while (READ_BIT(STM_32_RCC->CR, (0x1UL << (25U))) != 0) {} + CLEAR_BIT(STM_32_RCC->CR, (0x1UL << (26U))); + while (READ_BIT(STM_32_RCC->CR, (0x1UL << (27U))) != 0) {} + CLEAR_BIT(STM_32_RCC->CR, (0x1UL << (28U))); + while (READ_BIT(STM_32_RCC->CR, (0x1UL << (29U))) != 0) {} + STM_32_RCC->PLLCFGR = ((0x10UL << (0x0U)) | (0x040UL << (6U)) | (0x080UL << (6U)) | (0x4UL << (24U)) | 0x20000000U); + STM_32_RCC->PLLI2SCFGR = ((0x040UL << (6U)) | (0x080UL << (6U)) | (0x4UL << (24U)) | (0x2UL << (28U))); + STM_32_RCC->PLLSAICFGR = ((0x040UL << (6U)) | (0x080UL << (6U)) | (0x4UL << (24U)) | 0x20000000U); + CLEAR_BIT(STM_32_RCC->CIR, ((0x1UL << (8U)) | (0x1UL << (9U)) | (0x1UL << (10U)) | (0x1UL << (11U)) | (0x1UL << (12U)) | (0x1UL << (13U)) | (0x1UL << (14U)))); + SET_BIT(STM_32_RCC->CIR, ((0x1UL << (16U)) | (0x1UL << (17U)) | (0x1UL << (18U)) | (0x1UL << (19U)) | (0x1UL << (20U)) | (0x1UL << (21U)) | (0x1UL << (22U)) | (0x1UL << (23U)))); + CLEAR_BIT(STM_32_RCC->CSR, ((0x1UL << (0U)))); + SET_BIT(STM_32_RCC->CSR, ((0x1UL << (24U)))); + uint32_t sysclock = ((uint32_t)16000000U); + uint32_t a = ((sysclock / 1000U)); + uint32_t b = 15U; + STM_32_SysTick->LOAD = (uint32_t)(a - 1UL); + STM_32_SCB->SHPR[(((uint32_t)(-1))&0xFUL)-4UL] = (uint8_t)((((1UL << 4U)-1UL) << (8U - 4UL)) & (uint32_t)0xFFUL); + STM_32_SysTick->VAL = 0U; + STM_32_SysTick->CTRL = (1UL << 2U) | (1UL << 1U) | (1UL); + uint32_t c = ((uint32_t)((STM_32_SCB->AIRCR & (7UL << 8U)) >> 8U)); + uint32_t d = (c & (uint32_t)0x07UL); + uint32_t e; + uint32_t f; + e = ((7UL - d) > (uint32_t)(4UL)) ? (uint32_t)(4UL) : (7UL - d); + f = ((d + (uint32_t)(4UL)) < (uint32_t)(7UL)) ? (uint32_t)(0UL) : (uint32_t)((d - 7UL) + (uint32_t)(4UL)); + uint32_t g = (((b & (uint32_t)((1UL << (e)) - 1UL)) << f) | ((0UL & (uint32_t)((1UL << (f)) - 1UL)))); + STM_32_SCB->SHPR[(((uint32_t)(-1))&0xFUL)-4UL] = (uint8_t)((g << (8U - 4UL)) & (uint32_t)0xFFUL); +} + +void STM32::hal_deinit() { + STM_32_RCC->APB1RSTR = 0xFFFFFFFFU; + STM_32_RCC->APB1RSTR = 0x00U; + STM_32_RCC->APB2RSTR = 0xFFFFFFFFU; + STM_32_RCC->APB2RSTR = 0x00U; + STM_32_RCC->AHB1RSTR = 0xFFFFFFFFU; + STM_32_RCC->AHB1RSTR = 0x00U; + STM_32_RCC->AHB2RSTR = 0xFFFFFFFFU; + STM_32_RCC->AHB2RSTR = 0x00U; + STM_32_RCC->AHB3RSTR = 0xFFFFFFFFU; + STM_32_RCC->AHB3RSTR = 0x00U; +} + +void STM32::systick_deinit() { + STM_32_SysTick->CTRL = STM_32_SysTick->LOAD = STM_32_SysTick->VAL = 0; +} diff --git a/bootloader/drivers/stm32_drivers.h b/bootloader/drivers/stm32_drivers.h new file mode 100644 index 000000000..9efa636dd --- /dev/null +++ b/bootloader/drivers/stm32_drivers.h @@ -0,0 +1,159 @@ +#include + +/* + Here we implement a very little part of the code from the default stm32 libs because we only need the unload function. + Now we include the license of the original code as required. +*/ + +/** + * THIS CODE COMES FROM THE STM32_HAL LIBRARY (LICENSE ABOVE) AND HAVE BEEN MODIFIED + * WE USE ONLY THE HAL_deinit, RCC_deinit and systick_deninit FUNCTIONS AND ONLY COPIED THE CODE NEEDED. + * WE NEEDED THIS CODE TO BE ABLE TO BOOT THE STM32 BOOTLOADER + */ + +/* +This software component is provided to you as part of a software package and +applicable license terms are in the Package_license file. If you received this +software component outside of a package or without applicable license terms, +the terms of the BSD-3-Clause license shall apply. +You may obtain a copy of the BSD-3-Clause at: +https://opensource.org/licenses/BSD-3-Clause +*/ + +namespace STM32 { + + typedef struct + { + volatile uint32_t CR; + volatile uint32_t PLLCFGR; + volatile uint32_t CFGR; + volatile uint32_t CIR; + volatile uint32_t AHB1RSTR; + volatile uint32_t AHB2RSTR; + volatile uint32_t AHB3RSTR; + uint32_t RESERVED0; + volatile uint32_t APB1RSTR; + volatile uint32_t APB2RSTR; + uint32_t RESERVED1[2]; + volatile uint32_t AHB1ENR; + volatile uint32_t AHB2ENR; + volatile uint32_t AHB3ENR; + uint32_t RESERVED2; + volatile uint32_t APB1ENR; + volatile uint32_t APB2ENR; + uint32_t RESERVED3[2]; + volatile uint32_t AHB1LPENR; + volatile uint32_t AHB2LPENR; + volatile uint32_t AHB3LPENR; + uint32_t RESERVED4; + volatile uint32_t APB1LPENR; + volatile uint32_t APB2LPENR; + uint32_t RESERVED5[2]; + volatile uint32_t BDCR; + volatile uint32_t CSR; + uint32_t RESERVED6[2]; + volatile uint32_t SSCGR; + volatile uint32_t PLLI2SCFGR; + volatile uint32_t PLLSAICFGR; + volatile uint32_t DCKCFGR1; + volatile uint32_t DCKCFGR2; + } STM32_RCC_TypeDef; + + typedef struct + { + volatile uint32_t CTRL; + volatile uint32_t LOAD; + volatile uint32_t VAL; + volatile const uint32_t CALIB; + } STM32_SysTick_Type; + + typedef struct + { + volatile uint32_t ISER[8U]; + uint32_t RESERVED0[24U]; + volatile uint32_t ICER[8U]; + uint32_t RSERVED1[24U]; + volatile uint32_t ISPR[8U]; + uint32_t RESERVED2[24U]; + volatile uint32_t ICPR[8U]; + uint32_t RESERVED3[24U]; + volatile uint32_t IABR[8U]; + uint32_t RESERVED4[56U]; + volatile uint8_t IP[240U]; + uint32_t RESERVED5[644U]; + volatile uint32_t STIR; + } STM32_NVIC_Type; + + typedef struct { + volatile const uint32_t CPUID; + volatile uint32_t ICSR; + volatile uint32_t VTOR; + volatile uint32_t AIRCR; + volatile uint32_t SCR; + volatile uint32_t CCR; + volatile uint8_t SHPR[12U]; + volatile uint32_t SHCSR; + volatile uint32_t CFSR; + volatile uint32_t HFSR; + volatile uint32_t DFSR; + volatile uint32_t MMFAR; + volatile uint32_t BFAR; + volatile uint32_t AFSR; + volatile const uint32_t ID_PFR[2U]; + volatile const uint32_t ID_DFR; + volatile const uint32_t ID_AFR; + volatile const uint32_t ID_MFR[4U]; + volatile const uint32_t ID_ISAR[5U]; + uint32_t RESERVED0[1U]; + volatile const uint32_t CLIDR; + volatile const uint32_t CTR; + volatile const uint32_t CCSIDR; + volatile uint32_t CSSELR; + volatile uint32_t CPACR; + uint32_t RESERVED3[93U]; + volatile uint32_t STIR; + uint32_t RESERVED4[15U]; + volatile const uint32_t MVFR0; + volatile const uint32_t MVFR1; + volatile const uint32_t MVFR2; + uint32_t RESERVED5[1U]; + volatile uint32_t ICIALLU; + uint32_t RESERVED6[1U]; + volatile uint32_t ICIMVAU; + volatile uint32_t DCIMVAC; + volatile uint32_t DCISW; + volatile uint32_t DCCMVAU; + volatile uint32_t DCCMVAC; + volatile uint32_t DCCSW; + volatile uint32_t DCCIMVAC; + volatile uint32_t DCCISW; + uint32_t RESERVED7[6U]; + volatile uint32_t ITCMCR; + volatile uint32_t DTCMCR; + volatile uint32_t AHBPCR; + volatile uint32_t CACR; + volatile uint32_t AHBSCR; + uint32_t RESERVED8[1U]; + volatile uint32_t ABFSR; + } STM32_SCB_Type; + + #define RCC_BASE 0x40023800UL + #define SysTick_BASE 0xE000E010UL + #define NVIC_BASE 0xE000E100UL + #define SCB_BASE 0xE000ED00UL + + #define SET_BIT(REG, BIT) ((REG) |= (BIT)) + #define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT)) + #define READ_BIT(REG, BIT) ((REG) & (BIT)) + #define CLEAR_REG(REG) ((REG) = (0x0)) + #define READ_REG(REG) ((REG)) + + #define STM_32_RCC ((STM32::STM32_RCC_TypeDef *) RCC_BASE) + #define STM_32_SysTick ((STM32::STM32_SysTick_Type *) SysTick_BASE) + #define STM_32_NVIC ((STM32::STM32_NVIC_Type *) NVIC_BASE) + #define STM_32_SCB ((STM32_SCB_Type *) SCB_BASE) + + extern void rcc_deinit(); + extern void hal_deinit(); + extern void systick_deinit(); +} diff --git a/bootloader/interface.cpp b/bootloader/interface.cpp deleted file mode 100644 index e1cd6e334..000000000 --- a/bootloader/interface.cpp +++ /dev/null @@ -1,82 +0,0 @@ - -#include -#include - -#include -#include -#include - -#include "computer.h" -#include "cable.h" - -namespace Bootloader { - -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(); - pixelBufferSize = image->width() * image->height(); - } else { - return; - } - - KDColor pixelBuffer[4000]; - assert(pixelBufferSize <= 4000); - assert(Ion::stackSafe()); // That's a VERY big buffer we're allocating on the stack - - Ion::decompress( - data, - reinterpret_cast(pixelBuffer), - size, - pixelBufferSize * sizeof(KDColor) - ); - - KDRect bounds((320 - image->width()) / 2, offset, image->width(), image->height()); - - ctx->fillRectWithPixels(bounds, pixelBuffer, nullptr); -} - -void Interface::draw() { - KDContext * ctx = KDIonContext::sharedContext(); - ctx->fillRect(KDRect(0,0,320,240), KDColorBlack); - drawImage(ctx, ImageStore::Computer, 70); - drawImage(ctx, ImageStore::Cable, 172); - - 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); - - 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); - } - - Slot slots[2] = {Slot::A(), Slot::B()}; - - for(uint8_t i = 0; i < 2; i++) { - Slot slot = slots[i]; - - if (slot.kernelHeader()->isValid() && slot.userlandHeader()->isValid()) { - 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); - } 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); - } 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); - } - ctx->drawString(slot.kernelHeader()->patchLevel(), KDPoint(168, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); - } else { - ctx->drawString("Invalid", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack); - } - } - -} - -} diff --git a/bootloader/interface.h b/bootloader/interface.h deleted file mode 100644 index c163ddc4c..000000000 --- a/bootloader/interface.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef BOOTLOADER_INTERFACE -#define BOOTLOADER_INTERFACE - -#include -#include -#include - -namespace Bootloader { - -class Interface { - -private: - static void drawImage(KDContext* ctx, const Image* image, int offset); - -public: - static void draw(); - -}; - -} - -#endif \ No newline at end of file diff --git a/bootloader/interface/menus/about.cpp b/bootloader/interface/menus/about.cpp new file mode 100644 index 000000000..661c6a20f --- /dev/null +++ b/bootloader/interface/menus/about.cpp @@ -0,0 +1,20 @@ +#include "about.h" +#include + +Bootloader::AboutMenu::AboutMenu() : Menu(KDColorBlack, KDColorWhite, Messages::aboutMenuTitle, Messages::bootloaderVersion) { + setup(); +} + +void Bootloader::AboutMenu::setup() { + m_default_columns[0] = Column(Messages::aboutMessage1, k_small_font, 0, true); + m_default_columns[1] = Column(Messages::aboutMessage2, k_small_font, 0, true); + m_default_columns[2] = Column(Messages::aboutMessage3, k_small_font, 0, true); + m_default_columns[3] = Column(Messages::aboutMessage4, k_small_font, 0, true); + m_default_columns[4] = Column(Messages::aboutMessage5, k_small_font, 0, true); + + m_columns[0] = ColumnBinder(&m_default_columns[0]); + m_columns[1] = ColumnBinder(&m_default_columns[1]); + m_columns[2] = ColumnBinder(&m_default_columns[2]); + m_columns[3] = ColumnBinder(&m_default_columns[3]); + m_columns[4] = ColumnBinder(&m_default_columns[4]); +} diff --git a/bootloader/interface/menus/about.h b/bootloader/interface/menus/about.h new file mode 100644 index 000000000..00f482705 --- /dev/null +++ b/bootloader/interface/menus/about.h @@ -0,0 +1,17 @@ +#ifndef _BOOTLOADER_INTERFACE_ABOUT_H_ +#define _BOOTLOADER_INTERFACE_ABOUT_H_ + +#include + +namespace Bootloader { + class AboutMenu : public Menu { + public: + AboutMenu(); + + void setup() override; + void postOpen() override {}; + }; +} + + +#endif diff --git a/bootloader/interface/menus/crash.cpp b/bootloader/interface/menus/crash.cpp new file mode 100644 index 000000000..242299bc2 --- /dev/null +++ b/bootloader/interface/menus/crash.cpp @@ -0,0 +1,22 @@ +#include "crash.h" + +Bootloader::CrashMenu::CrashMenu(const char * err) : Menu(KDColorBlack, KDColorWhite, Bootloader::Messages::bootloaderCrashTitle, Bootloader::Messages::mainTitle), m_error(err) { + setup(); +} + +void Bootloader::CrashMenu::setup() { + m_default_columns[0] = Column(m_error, k_large_font, 0, true); + m_default_columns[1] = Column(Bootloader::Messages::bootloaderCrashMessage1, k_small_font, 0, true); + m_default_columns[2] = Column(Bootloader::Messages::bootloaderCrashMessage2, k_small_font, 0, true); + + m_columns[0] = ColumnBinder(&m_default_columns[0]); + m_columns[1] = ColumnBinder(&m_default_columns[1]); + m_columns[2] = ColumnBinder(&m_default_columns[2]); +} + +void Bootloader::CrashMenu::postOpen() { + // We override the open method + for (;;) { + // Infinite loop + } +} diff --git a/bootloader/interface/menus/crash.h b/bootloader/interface/menus/crash.h new file mode 100644 index 000000000..bcb74ec81 --- /dev/null +++ b/bootloader/interface/menus/crash.h @@ -0,0 +1,19 @@ +#ifndef _BOOTLOADER_INTERFACE_MENUS_CRASH_H_ +#define _BOOTLOADER_INTERFACE_MENUS_CRASH_H_ + +#include + +namespace Bootloader { + class CrashMenu : public Menu { + public: + CrashMenu(const char * error); + + void setup() override; + void postOpen() override; + + private: + const char * m_error; + }; +} + +#endif diff --git a/bootloader/interface/menus/dfu.cpp b/bootloader/interface/menus/dfu.cpp new file mode 100644 index 000000000..88e705e52 --- /dev/null +++ b/bootloader/interface/menus/dfu.cpp @@ -0,0 +1,37 @@ +#include "dfu.h" +#include +#include + +Bootloader::DfuMenu::DfuMenu(const char * text, const USBData * data) : Menu(KDColorBlack, KDColorWhite, Messages::dfuTitle, Messages::mainTitle), m_submenuText(text), m_data(data) { + setup(); +} + +void Bootloader::DfuMenu::setup() { + m_default_columns[0] = Column(m_submenuText, k_small_font, 0, true); + + m_columns[0] = ColumnBinder(&m_default_columns[0]); +} + +void Bootloader::DfuMenu::postOpen() { + // We override the open method + if (!m_data->getData().isProtectedInternal() && m_data->getData().isProtectedExternal()) { + // Because we want to flash the internal, we will jump into the stm32 bootloader + Bootloader::Boot::jumpToInternalBootloader(); + return; // We never reach this point + } + for (;;) { + Ion::USB::enable(); + do { + uint64_t scan = Ion::Keyboard::scan(); + if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back)) { + Ion::USB::disable(); + forceExit(); + return; + } else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) { + Ion::Power::standby(); + return; + } + } while (!Ion::USB::isEnumerated()); + Ion::USB::DFU(true, (void *)m_data); + } +} diff --git a/bootloader/interface/menus/dfu.h b/bootloader/interface/menus/dfu.h new file mode 100644 index 000000000..17a4873b2 --- /dev/null +++ b/bootloader/interface/menus/dfu.h @@ -0,0 +1,21 @@ +#ifndef _BOOTLOADER_INTERFACE_MENUS_DFU_H_ +#define _BOOTLOADER_INTERFACE_MENUS_DFU_H_ + +#include +#include + +namespace Bootloader { + class DfuMenu : public Menu { + public: + DfuMenu(const char * submenu, const USBData * usbData); + + void setup() override; + void postOpen() override; + + private: + const char * m_submenuText; + const USBData * m_data; + }; +} + +#endif diff --git a/bootloader/interface/menus/home.cpp b/bootloader/interface/menus/home.cpp new file mode 100644 index 000000000..61c69f95a --- /dev/null +++ b/bootloader/interface/menus/home.cpp @@ -0,0 +1,109 @@ +#include "home.h" +#include +#include +#include +#include + +Bootloader::AboutMenu * Bootloader::HomeMenu::aboutMenu() { + static AboutMenu * aboutMenu = new AboutMenu(); + return aboutMenu; +} + +Bootloader::InstallerMenu * Bootloader::HomeMenu::installerMenu() { + static InstallerMenu * installerMenu = new InstallerMenu(); + return installerMenu; +} + +Bootloader::HomeMenu::HomeMenu() : Menu(KDColorBlack, KDColorWhite, Messages::homeTitle, Messages::mainTitle) { + setup(); +} + +bool slotA_submenu() { + if (Bootloader::Slot::isFullyValid(Bootloader::Slot::A())) { + Bootloader::Boot::bootSlot(Bootloader::Slot::A()); + return true; + } + return false; +} + +bool slotKhi_submenu() { + if (Bootloader::Slot::isFullyValid(Bootloader::Slot::Khi())) { + Bootloader::Boot::bootSlot(Bootloader::Slot::Khi()); + return true; + } + return false; +} + +bool slotB_submenu() { + if (Bootloader::Slot::isFullyValid(Bootloader::Slot::B())) { + Bootloader::Boot::bootSlot(Bootloader::Slot::B()); + return true; + } + return false; +} + +bool installer_submenu() { + Bootloader::HomeMenu::installerMenu()->open(); + return true; +} + +bool about_submenu() { + Bootloader::HomeMenu::aboutMenu()->open(); + return true; +} + +const char * Bootloader::HomeMenu::getSlotOsText(Slot slot) { + if (Slot::isFullyValid(slot)) { + if (slot.userlandHeader()->isOmega() && slot.userlandHeader()->isUpsilon()) { + return Messages::upsilonSlot; + } else if (slot.userlandHeader()->isOmega() && slot.kernelHeader()->patchLevel()[0] != '\0') { + return Messages::omegaSlot; + } else if (slot.userlandHeader()->isOmega()) { + return Messages::khiSlot; + } else { + return Messages::epsilonSlot; + } + } + return nullptr; +} + +const char * Bootloader::HomeMenu::getSlotText(Slot slot) { + if(Slot::isFullyValid(slot)) { + if (slot.address() == Slot::A().address()) { + return Messages::homeSlotASubmenu; + } else if (slot.address() == Slot::Khi().address()) { + return Messages::homeSlotKhiSubmenu; + } else if (slot.address() == Slot::B().address()) { + return Messages::homeSlotBSubmenu; + } + } + return Messages::invalidSlot; +} + +const char * Bootloader::HomeMenu::getKernelText(Slot slot) { + return Slot::isFullyValid(slot) ? slot.kernelHeader()->patchLevel() : nullptr; +} + +const char * Bootloader::HomeMenu::getVersionText(Slot slot) { + return Slot::isFullyValid(slot) ? slot.userlandHeader()->isOmega() && slot.userlandHeader()->isUpsilon() ? slot.userlandHeader()->upsilonVersion() : slot.userlandHeader()->isOmega() ? slot.userlandHeader()->omegaVersion() : slot.kernelHeader()->version() : nullptr; +} + +void Bootloader::HomeMenu::setup() { + Slot A = Slot::A(); + Slot Khi = Slot::Khi(); + Slot B = Slot::B(); + + m_slot_columns[0] = SlotColumn(getSlotText(A), getKernelText(A), getSlotOsText(A), getVersionText(A), Ion::Keyboard::Key::One, k_small_font, 10, false, &slotA_submenu); + m_slot_columns[1] = SlotColumn(getSlotText(Khi), getKernelText(Khi), getSlotOsText(Khi), getVersionText(Khi), Ion::Keyboard::Key::Two, k_small_font, 10, false, &slotKhi_submenu); + m_slot_columns[2] = SlotColumn(getSlotText(B), getKernelText(B), getSlotOsText(B), getVersionText(B), Ion::Keyboard::Key::Three, k_small_font, 10, false, &slotB_submenu); + m_default_columns[0] = Column(Messages::homeInstallerSubmenu, Ion::Keyboard::Key::Four, k_small_font, 10, false, &installer_submenu); + m_default_columns[1] = Column(Messages::homeAboutSubmenu, Ion::Keyboard::Key::Five, k_small_font, 10, false, &about_submenu); + + + m_columns[0] = ColumnBinder(&m_slot_columns[0]); + m_columns[1] = ColumnBinder(&m_slot_columns[1]); + m_columns[2] = ColumnBinder(&m_slot_columns[2]); + m_columns[3] = ColumnBinder(&m_default_columns[0]); + m_columns[4] = ColumnBinder(&m_default_columns[1]); + +} diff --git a/bootloader/interface/menus/home.h b/bootloader/interface/menus/home.h new file mode 100644 index 000000000..da6f010b1 --- /dev/null +++ b/bootloader/interface/menus/home.h @@ -0,0 +1,29 @@ +#ifndef _BOOTLOADER_INTERFACE_MENUS_HOME_H_ +#define _BOOTLOADER_INTERFACE_MENUS_HOME_H_ + +#include +#include +#include +#include + +namespace Bootloader { + class HomeMenu : public Menu { + public: + HomeMenu(); + + void setup() override; + void postOpen() override {}; + + static AboutMenu * aboutMenu(); + static InstallerMenu * installerMenu(); + + private: + const char * getSlotOsText(Slot slot); + const char * getSlotText(Slot slot); + const char * getKernelText(Slot slot); + const char * getVersionText(Slot slot); + + }; +} + +#endif diff --git a/bootloader/interface/menus/installer.cpp b/bootloader/interface/menus/installer.cpp new file mode 100644 index 000000000..b6acf2c2e --- /dev/null +++ b/bootloader/interface/menus/installer.cpp @@ -0,0 +1,41 @@ +#include "installer.h" +#include +#include + +#include + +Bootloader::DfuMenu * Bootloader::InstallerMenu::SlotsDFU() { + USBData data = USBData::DEFAULT(); + static DfuMenu * slotsDfu = new DfuMenu(Messages::dfuSlotsUpdate, &data); + return slotsDfu; +} + +Bootloader::DfuMenu * Bootloader::InstallerMenu::BootloaderDFU() { + USBData data = USBData::BOOTLOADER_UPDATE(); + static DfuMenu * bootloaderDfu = new DfuMenu(Messages::dfuBootloaderUpdate, &data); + return bootloaderDfu; +} + +Bootloader::InstallerMenu::InstallerMenu() : Menu(KDColorBlack, KDColorWhite, Messages::installerTitle, Messages::mainTitle) { + setup(); +} + +bool slotsSubmenu() { + Bootloader::InstallerMenu::SlotsDFU()->open(); + return true; +} + +bool bootloaderSubmenu() { + Bootloader::InstallerMenu::BootloaderDFU()->open(); + return true; +} + +void Bootloader::InstallerMenu::setup() { + m_default_columns[0] = Column(Messages::installerText1, k_large_font, 0, true); + m_default_columns[1] = Column(Messages::installerSlotsSubmenu, Ion::Keyboard::Key::One, k_small_font, 30, false, &slotsSubmenu); + m_default_columns[2] = Column(Messages::installerBootloaderSubmenu, Ion::Keyboard::Key::Two, k_small_font, 30, false, &bootloaderSubmenu); + + m_columns[0] = ColumnBinder(&m_default_columns[0]); + m_columns[1] = ColumnBinder(&m_default_columns[1]); + m_columns[2] = ColumnBinder(&m_default_columns[2]); +} diff --git a/bootloader/interface/menus/installer.h b/bootloader/interface/menus/installer.h new file mode 100644 index 000000000..dd98e4a98 --- /dev/null +++ b/bootloader/interface/menus/installer.h @@ -0,0 +1,20 @@ +#ifndef _BOOTLOADER_INTERFACE_MENUS_INSTALLER_H_ +#define _BOOTLOADER_INTERFACE_MENUS_INSTALLER_H_ + +#include +#include + +namespace Bootloader { + class InstallerMenu : public Menu { + public: + InstallerMenu(); + + void setup() override; + void postOpen() override {}; + + static DfuMenu * SlotsDFU(); + static DfuMenu * BootloaderDFU(); + }; +} + +#endif diff --git a/bootloader/interface/menus/slot_recovery.cpp b/bootloader/interface/menus/slot_recovery.cpp new file mode 100644 index 000000000..f052d6be5 --- /dev/null +++ b/bootloader/interface/menus/slot_recovery.cpp @@ -0,0 +1,39 @@ +#include "slot_recovery.h" +#include + +Bootloader::SlotRecoveryMenu::SlotRecoveryMenu(USBData * usb) : Menu(KDColorBlack, KDColorWhite, Messages::recoveryTitle, Messages::mainTitle), m_data(usb) { + setup(); +} + +void Bootloader::SlotRecoveryMenu::setup() { + m_default_columns[0] = Column(Messages::recoveryMessage1, k_small_font, 0, true); + m_default_columns[1] = Column(Messages::recoveryMessage2, k_small_font, 0, true); + m_default_columns[2] = Column(Messages::recoveryMessage3, k_small_font, 0, true); + m_default_columns[3] = Column(Messages::recoveryMessage4, k_small_font, 0, true); + m_default_columns[4] = Column(Messages::recoveryMessage5, k_small_font, 0, true); + + m_columns[0] = ColumnBinder(&m_default_columns[0]); + m_columns[1] = ColumnBinder(&m_default_columns[1]); + m_columns[2] = ColumnBinder(&m_default_columns[2]); + m_columns[3] = ColumnBinder(&m_default_columns[3]); + m_columns[4] = ColumnBinder(&m_default_columns[4]); +} + +void Bootloader::SlotRecoveryMenu::postOpen() { + // We override the open method + for (;;) { + Ion::USB::enable(); + do { + uint64_t scan = Ion::Keyboard::scan(); + if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Back)) { + Ion::USB::disable(); + forceExit(); + return; + } else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) { + Ion::Power::standby(); + return; + } + } while (!Ion::USB::isEnumerated()); + Ion::USB::DFU(true, (void *)m_data); + } +} diff --git a/bootloader/interface/menus/slot_recovery.h b/bootloader/interface/menus/slot_recovery.h new file mode 100644 index 000000000..4eba92844 --- /dev/null +++ b/bootloader/interface/menus/slot_recovery.h @@ -0,0 +1,19 @@ +#ifndef _BOOTLOADER_INTERFACE_MENU_SLOT_RECOVERY_H +#define _BOOTLOADER_INTERFACE_MENU_SLOT_RECOVERY_H + +#include +#include + +namespace Bootloader { + class SlotRecoveryMenu : public Menu { + public: + SlotRecoveryMenu(USBData * usbData); + + void setup() override; + void postOpen() override; + private: + const USBData * m_data; + }; +} + +#endif diff --git a/bootloader/interface/menus/warning.cpp b/bootloader/interface/menus/warning.cpp new file mode 100644 index 000000000..fca936c14 --- /dev/null +++ b/bootloader/interface/menus/warning.cpp @@ -0,0 +1,35 @@ +#include "warning.h" +#include +#include + +Bootloader::WarningMenu::WarningMenu() : Menu(KDColorWhite, KDColorRed, Messages::epsilonWarningTitle, Messages::mainTitle, false, 3) { + setup(); +} + +bool proceed() { + Bootloader::Boot::bootSelectedSlot(); + return true; +} + +bool backoff() { + if (Bootloader::Boot::config()->slot() != nullptr) { + Bootloader::Boot::config()->clearSlot(); + } + return true; +} + +void Bootloader::WarningMenu::setup() { + m_default_columns[0] = Column(Messages::epsilonWarningMessage1, k_small_font, 0, true); + m_default_columns[1] = Column(Messages::epsilonWarningMessage2, k_small_font, 0, true); + m_default_columns[2] = Column(Messages::epsilonWarningMessage3, k_small_font, 0, true); + m_default_columns[3] = Column(Messages::epsilonWarningMessage4, k_small_font, 0, true); + m_default_columns[4] = Column(Messages::epsilonWarningMessage5, Ion::Keyboard::Key::EXE, k_small_font, 0, true, &proceed); + m_default_columns[5] = Column(Messages::epsilonWarningMessage6, Ion::Keyboard::Key::Back, k_small_font, 0, true, &backoff); + + m_columns[0] = ColumnBinder(&m_default_columns[0]); + m_columns[1] = ColumnBinder(&m_default_columns[1]); + m_columns[2] = ColumnBinder(&m_default_columns[2]); + m_columns[3] = ColumnBinder(&m_default_columns[3]); + m_columns[4] = ColumnBinder(&m_default_columns[4]); + m_columns[5] = ColumnBinder(&m_default_columns[5]); +} diff --git a/bootloader/interface/menus/warning.h b/bootloader/interface/menus/warning.h new file mode 100644 index 000000000..f2763f203 --- /dev/null +++ b/bootloader/interface/menus/warning.h @@ -0,0 +1,17 @@ +#ifndef _BOOTLOADER_INTERFACE_MENUS_WARNING_H_ +#define _BOOTLOADER_INTERFACE_MENUS_WARNING_H_ + +#include +#include + +namespace Bootloader { + class WarningMenu : public Menu { + public: + WarningMenu(); + + void setup() override; + void postOpen() override {}; + }; +} + +#endif diff --git a/bootloader/interface/src/menu.cpp b/bootloader/interface/src/menu.cpp new file mode 100644 index 000000000..d45fb8466 --- /dev/null +++ b/bootloader/interface/src/menu.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include + +#include + +const Ion::Keyboard::Key Bootloader::Menu::k_breaking_keys[]; + +void Bootloader::Menu::setup() { + // Here we add the columns to the menu. +} + +void Bootloader::Menu::open(bool noreturn) { + showMenu(); + + uint64_t scan = 0; + bool exit = false; + + postOpen(); + + while(!exit && !m_forced_exit) { + scan = Ion::Keyboard::scan(); + exit = !handleKey(scan); + if (noreturn) { + exit = false; + } + } +} + +int Bootloader::Menu::calculateCenterX(const char * text, int fontWidth) { + return (getScreen().width() - fontWidth * strlen(text)) / 2; +} + +void Bootloader::Menu::showMenu() { + KDContext * ctx = KDIonContext::sharedContext(); + ctx->fillRect(getScreen(), m_background); + Interface::drawImage(ctx, ImageStore::Computer, 25); + int y = ImageStore::Computer->height() + 25 + 10; + int x = calculateCenterX(m_title, largeFontWidth()); + ctx->drawString(m_title, KDPoint(x, y), k_large_font, m_foreground, m_background); + y += largeFontHeight() + 10; + + //TODO: center the columns if m_centerY is true + + for (ColumnBinder column : m_columns) { + if (column.isNull()) { + continue; + } + if (column.type() == ColumnType::SLOT) { + y += ((SlotColumn *)column.getColumn())->draw(ctx, y, m_background, m_foreground) + m_margin; + } else if (column.type() == ColumnType::DEFAULT) { + y += ((Column *)column.getColumn())->draw(ctx, y, m_background, m_foreground) + m_margin; + } + } + + if (m_bottom != nullptr) { + y = getScreen().height() - smallFontHeight() - 10; + x = calculateCenterX(m_bottom, smallFontWidth()); + ctx->drawString(m_bottom, KDPoint(x, y), k_small_font, m_foreground, m_background); + } +} + +bool Bootloader::Menu::handleKey(uint64_t key) { + for (Ion::Keyboard::Key breaking : this->k_breaking_keys) { + if (Ion::Keyboard::State(breaking) == key) { + return false; + } + } + if (key == Ion::Keyboard::State(Ion::Keyboard::Key::OnOff)) { + Ion::Power::standby(); + return false; + } + for (ColumnBinder column : m_columns) { + if (column.isNull()) { + continue; + } else { + if (column.type() == ColumnType::SLOT) { + if (((SlotColumn *)column.getColumn())->didHandledEvent(key)) { + redraw(); + } + } else if (column.type() == ColumnType::DEFAULT) { + if (((Column *)column.getColumn())->didHandledEvent(key)) { + redraw(); + } + } + } + } + return true; +} + +bool Bootloader::Menu::Column::didHandledEvent(uint64_t key) { + if (isMyKey(key) && isClickable()) { + return m_callback(); + } + return false; +} + +int Bootloader::Menu::Column::draw(KDContext * ctx, int y, KDColor background, KDColor foreground) { + int x = m_extraX; + if (m_center) { + x += Bootloader::Menu::calculateCenterX(m_text, m_font->glyphSize().width()); + } + ctx->drawString(m_text, KDPoint(x, y), m_font, foreground, background); + return m_font->glyphSize().height(); +} + +int Bootloader::Menu::SlotColumn::draw(KDContext * ctx, int y, KDColor background, KDColor foreground) { + int x = m_extraX; + + int width = strlen(m_text); + if (m_kernalPatch != nullptr) { + width += strlen(m_kernalPatch) + m_font->glyphSize().width(); + } + if (m_osType != nullptr) { + width += strlen(m_osType) + m_font->glyphSize().width(); + } + if (m_center) { + x += Bootloader::Menu::getScreen().width() - width * m_font->glyphSize().width(); + } + ctx->drawString(m_text, KDPoint(x, y), m_font, foreground, background); + x += strlen(m_text) * m_font->glyphSize().width() + m_font->glyphSize().width(); + if (m_kernalPatch != nullptr) { + ctx->drawString(m_kernalPatch, KDPoint(x, y), m_font, foreground, background); + } + x += strlen(m_kernalPatch) * m_font->glyphSize().width() + m_font->glyphSize().width(); + if (m_osType != nullptr) { + ctx->drawString(m_osType, KDPoint(x, y), m_font, foreground, background); + } + x += strlen(m_osType) * m_font->glyphSize().width() + m_font->glyphSize().width(); + if (m_kernelVersion != nullptr) { + ctx->drawString(m_kernelVersion, KDPoint(x, y), m_font, foreground, background); + } + return m_font->glyphSize().height(); +} diff --git a/bootloader/interface/src/menu.h b/bootloader/interface/src/menu.h new file mode 100644 index 000000000..f623710c2 --- /dev/null +++ b/bootloader/interface/src/menu.h @@ -0,0 +1,126 @@ +#ifndef _BOOTLOADER_MENU_H_ +#define _BOOTLOADER_MENU_H_ + +#include +#include +#include + +namespace Bootloader { + class Menu { + public: + Menu() : Menu(KDColorBlack, KDColorWhite, Messages::mainTitle) { }; + Menu(KDColor forground, KDColor background, const char * title) : Menu(forground, background, title, nullptr) {}; + Menu(KDColor forground, KDColor background, const char * title, const char * bottom) : Menu(forground, background, title, bottom, false) {}; + Menu(KDColor forground, KDColor background, const char * title, const char * bottom, bool centerY) : Menu(forground, background, title, bottom, centerY, k_columns_margin) {}; + Menu(KDColor forground, KDColor background, const char * title, const char * bottom, bool centerY, int margin) : m_columns(), m_default_columns(), m_slot_columns(), m_background(background), m_title(title), m_foreground(forground), m_bottom(bottom), m_centerY(centerY), m_forced_exit(false), m_margin(margin) { + setup(); + } + static const int k_columns_margin = 5; + + virtual void setup() = 0; + virtual void postOpen() = 0; + + enum ColumnType { + DEFAULT, + SLOT + }; + + class Column { + public: + Column() : m_text(nullptr), m_key(Ion::Keyboard::Key::None), m_font(KDFont::SmallFont), m_extraX(0), m_center(false), m_callback(nullptr), m_clickable(false) {}; + + Column(const char * t, Ion::Keyboard::Key k, const KDFont * font, int extraX, bool center, bool(*pointer)()) : m_text(t), m_key(k), m_font(font), m_extraX(extraX), m_center(center), m_callback(pointer), m_clickable(true) {}; + Column(const char * t, const KDFont * font, int extraX, bool center) : m_text(t), m_key(Ion::Keyboard::Key::None), m_font(font), m_extraX(extraX), m_center(center), m_callback(nullptr), m_clickable(false) {}; + + bool isNull() const { return m_text == nullptr; }; + bool isClickable() const { return m_clickable; }; + bool didHandledEvent(uint64_t key); + virtual int draw(KDContext * ctx, int y, KDColor background, KDColor foreground); + virtual int columnType() { return ColumnType::DEFAULT; }; + + private: + bool isMyKey(uint64_t key) const { return Ion::Keyboard::State(m_key) == key; }; + protected: + const char * m_text; + Ion::Keyboard::Key m_key; + const KDFont * m_font; + int m_extraX; + bool m_center; + bool (*m_callback)(); + bool m_clickable; + }; + + class SlotColumn : public Column { + public: + SlotColumn() : Column(), m_kernalPatch(nullptr), m_osType(nullptr), m_kernelVersion(nullptr) {}; + + SlotColumn(const char * t, Ion::Keyboard::Key k, const KDFont * font, int extraX, bool center, bool(*pointer)()) : Column(t, k, font, extraX, center, pointer), m_kernalPatch(nullptr), m_osType(nullptr), m_kernelVersion(nullptr) {}; + SlotColumn(const char * t, const char * k, const char * o, const char * kernelV, Ion::Keyboard::Key key, const KDFont * font, int extraX, bool center, bool(*pointer)()) : Column(t, key, font, extraX, center, pointer), m_kernalPatch(k), m_osType(o), m_kernelVersion(kernelV) {}; + + int draw(KDContext * ctx, int y, KDColor background, KDColor foreground) override; + virtual int columnType() { return ColumnType::SLOT; }; + + private: + const char * m_kernalPatch; + const char * m_osType; + const char * m_kernelVersion; + }; + + class ColumnBinder { + public: + ColumnBinder() : m_pointer(nullptr), m_type(ColumnType::DEFAULT) {}; + ColumnBinder(Column * pointer) : m_pointer(pointer), m_type(ColumnType::DEFAULT) {}; + ColumnBinder(SlotColumn * pointer) : m_pointer(pointer), m_type(ColumnType::SLOT) {}; + + bool isNull() const { return m_pointer == nullptr; }; + void * getColumn() const { return m_pointer; }; + ColumnType type() const { return m_type; }; + private: + void * m_pointer; + ColumnType m_type; + }; + + void open(bool noreturn = false); + void redraw() { showMenu(); }; + + static int calculateCenterX(const char * text, int fontWidth); + + static constexpr const KDFont * k_small_font = KDFont::SmallFont; + static constexpr const KDFont * k_large_font = KDFont::LargeFont; + + static const KDRect getScreen() { return KDRect(0, 0, 320, 240); }; + + protected: + void forceExit() { m_forced_exit = true; }; + + private: + static const int k_max_columns = 6; + + static constexpr Ion::Keyboard::Key k_breaking_keys[] = {Ion::Keyboard::Key::Back, Ion::Keyboard::Key::Home}; + + int smallFontHeight() const { return k_small_font->glyphSize().height(); }; + int largeFontHeight() const { return k_large_font->glyphSize().height(); }; + + int smallFontWidth() const { return k_small_font->glyphSize().width(); }; + int largeFontWidth() const { return k_large_font->glyphSize().width(); }; + + bool handleKey(uint64_t key); + void showMenu(); + + protected: + ColumnBinder m_columns[k_max_columns]; + // Columns Storage + Column m_default_columns[k_max_columns]; + SlotColumn m_slot_columns[k_max_columns]; + KDColor m_background; + KDColor m_foreground; + const char * m_title; + const char * m_bottom; + bool m_centerY; + int m_margin; + private: + bool m_forced_exit; + }; +} + +#endif // _BOOTLOADER_MENU_H_ diff --git a/bootloader/interface/static/interface.cpp b/bootloader/interface/static/interface.cpp new file mode 100644 index 000000000..4244a227b --- /dev/null +++ b/bootloader/interface/static/interface.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +#include + +namespace Bootloader { + +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(); + pixelBufferSize = image->width() * image->height(); + } else { + return; + } + + KDColor pixelBuffer[4000]; + assert(pixelBufferSize <= 4000); + assert(Ion::stackSafe()); // That's a VERY big buffer we're allocating on the stack + + Ion::decompress( + data, + reinterpret_cast(pixelBuffer), + size, + pixelBufferSize * sizeof(KDColor) + ); + + KDRect bounds((320 - image->width()) / 2, offset, image->width(), image->height()); + + ctx->fillRectWithPixels(bounds, pixelBuffer, nullptr); +} + +void Interface::drawFlasher() { + 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::mainTitle)) / 2; + ctx->drawString(Messages::mainTitle, KDPoint(initPos, ImageStore::Computer->height() + fontSize.height() + 10), KDFont::LargeFont, KDColorBlack, KDColorWhite); + int y = ImageStore::Computer->height() + (KDFont::LargeFont->glyphSize().height() + 10) + (KDFont::SmallFont->glyphSize().height() + 10); + initPos = (320 - KDFont::SmallFont->glyphSize().width() * strlen(Messages::dfuSlotsUpdate)) / 2; + ctx->drawString(Messages::dfuSlotsUpdate, 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); +} + +} diff --git a/bootloader/interface/static/interface.h b/bootloader/interface/static/interface.h new file mode 100644 index 000000000..3741b5ec1 --- /dev/null +++ b/bootloader/interface/static/interface.h @@ -0,0 +1,19 @@ +#ifndef BOOTLOADER_INTERFACE_STATIC_INTERFACE_H +#define BOOTLOADER_INTERFACE_STATIC_INTERFACE_H + +#include +#include +#include + +namespace Bootloader { +class Interface { + +public: + static void drawImage(KDContext * ctx, const Image * image, int offset); + static void drawLoading(); + static void drawFlasher(); +}; + +} + +#endif diff --git a/bootloader/interface/static/messages.h b/bootloader/interface/static/messages.h new file mode 100644 index 000000000..e47764ac8 --- /dev/null +++ b/bootloader/interface/static/messages.h @@ -0,0 +1,86 @@ +#ifndef BOOTLOADER_INTERFACE_STATIC_MESSAGES_H +#define BOOTLOADER_INTERFACE_STATIC_MESSAGES_H + +namespace Bootloader { + +class Messages { +public: + constexpr static const char * mainTitle = "Upsilon Calculator"; + + // Home menu + constexpr static const char * homeTitle = "Select a slot"; + + // Slots OS Type + constexpr static const char * upsilonSlot = "-- Upsilon "; + constexpr static const char * khiSlot = "-- Khi "; + constexpr static const char * omegaSlot = "-- Omega "; + constexpr static const char * epsilonSlot = "-- Epsilon "; + constexpr static const char * invalidSlot = "X - Invalid slot"; + + // Home Submenu + constexpr static const char * homeSlotASubmenu = "1 - Slot A"; + constexpr static const char * homeSlotKhiSubmenu = "2 - Slot Khi"; + constexpr static const char * homeSlotBSubmenu = "3 - Slot B"; + constexpr static const char * homeInstallerSubmenu = "4 - Installer Mode"; + constexpr static const char * homeAboutSubmenu = "5 - About"; + constexpr static const char * homeRebootSubmenu = "6 - Reboot"; + + // DFU menu + constexpr static const char * dfuTitle = "Installer"; + + constexpr static const char * dfuSlotsUpdate = "Waiting for Slots..."; + constexpr static const char * dfuBootloaderUpdate = "Waiting for Bootloader..."; + + // Installer menu + constexpr static const char * installerTitle = "Installer mode"; + + constexpr static const char * installerText1 = "Please select a mode:"; + constexpr static const char * installerSlotsSubmenu = "1 - Flash Slots"; + constexpr static const char * installerBootloaderSubmenu = "2 - Flash Bootloader"; + + // Bootloader Crash Handler + constexpr static const char * bootloaderCrashTitle = "BOOTLOADER CRASH"; + + constexpr static const char * bootloaderCrashMessage1 = "The bootloader has crashed."; + constexpr static const char * bootloaderCrashMessage2 = "Please restart the calculator."; + + // Recovery menu + constexpr static const char * recoveryTitle = "Recovery mode"; + + constexpr static const char * recoveryMessage1 = "The bootloader has detected a crash."; + constexpr static const char * recoveryMessage2 = "Plug the calculator to a device capable of"; + constexpr static const char * recoveryMessage3 = "accessing the internal storage."; + constexpr static const char * recoveryMessage4 = "Press Back to continue."; + constexpr static const char * recoveryMessage5 = "(you will not be able to recover your data !)"; + + // Warning menu + constexpr static const char * epsilonWarningTitle = "Epsilon Slot"; + + constexpr static const char * epsilonWarningMessage1 = "!! WARNING !! "; + constexpr static const char * epsilonWarningMessage2 = "This version of epsilon"; + constexpr static const char * epsilonWarningMessage3 = "can lock the calculator."; + constexpr static const char * epsilonWarningMessage4 = "Proceed the boot ?"; + constexpr static const char * epsilonWarningMessage5 = "EXE - Yes"; + constexpr static const char * epsilonWarningMessage6 = "BACK - No"; + + // About menu + constexpr static const char * aboutMenuTitle = "About"; + + constexpr static const char * aboutMessage1 = "This is the bootloader of"; + constexpr static const char * aboutMessage2 = "the Upsilon Calculator."; + constexpr static const char * aboutMessage3 = "It is used to install"; + constexpr static const char * aboutMessage4 = "and select the OS"; + constexpr static const char * aboutMessage5 = "to boot."; + + constexpr static const char * bootloaderVersion = "Version 1.0.0 - FREED0M"; + + //USB NAMES + constexpr static const char * usbUpsilonBootloader = "Upsilon Bootloader"; + constexpr static const char * usbUpsilonRecovery = "Upsilon Recovery"; + constexpr static const char * usbBootloaderUpdate = "Bootloader Update"; + +}; + +}; + +#endif diff --git a/bootloader/main.cpp b/bootloader/main.cpp index 70c632136..c0f0995db 100644 --- a/bootloader/main.cpp +++ b/bootloader/main.cpp @@ -3,6 +3,11 @@ #include #include +#include +#include +#include +#include +#include __attribute__ ((noreturn)) void ion_main(int argc, const char * const argv[]) { // Clear the screen @@ -10,33 +15,47 @@ __attribute__ ((noreturn)) void ion_main(int argc, const char * const argv[]) { // 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::isFullyValid(Bootloader::Slot::A()); + + if (isSlotA) { + Bootloader::ExamMode::ExamMode SlotAExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotAExamMode(!Bootloader::Slot::A().userlandHeader()->isOmega()); + 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::isFullyValid(Bootloader::Slot::B()); + + if (isSlotB) { + Bootloader::ExamMode::ExamMode SlotBExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotBExamMode(!Bootloader::Slot::B().userlandHeader()->isOmega()); + 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(); + // I have no idea if this will work, but if Pariss did a good job, it should - // 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); + bool isKhiSlot = Bootloader::Slot::isFullyValid(Bootloader::Slot::Khi()); + + if (isKhiSlot) { + Bootloader::ExamMode::ExamMode KhiExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotKhiExamMode(); + if (KhiExamMode != Bootloader::ExamMode::ExamMode::Off && KhiExamMode != Bootloader::ExamMode::ExamMode::Unknown && isKhiSlot) { + // We boot the slot in exam_mode + Bootloader::Slot::Khi().boot(); + } } + if (Bootloader::Recovery::hasCrashed()) { + Bootloader::Recovery::recoverData(); + } + + Bootloader::Interface::drawLoading(); + // Boot the firmware Bootloader::Boot::boot(); } diff --git a/bootloader/recovery.cpp b/bootloader/recovery.cpp new file mode 100644 index 000000000..8c849f781 --- /dev/null +++ b/bootloader/recovery.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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); + CrashMenu menu(error); + menu.open(true); +} + +bool Bootloader::Recovery::hasCrashed() { + 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::recoverData() { + Ion::Device::Board::initPeripherals(false); + Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorWhite); + Ion::Backlight::init(); + + USBData udata = USBData::Recovery((uint32_t)getSlotConcerned().getStorageAddress(), (uint32_t)getSlotConcerned().getStorageSize()); + + SlotRecoveryMenu menu = SlotRecoveryMenu(&udata); + menu.open(); + + // Invalidate storage header + *(uint32_t *)(getSlotConcerned().getStorageAddress()) = (uint32_t)0x0; + +} diff --git a/bootloader/recovery.h b/bootloader/recovery.h new file mode 100644 index 000000000..001f8eaab --- /dev/null +++ b/bootloader/recovery.h @@ -0,0 +1,31 @@ +#ifndef BOOTLOADER_RECOVERY_H_ +#define BOOTLOADER_RECOVERY_H_ + +#include +#include + +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 recoverData(); + static bool hasCrashed(); + +}; +}; + +#endif //BOOTLOADER_RECOVERY_H_ diff --git a/bootloader/kernel_header.cpp b/bootloader/slots/kernel_header.cpp similarity index 60% rename from bootloader/kernel_header.cpp rename to bootloader/slots/kernel_header.cpp index 7dac97a06..c323046d9 100644 --- a/bootloader/kernel_header.cpp +++ b/bootloader/slots/kernel_header.cpp @@ -1,4 +1,5 @@ -#include +#include +#include namespace Bootloader { @@ -22,4 +23,12 @@ const void(*KernelHeader::startPointer() const)() { return m_startPointer; } +const bool KernelHeader::isAboveVersion16 () const { + int sum = Utility::versionSum(m_version, 2); + char newVersion[] = "16"; + int min = Utility::versionSum(newVersion, 2); + return sum >= min; +} + + } diff --git a/bootloader/kernel_header.h b/bootloader/slots/kernel_header.h similarity index 79% rename from bootloader/kernel_header.h rename to bootloader/slots/kernel_header.h index 017036b62..835aa9338 100644 --- a/bootloader/kernel_header.h +++ b/bootloader/slots/kernel_header.h @@ -1,7 +1,8 @@ -#ifndef BOOTLOADER_KERNEL_HEADER_H -#define BOOTLOADER_KERNEL_HEADER_H +#ifndef BOOTLOADER_SLOTS_KERNEL_HEADER_H +#define BOOTLOADER_SLOTS_KERNEL_HEADER_H #include +#include namespace Bootloader { @@ -10,6 +11,7 @@ public: const char * version() const; const char * patchLevel() const; const bool isValid() const; + const bool isAboveVersion16() const; const uint32_t* stackPointer() const; const void(*startPointer() const)(); diff --git a/bootloader/slot.cpp b/bootloader/slots/slot.cpp similarity index 60% rename from bootloader/slot.cpp rename to bootloader/slots/slot.cpp index b23655aed..d8ce93398 100644 --- a/bootloader/slot.cpp +++ b/bootloader/slots/slot.cpp @@ -1,5 +1,7 @@ -#include +#include #include +#include +#include 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,15 @@ 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(); + } + // Configure the MPU for the booted firmware Ion::Device::Board::bootloaderMPU(); diff --git a/bootloader/slot.h b/bootloader/slots/slot.h similarity index 61% rename from bootloader/slot.h rename to bootloader/slots/slot.h index 15a883f39..8aa97b4d3 100644 --- a/bootloader/slot.h +++ b/bootloader/slots/slot.h @@ -1,5 +1,5 @@ -#ifndef BOOTLOADER_SLOT_H -#define BOOTLOADER_SLOT_H +#ifndef BOOTLOADER_SLOTS_SLOT_H +#define BOOTLOADER_SLOTS_SLOT_H #include @@ -13,18 +13,26 @@ class Slot { public: Slot(uint32_t address) : m_kernelHeader(reinterpret_cast(address)), - m_userlandHeader(reinterpret_cast(address + 64 * 1024)) { } + m_userlandHeader(reinterpret_cast(address + 64 * 1024)), + m_address(address) { } const KernelHeader* kernelHeader() const; const UserlandHeader* userlandHeader() const; [[ noreturn ]] void boot() const; + const uint32_t address() const { return m_address; } static const Slot A(); static const Slot B(); + static const Slot Khi(); + + static bool isFullyValid(const Slot& slot) { + return slot.kernelHeader()->isValid() && slot.userlandHeader()->isValid(); + } private: const KernelHeader* m_kernelHeader; const UserlandHeader* m_userlandHeader; + const uint32_t m_address; }; diff --git a/bootloader/slots/slot_exam_mode.cpp b/bootloader/slots/slot_exam_mode.cpp new file mode 100644 index 000000000..45b312323 --- /dev/null +++ b/bootloader/slots/slot_exam_mode.cpp @@ -0,0 +1,209 @@ +#include +#include +#include +#include + +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(persitence_start_32); + uint8_t * end = reinterpret_cast(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(persitence_start_32); + uint8_t * end = reinterpret_cast(persitence_end_32 + 1) - 1; + while (end >= start + 2 && *end == 0xFF) { + end -= 1; + } + return end - 1; + } + +} + +uint8_t * SignificantSlotKhiExamModeAddress() { + uint32_t * persitence_start_32 = (uint32_t *)SlotsExamMode::getSlotKhiStartExamAddress(); + uint32_t * persitence_end_32 = (uint32_t *)SlotsExamMode::getSlotKhiEndExamAddress(); + + 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::getSlotKhiStartExamAddress()) >= 0); + Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotKhiStartExamAddress())); + return (uint8_t *)SlotsExamMode::getSlotKhiStartExamAddress(); + } + + return persitence_start_8; +} + +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); + } + +} + +uint8_t SlotsExamMode::FetchSlotKhiExamMode() { + uint8_t * readingAddress = SignificantSlotKhiExamModeAddress(); + // Count the number of 0[3] before reading address + uint32_t nbOfZerosBefore = ((readingAddress - (uint8_t *)getSlotKhiStartExamAddress()) * numberOfBitsInByte) % 4; + // Count the number of 0[3] at reading address + size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*readingAddress)) % 4; + return (nbOfZerosBefore + numberOfLeading0) % 4; +} + +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; +} + +uint32_t SlotsExamMode::getSlotKhiStartExamAddress() { + return SlotKhiExamModeBufferStart; +} + +uint32_t SlotsExamMode::getSlotKhiEndExamAddress() { + return SlotKhiExamModeBufferEnd; +} + +} +} diff --git a/bootloader/slots/slot_exam_mode.h b/bootloader/slots/slot_exam_mode.h new file mode 100644 index 000000000..d9e380e9a --- /dev/null +++ b/bootloader/slots/slot_exam_mode.h @@ -0,0 +1,53 @@ +#ifndef BOOTLOADER_SLOTS_EXAM_MODE_H +#define BOOTLOADER_SLOTS_EXAM_MODE_H + +extern "C" { + #include + } + +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; + +static const uint32_t SlotKhiExamModeBufferStart = 0x90181000; +static const uint32_t SlotKhiExamModeBufferEnd = 0x90183000; + +class SlotsExamMode{ + public: + static uint8_t FetchSlotAExamMode(bool newVersion); + static uint8_t FetchSlotBExamMode(bool newVerion); + static uint8_t FetchSlotKhiExamMode(); + + static uint32_t getSlotAStartExamAddress(bool newVersion); + static uint32_t getSlotAEndExamAddress(bool newVersion); + static uint32_t getSlotBStartExamAddress(bool newVersion); + static uint32_t getSlotBEndExamAddress(bool newVersion); + static uint32_t getSlotKhiStartExamAddress(); + static uint32_t getSlotKhiEndExamAddress(); + +}; + +enum class ExamMode : int8_t { + Unknown = -1, + Off = 0, + Standard = 1, + NoSym = 2, + NoSymNoText = 3, + Dutch = 4, + }; + +} +} + +#endif diff --git a/bootloader/userland_header.cpp b/bootloader/slots/userland_header.cpp similarity index 71% rename from bootloader/userland_header.cpp rename to bootloader/slots/userland_header.cpp index 349d7708a..3c8a91908 100644 --- a/bootloader/userland_header.cpp +++ b/bootloader/slots/userland_header.cpp @@ -1,4 +1,4 @@ -#include +#include namespace Bootloader { @@ -23,11 +23,19 @@ const char * UserlandHeader::omegaVersion() const { } const bool UserlandHeader::isUpsilon() const { - return m_upsilonMagicHeader == UpsilonMagic && m_upsilonMagicFooter == UpsilonMagic; + return m_upsilonMagicHeader == UpsilonMagic && m_upsilonMagicHeader == 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; +} + } diff --git a/bootloader/userland_header.h b/bootloader/slots/userland_header.h similarity index 88% rename from bootloader/userland_header.h rename to bootloader/slots/userland_header.h index dbcfb4539..382e409b0 100644 --- a/bootloader/userland_header.h +++ b/bootloader/slots/userland_header.h @@ -1,5 +1,5 @@ -#ifndef BOOTLOADER_USERLAND_HEADER_H -#define BOOTLOADER_USERLAND_HEADER_H +#ifndef BOOTLOADER_SLOTS_USERLAND_HEADER_H +#define BOOTLOADER_SLOTS_USERLAND_HEADER_H #include #include @@ -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(); diff --git a/bootloader/trampoline.cpp b/bootloader/trampoline.cpp index 4d1013f34..876db0496 100644 --- a/bootloader/trampoline.cpp +++ b/bootloader/trampoline.cpp @@ -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 }; diff --git a/bootloader/usb_data.cpp b/bootloader/usb_data.cpp new file mode 100644 index 000000000..bd8843791 --- /dev/null +++ b/bootloader/usb_data.cpp @@ -0,0 +1,39 @@ +#include +#include + +#include +#include +#include +#include + +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)); + 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'; + 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::usbUpsilonBootloader, ProtectionState(false, true)); +} + +const Bootloader::USBData Bootloader::USBData::BOOTLOADER_UPDATE() { + return USBData("@Flash/0x08000000/04*016Kg", Messages::usbBootloaderUpdate, ProtectionState(true, false)); +} + +Bootloader::USBData Bootloader::USBData::Recovery(uint32_t startAddress, uint32_t size) { + return USBData(buildStringDescriptor(StringHeader::SRAM(), startAddress, size), Messages::usbUpsilonRecovery, ProtectionState(false, false)); +} diff --git a/bootloader/usb_data.h b/bootloader/usb_data.h new file mode 100644 index 000000000..a0477edbd --- /dev/null +++ b/bootloader/usb_data.h @@ -0,0 +1,56 @@ +#ifndef BOOTLOADER_USB_DATA_H_ +#define BOOTLOADER_USB_DATA_H_ + +#include +#include + +namespace Bootloader { + +class ProtectionState { + public: + ProtectionState(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, ProtectionState data = ProtectionState()) : m_stringDescriptor(desc), m_name(name), m_data(data) {}; + + const char * stringDescriptor() const { return m_stringDescriptor; } + const char * getName() const { return m_name; } + ProtectionState getData() const { return m_data; } + + static const char * buildStringDescriptor(StringHeader header, uint32_t startAddress, uint32_t size); + + static const USBData DEFAULT(); + static const USBData BOOTLOADER_UPDATE(); + static USBData Recovery(uint32_t startAddress, uint32_t size); + + private: + const char * m_stringDescriptor; + const char * m_name; + ProtectionState m_data; + +}; +} + +#endif //BOOTLOADER_USB_DATA_H_ diff --git a/bootloader/utility.cpp b/bootloader/utility.cpp new file mode 100644 index 000000000..fb6d676ac --- /dev/null +++ b/bootloader/utility.cpp @@ -0,0 +1,10 @@ +#include +#include + +int Utility::versionSum(const char * version, int length) { + int sum = 0; + for (int i = 0; i < length; i++) { + sum += version[i] * (strlen(version) * 100 - i * 10); + } + return sum; +} diff --git a/bootloader/utility.h b/bootloader/utility.h new file mode 100644 index 000000000..6a2c0d590 --- /dev/null +++ b/bootloader/utility.h @@ -0,0 +1,8 @@ +#ifndef _BOOTLOADER_ITOA_H_ +#define _BOOTLOADER_ITOA_H_ + +namespace Utility { + extern int versionSum(const char * version, int length); +} + +#endif diff --git a/build/device/secure_ext.py b/build/device/secure_ext.py index 38c555a65..4d0526800 100644 --- a/build/device/secure_ext.py +++ b/build/device/secure_ext.py @@ -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]) diff --git a/build/targets.device.bootloader.mak b/build/targets.device.bootloader.mak index d5b7ec1a8..48466b778 100644 --- a/build/targets.device.bootloader.mak +++ b/build/targets.device.bootloader.mak @@ -35,7 +35,6 @@ HANDY_TARGETS += epsilon.A epsilon.B .PHONY: epsilon epsilon: $(BUILD_DIR)/epsilon.onboarding.bin - $(PYTHON) build/device/secure_ext.py $(BUILD_DIR)/epsilon.onboarding.bin .DEFAULT_GOAL := epsilon .PHONY: %_flash @@ -54,4 +53,3 @@ binpack: $(BUILD_DIR)/epsilon.onboarding.bin cp $(BUILD_DIR)/epsilon.onboarding.bin $(BUILD_DIR)/binpack cd $(BUILD_DIR) && for binary in epsilon.onboarding.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.bin diff --git a/build/targets.device.n0110.mak b/build/targets.device.n0110.mak index 570ae0b31..c78249b45 100644 --- a/build/targets.device.n0110.mak +++ b/build/targets.device.n0110.mak @@ -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 diff --git a/ion/include/ion/internal_storage.h b/ion/include/ion/internal_storage.h index 933877edb..c85282df2 100644 --- a/ion/include/ion/internal_storage.h +++ b/ion/include/ion/internal_storage.h @@ -161,9 +161,6 @@ protected: }; RecordIterator end() const { return RecordIterator(nullptr); } - mutable Record m_lastRecordRetrieved; - mutable char * m_lastRecordRetrievedPointer; - MetadataMapHeader * m_metadataMapHeader; private: constexpr static uint32_t Magic = 0xEE0BDDBA; constexpr static size_t k_maxRecordSize = (1 << sizeof(record_size_t)*8); @@ -196,6 +193,10 @@ private: char m_buffer[k_storageSize]; uint32_t m_magicFooter; StorageDelegate * m_delegate; +protected: + mutable Record m_lastRecordRetrieved; + mutable char * m_lastRecordRetrievedPointer; + MetadataMapHeader * m_metadataMapHeader; }; /* Some apps memoize records and need to be notified when a record might have diff --git a/ion/include/ion/usb.h b/ion/include/ion/usb.h index d9ec8afd7..6650d79c3 100644 --- a/ion/include/ion/usb.h +++ b/ion/include/ion/usb.h @@ -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(); diff --git a/ion/src/device/Makefile b/ion/src/device/Makefile index 37943dbe2..cc5594242 100644 --- a/ion/src/device/Makefile +++ b/ion/src/device/Makefile @@ -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 diff --git a/ion/src/device/bootloader/boot/rt0.cpp b/ion/src/device/bootloader/boot/rt0.cpp index cd4bee46f..a631356bb 100644 --- a/ion/src/device/bootloader/boot/rt0.cpp +++ b/ion/src/device/bootloader/boot/rt0.cpp @@ -6,15 +6,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include 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 diff --git a/ion/src/device/bootloader/drivers/board.cpp b/ion/src/device/bootloader/drivers/board.cpp index 03e47bc2c..a10d766c1 100644 --- a/ion/src/device/bootloader/drivers/board.cpp +++ b/ion/src/device/bootloader/drivers/board.cpp @@ -418,6 +418,9 @@ bool pcbVersionIsLocked() { return *reinterpret_cast(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex)) == 0; } + +void jumpToInternalBootloader() {} + } } } diff --git a/ion/src/device/bootloader/drivers/external_flash.cpp b/ion/src/device/bootloader/drivers/external_flash.cpp index 612efc017..5e3844985 100644 --- a/ion/src/device/bootloader/drivers/external_flash.cpp +++ b/ion/src/device/bootloader/drivers/external_flash.cpp @@ -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(FlashAddressSpaceSize), reinterpret_cast(¤tStatusRegister2), 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(FlashAddressSpaceSize), reinterpret_cast(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(FlashAddressSpaceSize), reinterpret_cast(¤tStatusRegister2), 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(FlashAddressSpaceSize), reinterpret_cast(registers), sizeof(registers), sOperatingModes101); + wait(); + set_as_memory_mapped(); +} + void MassErase() { if (Config::NumberOfSectors == 0) { return; diff --git a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp index 2a98b5bf6..6ead8bc27 100644 --- a/ion/src/device/bootloader/drivers/external_flash_tramp.cpp +++ b/ion/src/device/bootloader/drivers/external_flash_tramp.cpp @@ -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(Ion::Device::Trampoline::address(Ion::Device::Trampoline::ExternalFlashEraseSector)))(i); + asm("cpsie if"); +} + +void LockSlotA() { +} + +void LockSlotB() { } } diff --git a/bootloader/usb_desc.cpp b/ion/src/device/bootloader/drivers/usb_desc.cpp similarity index 100% rename from bootloader/usb_desc.cpp rename to ion/src/device/bootloader/drivers/usb_desc.cpp diff --git a/ion/src/device/bootloader/internal_flash.ld b/ion/src/device/bootloader/internal_flash.ld new file mode 100644 index 000000000..e441c6527 --- /dev/null +++ b/ion/src/device/bootloader/internal_flash.ld @@ -0,0 +1,131 @@ +/* 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 + + .fake_isr_function : { + . = ALIGN(4); + _fake_isr_function_start = .; + KEEP(*(.fake_isr_function)) + KEEP(*(.fake_isr_function.*)) + _fake_isr_function_end = .; + } + + .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 +} diff --git a/ion/src/device/bootloader/usb/Makefile b/ion/src/device/bootloader/usb/Makefile new file mode 100644 index 000000000..f3d202530 --- /dev/null +++ b/ion/src/device/bootloader/usb/Makefile @@ -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 \ +) diff --git a/ion/src/device/bootloader/usb/boot.cpp b/ion/src/device/bootloader/usb/boot.cpp new file mode 100644 index 000000000..0eb8d71d6 --- /dev/null +++ b/ion/src/device/bootloader/usb/boot.cpp @@ -0,0 +1,2 @@ +extern "C" void abort() { +} diff --git a/ion/src/device/bootloader/usb/calculator.cpp b/ion/src/device/bootloader/usb/calculator.cpp new file mode 100644 index 000000000..db8d8df3d --- /dev/null +++ b/ion/src/device/bootloader/usb/calculator.cpp @@ -0,0 +1,94 @@ +#include "calculator.h" +#include +#include +#include +#include + +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(data)->stringDescriptor(), data == nullptr ? "Upsilon Bootloader" : static_cast(data)->getName()); + + if (data != nullptr) { + c.setConfigData(static_cast(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; itype() != 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; +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/calculator.h b/ion/src/device/bootloader/usb/calculator.h new file mode 100644 index 000000000..68e967cf8 --- /dev/null +++ b/ion/src/device/bootloader/usb/calculator.h @@ -0,0 +1,176 @@ +#ifndef ION_DEVICE_SHARED_USB_CALCULATOR_H +#define ION_DEVICE_SHARED_USB_CALCULATOR_H + +#include +#include +#include +#include +#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 + +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 should 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 diff --git a/ion/src/device/bootloader/usb/dfu.ld b/ion/src/device/bootloader/usb/dfu.ld new file mode 100644 index 000000000..957bcfdd5 --- /dev/null +++ b/ion/src/device/bootloader/usb/dfu.ld @@ -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.*) + } +} + diff --git a/ion/src/device/bootloader/usb/dfu_interface.cpp b/ion/src/device/bootloader/usb/dfu_interface.cpp new file mode 100644 index 000000000..05e86f4ed --- /dev/null +++ b/ion/src/device/bootloader/usb/dfu_interface.cpp @@ -0,0 +1,301 @@ +#include "dfu_interface.h" +#include +#include +#include + +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::ProtectionState config = getDfuConfig(); + + // 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); + } + /* 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::ProtectionState config = getDfuConfig(); + + if (m_writeAddress >= 0x08000000 && m_writeAddress <= 0x08010000 && !m_dfuData.isProtectedInternal()) { + Flash::WriteMemory(reinterpret_cast(m_writeAddress), m_largeBuffer, m_largeBufferLength); + } else if (m_writeAddress >= 0x90000000 && m_writeAddress <= 0x90800000 && !m_dfuData.isProtectedExternal()) { + Flash::WriteMemory(reinterpret_cast(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::ProtectionState(!m_dfuConfig.isProtectedInternal(), !m_dfuConfig.isProtectedExternal()); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/dfu_interface.h b/ion/src/device/bootloader/usb/dfu_interface.h new file mode 100644 index 000000000..4fa319bd9 --- /dev/null +++ b/ion/src/device/bootloader/usb/dfu_interface.h @@ -0,0 +1,192 @@ +#ifndef ION_DEVICE_SHARED_USB_DFU_INTERFACE_H +#define ION_DEVICE_SHARED_USB_DFU_INTERFACE_H + +#include +#include +#include "stack/device.h" +#include "stack/interface.h" +#include "stack/endpoint0.h" +#include "stack/setup_packet.h" +#include "stack/streamable.h" +#include + +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(), + 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::ProtectionState data) { m_dfuConfig = data; copyDfuData(); } + Bootloader::ProtectionState 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::ProtectionState m_dfuConfig; + uint32_t m_eraseAddress; + Bootloader::ProtectionState m_dfuData; +}; + +} +} +} + +#endif diff --git a/ion/src/device/bootloader/usb/dfu_relocated.cpp b/ion/src/device/bootloader/usb/dfu_relocated.cpp new file mode 100644 index 000000000..7669a3103 --- /dev/null +++ b/ion/src/device/bootloader/usb/dfu_relocated.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#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(&_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(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. */ +} + +} +} diff --git a/ion/src/device/bootloader/usb/dfu_xip.cpp b/ion/src/device/bootloader/usb/dfu_xip.cpp new file mode 100644 index 000000000..55500cf3c --- /dev/null +++ b/ion/src/device/bootloader/usb/dfu_xip.cpp @@ -0,0 +1,13 @@ +#include +#include "calculator.h" + +namespace Ion { +namespace USB { + +void DFU(bool exitWithKeyboard, void * data) { + Ion::updateSlotInfo(); + Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard, data); +} + +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/bos_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/bos_descriptor.cpp new file mode 100644 index 000000000..2099df6b4 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/bos_descriptor.cpp @@ -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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/bos_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/bos_descriptor.h new file mode 100644 index 000000000..25529c0e5 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/bos_descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/configuration_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/configuration_descriptor.cpp new file mode 100644 index 000000000..c4d97a368 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/configuration_descriptor.cpp @@ -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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/configuration_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/configuration_descriptor.h new file mode 100644 index 000000000..9f994b6e3 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/configuration_descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/descriptor.cpp new file mode 100644 index 000000000..373388cb3 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/descriptor.cpp @@ -0,0 +1,15 @@ +#include "descriptor.h" +#include + +namespace Ion { +namespace Device { +namespace USB { + +void Descriptor::push(Channel * c) const { + c->push(bLength()); + c->push(m_bDescriptorType); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/descriptor.h new file mode 100644 index 000000000..b413b3fe4 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/device_capability_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/device_capability_descriptor.cpp new file mode 100644 index 000000000..8f5d6abb8 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/device_capability_descriptor.cpp @@ -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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/device_capability_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/device_capability_descriptor.h new file mode 100644 index 000000000..8b67cabc6 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/device_capability_descriptor.h @@ -0,0 +1,31 @@ +#ifndef ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABILITY_DESCRIPTOR_H +#define ION_DEVICE_SHARED_USB_STACK_DEVICE_CAPABILITY_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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/device_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/device_descriptor.cpp new file mode 100644 index 000000000..424e891e8 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/device_descriptor.cpp @@ -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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/device_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/device_descriptor.h new file mode 100644 index 000000000..3b526f43f --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/device_descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/dfu_functional_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/dfu_functional_descriptor.cpp new file mode 100644 index 000000000..414421d9b --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/dfu_functional_descriptor.cpp @@ -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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/dfu_functional_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/dfu_functional_descriptor.h new file mode 100644 index 000000000..604392895 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/dfu_functional_descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/extended_compat_id_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/extended_compat_id_descriptor.cpp new file mode 100644 index 000000000..024ac1552 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/extended_compat_id_descriptor.cpp @@ -0,0 +1,61 @@ +#include "extended_compat_id_descriptor.h" +#include + +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]); + } +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/extended_compat_id_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/extended_compat_id_descriptor.h new file mode 100644 index 000000000..016beab97 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/extended_compat_id_descriptor.h @@ -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 descriptor’s 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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/interface_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/interface_descriptor.cpp new file mode 100644 index 000000000..75f193aba --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/interface_descriptor.cpp @@ -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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/interface_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/interface_descriptor.h new file mode 100644 index 000000000..6a49bfb8a --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/interface_descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/language_id_string_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/language_id_string_descriptor.cpp new file mode 100644 index 000000000..8027fb75c --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/language_id_string_descriptor.cpp @@ -0,0 +1,19 @@ +#include "language_id_string_descriptor.h" +#include + +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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/language_id_string_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/language_id_string_descriptor.h new file mode 100644 index 000000000..0f72abb91 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/language_id_string_descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/microsoft_os_string_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/microsoft_os_string_descriptor.cpp new file mode 100644 index 000000000..b125b8efc --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/microsoft_os_string_descriptor.cpp @@ -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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/microsoft_os_string_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/microsoft_os_string_descriptor.h new file mode 100644 index 000000000..dfe3a7ab2 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/microsoft_os_string_descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/platform_device_capability_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/platform_device_capability_descriptor.cpp new file mode 100644 index 000000000..d5c7756ba --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/platform_device_capability_descriptor.cpp @@ -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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/platform_device_capability_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/platform_device_capability_descriptor.h new file mode 100644 index 000000000..3c117348c --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/platform_device_capability_descriptor.h @@ -0,0 +1,47 @@ +#ifndef ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABILITY_DESCRIPTOR_H +#define ION_DEVICE_SHARED_USB_STACK_PLATFORM_DEVICE_CAPABILITY_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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/string_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/string_descriptor.cpp new file mode 100644 index 000000000..044e54fb0 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/string_descriptor.cpp @@ -0,0 +1,25 @@ +#include "string_descriptor.h" +#include + +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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/string_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/string_descriptor.h new file mode 100644 index 000000000..1391c4aec --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/string_descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/url_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/url_descriptor.cpp new file mode 100644 index 000000000..2b4580b8a --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/url_descriptor.cpp @@ -0,0 +1,25 @@ +#include "url_descriptor.h" +#include + +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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/url_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/url_descriptor.h new file mode 100644 index 000000000..7dee33e7c --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/url_descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/descriptor/webusb_platform_descriptor.cpp b/ion/src/device/bootloader/usb/stack/descriptor/webusb_platform_descriptor.cpp new file mode 100644 index 000000000..d859f5927 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/webusb_platform_descriptor.cpp @@ -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); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/descriptor/webusb_platform_descriptor.h b/ion/src/device/bootloader/usb/stack/descriptor/webusb_platform_descriptor.h new file mode 100644 index 000000000..549bcdae2 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/descriptor/webusb_platform_descriptor.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/device.cpp b/ion/src/device/bootloader/usb/stack/device.cpp new file mode 100644 index 000000000..294b647e1 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/device.cpp @@ -0,0 +1,168 @@ +#include "device.h" +#include +#include +#include + +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; +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/device.h b/ion/src/device/bootloader/usb/stack/device.h new file mode 100644 index 000000000..04f907108 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/device.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/endpoint0.cpp b/ion/src/device/bootloader/usb/stack/endpoint0.cpp new file mode 100644 index 000000000..32121076f --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/endpoint0.cpp @@ -0,0 +1,348 @@ +#include "endpoint0.h" +#include +#include +#include "device.h" +#include "interface.h" +#include "request_recipient.h" +#include + +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(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(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(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; +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/endpoint0.h b/ion/src/device/bootloader/usb/stack/endpoint0.h new file mode 100644 index 000000000..42d39dbef --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/endpoint0.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/interface.cpp b/ion/src/device/bootloader/usb/stack/interface.cpp new file mode 100644 index 000000000..835fe2cfb --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/interface.cpp @@ -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; +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/interface.h b/ion/src/device/bootloader/usb/stack/interface.h new file mode 100644 index 000000000..d1a98a16c --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/interface.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/request_recipient.cpp b/ion/src/device/bootloader/usb/stack/request_recipient.cpp new file mode 100644 index 000000000..dd781cfa1 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/request_recipient.cpp @@ -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; +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/request_recipient.h b/ion/src/device/bootloader/usb/stack/request_recipient.h new file mode 100644 index 000000000..8973fba8c --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/request_recipient.h @@ -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 diff --git a/ion/src/device/bootloader/usb/stack/setup_packet.cpp b/ion/src/device/bootloader/usb/stack/setup_packet.cpp new file mode 100644 index 000000000..77882d6a5 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/setup_packet.cpp @@ -0,0 +1,38 @@ +#include "setup_packet.h" +#include + +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; +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/setup_packet.h b/ion/src/device/bootloader/usb/stack/setup_packet.h new file mode 100644 index 000000000..d68ec32fb --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/setup_packet.h @@ -0,0 +1,65 @@ +#ifndef ION_DEVICE_SHARED_USB_SETUP_PACKET_H +#define ION_DEVICE_SHARED_USB_SETUP_PACKET_H + +#include + +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 diff --git a/ion/src/device/bootloader/usb/stack/streamable.cpp b/ion/src/device/bootloader/usb/stack/streamable.cpp new file mode 100644 index 000000000..f680439ce --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/streamable.cpp @@ -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(); +} + +} +} +} diff --git a/ion/src/device/bootloader/usb/stack/streamable.h b/ion/src/device/bootloader/usb/stack/streamable.h new file mode 100644 index 000000000..c6a79ad62 --- /dev/null +++ b/ion/src/device/bootloader/usb/stack/streamable.h @@ -0,0 +1,44 @@ +#ifndef ION_DEVICE_SHARED_USB_STREAMABLE_H +#define ION_DEVICE_SHARED_USB_STREAMABLE_H + +#include +#include + +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 + void push(T data) { + if (m_sizeLeft >= sizeof(T)) { + T * typedPointer = static_cast(m_pointer); + *typedPointer++ = data; // Actually push the data + m_pointer = static_cast(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 diff --git a/ion/src/device/flasher/main.cpp b/ion/src/device/flasher/main.cpp index 09f05e8ca..3dcfcd80d 100644 --- a/ion/src/device/flasher/main.cpp +++ b/ion/src/device/flasher/main.cpp @@ -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); } } diff --git a/ion/src/device/n0100/boot/rt0.cpp b/ion/src/device/n0100/boot/rt0.cpp index 4e2f22a56..454d41c7e 100644 --- a/ion/src/device/n0100/boot/rt0.cpp +++ b/ion/src/device/n0100/boot/rt0.cpp @@ -1,31 +1,31 @@ #include #include +#include #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include 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 diff --git a/ion/src/device/n0110/Makefile b/ion/src/device/n0110/Makefile index 11c3b224f..24b1a7204 100644 --- a/ion/src/device/n0110/Makefile +++ b/ion/src/device/n0110/Makefile @@ -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 \ diff --git a/ion/src/device/n0110/boot/rt0.cpp b/ion/src/device/n0110/boot/rt0.cpp index 4e2f22a56..454d41c7e 100644 --- a/ion/src/device/n0110/boot/rt0.cpp +++ b/ion/src/device/n0110/boot/rt0.cpp @@ -1,31 +1,31 @@ #include #include +#include #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include 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 diff --git a/ion/src/device/n0110/drivers/board.cpp b/ion/src/device/n0110/drivers/board.cpp index ae6b8ac06..7060da3e5 100644 --- a/ion/src/device/n0110/drivers/board.cpp +++ b/ion/src/device/n0110/drivers/board.cpp @@ -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); @@ -436,6 +434,8 @@ bool pcbVersionIsLocked() { return *reinterpret_cast(InternalFlash::Config::OTPLockAddress(k_pcbVersionOTPIndex)) == 0; } +void jumpToInternalBootloader() {} + } } } diff --git a/ion/src/device/n0110/drivers/config/clocks.h b/ion/src/device/n0110/drivers/config/clocks.h index 9043f71ca..0cc551cb0 100644 --- a/ion/src/device/n0110/drivers/config/clocks.h +++ b/ion/src/device/n0110/drivers/config/clocks.h @@ -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"); } } } diff --git a/ion/src/device/n0110/drivers/external_flash.cpp b/ion/src/device/n0110/drivers/external_flash.cpp index 612efc017..5e3844985 100644 --- a/ion/src/device/n0110/drivers/external_flash.cpp +++ b/ion/src/device/n0110/drivers/external_flash.cpp @@ -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(FlashAddressSpaceSize), reinterpret_cast(¤tStatusRegister2), 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(FlashAddressSpaceSize), reinterpret_cast(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(FlashAddressSpaceSize), reinterpret_cast(¤tStatusRegister2), 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(FlashAddressSpaceSize), reinterpret_cast(registers), sizeof(registers), sOperatingModes101); + wait(); + set_as_memory_mapped(); +} + void MassErase() { if (Config::NumberOfSectors == 0) { return; diff --git a/ion/src/device/n0110/flash.ld b/ion/src/device/n0110/flash.ld index dccce34bc..eb5578ac6 100644 --- a/ion/src/device/n0110/flash.ld +++ b/ion/src/device/n0110/flash.ld @@ -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). * diff --git a/ion/src/device/shared/Makefile b/ion/src/device/shared/Makefile index 2db3b7db4..440100526 100644 --- a/ion/src/device/shared/Makefile +++ b/ion/src/device/shared/Makefile @@ -1,5 +1,9 @@ include ion/src/device/shared/boot/Makefile +ifneq ($(filter bootloader, ${MAKECMDGOALS}), bootloader) include ion/src/device/shared/usb/Makefile +else +include ion/src/device/bootloader/usb/Makefile +endif include ion/src/device/shared/drivers/Makefile ion_device_src += $(addprefix ion/src/device/shared/, \ diff --git a/ion/src/device/shared/boot/Makefile b/ion/src/device/shared/boot/Makefile index 17dc4a4ad..66415b335 100644 --- a/ion/src/device/shared/boot/Makefile +++ b/ion/src/device/shared/boot/Makefile @@ -1,3 +1,9 @@ +ifeq ($(filter bootloader, ${MAKECMDGOALS}), bootloader) +ion_device_src += $(addprefix bootloader/boot/, \ + isr.c \ +) +else ion_device_src += $(addprefix ion/src/device/shared/boot/, \ isr.c \ ) +endif diff --git a/ion/src/device/shared/boot/isr.c b/ion/src/device/shared/boot/isr.c index 2ebde5c64..5c201aa67 100644 --- a/ion/src/device/shared/boot/isr.c +++ b/ion/src/device/shared/boot/isr.c @@ -18,11 +18,11 @@ ISR InitialisationVector[INITIALISATION_VECTOR_SIZE] = { (ISR)&_stack_start, // Stack start start, // Reset service routine, - nmi_abort, // NMI service routine, + 0, // NMI service routine, abort, // HardFault service routine, 0, // MemManage service routine, - bf_abort, // BusFault service routine, - uf_abort, // UsageFault service routine, + 0, // BusFault service routine, + 0, // UsageFault service routine, 0, 0, 0, 0, // Reserved 0, // SVCall service routine, 0, // DebugMonitor service routine, diff --git a/ion/src/device/shared/boot/isr.h b/ion/src/device/shared/boot/isr.h index 05812cd60..ca5becb13 100644 --- a/ion/src/device/shared/boot/isr.h +++ b/ion/src/device/shared/boot/isr.h @@ -5,14 +5,6 @@ extern "C" { #endif -void bf_abort(); -void uf_abort(); -void nmi_abort(); -void abort_init(); -void abort_core(const char *); -void abort_screen(const char *); -void abort_sleeping(); -void abort_economy(); void start(); void abort(); void isr_systick(); diff --git a/ion/src/device/shared/drivers/Makefile b/ion/src/device/shared/drivers/Makefile index 67bfb6ff5..2db043c5f 100644 --- a/ion/src/device/shared/drivers/Makefile +++ b/ion/src/device/shared/drivers/Makefile @@ -25,6 +25,5 @@ ion_device_src += $(addprefix ion/src/device/shared/drivers/, \ swd.cpp \ timing.cpp \ usb.cpp \ - usb_desc.cpp \ wakeup.cpp \ ) diff --git a/ion/src/device/shared/drivers/battery.cpp b/ion/src/device/shared/drivers/battery.cpp index 8686c2a97..c0f4877bd 100644 --- a/ion/src/device/shared/drivers/battery.cpp +++ b/ion/src/device/shared/drivers/battery.cpp @@ -4,10 +4,10 @@ /* To measure the battery voltage, we're using the internal ADC. The ADC works * by comparing the input voltage to a reference voltage. The only fixed voltage - * we have around is 2.8V, so that's the one we're using as a reference. However, + * we have around is 2.8V, so that's the one we're using as a refrence. However, * and ADC can only measure voltage that is lower than the reference voltage. So * we need to use a voltage divider before sampling Vbat. - * To avoid draining the battery, we're using a high-impedance voltage divider, + * To avoid draining the battery, we're using an high-impedence voltage divider, * so we need to be careful when sampling the ADC. See AN2834 for more info. */ diff --git a/ion/src/device/shared/drivers/board.h b/ion/src/device/shared/drivers/board.h index 8d1d1bbb6..ff6e88ac8 100644 --- a/ion/src/device/shared/drivers/board.h +++ b/ion/src/device/shared/drivers/board.h @@ -40,6 +40,7 @@ void writePCBVersion(PCBVersion version); void lockPCBVersion(); bool pcbVersionIsLocked(); +void jumpToInternalBootloader(); } } } diff --git a/ion/src/device/shared/drivers/exam_mode.cpp b/ion/src/device/shared/drivers/exam_mode.cpp index 0f319bc0d..51fd7be06 100644 --- a/ion/src/device/shared/drivers/exam_mode.cpp +++ b/ion/src/device/shared/drivers/exam_mode.cpp @@ -51,28 +51,28 @@ size_t numberOfBitsAfterLeadingZeroes(int i) { } uint8_t * SignificantExamModeAddress() { - uint32_t * persistence_start_32 = (uint32_t *)&_exam_mode_buffer_start; - uint32_t * persistence_end_32 = (uint32_t *)&_exam_mode_buffer_end; - assert((persistence_end_32 - persistence_start_32) % 4 == 0); - while (persistence_start_32 < persistence_end_32 && *persistence_start_32 == 0x0) { + uint32_t * persitence_start_32 = (uint32_t *)&_exam_mode_buffer_start; + uint32_t * persitence_end_32 = (uint32_t *)&_exam_mode_buffer_end; + 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 - persistence_start_32++; + persitence_start_32++; } - uint8_t * persistence_start_8 = (uint8_t *)persistence_start_32; - uint8_t * persistence_end_8 = (uint8_t *)persistence_end_32; - while (persistence_start_8 < persistence_end_8 && *persistence_start_8 == 0x0) { + 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 - persistence_start_8++; + persitence_start_8++; } - if (persistence_start_8 == persistence_end_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 - || (persistence_start_8 + 1 == persistence_end_8 && *persistence_start_8 == 1)) { + || (persitence_start_8 + 1 == persitence_end_8 && *persitence_start_8 == 1)) { assert(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_buffer_start) >= 0); Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress((uint32_t)&_exam_mode_buffer_start)); return (uint8_t *)&_exam_mode_buffer_start; } - return persistence_start_8; + return persitence_start_8; } uint8_t FetchExamMode() { diff --git a/ion/src/device/shared/drivers/external_flash.h b/ion/src/device/shared/drivers/external_flash.h index f2c1ddd6f..08ce49fb0 100644 --- a/ion/src/device/shared/drivers/external_flash.h +++ b/ion/src/device/shared/drivers/external_flash.h @@ -35,6 +35,9 @@ void EraseSector(int i); void WriteMemory(uint8_t * destination, const uint8_t * source, size_t length); void JDECid(uint8_t * manufacturerID, uint8_t * memoryType, uint8_t * capacityType); +void LockSlotA(); +void LockSlotB(); + static constexpr uint8_t NumberOfAddressBitsInChip = 23; // 2^23 Bytes = 8 MBytes static constexpr uint32_t FlashAddressSpaceSize = 1 << NumberOfAddressBitsInChip; diff --git a/ion/src/device/shared/drivers/flash.cpp b/ion/src/device/shared/drivers/flash.cpp index 692bd4df8..5fff806be 100644 --- a/ion/src/device/shared/drivers/flash.cpp +++ b/ion/src/device/shared/drivers/flash.cpp @@ -50,6 +50,38 @@ void WriteMemory(uint8_t * destination, uint8_t * source, size_t length) { } } +void DisableInternalProtection() { + InternalFlash::DisableProtection(); +} + +void EnableInternalProtection() { + InternalFlash::EnableProtection(); +} + +void SetInternalSectorProtection(int i, bool protect) { + InternalFlash::SetSectorProtection(i, protect); +} + +void EnableInternalSessionLock() { + InternalFlash::EnableSessionLock(); +} + +void EnableInternalFlashInterrupt() { + InternalFlash::EnableFlashInterrupt(); +} + +void ClearInternalFlashErrors() { + InternalFlash::ClearErrors(); +} + +void LockSlotA() { + ExternalFlash::LockSlotA(); +} + +void LockSlotB() { + ExternalFlash::LockSlotB(); +} + } } } diff --git a/ion/src/device/shared/drivers/flash.h b/ion/src/device/shared/drivers/flash.h index 4e61b1fa9..60714542b 100644 --- a/ion/src/device/shared/drivers/flash.h +++ b/ion/src/device/shared/drivers/flash.h @@ -15,6 +15,15 @@ void MassErase(); void EraseSector(int i); void WriteMemory(uint8_t * destination, uint8_t * source, size_t length); +void DisableInternalProtection(); +void EnableInternalProtection(); +void SetInternalSectorProtection(int i, bool protect); +void EnableInternalSessionLock(); // Will cause BUSERR when enabled +void EnableInternalFlashInterrupt(); +void ClearInternalFlashErrors(); +void LockSlotA(); +void LockSlotB(); + } } } diff --git a/ion/src/device/shared/drivers/internal_flash.cpp b/ion/src/device/shared/drivers/internal_flash.cpp index cd8fa60bd..136bfb842 100644 --- a/ion/src/device/shared/drivers/internal_flash.cpp +++ b/ion/src/device/shared/drivers/internal_flash.cpp @@ -31,6 +31,87 @@ static void open() { FLASH.CR()->setPSIZE(MemoryAccessWidth); } +static void open_protection() { + if (FLASH.OPTCR()->getLOCK()) { + FLASH.OPTKEYR()->set(0x08192A3B); + FLASH.OPTKEYR()->set(0x4C5D6E7F); + } +} + +static void close_protection() { + if(!FLASH.OPTCR()->getLOCK()) { + FLASH.OPTCR()->setLOCK(true); + } +} + +static void disable_protection_at(int i) { + if (!FLASH.OPTCR()->getLOCK()) { + switch (i) + { + case 0: + FLASH.OPTCR()->setnWRP0(true); + break; + case 1: + FLASH.OPTCR()->setnWRP1(true); + break; + case 2: + FLASH.OPTCR()->setnWRP2(true); + break; + case 3: + FLASH.OPTCR()->setnWRP3(true); + break; + case 4: + FLASH.OPTCR()->setnWRP4(true); + break; + case 5: + FLASH.OPTCR()->setnWRP5(true); + break; + case 6: + FLASH.OPTCR()->setnWRP6(true); + break; + case 7: + FLASH.OPTCR()->setnWRP7(true); + break; + default: + break; + } + } +} + +static void enable_protection_at(int i) { + if (!FLASH.OPTCR()->getLOCK()) { + switch (i) + { + case 0: + FLASH.OPTCR()->setnWRP0(false); + break; + case 1: + FLASH.OPTCR()->setnWRP1(false); + break; + case 2: + FLASH.OPTCR()->setnWRP2(false); + break; + case 3: + FLASH.OPTCR()->setnWRP3(false); + break; + case 4: + FLASH.OPTCR()->setnWRP4(false); + break; + case 5: + FLASH.OPTCR()->setnWRP5(false); + break; + case 6: + FLASH.OPTCR()->setnWRP6(false); + break; + case 7: + FLASH.OPTCR()->setnWRP7(false); + break; + default: + break; + } + } +} + static void close() { // Clear error flags class FLASH::SR sr(0); @@ -247,6 +328,52 @@ void WriteMemory(uint8_t * destination, uint8_t * source, size_t length) { close(); } +void EnableProtection() { + close_protection(); +} + +void DisableProtection() { + open_protection(); +} + +void SetSectorProtection(int i, bool protect) { + if (protect) { + enable_protection_at(i); + } else { + disable_protection_at(i); + } +} + +void EnableSessionLock() { + if (FLASH.OPTCR()->getLOCK()) { + // writing bullshit to the lock register to lock it until next core reset + FLASH.OPTKEYR()->set(0x00000000); + FLASH.OPTKEYR()->set(0xFFFFFFFF); + } +} + +void EnableFlashInterrupt() { + open(); + FLASH.CR()->setERRIE(true); + wait(); + FLASH.CR()->setEOPIE(true); + wait(); + FLASH.CR()->setRDERRIE(true); + wait(); + close(); +} + +void ClearErrors() { + class FLASH::SR sr(0); + // Error flags are cleared by writing 1 + sr.setERSERR(true); + sr.setPGPERR(true); + sr.setPGAERR(true); + sr.setWRPERR(true); + sr.setEOP(true); + FLASH.SR()->set(sr); +} + } } } diff --git a/ion/src/device/shared/drivers/internal_flash.h b/ion/src/device/shared/drivers/internal_flash.h index befe83b3e..818a0a414 100644 --- a/ion/src/device/shared/drivers/internal_flash.h +++ b/ion/src/device/shared/drivers/internal_flash.h @@ -15,6 +15,13 @@ void EraseSector(int i); void WriteMemory(uint8_t * destination, uint8_t * source, size_t length); +void EnableProtection(); +void DisableProtection(); +void SetSectorProtection(int i, bool protect); +void EnableSessionLock(); +void EnableFlashInterrupt(); +void ClearErrors(); + /* The Device is powered by a 2.8V LDO. This allows us to perform writes to the * Flash 32 bits at once. */ constexpr Regs::FLASH::CR::PSIZE MemoryAccessWidth = Regs::FLASH::CR::PSIZE::X32; diff --git a/ion/src/device/shared/ram.ld b/ion/src/device/shared/ram.ld index ef027f3e0..6cf176697 100644 --- a/ion/src/device/shared/ram.ld +++ b/ion/src/device/shared/ram.ld @@ -22,11 +22,7 @@ MEMORY { * object). Using a stack too small would result in some memory being * overwritten (for instance, vtables that live in the .rodata section). */ -/* The image is quite large too! - * So we put the stack to 18K so there's still space - * for our image, if not LD will throw an error. */ - -STACK_SIZE = 18K; +STACK_SIZE = 32K; SECTIONS { .isr_vector_table ORIGIN(RAM_BUFFER) : { diff --git a/ion/src/device/shared/regs/flash.h b/ion/src/device/shared/regs/flash.h index f17637710..4896e2b42 100644 --- a/ion/src/device/shared/regs/flash.h +++ b/ion/src/device/shared/regs/flash.h @@ -28,6 +28,9 @@ public: class KEYR : public Register32 { }; + class OPTKEYR : public Register32 { + }; + class CR : public Register32 { public: enum class PSIZE : uint8_t { @@ -42,6 +45,9 @@ public: REGS_FIELD(SNB, uint8_t, 6, 3); REGS_TYPE_FIELD(PSIZE, 9, 8); REGS_BOOL_FIELD(STRT, 16); + REGS_BOOL_FIELD(EOPIE, 24); + REGS_BOOL_FIELD(ERRIE, 25); + REGS_BOOL_FIELD(RDERRIE, 26) REGS_BOOL_FIELD(LOCK, 31); }; @@ -56,11 +62,26 @@ public: REGS_BOOL_FIELD(EOP, 0); }; + class OPTCR : public Register32 { + public: + REGS_BOOL_FIELD(nWRP0, 16); + REGS_BOOL_FIELD(nWRP1, 17); + REGS_BOOL_FIELD(nWRP2, 18); + REGS_BOOL_FIELD(nWRP3, 19); + REGS_BOOL_FIELD(nWRP4, 20); + REGS_BOOL_FIELD(nWRP5, 21); + REGS_BOOL_FIELD(nWRP6, 22); + REGS_BOOL_FIELD(nWRP7, 23); + REGS_BOOL_FIELD(LOCK, 0); + }; + constexpr FLASH() {}; REGS_REGISTER_AT(ACR, 0x00); REGS_REGISTER_AT(KEYR, 0x04); + REGS_REGISTER_AT(OPTKEYR, 0x08); REGS_REGISTER_AT(SR, 0x0C); REGS_REGISTER_AT(CR, 0x10); + REGS_REGISTER_AT(OPTCR, 0x14); private: constexpr uint32_t Base() const { return 0x40023C00; diff --git a/ion/src/device/shared/regs/rcc.h b/ion/src/device/shared/regs/rcc.h index 135508574..050512632 100644 --- a/ion/src/device/shared/regs/rcc.h +++ b/ion/src/device/shared/regs/rcc.h @@ -14,6 +14,12 @@ public: public: REGS_BOOL_FIELD(HSION, 0); REGS_BOOL_FIELD(HSIRDY, 1); + REGS_BOOL_FIELD(HSITRIM1, 3); + REGS_BOOL_FIELD(HSITRIM2, 4); + REGS_BOOL_FIELD(HSITRIM3, 5); + REGS_BOOL_FIELD(HSITRIM4, 6); + REGS_BOOL_FIELD(HSITRIM5, 7); + REGS_BOOL_FIELD(HSICAL, 8); REGS_BOOL_FIELD(HSEON, 16); REGS_BOOL_FIELD_R(HSERDY, 17); REGS_BOOL_FIELD(PLLON, 24); diff --git a/ion/src/device/shared/regs/syscfg.h b/ion/src/device/shared/regs/syscfg.h index 82dfddda6..e044a65f6 100644 --- a/ion/src/device/shared/regs/syscfg.h +++ b/ion/src/device/shared/regs/syscfg.h @@ -5,6 +5,8 @@ #include #include "gpio.h" +#define REGS_SYSCFG_CONFIG_F412 1 + namespace Ion { namespace Device { namespace Regs { diff --git a/ion/src/device/shared/usb/Makefile b/ion/src/device/shared/usb/Makefile index 02e931b6a..3ed92c4bd 100644 --- a/ion/src/device/shared/usb/Makefile +++ b/ion/src/device/shared/usb/Makefile @@ -93,3 +93,7 @@ $(BUILD_DIR)/ion/src/device/shared/usb/dfu.o: $(BUILD_DIR)/ion/src/device/shared ion_device_src += ion/src/device/shared/usb/dfu.cpp:-usbxip ion_device_src += ion/src/device/shared/usb/dfu_relocated.cpp:-usbxip + +ion_device_src += $(addprefix ion/src/device/shared/drivers/, \ + usb_desc.cpp \ +) diff --git a/ion/src/device/shared/usb/calculator.cpp b/ion/src/device/shared/usb/calculator.cpp index 6d0174390..73d6bf579 100644 --- a/ion/src/device/shared/usb/calculator.cpp +++ b/ion/src/device/shared/usb/calculator.cpp @@ -7,11 +7,11 @@ namespace Ion { namespace Device { namespace USB { -void Calculator::PollAndReset(bool exitWithKeyboard, bool unlock, int level) { +void Calculator::PollAndReset(bool exitWithKeyboard) { char serialNumber[Ion::Device::SerialNumber::Length+1]; Ion::Device::SerialNumber::copy(serialNumber); Calculator c(serialNumber); - + /* 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; @@ -19,10 +19,6 @@ void Calculator::PollAndReset(bool exitWithKeyboard, bool unlock, int level) { uint8_t exitKeyColumn = Ion::Device::Keyboard::columnForKey(exitKey); Ion::Device::Keyboard::activateRow(exitKeyRow); - c.m_dfuInterface.setLevel(level); - if (unlock) { - c.m_dfuInterface.unlockDfu(); - } while (!(exitWithKeyboard && !c.isErasingAndWriting() && Ion::Device::Keyboard::columnIsActive(exitKeyColumn)) && Ion::USB::isPlugged() && diff --git a/ion/src/device/shared/usb/calculator.h b/ion/src/device/shared/usb/calculator.h index a2a3581ea..308beb66b 100644 --- a/ion/src/device/shared/usb/calculator.h +++ b/ion/src/device/shared/usb/calculator.h @@ -26,7 +26,7 @@ namespace USB { class Calculator : public Device { public: - static void PollAndReset(bool exitWithKeyboard, bool unlocked, int level) + static void PollAndReset(bool exitWithKeyboard) __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 @@ -93,7 +93,7 @@ public: &m_webUSBPlatformDescriptor), m_languageStringDescriptor(), m_manufacturerStringDescriptor("NumWorks"), - m_productStringDescriptor("NumWorks Calculator"), + m_productStringDescriptor("Upsilon Calculator"), m_serialNumberStringDescriptor(serialNumber), m_interfaceStringDescriptor(stringDescriptor()), //m_interfaceStringDescriptor("@SRAM/0x20000000/01*256Ke"), diff --git a/ion/src/device/shared/usb/dfu_interface.cpp b/ion/src/device/shared/usb/dfu_interface.cpp index 836153d4a..2b56a2ebc 100644 --- a/ion/src/device/shared/usb/dfu_interface.cpp +++ b/ion/src/device/shared/usb/dfu_interface.cpp @@ -1,15 +1,7 @@ - #include "dfu_interface.h" - -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include namespace Ion { namespace Device { @@ -17,7 +9,7 @@ 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 { +void DFUInterface::StatusData::push(Channel * c) const { c->push(m_bStatus); c->push(m_bwPollTimeout[2]); c->push(m_bwPollTimeout[1]); @@ -26,20 +18,20 @@ void DFUInterface::StatusData::push(Channel *c) const { c->push(m_iString); } -void DFUInterface::StateData::push(Channel *c) const { +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) { +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: + case (uint8_t) DFUDownloadCommand::SetAddressPointer: setAddressPointerCommand(request, transferBuffer, *transferBufferLength); return; - case (uint8_t)DFUDownloadCommand::Erase: + case (uint8_t) DFUDownloadCommand::Erase: eraseCommand(transferBuffer, *transferBufferLength); return; default: @@ -63,17 +55,17 @@ void DFUInterface::wholeDataReceivedCallback(SetupPacket *request, uint8_t *tran } } -void DFUInterface::wholeDataSentCallback(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength) { - if (request->bRequest() == (uint8_t)DFURequest::GetStatus) { +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. */ + * "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(); @@ -89,34 +81,31 @@ void DFUInterface::wholeDataSentCallback(SetupPacket *request, uint8_t *transfer } } -bool DFUInterface::processSetupInRequest(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) { +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: + case (uint8_t) DFURequest::Detach: m_device->detach(); return true; - case (uint8_t)DFURequest::Download: + case (uint8_t) DFURequest::Download: return processDownloadRequest(request->wLength(), transferBufferLength); - case (uint8_t)DFURequest::Upload: + case (uint8_t) DFURequest::Upload: return processUploadRequest(request, transferBuffer, transferBufferLength, transferBufferMaxLength); - case (uint8_t)DFURequest::GetStatus: + case (uint8_t) DFURequest::GetStatus: return getStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength); - case (uint8_t)DFURequest::ClearStatus: + case (uint8_t) DFURequest::ClearStatus: return clearStatus(request, transferBuffer, transferBufferLength, transferBufferMaxLength); - case (uint8_t)DFURequest::GetState: + case (uint8_t) DFURequest::GetState: return getState(transferBuffer, transferBufferLength, transferBufferMaxLength); - case (uint8_t)DFURequest::Abort: + case (uint8_t) DFURequest::Abort: return dfuAbort(transferBufferLength); - case (uint8_t)DFURequest::Unlock: - m_dfuUnlocked = true; - return true; } return false; } -bool DFUInterface::processDownloadRequest(uint16_t wLength, uint16_t *transferBufferLength) { +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; @@ -134,8 +123,8 @@ bool DFUInterface::processDownloadRequest(uint16_t wLength, uint16_t *transferBu 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) { +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; } @@ -145,7 +134,7 @@ bool DFUInterface::processUploadRequest(SetupPacket *request, uint8_t *transferB * the command codes for : * Get command / Set Address Pointer / Erase / Read Unprotect * We no not need it for now. */ - return false; + return false; } else if (request->wValue() == 1) { m_ep0->stallTransaction(); return false; @@ -164,7 +153,7 @@ bool DFUInterface::processUploadRequest(SetupPacket *request, uint8_t *transferB return true; } -void DFUInterface::setAddressPointerCommand(SetupPacket *request, uint8_t *transferBuffer, uint16_t transferBufferLength) { +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] @@ -186,7 +175,7 @@ void DFUInterface::changeAddressPointerIfNeeded() { m_status = Status::OK; } -void DFUInterface::eraseCommand(uint8_t *transferBuffer, uint16_t transferBufferLength) { +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; @@ -200,12 +189,12 @@ void DFUInterface::eraseCommand(uint8_t *transferBuffer, uint16_t transferBuffer // Sector erase assert(transferBufferLength == 5); - m_eraseAddress = transferBuffer[1] + uint32_t eraseAddress = transferBuffer[1] + (transferBuffer[2] << 8) + (transferBuffer[3] << 16) + (transferBuffer[4] << 24); - m_erasePage = Flash::SectorAtAddress(m_eraseAddress); + m_erasePage = Flash::SectorAtAddress(eraseAddress); if (m_erasePage < 0) { // Unrecognized sector m_state = State::dfuERROR; @@ -213,26 +202,25 @@ void DFUInterface::eraseCommand(uint8_t *transferBuffer, uint16_t transferBuffer } } + void DFUInterface::eraseMemoryIfNeeded() { if (m_erasePage < 0) { + // There was no erase waiting. return; } willErase(); - - #if 0 // We don't erase now the flash memory to avoid crash if writing is refused if (m_erasePage == Flash::TotalNumberOfSectors()) { Flash::MassErase(); + } else { + Flash::EraseSector(m_erasePage); } - #endif - if ((m_eraseAddress >= k_ExternalBorderAddress && m_eraseAddress < ExternalFlash::Config::EndAddress) || m_dfuUnlocked) { - int32_t order = Flash::SectorAtAddress(m_eraseAddress); - Flash::EraseSector(order); - } + /* 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; - m_erasePage = -1; } void DFUInterface::writeOnMemory() { @@ -240,90 +228,7 @@ void DFUInterface::writeOnMemory() { // Write on SRAM // FIXME We should check that we are not overriding the current instructions. memcpy((void *)m_writeAddress, m_largeBuffer, m_largeBufferLength); - resetFlashParameters(); // We are writing in SRAM, so we can reset flash parameters } else if (Flash::SectorAtAddress(m_writeAddress) >= 0) { - if (m_dfuLevel == 2) { // We don't accept update - m_largeBufferLength = 0; - m_state = State::dfuERROR; - m_status = Status::errWRITE; - return; - } - - int currentMemoryType; // Detection of the current memory type (Internal or External) - - if (m_writeAddress >= InternalFlash::Config::StartAddress && m_writeAddress <= InternalFlash::Config::EndAddress) { - // We are writing in Internal where live the internal recovery (it's the most sensitive memory type) - if (m_isInternalLocked && !m_dfuUnlocked) { - // We have to check if external was written in order to - // prevent recovery mode loop or the necessity to activate STM bootloader (which is like a superuser mode) - // Nevertheless, unlike NumWorks, we don't forbid its access. - m_largeBufferLength = 0; - m_state = State::dfuERROR; - m_status = Status::errTARGET; - leaveDFUAndReset(false); - return; - } - - currentMemoryType = 0; - - // If the protection is activated, - // we check the internal magic code in order to prevent the NumWorks' Bootloader flash - - if (m_isFirstInternalPacket && !m_dfuUnlocked) { - for (int i = 0; i < 4; i++) { - if (k_omegaMagic[i] != m_largeBuffer[k_internalMagicAddress + i]) { - m_largeBufferLength = 0; - m_state = State::dfuERROR; - m_status = Status::errVERIFY; - return; - } - } - // We only check the first packet because there is some predictable data in there - m_isFirstInternalPacket = false; - } - } else { - - currentMemoryType = 1; - // We are writing in the external part where live the users apps. It's not a sensitive memory, - // but we check it in Upsilon Mode to ensure compatibility between the internal and the external. - if (m_writeAddress < k_ExternalBorderAddress && m_isFirstExternalPacket && m_dfuLevel == 0 && - !m_dfuUnlocked) { - // We skip any data verification if the user is writing in the Optionals Applications part in the - // external (Externals Apps) - for (int i = 0; i < 4; i++) { - if (k_externalUpsilonMagic[i] != m_largeBuffer[k_externalMagicAddress + i]) { - m_largeBufferLength = 0; - leaveDFUAndReset(false); - return; - } - m_largeBuffer[k_externalMagicAddress + i] = 0; - } - } - // We only check the first packet because there is some predictable data in there, - // and we unlock the internal memory - m_isFirstExternalPacket = false; - m_isInternalLocked = false; - } - - // We check if we changed the memory type where we are writing from last time. - if (m_lastMemoryType >= 0 && currentMemoryType != m_lastMemoryType) { - m_lastMemoryType = -1; - } - - m_erasePage = Flash::SectorAtAddress(m_writeAddress); - - // We check if the Sector where we are writing was not already erased and if not, we erase it. - if ((m_lastMemoryType < 0 || m_erasePage != m_lastPageErased) && - m_writeAddress < k_ExternalBorderAddress && !m_dfuUnlocked) { - Flash::EraseSector(m_erasePage); - m_lastMemoryType = currentMemoryType; - } - - m_lastPageErased = m_erasePage; - m_erasePage = -1; - - // We wait a little before writing in order to prevent some memory error. - Ion::Timing::msleep(1); Flash::WriteMemory(reinterpret_cast(m_writeAddress), m_largeBuffer, m_largeBufferLength); } else { // Invalid write address @@ -332,6 +237,7 @@ void DFUInterface::writeOnMemory() { m_status = Status::errTARGET; return; } + // Reset the buffer length m_largeBufferLength = 0; // Change the interface state and status @@ -339,7 +245,8 @@ void DFUInterface::writeOnMemory() { m_status = Status::OK; } -bool DFUInterface::getStatus(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) { + +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; @@ -351,30 +258,26 @@ bool DFUInterface::getStatus(SetupPacket *request, uint8_t *transferBuffer, uint return true; } -bool DFUInterface::clearStatus(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) { +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) { +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) { +bool DFUInterface::dfuAbort(uint16_t * transferBufferLength) { m_status = Status::OK; m_state = State::dfuIDLE; *transferBufferLength = 0; return true; } -void DFUInterface::leaveDFUAndReset(bool do_reset) { - resetFlashParameters(); - m_isInternalLocked = true; - m_isFirstInternalPacket = true; - m_isFirstExternalPacket = true; - m_device->setResetOnDisconnect(do_reset); +void DFUInterface::leaveDFUAndReset() { + m_device->setResetOnDisconnect(true); m_device->detach(); } diff --git a/ion/src/device/shared/usb/dfu_interface.h b/ion/src/device/shared/usb/dfu_interface.h index 44ccdb558..762243f06 100644 --- a/ion/src/device/shared/usb/dfu_interface.h +++ b/ion/src/device/shared/usb/dfu_interface.h @@ -8,20 +8,15 @@ #include "stack/endpoint0.h" #include "stack/setup_packet.h" #include "stack/streamable.h" -#include -#include -namespace Ion -{ -namespace Device -{ -namespace USB -{ +namespace Ion { +namespace Device { +namespace USB { class DFUInterface : public Interface { public: - DFUInterface(Device *device, Endpoint0 *ep0, uint8_t bInterfaceAlternateSetting) : + DFUInterface(Device * device, Endpoint0 * ep0, uint8_t bInterfaceAlternateSetting) : Interface(ep0), m_device(device), m_status(Status::OK), @@ -32,25 +27,14 @@ public: m_largeBuffer{0}, m_largeBufferLength(0), m_writeAddress(0), - m_eraseAddress(0), m_bInterfaceAlternateSetting(bInterfaceAlternateSetting), - m_isErasingAndWriting(false), - m_isFirstInternalPacket(true), - m_isInternalLocked(true), - m_isFirstExternalPacket(true), - m_lastMemoryType(-1), - m_lastPageErased(-1), - m_dfuUnlocked(false), - m_dfuLevel(0) { + m_isErasingAndWriting(false) + { } - 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; + 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 unlockDfu() { m_dfuUnlocked = true; }; - void setLevel(uint8_t lvl) { m_dfuLevel = lvl; } protected: void setActiveInterfaceAlternative(uint8_t interfaceAlternativeIndex) override { @@ -59,7 +43,7 @@ protected: uint8_t getActiveInterfaceAlternative() override { return m_bInterfaceAlternateSetting; } - bool processSetupInRequest(SetupPacket *request, uint8_t *transferBuffer, uint16_t *transferBufferLength, uint16_t transferBufferMaxLength) override; + bool processSetupInRequest(SetupPacket * request, uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t transferBufferMaxLength) override; private: // DFU Request Codes @@ -70,8 +54,7 @@ private: GetStatus = 3, ClearStatus = 4, GetState = 5, - Abort = 6, - Unlock = 11 + Abort = 6 }; // DFU Download Command Codes @@ -102,97 +85,82 @@ private: }; enum class State : uint8_t { - appIDLE = 0, - appDETACH = 1, - dfuIDLE = 2, - dfuDNLOADSYNC = 3, - dfuDNBUSY = 4, - dfuDNLOADIDLE = 5, - dfuMANIFESTSYNC = 6, - dfuMANIFEST = 7, + appIDLE = 0, + appDETACH = 1, + dfuIDLE = 2, + dfuDNLOADSYNC = 3, + dfuDNBUSY = 4, + dfuDNLOADIDLE = 5, + dfuMANIFESTSYNC = 6, + dfuMANIFEST = 7, dfuMANIFESTWAITRESET = 8, - dfuUPLOADIDLE = 9, - dfuERROR = 10 + dfuUPLOADIDLE = 9, + dfuERROR = 10 }; class StatusData : public Streamable { public: - StatusData(Status status, State state, uint32_t pollTimeout = 10) : - /* 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), + 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; + void push(Channel * c) const override; private: - uint8_t m_bStatus; // Status resulting from the execution of the most recent request + 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_bState; // State of the device immediately following transmission of this response uint8_t m_iString; }; - class StateData : public Streamable - { + class StateData : public Streamable { public: - StateData(State state) : m_bState((uint8_t) state) {} + StateData(State state) : m_bState((uint8_t)state) {} protected: - void push(Channel *c) const override; + 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; - constexpr static uint32_t k_ExternalBorderAddress = 0x90200000; - - const static int k_internalMagicAddress = 0x1C4; - constexpr static int k_externalMagicAddress = 0x44f; - constexpr static uint8_t k_omegaMagic[4] = {0xF0, 0x0D, 0xC0, 0xDE}; - // TODO maybe do: add seperated upsilon magic (k_upsilonMagic) - constexpr static uint8_t k_externalUpsilonMagic[4] = {0x32, 0x30, 0x30, 0x36}; + * 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); + 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 setAddressPointerCommand(SetupPacket * request, uint8_t * transferBuffer, uint16_t transferBufferLength); void changeAddressPointerIfNeeded(); // Access memory - void eraseCommand(uint8_t *transferBuffer, uint16_t transferBufferLength); + void eraseCommand(uint8_t * transferBuffer, uint16_t transferBufferLength); void eraseMemoryIfNeeded(); - void eraseMemoryIfNeededWithoutErasingAtAll(); 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); + 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); + bool getState(uint8_t * transferBuffer, uint16_t * transferBufferLength, uint16_t maxSize); // Abort - bool dfuAbort(uint16_t *transferBufferLength); + bool dfuAbort(uint16_t * transferBufferLength); // Leave DFU - void leaveDFUAndReset(bool do_reset = true); - + 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 resetFlashParameters() { - m_lastMemoryType = -1; - m_lastPageErased = -1; - } - Device *m_device; + Device * m_device; Status m_status; State m_state; uint32_t m_addressPointer; @@ -201,16 +169,8 @@ private: uint8_t m_largeBuffer[Endpoint0::MaxTransferSize]; uint16_t m_largeBufferLength; uint32_t m_writeAddress; - uint32_t m_eraseAddress; uint8_t m_bInterfaceAlternateSetting; bool m_isErasingAndWriting; - bool m_isFirstInternalPacket; - bool m_isInternalLocked; - bool m_isFirstExternalPacket; - uint8_t m_lastMemoryType; // -1: None; 0: internal; 1: external - uint8_t m_lastPageErased; // -1 default value - bool m_dfuUnlocked; - uint8_t m_dfuLevel; // 0: Upsilon only, 1: Omega-forked only, 2: No update }; } diff --git a/ion/src/device/shared/usb/dfu_relocated.cpp b/ion/src/device/shared/usb/dfu_relocated.cpp index 77433ee4c..72b9b61be 100644 --- a/ion/src/device/shared/usb/dfu_relocated.cpp +++ b/ion/src/device/shared/usb/dfu_relocated.cpp @@ -12,9 +12,9 @@ extern char _dfu_bootloader_flash_end; namespace Ion { namespace USB { -typedef void (*PollFunctionPointer)(bool exitWithKeyboard, bool unlocked, int level); +typedef void (*PollFunctionPointer)(bool exitWithKeyboard, void * data); -void DFU(bool exitWithKeyboard, bool unlocked, int level) { +void DFU(bool exitWithKeyboard, void * data) { Ion::updateSlotInfo(); /* DFU transfers can serve two purposes: @@ -76,7 +76,7 @@ void DFU(bool exitWithKeyboard, bool unlocked, int level) { * add-symbol-file ion/src/device/usb/dfu.elf 0x20038000 */ - dfu_bootloader_entry(exitWithKeyboard, unlocked, level); + dfu_bootloader_entry(exitWithKeyboard, data); /* 5- Restore interrupts */ Device::Timing::init(); diff --git a/ion/src/device/shared/usb/dfu_xip.cpp b/ion/src/device/shared/usb/dfu_xip.cpp index b94a77bd5..1a223d4f5 100644 --- a/ion/src/device/shared/usb/dfu_xip.cpp +++ b/ion/src/device/shared/usb/dfu_xip.cpp @@ -4,9 +4,9 @@ namespace Ion { namespace USB { -void DFU(bool exitWithKeyboard, bool unlocked, int level) { +void DFU(bool exitWithKeyboard, void * data) { Ion::updateSlotInfo(); - Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard, unlocked, level); + Ion::Device::USB::Calculator::PollAndReset(exitWithKeyboard); } } diff --git a/ion/src/shared/dummy/usb.cpp b/ion/src/shared/dummy/usb.cpp index 6a7fc8e19..904b056bc 100644 --- a/ion/src/shared/dummy/usb.cpp +++ b/ion/src/shared/dummy/usb.cpp @@ -15,7 +15,7 @@ bool isEnumerated() { void clearEnumerationInterrupt() { } -void DFU(bool, bool, int) { +void DFU(bool, void*) { } void enable() { diff --git a/ion/src/simulator/3ds/driver/usb.cpp b/ion/src/simulator/3ds/driver/usb.cpp index 17cea3ce7..27d63274c 100644 --- a/ion/src/simulator/3ds/driver/usb.cpp +++ b/ion/src/simulator/3ds/driver/usb.cpp @@ -13,7 +13,7 @@ bool Ion::USB::isEnumerated() { void Ion::USB::clearEnumerationInterrupt() { } -void Ion::USB::DFU(bool, bool, int) { +void Ion::USB::DFU(bool, void *) { } void Ion::USB::enable() { diff --git a/liba/Makefile b/liba/Makefile index 639bf196d..98cf53848 100644 --- a/liba/Makefile +++ b/liba/Makefile @@ -24,6 +24,7 @@ liba_src += $(addprefix liba/src/, \ strlcpy.c \ strlen.c \ external/sqlite/mem5.c \ + itoa.c \ ) liba_src += $(addprefix liba/src/external/openbsd/, \ diff --git a/liba/include/stdlib.h b/liba/include/stdlib.h index 6c27b3d6e..7bac0e2fa 100644 --- a/liba/include/stdlib.h +++ b/liba/include/stdlib.h @@ -10,6 +10,7 @@ void free(void *ptr); void * malloc(size_t size); void * realloc(void *ptr, size_t size); void * calloc(size_t count, size_t size); +char * itoa(int value, char *str, int base); void abort(void) __attribute__((noreturn)); diff --git a/liba/src/itoa.c b/liba/src/itoa.c new file mode 100644 index 000000000..25c5a61bf --- /dev/null +++ b/liba/src/itoa.c @@ -0,0 +1,60 @@ +#include +#include + +// 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* 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); +} diff --git a/themes/icons.json b/themes/icons.json index 953bca5cf..514a92601 100644 --- a/themes/icons.json +++ b/themes/icons.json @@ -43,6 +43,5 @@ "apps/probability/images/student_icon.png" : "probability/student_icon.png", "apps/probability/images/uniform_icon.png" : "probability/uniform_icon.png", - "bootloader/cable.png": "bootloader/cable.png", "bootloader/computer.png": "bootloader/computer.png" } diff --git a/themes/themes/local/upsilon_light/bootloader/computer.png b/themes/themes/local/upsilon_light/bootloader/computer.png index d30a99af2..7768daf73 100644 Binary files a/themes/themes/local/upsilon_light/bootloader/computer.png and b/themes/themes/local/upsilon_light/bootloader/computer.png differ