[bootloader] Fix exam mode with Epsilon 19 and add support for Epsilon 20 (#302)

* [bootloader] Fix exam mode on Epsilon 19

* [bootloader] Fix comment indentation

* [bootloader] Restore comment indentation (even if GitHub tell that it isn't indented properly)

* [bootloader] Turn tabulations into spaces

* [bootloader] Mark Epsilon 19 as "safe to boot"

* [bootloader] Fix Epsilon 20 boot

* [bootloader] Chang version to 1.0.2
This commit is contained in:
Yaya-Cout
2022-12-03 18:14:43 +01:00
committed by GitHub
parent 08eb1aec99
commit 851ec2ab69
10 changed files with 196 additions and 141 deletions

2
.gitignore vendored
View File

@@ -8,3 +8,5 @@ epsilon.map
.gradle
.idea/
.vs
.cache/
compile_commands.json

View File

@@ -107,7 +107,7 @@ 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";
const char * min = "20.0.0";
int versionSum = Utility::versionSum(version, strlen(version));
int minimalVersionTrigger = Utility::versionSum(min, strlen(min));
if (versionSum >= minimalVersionTrigger) {

View File

@@ -57,7 +57,7 @@ public:
constexpr static const char * epsilonWarningTitle = "Epsilon Slot";
constexpr static const char * epsilonWarningMessage1 = "!! WARNING !! ";
constexpr static const char * epsilonWarningMessage2 = "This version of epsilon";
constexpr static const char * epsilonWarningMessage2 = "This version of Epsilon";
constexpr static const char * epsilonWarningMessage3 = "can lock the calculator.";
constexpr static const char * epsilonWarningMessage4 = "Proceed the boot ?";
constexpr static const char * epsilonWarningMessage5 = "EXE - Yes";
@@ -72,7 +72,7 @@ public:
constexpr static const char * aboutMessage4 = "and select the OS";
constexpr static const char * aboutMessage5 = "to boot.";
constexpr static const char * bootloaderVersion = "Version 1.0.1 - FREED0M.19";
constexpr static const char * bootloaderVersion = "Version 1.0.2 - FREED0M.20";
//USB NAMES
constexpr static const char * usbUpsilonBootloader = "Upsilon Bootloader";

View File

@@ -20,30 +20,29 @@ __attribute__ ((noreturn)) void ion_main(int argc, const char * const argv[]) {
bool isSlotA = Bootloader::Slot::isFullyValid(Bootloader::Slot::A());
if (isSlotA) {
Bootloader::ExamMode::ExamMode SlotAExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotAExamMode(!Bootloader::Slot::A().userlandHeader()->isOmega());
Bootloader::ExamMode::ExamMode SlotAExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotAExamMode(Bootloader::Slot::A().userlandHeader()->version());
if (SlotAExamMode != Bootloader::ExamMode::ExamMode::Off && SlotAExamMode != Bootloader::ExamMode::ExamMode::Unknown) {
// We boot the slot in exam_mode
Bootloader::Slot::A().boot();
}
}
}
bool isSlotB = Bootloader::Slot::isFullyValid(Bootloader::Slot::B());
if (isSlotB) {
Bootloader::ExamMode::ExamMode SlotBExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotBExamMode(!Bootloader::Slot::B().userlandHeader()->isOmega());
Bootloader::ExamMode::ExamMode SlotBExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotBExamMode(Bootloader::Slot::B().userlandHeader()->version());
if (SlotBExamMode != Bootloader::ExamMode::ExamMode::Off && SlotBExamMode != Bootloader::ExamMode::ExamMode::Unknown && isSlotB) {
// We boot the slot in exam_mode
Bootloader::Slot::B().boot();
}
}
// I have no idea if this will work, but if Pariss did a good job, it should
// I have no idea if this will work, but if Parisse did a good job, it should
bool isKhiSlot = Bootloader::Slot::isFullyValid(Bootloader::Slot::Khi());
if (isKhiSlot) {
Bootloader::ExamMode::ExamMode KhiExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotKhiExamMode();
Bootloader::ExamMode::ExamMode KhiExamMode = (Bootloader::ExamMode::ExamMode)Bootloader::ExamMode::SlotsExamMode::FetchSlotKhiExamMode(Bootloader::Slot::Khi().userlandHeader()->version());
if (KhiExamMode != Bootloader::ExamMode::ExamMode::Off && KhiExamMode != Bootloader::ExamMode::ExamMode::Unknown && isKhiSlot) {
// We boot the slot in exam_mode
Bootloader::Slot::Khi().boot();

View File

@@ -1,6 +1,7 @@
#include <bootloader/slots/slot.h>
#include <ion/src/device/shared/drivers/board.h>
#include <ion/src/device/shared/drivers/flash.h>
#include <ion/src/device/shared/drivers/external_flash.h>
#include <bootloader/boot.h>
extern "C" void jump_to_firmware(const uint32_t* stackPtr, const void(*startPtr)(void));
@@ -24,7 +25,13 @@ const KernelHeader* Slot::kernelHeader() const {
}
const UserlandHeader* Slot::userlandHeader() const {
return m_userlandHeader;
if (m_userlandHeader->isValid()) {
return m_userlandHeader;
} else if (m_userland2Header->isValid()) {
return m_userland2Header;
} else {
return m_userlandHeader;
}
}
[[ noreturn ]] void Slot::boot() const {
@@ -37,6 +44,24 @@ const UserlandHeader* Slot::userlandHeader() const {
Ion::Device::Flash::LockSlotA();
}
// Erase the bootloader integrated in slots in Epsilon 20
if (m_userland2Header->isValid()) {
if (m_address == 0x90000000) {
// Check if bootloader is present in slot A
if (*(uint32_t*)0x90010000 != 0xFFFFFFFF) {
// Erase bootloader in slot A
Ion::Device::ExternalFlash::EraseSector(9);
}
}
else if (m_address == 0x90400000) {
// Check if bootloader is present in slot B
if (*(uint32_t*)0x90410000 != 0xFFFFFFFF) {
// Erase bootloader in slot B
Ion::Device::ExternalFlash::EraseSector(73);
}
}
}
// Configure the MPU for the booted firmware
Ion::Device::Board::bootloaderMPU();

View File

@@ -14,7 +14,8 @@ public:
Slot(uint32_t address) :
m_kernelHeader(reinterpret_cast<KernelHeader*>(address)),
m_userlandHeader(reinterpret_cast<UserlandHeader*>(address + 64 * 1024)),
m_address(address) { }
m_userland2Header(reinterpret_cast<UserlandHeader*>(address + 128 * 1024)),
m_address(address) {}
const KernelHeader* kernelHeader() const;
const UserlandHeader* userlandHeader() const;
@@ -32,6 +33,7 @@ public:
private:
const KernelHeader* m_kernelHeader;
const UserlandHeader* m_userlandHeader;
const UserlandHeader* m_userland2Header;
const uint32_t m_address;
};

View File

@@ -40,88 +40,70 @@ size_t numberOfBitsAfterLeadingZeroes(int i) {
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 SlotsExamMode::FetchSlotExamMode(const char * version, const char * Slot) {
// Get start and end from version and slot
uint32_t start = 0;
uint32_t end = 0;
if (Slot == "A") {
// If version under 16 get old addresses
if (version[0] < '1' || (version[0] == '1' && version[1] < '6')) {
start = getSlotAStartExamAddress(0);
end = getSlotAEndExamAddress(0);
}
// Else get new addresses
else {
start = getSlotAStartExamAddress(1);
end = getSlotAEndExamAddress(1);
}
}
else if (Slot == "B") {
// If version under 16 get old
if (version[0] < '1' || (version[0] == '1' && version[1] < '6')) {
start = getSlotBStartExamAddress(0);
end = getSlotBEndExamAddress(0);
}
// Else get new
else {
start = getSlotBStartExamAddress(1);
end = getSlotBEndExamAddress(1);
}
} else if (Slot == "Khi") {
// We directly get the address of the Khi exam mode without checking the
// version, because on Khi, version is KhiCAS version, not the OS version
start = getSlotKhiStartExamAddress();
end = getSlotKhiEndExamAddress();
}
if (strcmp("15.9.0", version) >= 0) {
return examFetch15(start, end);
} else if (strcmp("16.9.0", version) > 0) {
return examFetch16(start, end);
}
else if (strcmp("19.0.0", version) > 0) {
return examFetch1718(start, end);
}
else {
return examFetch19(start, end);
}
}
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(const char* version) {
return FetchSlotExamMode(version, "A");
}
uint8_t * SignificantSlotKhiExamModeAddress() {
uint32_t * persitence_start_32 = (uint32_t *)SlotsExamMode::getSlotKhiStartExamAddress();
uint32_t * persitence_end_32 = (uint32_t *)SlotsExamMode::getSlotKhiEndExamAddress();
uint8_t SlotsExamMode::FetchSlotBExamMode(const char* version) {
return FetchSlotExamMode(version, "B");
}
uint8_t SlotsExamMode::FetchSlotKhiExamMode(const char* version) {
return FetchSlotExamMode(version, "Khi");
}
uint8_t SlotsExamMode::examFetch15(uint32_t start, uint32_t end) {
uint32_t * persitence_start_32 = (uint32_t *)start;
uint32_t * persitence_end_32 = (uint32_t *)end;
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
@@ -136,65 +118,86 @@ uint8_t * SignificantSlotKhiExamModeAddress() {
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::getSlotKhiStartExamAddress()) >= 0);
Ion::Device::Flash::EraseSector(Ion::Device::Flash::SectorAtAddress(SlotsExamMode::getSlotKhiStartExamAddress()));
return (uint8_t *)SlotsExamMode::getSlotKhiStartExamAddress();
}
return persitence_start_8;
}
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;
assert(Ion::Device::Flash::SectorAtAddress(start) >= 0);
Ion::Device::Flash::EraseSector(start);
uint32_t nbOfZerosBefore = (((uint8_t*)start - (uint8_t*)start) * numberOfBitsInByte) % 4;
// Count the number of 0[3] at reading address
size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*readingAddress)) % 4;
size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*(uint8_t*)start)) % 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;
uint32_t nbOfZerosBefore = ((persitence_start_8 - (uint8_t*)start) * numberOfBitsInByte) % 4;
// Count the number of 0[3] at reading address
size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*readingAddress)) % 4;
size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*persitence_start_8)) % 4;
return (nbOfZerosBefore + numberOfLeading0) % 4;
} else {
return *((uint8_t *)readingAddress);
}
uint8_t SlotsExamMode::examFetch16(uint32_t start, uint32_t end) {
uint8_t* persitence_start_8 = (uint8_t*)start;
uint8_t* persitence_end_8 = (uint8_t*)end;
while (persitence_start_8 + 1 <= persitence_end_8 && (*persitence_start_8 != 0xFF)) {
// Scan by groups of 8 bits to reach first non-zero bit
persitence_start_8++;
}
return *(persitence_start_8 - 1);
}
uint8_t SlotsExamMode::FetchSlotKhiExamMode() {
uint8_t * readingAddress = SignificantSlotKhiExamModeAddress();
// Count the number of 0[3] before reading address
uint32_t nbOfZerosBefore = ((readingAddress - (uint8_t *)getSlotKhiStartExamAddress()) * numberOfBitsInByte) % 4;
// Count the number of 0[3] at reading address
size_t numberOfLeading0 = (numberOfBitsInByte - numberOfBitsAfterLeadingZeroes(*readingAddress)) % 4;
return (nbOfZerosBefore + numberOfLeading0) % 4;
uint8_t SlotsExamMode::examFetch1718(uint32_t start, uint32_t end) {
uint8_t* persitence_start_8 = (uint8_t*)start;
uint8_t* persitence_end_8 = (uint8_t*)end;
while (persitence_start_8 + 1 <= persitence_end_8 && (*persitence_start_8 != 0xFF)) {
// Scan by groups of 8 bits to reach first non-zero bit
persitence_start_8++;
}
uint32_t SlotsExamMode::getSlotAStartExamAddress(bool newVersion) {
return newVersion ? SlotAExamModeBufferStartNewVersions : SlotAExamModeBufferStartOldVersions;
return *(persitence_start_8 - 2);
}
uint32_t SlotsExamMode::getSlotAEndExamAddress(bool newVersion) {
return newVersion ? SlotAExamModeBufferEndNewVersions : SlotAExamModeBufferEndOldVersions;
uint8_t SlotsExamMode::examFetch19(uint32_t start, uint32_t end) {
uint16_t* start16 = (uint16_t*)start;
uint16_t* end16 = (uint16_t*)end;
while (start16 + 1 <= end16 && *start16 != 0xFFFF) {
start16++;
}
return *(start16 - 1) >> 8;
}
uint32_t SlotsExamMode::getSlotBStartExamAddress(bool newVersion) {
return newVersion ? SlotBExamModeBufferStartNewVersions : SlotBExamModeBufferStartOldVersions;
uint32_t SlotsExamMode::getSlotAStartExamAddress(int ExamVersion) {
if (ExamVersion == 0) {
return SlotAExamModeBufferStartOldVersions;
}
else {
return SlotAExamModeBufferStartNewVersions;
}
}
uint32_t SlotsExamMode::getSlotBEndExamAddress(bool newVersion) {
return newVersion ? SlotBExamModeBufferEndNewVersions : SlotBExamModeBufferEndOldVersions;
uint32_t SlotsExamMode::getSlotAEndExamAddress(int ExamVersion) {
if (ExamVersion == 0) {
return SlotAExamModeBufferEndOldVersions;
}
else {
return SlotAExamModeBufferEndNewVersions;;
}
}
uint32_t SlotsExamMode::getSlotBStartExamAddress(int ExamVersion) {
if (ExamVersion == 0) {
return SlotBExamModeBufferStartOldVersions;
}
else {
return SlotBExamModeBufferStartNewVersions;
}
}
uint32_t SlotsExamMode::getSlotBEndExamAddress(int ExamVersion) {
if (ExamVersion == 0) {
return SlotBExamModeBufferEndOldVersions;
}
else {
return SlotBExamModeBufferEndNewVersions;
}
}
uint32_t SlotsExamMode::getSlotKhiStartExamAddress() {

View File

@@ -25,17 +25,23 @@ static const uint32_t SlotKhiExamModeBufferEnd = 0x90183000;
class SlotsExamMode{
public:
static uint8_t FetchSlotAExamMode(bool newVersion);
static uint8_t FetchSlotBExamMode(bool newVerion);
static uint8_t FetchSlotKhiExamMode();
static uint32_t getSlotAStartExamAddress(bool newVersion);
static uint32_t getSlotAEndExamAddress(bool newVersion);
static uint32_t getSlotBStartExamAddress(bool newVersion);
static uint32_t getSlotBEndExamAddress(bool newVersion);
static uint8_t FetchSlotExamMode(const char* version, const char* Slot);
static uint8_t FetchSlotAExamMode(const char* version);
static uint8_t FetchSlotBExamMode(const char* version);
static uint8_t FetchSlotKhiExamMode(const char* version);
static uint32_t getSlotAStartExamAddress(int ExamVersion);
static uint32_t getSlotAEndExamAddress(int ExamVersion);
static uint32_t getSlotBStartExamAddress(int ExamVersion);
static uint32_t getSlotBEndExamAddress(int ExamVersion);
static uint32_t getSlotKhiStartExamAddress();
static uint32_t getSlotKhiEndExamAddress();
static uint8_t examFetch15(uint32_t start, uint32_t end);
static uint8_t examFetch1718(uint32_t start, uint32_t end);
static uint8_t examFetch16(uint32_t start, uint32_t end);
static uint8_t examFetch19(uint32_t start, uint32_t end);
};
enum class ExamMode : int8_t {

View File

@@ -10,7 +10,9 @@ const char * UserlandHeader::version() const {
}
const bool UserlandHeader::isValid() const {
return m_header == Magic && m_footer == Magic;
// We only verify only the first Magic Number, to display version such as
// Epsilon 16 with older UserlandHeader layout
return m_header == Magic;
}
const bool UserlandHeader::isOmega() const {

View File

@@ -1,10 +1,26 @@
#include <bootloader/utility.h>
#include <string.h>
// This function takes a pointer to a string (version) and the size of the
// string (versionSize) and returns an integer representing the version.
// Example: "1.2.3" will return 10203.
// "1.0.1-dev" will return 10001.
int Utility::versionSum(const char * version, int length) {
int sum = 0;
for (int i = 0; i < length; i++) {
sum += version[i] * (strlen(version) * 100 - i * 10);
int currentNumber = 0;
// List of numbers that are allowed in a version
const char * allowedNumbers = "0123456789";
for (int i = 0; i < length; i++) {
if (version[i] == '.') {
sum = sum * 100 + currentNumber;
currentNumber = 0;
} else if (strchr(allowedNumbers, version[i]) != nullptr) {
currentNumber = currentNumber * 10 + (version[i] - '0');
} else {
// We found a character that is not a number or a dot, so we stop
break;
}
}
sum = sum * 100 + currentNumber;
return sum;
}