mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[bootloader] Added dual boot
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
|
||||
bootloader_src += $(addprefix bootloader/,\
|
||||
boot.cpp \
|
||||
main.cpp \
|
||||
kernel_header.cpp \
|
||||
userland_header.cpp \
|
||||
slot.cpp \
|
||||
interface.cpp \
|
||||
jump_to_firmware.s \
|
||||
trampoline.cpp \
|
||||
|
||||
52
bootloader/boot.cpp
Normal file
52
bootloader/boot.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <bootloader/boot.h>
|
||||
#include <bootloader/slot.h>
|
||||
#include <ion.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);
|
||||
}
|
||||
|
||||
[[ noreturn ]] void Boot::boot() {
|
||||
assert(mode() != BootMode::Unknown);
|
||||
|
||||
if (mode() == BootMode::SlotA)
|
||||
Slot::A().boot();
|
||||
else if (mode() == BootMode::SlotB)
|
||||
Slot::B().boot();
|
||||
|
||||
for(;;);
|
||||
}
|
||||
|
||||
[[ noreturn ]] void Boot::bootloader() {
|
||||
Bootloader::Interface::draw();
|
||||
for(;;) {
|
||||
Ion::USB::enable();
|
||||
while (!Ion::USB::isEnumerated());
|
||||
Ion::USB::DFU(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
28
bootloader/boot.h
Normal file
28
bootloader/boot.h
Normal 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);
|
||||
[[ noreturn ]] static void boot();
|
||||
[[ norteurn ]] static void bootloader();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2,7 +2,9 @@
|
||||
#include <assert.h>
|
||||
#include <ion.h>
|
||||
|
||||
#include "interface.h"
|
||||
#include <bootloader/interface.h>
|
||||
#include <bootloader/slot.h>
|
||||
#include <bootloader/boot.h>
|
||||
|
||||
#include "computer.h"
|
||||
#include "cable.h"
|
||||
@@ -42,6 +44,36 @@ void Interface::draw() {
|
||||
KDContext * ctx = KDIonContext::sharedContext();
|
||||
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()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
#include <bootloader/kernel_header.h>
|
||||
|
||||
extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void));
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
|
||||
const KernelHeader* s_kernelHeaderA = reinterpret_cast<const struct KernelHeader*>(0x90000000);
|
||||
const KernelHeader* s_kernelHeaderB = reinterpret_cast<const struct KernelHeader*>(0x90400000);
|
||||
|
||||
const char * KernelHeader::version() const {
|
||||
return m_version;
|
||||
}
|
||||
@@ -20,10 +14,12 @@ const bool KernelHeader::isValid() const {
|
||||
return m_header == Magic && m_footer == Magic;
|
||||
}
|
||||
|
||||
[[ noreturn ]] void KernelHeader::boot() const {
|
||||
jump_to_firmware(m_stackPointer, m_startPointer);
|
||||
for(;;);
|
||||
const uint32_t* KernelHeader::stackPointer() const {
|
||||
return m_stackPointer;
|
||||
}
|
||||
|
||||
const void(*KernelHeader::startPointer() const)() {
|
||||
return m_startPointer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ public:
|
||||
const char * version() const;
|
||||
const char * patchLevel() const;
|
||||
const bool isValid() const;
|
||||
[[ noreturn ]] void boot() const;
|
||||
|
||||
const uint32_t* stackPointer() const;
|
||||
const void(*startPointer() const)();
|
||||
|
||||
private:
|
||||
KernelHeader();
|
||||
@@ -25,9 +27,6 @@ private:
|
||||
const void(*m_startPointer)();
|
||||
};
|
||||
|
||||
extern const KernelHeader* s_kernelHeaderA;
|
||||
extern const KernelHeader* s_kernelHeaderB;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
|
||||
#include <ion.h>
|
||||
#include <bootloader/kernel_header.h>
|
||||
#include <assert.h>
|
||||
#include <ion/src/device/shared/drivers/board.h>
|
||||
|
||||
#include "interface.h"
|
||||
#include <bootloader/boot.h>
|
||||
|
||||
void ion_main(int argc, const char * const argv[]) {
|
||||
// Clear the screen
|
||||
@@ -12,18 +11,27 @@ void ion_main(int argc, const char * const argv[]) {
|
||||
// Initialize the backlight
|
||||
Ion::Backlight::init();
|
||||
|
||||
if (Bootloader::Boot::mode() == Bootloader::BootMode::Unknown)
|
||||
Bootloader::Boot::setMode(Bootloader::BootMode::SlotA);
|
||||
|
||||
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();
|
||||
|
||||
if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Four)) {
|
||||
Bootloader::Interface::draw();
|
||||
while (true) {
|
||||
Ion::USB::enable();
|
||||
while (!Ion::USB::isEnumerated()) {
|
||||
}
|
||||
Ion::USB::DFU(false);
|
||||
}
|
||||
Bootloader::Boot::bootloader();
|
||||
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::One)) {
|
||||
Bootloader::Boot::setMode(Bootloader::BootMode::SlotA);
|
||||
} else if (scan == Ion::Keyboard::State(Ion::Keyboard::Key::Two)) {
|
||||
Bootloader::Boot::setMode(Bootloader::BootMode::SlotB);
|
||||
}
|
||||
|
||||
Ion::Device::Board::bootloaderMPU();
|
||||
Bootloader::s_kernelHeaderA->boot();
|
||||
Bootloader::Boot::boot();
|
||||
}
|
||||
|
||||
28
bootloader/slot.cpp
Normal file
28
bootloader/slot.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include <bootloader/slot.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 {
|
||||
jump_to_firmware(kernelHeader()->stackPointer(), kernelHeader()->startPointer());
|
||||
for(;;);
|
||||
}
|
||||
|
||||
}
|
||||
33
bootloader/slot.h
Normal file
33
bootloader/slot.h
Normal 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
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <ion/src/device/shared/drivers/flash.h>
|
||||
#include <ion/src/device/n0110/drivers/power.h>
|
||||
|
||||
#include "trampoline.h"
|
||||
#include <bootloader/trampoline.h>
|
||||
|
||||
namespace Bootloader {
|
||||
|
||||
|
||||
@@ -17,4 +17,9 @@ const bool UserlandHeader::isOmega() const {
|
||||
return m_ohm_header == OmegaMagic && m_ohm_footer == OmegaMagic;
|
||||
}
|
||||
|
||||
|
||||
const char * UserlandHeader::omegaVersion() const {
|
||||
return m_omegaVersion;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ public:
|
||||
const char * version() const;
|
||||
const bool isValid() const;
|
||||
const bool isOmega() const;
|
||||
const char * omegaVersion() const;
|
||||
|
||||
private:
|
||||
UserlandHeader();
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
/* Same as flash.ld but everything is linked in internal flash */
|
||||
|
||||
MEMORY {
|
||||
INTERNAL_FLASH (rx) : ORIGIN = 0x00200000, LENGTH = 64K
|
||||
INTERNAL_FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
|
||||
SRAM (rw) : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
STACK_SIZE = 32K;
|
||||
TRAMPOLINES_OFFSET = 0xE000;
|
||||
FLASH_SECOND_SECTOR_OFFSET = 16K;
|
||||
FLASH_SECOND_SECTOR_SIZE = 16K;
|
||||
|
||||
SECTIONS {
|
||||
.isr_vector_table ORIGIN(INTERNAL_FLASH) : {
|
||||
@@ -30,6 +32,20 @@ SECTIONS {
|
||||
KEEP(*(.header))
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.rodata : {
|
||||
. = ALIGN(4);
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.exam_mode_buffer ORIGIN(INTERNAL_FLASH) + FLASH_SECOND_SECTOR_OFFSET : {
|
||||
_exam_mode_buffer_start = .;
|
||||
KEEP(*(.exam_mode_buffer))
|
||||
/* Note: We don't increment "." here, we set it. */
|
||||
. = ORIGIN(INTERNAL_FLASH) + FLASH_SECOND_SECTOR_OFFSET + FLASH_SECOND_SECTOR_SIZE;
|
||||
_exam_mode_buffer_end = .;
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.text : {
|
||||
. = ALIGN(4);
|
||||
*(.text)
|
||||
@@ -43,12 +59,6 @@ SECTIONS {
|
||||
_init_array_end = .;
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.rodata : {
|
||||
. = ALIGN(4);
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
} >INTERNAL_FLASH
|
||||
|
||||
.data : {
|
||||
/* The data section is written to Flash but linked as if it were in RAM.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user