mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[bootloader] Compatibility with Omega 2.0
This commit is contained in:
21
bootloader/Makefile
Normal file
21
bootloader/Makefile
Normal 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
82
bootloader/boot.cpp
Normal 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
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);
|
||||
__attribute__ ((noreturn)) static void boot();
|
||||
__attribute__ ((noreturn)) static void bootloader();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
83
bootloader/interface.cpp
Normal file
83
bootloader/interface.cpp
Normal 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
22
bootloader/interface.h
Normal 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
|
||||
9
bootloader/jump_to_firmware.s
Normal file
9
bootloader/jump_to_firmware.s
Normal 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
|
||||
25
bootloader/kernel_header.cpp
Normal file
25
bootloader/kernel_header.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
32
bootloader/kernel_header.h
Normal file
32
bootloader/kernel_header.h
Normal 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
42
bootloader/main.cpp
Normal 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
33
bootloader/slot.cpp
Normal 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
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
|
||||
42
bootloader/trampoline.cpp
Normal file
42
bootloader/trampoline.cpp
Normal 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
14
bootloader/trampoline.h
Normal 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
12
bootloader/usb_desc.cpp
Normal 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";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
33
bootloader/userland_header.cpp
Normal file
33
bootloader/userland_header.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
50
bootloader/userland_header.h
Normal file
50
bootloader/userland_header.h
Normal 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
|
||||
Reference in New Issue
Block a user