[bootloader/storage] new bootloader and fix python issue

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

View File

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

View File

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

View File

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

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

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

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

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

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

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

View File

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

View File

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

View File

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

64
bootloader/itoa.cpp Normal file
View File

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

11
bootloader/itoa.h Normal file
View File

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

View File

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

View File

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

View File

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

60
bootloader/messages.h Normal file
View File

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

130
bootloader/recovery.cpp Normal file
View File

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

32
bootloader/recovery.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

39
bootloader/usb_data.cpp Normal file
View File

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

56
bootloader/usb_data.h Normal file
View File

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

View File

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

View File

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