[bootloader] Compatibility with Omega 2.0

This commit is contained in:
Laury
2022-03-17 20:05:20 +01:00
parent 19e5562228
commit 6dc31401fe
112 changed files with 4372 additions and 147 deletions

21
bootloader/Makefile Normal file
View File

@@ -0,0 +1,21 @@
bootloader_src += $(addprefix bootloader/,\
boot.cpp \
main.cpp \
kernel_header.cpp \
userland_header.cpp \
slot.cpp \
interface.cpp \
jump_to_firmware.s \
trampoline.cpp \
usb_desc.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)
$(eval $(call depends_on_image,bootloader/interface.cpp,$(bootloader_images)))

82
bootloader/boot.cpp Normal file
View File

@@ -0,0 +1,82 @@
#include <bootloader/boot.h>
#include <bootloader/slot.h>
#include <ion.h>
#include <ion/src/device/shared/drivers/reset.h>
#include <bootloader/interface.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;
}
void Boot::setMode(BootMode mode) {
BootMode currentMode = Boot::mode();
if (currentMode == mode)
return;
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);
}
__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();
}
}
// Achivement unlocked: How Did We Get Here?
bootloader();
}
__attribute__ ((noreturn)) void Boot::bootloader() {
for(;;) {
// Draw the interfaces and infos
Bootloader::Interface::draw();
// Enable USB
Ion::USB::enable();
// Wait for the device to be enumerated
do {
// 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();
}
} while (!Ion::USB::isEnumerated());
// Launch the DFU stack, allowing to press Back to quit and reset
Ion::USB::DFU(true);
}
}
}

28
bootloader/boot.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef BOOTLOADER_BOOT_H
#define BOOTLOADER_BOOT_H
#include <stdint.h>
namespace Bootloader {
enum BootMode: uint8_t {
SlotA = 0,
SlotB = 1,
// These modes exists so that you can launch the bootloader from a running slot
// They mean "Launch bootloader then go back to slot X"
SlotABootloader = 2,
SlotBBootloader = 3,
Unknown
};
class Boot {
public:
static BootMode mode();
static void setMode(BootMode mode);
__attribute__ ((noreturn)) static void boot();
__attribute__ ((noreturn)) static void bootloader();
};
}
#endif

83
bootloader/interface.cpp Normal file
View File

