mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[bootloader/storage] new bootloader and fix python issue
This commit is contained in:
@@ -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)))
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
129
bootloader/boot/isr.c
Normal 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
23
bootloader/boot/isr.h
Normal 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
146
bootloader/boot/rt0.cpp
Normal 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();
|
||||
}
|
||||
444
bootloader/drivers/board.cpp
Normal file
444
bootloader/drivers/board.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
64
bootloader/itoa.cpp
Normal 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
11
bootloader/itoa.h
Normal 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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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)();
|
||||
|
||||
@@ -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
60
bootloader/messages.h
Normal 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
130
bootloader/recovery.cpp
Normal 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
32
bootloader/recovery.h
Normal 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_
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
166
bootloader/slot_exam_mode.cpp
Normal file
166
bootloader/slot_exam_mode.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
47
bootloader/slot_exam_mode.h
Normal file
47
bootloader/slot_exam_mode.h
Normal 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
|
||||
@@ -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
39
bootloader/usb_data.cpp
Normal 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
56
bootloader/usb_data.h
Normal 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_
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user