@@ -0,0 +1,83 @@
#include <assert.h>
#include <ion.h>
#include <bootloader/interface.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;
size_t size;
size_t pixelBufferSize;
if (image != nullptr) {
data = image->compressedPixelData();
size = image->compressedPixelDataSize();
pixelBufferSize = image->width() * image->height();
} else {
return;
}
KDColor pixelBuffer[4000];
assert(pixelBufferSize <= 4000);
assert(Ion::stackSafe()); // That's a VERY big buffer we're allocating on the stack
Ion::decompress(
data,
reinterpret_cast<uint8_t *>(pixelBuffer),
size,
pixelBufferSize * sizeof(KDColor)
);
KDRect bounds((320 - image->width()) / 2, offset, image->width(), image->height());
ctx->fillRectWithPixels(bounds, pixelBuffer, nullptr);
}
void Interface::draw() {
KDContext * ctx = KDIonContext::sharedContext();
ctx->fillRect(KDRect(0,0,320,240), KDColorBlack);
drawImage(ctx, ImageStore::Computer, 70);
drawImage(ctx, ImageStore::Cable, 172);
ctx->drawString("Slot A:", KDPoint(0, 0), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString("Slot B:", KDPoint(0, 13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString("Current:", KDPoint(0, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack);
if (Boot::mode() == BootMode::SlotA) {
ctx->drawString("Slot A", KDPoint(63, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack);
} else if (Boot::mode() == BootMode::SlotB) {
ctx->drawString("Slot B", KDPoint(63, 26), KDFont::SmallFont, KDColorWhite, KDColorBlack);
}
Slot slots[2] = {Slot::A(), Slot::B()};
for(uint8_t i = 0; i < 2; i++) {
Slot slot = slots[i];
if (slot.kernelHeader()->isValid() && slot.userlandHeader()->isValid()) {
if (slot.userlandHeader()->isOmega() && slot.userlandHeader()->isUpsilon()) {
ctx->drawString("Upsilon", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString(slot.userlandHeader()->upsilonVersion(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
} else if (slot.userlandHeader()->isOmega()) {
ctx->drawString("Omega", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString(slot.userlandHeader()->omegaVersion(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
} else {
ctx->drawString("Epsilon", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
ctx->drawString(slot.userlandHeader()->version(), KDPoint(112, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
}
ctx->drawString(slot.kernelHeader()->patchLevel(), KDPoint(168, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
} else {
ctx->drawString("Invalid", KDPoint(56, i*13), KDFont::SmallFont, KDColorWhite, KDColorBlack);
}
}
}
}

22
bootloader/interface.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef BOOTLOADER_INTERFACE
#define BOOTLOADER_INTERFACE
#include <stdint.h>
#include <kandinsky/context.h>
#include <escher/image.h>
namespace Bootloader {
class Interface {
private:
static void drawImage(KDContext* ctx, const Image* image, int offset);
public:
static void draw();
};
}
#endif

View File

@@ -0,0 +1,9 @@
.syntax unified
.section .text.jump_to_firmware
.align 2
.thumb
.global jump_to_firmware
jump_to_firmware:
msr msp, r0
bx r1

View File

@@ -0,0 +1,25 @@
#include <bootloader/kernel_header.h>
namespace Bootloader {
const char * KernelHeader::version() const {
return m_version;
}
const char * KernelHeader::patchLevel() const {
return m_patchLevel;
}
const bool KernelHeader::isValid() const {
return m_header == Magic && m_footer == Magic;
}
const uint32_t* KernelHeader::stackPointer() const {
return m_stackPointer;
}
const void(*KernelHeader::startPointer() const)() {
return m_startPointer;
}
}

View File

@@ -0,0 +1,32 @@
#ifndef BOOTLOADER_KERNEL_HEADER_H
#define BOOTLOADER_KERNEL_HEADER_H
#include <stdint.h>
namespace Bootloader {
class KernelHeader {
public:
const char * version() const;
const char * patchLevel() const;
const bool isValid() const;
const uint32_t* stackPointer() const;
const void(*startPointer() const)();
private:
KernelHeader();
constexpr static uint32_t Magic = 0xDEC00DF0;
const uint32_t m_unknown;
const uint32_t m_signature;
const uint32_t m_header;
const char m_version[8];
const char m_patchLevel[8];
const uint32_t m_footer;
const uint32_t* m_stackPointer;
const void(*m_startPointer)();
};
}
#endif

42
bootloader/main.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include <ion.h>
#include <assert.h>
#include <bootloader/boot.h>
__attribute__ ((noreturn)) void ion_main(int argc, const char * const argv[]) {
// Clear the screen
Ion::Display::pushRectUniform(KDRect(0,0,320,240), KDColorBlack);
// 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);
}
// 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();
}
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);
}
// Boot the firmware
Bootloader::Boot::boot();
}

33
bootloader/slot.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include <bootloader/slot.h>
#include <ion/src/device/shared/drivers/board.h>
extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void));
namespace Bootloader {
const Slot Slot::A() {
return Slot(0x90000000);
}
const Slot Slot::B() {
return Slot(0x90400000);
}
const KernelHeader* Slot::kernelHeader() const {
return m_kernelHeader;
}
const UserlandHeader* Slot::userlandHeader() const {
return m_userlandHeader;
}
[[ noreturn ]] void Slot::boot() const {
// Configure the MPU for the booted firmware
Ion::Device::Board::bootloaderMPU();
// Jump
jump_to_firmware(kernelHeader()->stackPointer(), kernelHeader()->startPointer());
for(;;);
}
}

33
bootloader/slot.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef BOOTLOADER_SLOT_H
#define BOOTLOADER_SLOT_H
#include <stdint.h>
#include "kernel_header.h"
#include "userland_header.h"
namespace Bootloader {
class Slot {
public:
Slot(uint32_t address) :
m_kernelHeader(reinterpret_cast<KernelHeader*>(address)),
m_userlandHeader(reinterpret_cast<UserlandHeader*>(address + 64 * 1024)) { }
const KernelHeader* kernelHeader() const;
const UserlandHeader* userlandHeader() const;
[[ noreturn ]] void boot() const;
static const Slot A();
static const Slot B();
private:
const KernelHeader* m_kernelHeader;
const UserlandHeader* m_userlandHeader;
};
}
#endif

42
bootloader/trampoline.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include <string.h>
#include <ion/src/device/shared/drivers/external_flash.h>
#include <ion/src/device/n0110/drivers/power.h>
#include <bootloader/trampoline.h>
#include <bootloader/boot.h>
namespace Bootloader {
void __attribute__((noinline)) suspend() {
Ion::Device::Power::internalFlashSuspend(true);
}
void* Trampolines[TRAMPOLINES_COUNT]
__attribute__((section(".trampolines_table")))
__attribute__((used))
= {
(void*) Bootloader::suspend, // Suspend
(void*) Ion::Device::ExternalFlash::EraseSector, // External erase
(void*) Ion::Device::ExternalFlash::WriteMemory, // External write
(void*) memcmp,
(void*) memcpy,
(void*) memmove,
(void*) memset,
(void*) strchr,
(void*) strcmp,
(void*) strlcat,
(void*) strlcpy,
(void*) strlen,
(void*) strncmp
};
void* CustomTrampolines[CUSTOM_TRAMPOLINES_COUNT]
__attribute__((section(".custom_trampolines_table")))
__attribute__((used))
= {
(void*) Bootloader::Boot::mode,
(void*) Bootloader::Boot::setMode
};
}

14
bootloader/trampoline.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef BOOTLOADER_TRAMPOLINE_H
#define BOOTLOADER_TRAMPOLINE_H
namespace Bootloader {
#define TRAMPOLINES_COUNT 13
extern void* Trampolines[TRAMPOLINES_COUNT];
#define CUSTOM_TRAMPOLINES_COUNT 2
extern void* CustomTrampolines[CUSTOM_TRAMPOLINES_COUNT];
}
#endif

12
bootloader/usb_desc.cpp Normal file
View File

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

View File

@@ -0,0 +1,33 @@
#include <bootloader/userland_header.h>
namespace Bootloader {
const UserlandHeader* s_UserlandHeaderA = reinterpret_cast<const struct UserlandHeader*>(0x90010000);
const UserlandHeader* s_UserlandHeaderB = reinterpret_cast<const struct UserlandHeader*>(0x90410000);
const char * UserlandHeader::version() const {
return m_expectedEpsilonVersion;
}
const bool UserlandHeader::isValid() const {
return m_header == Magic && m_footer == Magic;
}
const bool UserlandHeader::isOmega() const {
return m_omegaMagicHeader == OmegaMagic && m_omegaMagicFooter == OmegaMagic;
}
const char * UserlandHeader::omegaVersion() const {
return m_omegaVersion;
}
const bool UserlandHeader::isUpsilon() const {
return m_upsilonMagicHeader == UpsilonMagic && m_upsilonMagicFooter == UpsilonMagic;
}
const char * UserlandHeader::upsilonVersion() const {
return m_UpsilonVersion;
}
}

View File

@@ -0,0 +1,50 @@
#ifndef BOOTLOADER_USERLAND_HEADER_H
#define BOOTLOADER_USERLAND_HEADER_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
namespace Bootloader {
class UserlandHeader {
public:
const char * version() const;
const bool isValid() const;
const bool isOmega() const;
const char * omegaVersion() const;
const bool isUpsilon() const;
const char * upsilonVersion() const;
private:
UserlandHeader();
constexpr static uint32_t Magic = 0xDEC0EDFE;
constexpr static uint32_t OmegaMagic = 0xEFBEADDE;
constexpr static uint32_t UpsilonMagic = 0x55707369;
uint32_t m_header;
const char m_expectedEpsilonVersion[8];
void * m_storageAddressRAM;
size_t m_storageSizeRAM;
/* We store the range addresses of external apps memory because storing the
* size is complicated due to c++11 constexpr. */
uint32_t m_externalAppsFlashStart;
uint32_t m_externalAppsFlashEnd;
uint32_t m_externalAppsRAMStart;
uint32_t m_externalAppsRAMEnd;
uint32_t m_footer;
uint32_t m_omegaMagicHeader;
const char m_omegaVersion[16];
const volatile char m_username[16];
uint32_t m_omegaMagicFooter;
uint32_t m_upsilonMagicHeader;
const char m_UpsilonVersion[16];
uint32_t m_osType;
uint32_t m_upsilonMagicFooter;
};
extern const UserlandHeader* s_userlandHeaderA;
extern const UserlandHeader* s_userlandHeaderB;
}
#endif