feat: ext2 filesystem, installer updates, add update feature, and more
This commit is contained in:
@@ -32,9 +32,9 @@ namespace Montauk {
|
|||||||
|
|
||||||
static int Sys_ReadDir(const char* path, const char** outNames, int maxEntries) {
|
static int Sys_ReadDir(const char* path, const char** outNames, int maxEntries) {
|
||||||
// Get entries from VFS into a kernel-local array
|
// Get entries from VFS into a kernel-local array
|
||||||
const char* kernelNames[64];
|
const char* kernelNames[256];
|
||||||
int max = maxEntries;
|
int max = maxEntries;
|
||||||
if (max > 64) max = 64;
|
if (max > 256) max = 256;
|
||||||
int count = Fs::Vfs::VfsReadDir(path, kernelNames, max);
|
int count = Fs::Vfs::VfsReadDir(path, kernelNames, max);
|
||||||
if (count <= 0) return count;
|
if (count <= 0) return count;
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ namespace Montauk {
|
|||||||
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
||||||
uint64_t userVa = proc->heapNext;
|
uint64_t userVa = proc->heapNext;
|
||||||
proc->heapNext += 0x1000;
|
proc->heapNext += 0x1000;
|
||||||
Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa);
|
if (!Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa)) return -1;
|
||||||
|
|
||||||
// Copy strings into the user page and write pointers to outNames
|
// Copy strings into the user page and write pointers to outNames
|
||||||
uint64_t offset = 0;
|
uint64_t offset = 0;
|
||||||
|
|||||||
@@ -50,11 +50,13 @@ namespace Montauk {
|
|||||||
constexpr uint64_t userVa = 0x50000000ULL;
|
constexpr uint64_t userVa = 0x50000000ULL;
|
||||||
|
|
||||||
for (uint64_t i = 0; i < numPages; i++) {
|
for (uint64_t i = 0; i < numPages; i++) {
|
||||||
Memory::VMM::Paging::MapUserInWC(
|
if (!Memory::VMM::Paging::MapUserInWC(
|
||||||
proc->pml4Phys,
|
proc->pml4Phys,
|
||||||
fbPhys + i * 0x1000,
|
fbPhys + i * 0x1000,
|
||||||
userVa + i * 0x1000
|
userVa + i * 0x1000
|
||||||
);
|
)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return userVa;
|
return userVa;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace Montauk {
|
|||||||
void* page = Memory::g_pfa->AllocateZeroed();
|
void* page = Memory::g_pfa->AllocateZeroed();
|
||||||
if (page == nullptr) return 0;
|
if (page == nullptr) return 0;
|
||||||
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
||||||
Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa + i * 0x1000);
|
if (!Memory::VMM::Paging::MapUserIn(proc->pml4Phys, physAddr, userVa + i * 0x1000)) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
proc->heapNext += size;
|
proc->heapNext += size;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <Drivers/Storage/Gpt.hpp>
|
#include <Drivers/Storage/Gpt.hpp>
|
||||||
#include <Fs/FsProbe.hpp>
|
#include <Fs/FsProbe.hpp>
|
||||||
#include <Fs/Fat32.hpp>
|
#include <Fs/Fat32.hpp>
|
||||||
|
#include <Fs/Ext2.hpp>
|
||||||
|
|
||||||
#include "Syscall.hpp"
|
#include "Syscall.hpp"
|
||||||
|
|
||||||
@@ -115,6 +116,10 @@ namespace Montauk {
|
|||||||
return (int64_t)Fs::Fat32::Format(
|
return (int64_t)Fs::Fat32::Format(
|
||||||
part->BlockDevIndex, part->StartLba, part->SectorCount,
|
part->BlockDevIndex, part->StartLba, part->SectorCount,
|
||||||
params->label[0] ? params->label : nullptr);
|
params->label[0] ? params->label : nullptr);
|
||||||
|
case FS_TYPE_EXT2:
|
||||||
|
return (int64_t)Fs::Ext2::Format(
|
||||||
|
part->BlockDevIndex, part->StartLba, part->SectorCount,
|
||||||
|
params->label[0] ? params->label : nullptr);
|
||||||
default:
|
default:
|
||||||
return -1; // unknown filesystem type
|
return -1; // unknown filesystem type
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ namespace Montauk {
|
|||||||
|
|
||||||
// Filesystem type IDs for SYS_FSFORMAT
|
// Filesystem type IDs for SYS_FSFORMAT
|
||||||
static constexpr int FS_TYPE_FAT32 = 1;
|
static constexpr int FS_TYPE_FAT32 = 1;
|
||||||
|
static constexpr int FS_TYPE_EXT2 = 2;
|
||||||
|
|
||||||
struct FsFormatParams {
|
struct FsFormatParams {
|
||||||
int32_t partIndex; // global partition index
|
int32_t partIndex; // global partition index
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ namespace Montauk {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Don't draw over the framebuffer once the GUI is active
|
||||||
|
if (Kt::g_suppressKernelLog) return;
|
||||||
Kt::Print(text);
|
Kt::Print(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,6 +37,8 @@ namespace Montauk {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Don't draw over the framebuffer once the GUI is active
|
||||||
|
if (Kt::g_suppressKernelLog) return;
|
||||||
Kt::Putchar(c);
|
Kt::Putchar(c);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -67,7 +67,10 @@ namespace WinServer {
|
|||||||
}
|
}
|
||||||
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
||||||
slot.pixelPhysPages[i] = physAddr;
|
slot.pixelPhysPages[i] = physAddr;
|
||||||
Memory::VMM::Paging::MapUserIn(ownerPml4, physAddr, userVa + (uint64_t)i * 0x1000);
|
if (!Memory::VMM::Paging::MapUserIn(ownerPml4, physAddr, userVa + (uint64_t)i * 0x1000)) {
|
||||||
|
slot.used = false;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slot.ownerVa = userVa;
|
slot.ownerVa = userVa;
|
||||||
@@ -173,8 +176,10 @@ namespace WinServer {
|
|||||||
uint64_t userVa = heapNext;
|
uint64_t userVa = heapNext;
|
||||||
|
|
||||||
for (int i = 0; i < slot.pixelNumPages; i++) {
|
for (int i = 0; i < slot.pixelNumPages; i++) {
|
||||||
Memory::VMM::Paging::MapUserIn(callerPml4, slot.pixelPhysPages[i],
|
if (!Memory::VMM::Paging::MapUserIn(callerPml4, slot.pixelPhysPages[i],
|
||||||
userVa + (uint64_t)i * 0x1000);
|
userVa + (uint64_t)i * 0x1000)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slot.desktopVa = userVa;
|
slot.desktopVa = userVa;
|
||||||
@@ -230,7 +235,9 @@ namespace WinServer {
|
|||||||
if (page == nullptr) return -1;
|
if (page == nullptr) return -1;
|
||||||
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
||||||
slot.pixelPhysPages[i] = physAddr;
|
slot.pixelPhysPages[i] = physAddr;
|
||||||
Memory::VMM::Paging::MapUserIn(ownerPml4, physAddr, userVa + (uint64_t)i * 0x1000);
|
if (!Memory::VMM::Paging::MapUserIn(ownerPml4, physAddr, userVa + (uint64_t)i * 0x1000)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slot.width = newW;
|
slot.width = newW;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Ext2.hpp
|
||||||
|
* ext2 filesystem driver
|
||||||
|
* Copyright (c) 2026 Daniel Hammer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <Fs/Vfs.hpp>
|
||||||
|
|
||||||
|
namespace Fs::Ext2 {
|
||||||
|
|
||||||
|
// Try to mount an ext2 filesystem at the given partition range.
|
||||||
|
// Returns a FsDriver* on success, nullptr if not a valid ext2 volume.
|
||||||
|
Vfs::FsDriver* Mount(int blockDevIndex, uint64_t startLba, uint64_t sectorCount);
|
||||||
|
|
||||||
|
// Register Ext2::Mount as a filesystem probe with FsProbe.
|
||||||
|
void RegisterProbe();
|
||||||
|
|
||||||
|
// Format a partition as ext2.
|
||||||
|
// Returns 0 on success, -1 on error.
|
||||||
|
int Format(int blockDevIndex, uint64_t startLba, uint64_t sectorCount,
|
||||||
|
const char* volumeLabel);
|
||||||
|
|
||||||
|
};
|
||||||
@@ -79,6 +79,11 @@ namespace Fs::Fat32 {
|
|||||||
uint8_t* clusterBuf;
|
uint8_t* clusterBuf;
|
||||||
int clusterBufPages;
|
int clusterBufPages;
|
||||||
|
|
||||||
|
// In-memory FAT cache (page-allocated)
|
||||||
|
uint32_t* fatCache;
|
||||||
|
int fatCachePages;
|
||||||
|
uint32_t fatCacheEntries; // number of valid 4-byte entries
|
||||||
|
|
||||||
// Open file handles
|
// Open file handles
|
||||||
Fat32File files[MaxFilesPerInstance];
|
Fat32File files[MaxFilesPerInstance];
|
||||||
|
|
||||||
@@ -127,6 +132,12 @@ namespace Fs::Fat32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t GetNextCluster(const Fat32Instance& inst, uint32_t cluster) {
|
static uint32_t GetNextCluster(const Fat32Instance& inst, uint32_t cluster) {
|
||||||
|
// Use in-memory FAT cache if available (avoids disk I/O per lookup)
|
||||||
|
if (inst.fatCache && cluster < inst.fatCacheEntries) {
|
||||||
|
return inst.fatCache[cluster] & 0x0FFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: read from disk
|
||||||
uint32_t fatOffset = cluster * 4;
|
uint32_t fatOffset = cluster * 4;
|
||||||
uint32_t fatSector = fatOffset / inst.bytesPerSector;
|
uint32_t fatSector = fatOffset / inst.bytesPerSector;
|
||||||
uint32_t entryOffset = fatOffset % inst.bytesPerSector;
|
uint32_t entryOffset = fatOffset % inst.bytesPerSector;
|
||||||
@@ -183,10 +194,30 @@ namespace Fs::Fat32 {
|
|||||||
if (!WritePartSectors(inst, fatStart + fatSector, 1, sectorBuf)) return false;
|
if (!WritePartSectors(inst, fatStart + fatSector, 1, sectorBuf)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep in-memory FAT cache in sync
|
||||||
|
if (inst.fatCache && cluster < inst.fatCacheEntries) {
|
||||||
|
uint32_t existing = inst.fatCache[cluster];
|
||||||
|
inst.fatCache[cluster] = (existing & 0xF0000000) | (value & 0x0FFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t AllocateCluster(Fat32Instance& inst) {
|
static uint32_t AllocateCluster(Fat32Instance& inst) {
|
||||||
|
// Fast path: scan the in-memory FAT cache
|
||||||
|
if (inst.fatCache) {
|
||||||
|
uint32_t limit = inst.clusterCount + 2;
|
||||||
|
if (limit > inst.fatCacheEntries) limit = inst.fatCacheEntries;
|
||||||
|
for (uint32_t cluster = 2; cluster < limit; cluster++) {
|
||||||
|
if ((inst.fatCache[cluster] & 0x0FFFFFFF) == CLUSTER_FREE) {
|
||||||
|
if (!WriteFatEntry(inst, cluster, 0x0FFFFFFF)) return 0;
|
||||||
|
return cluster;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow path: read FAT sectors from disk
|
||||||
uint8_t sectorBuf[512];
|
uint8_t sectorBuf[512];
|
||||||
uint32_t entriesPerSector = inst.bytesPerSector / 4;
|
uint32_t entriesPerSector = inst.bytesPerSector / 4;
|
||||||
|
|
||||||
@@ -1575,6 +1606,31 @@ namespace Fs::Fat32 {
|
|||||||
nullptr, inst.clusterBufPages);
|
nullptr, inst.clusterBufPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load entire FAT into memory for fast cluster chain lookups.
|
||||||
|
// fatSize32 is in sectors; each sector is bytesPerSector bytes.
|
||||||
|
uint64_t fatBytes = (uint64_t)fatSize32 * bytesPerSector;
|
||||||
|
inst.fatCachePages = (int)((fatBytes + 0xFFF) / 0x1000);
|
||||||
|
inst.fatCacheEntries = (uint32_t)(fatBytes / 4);
|
||||||
|
inst.fatCache = (uint32_t*)Memory::g_pfa->ReallocConsecutive(
|
||||||
|
nullptr, inst.fatCachePages);
|
||||||
|
if (inst.fatCache) {
|
||||||
|
uint8_t* dst = (uint8_t*)inst.fatCache;
|
||||||
|
uint64_t remaining = fatBytes;
|
||||||
|
uint64_t fatPartSector = reservedSectors;
|
||||||
|
while (remaining > 0) {
|
||||||
|
uint32_t chunk = (remaining > 4096) ? 4096 : (uint32_t)remaining;
|
||||||
|
uint32_t secs = (chunk + bytesPerSector - 1) / bytesPerSector;
|
||||||
|
if (!ReadPartSectors(inst, fatPartSector, secs, dst)) {
|
||||||
|
// If read fails, disable cache and fall back to per-lookup reads
|
||||||
|
inst.fatCache = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dst += secs * bytesPerSector;
|
||||||
|
fatPartSector += secs;
|
||||||
|
remaining -= secs * bytesPerSector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clear file handles
|
// Clear file handles
|
||||||
for (int i = 0; i < MaxFilesPerInstance; i++) {
|
for (int i = 0; i < MaxFilesPerInstance; i++) {
|
||||||
inst.files[i].inUse = false;
|
inst.files[i].inUse = false;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "FsProbe.hpp"
|
#include "FsProbe.hpp"
|
||||||
#include <Drivers/Storage/Gpt.hpp>
|
#include <Drivers/Storage/Gpt.hpp>
|
||||||
#include <Terminal/Terminal.hpp>
|
#include <Terminal/Terminal.hpp>
|
||||||
|
#include <Libraries/Memory.hpp>
|
||||||
|
|
||||||
namespace Fs::FsProbe {
|
namespace Fs::FsProbe {
|
||||||
|
|
||||||
@@ -19,15 +20,24 @@ namespace Fs::FsProbe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsEfiPartition(const Drivers::Storage::Gpt::PartitionInfo* part) {
|
||||||
|
auto& a = part->TypeGuid;
|
||||||
|
auto& b = Drivers::Storage::Gpt::GUID_EFI_SYSTEM;
|
||||||
|
return a.Data1 == b.Data1 && a.Data2 == b.Data2 && a.Data3 == b.Data3 &&
|
||||||
|
memcmp(a.Data4, b.Data4, 8) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
void MountPartitions(int firstDrive) {
|
void MountPartitions(int firstDrive) {
|
||||||
int partCount = Drivers::Storage::Gpt::GetPartitionCount();
|
int partCount = Drivers::Storage::Gpt::GetPartitionCount();
|
||||||
if (partCount == 0 || g_probeCount == 0) return;
|
if (partCount == 0 || g_probeCount == 0) return;
|
||||||
|
|
||||||
int driveNum = firstDrive;
|
int driveNum = firstDrive;
|
||||||
|
|
||||||
|
// First pass: mount non-EFI partitions so they get lower drive numbers
|
||||||
for (int i = 0; i < partCount && driveNum < Vfs::MaxDrives; i++) {
|
for (int i = 0; i < partCount && driveNum < Vfs::MaxDrives; i++) {
|
||||||
auto* part = Drivers::Storage::Gpt::GetPartition(i);
|
auto* part = Drivers::Storage::Gpt::GetPartition(i);
|
||||||
if (!part) continue;
|
if (!part) continue;
|
||||||
|
if (IsEfiPartition(part)) continue;
|
||||||
|
|
||||||
for (int p = 0; p < g_probeCount; p++) {
|
for (int p = 0; p < g_probeCount; p++) {
|
||||||
Vfs::FsDriver* driver = g_probes[p](
|
Vfs::FsDriver* driver = g_probes[p](
|
||||||
@@ -42,6 +52,26 @@ namespace Fs::FsProbe {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Second pass: mount EFI system partitions after all others
|
||||||
|
for (int i = 0; i < partCount && driveNum < Vfs::MaxDrives; i++) {
|
||||||
|
auto* part = Drivers::Storage::Gpt::GetPartition(i);
|
||||||
|
if (!part) continue;
|
||||||
|
if (!IsEfiPartition(part)) continue;
|
||||||
|
|
||||||
|
for (int p = 0; p < g_probeCount; p++) {
|
||||||
|
Vfs::FsDriver* driver = g_probes[p](
|
||||||
|
part->BlockDevIndex, part->StartLba, part->SectorCount);
|
||||||
|
|
||||||
|
if (driver) {
|
||||||
|
Vfs::RegisterDrive(driveNum, driver);
|
||||||
|
Kt::KernelLogStream(Kt::OK, "FsProbe") << "Mounted EFI partition "
|
||||||
|
<< i << " as drive " << driveNum;
|
||||||
|
driveNum++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int MountPartition(int partIndex, int driveNum) {
|
int MountPartition(int partIndex, int driveNum) {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#include <Fs/Ramdisk.hpp>
|
#include <Fs/Ramdisk.hpp>
|
||||||
#include <Fs/Vfs.hpp>
|
#include <Fs/Vfs.hpp>
|
||||||
#include <Fs/Fat32.hpp>
|
#include <Fs/Fat32.hpp>
|
||||||
|
#include <Fs/Ext2.hpp>
|
||||||
#include <Fs/FsProbe.hpp>
|
#include <Fs/FsProbe.hpp>
|
||||||
#include <Sched/Scheduler.hpp>
|
#include <Sched/Scheduler.hpp>
|
||||||
#include <Api/Syscall.hpp>
|
#include <Api/Syscall.hpp>
|
||||||
@@ -206,6 +207,7 @@ extern "C" void kmain() {
|
|||||||
// Register filesystem probes and auto-mount partitions.
|
// Register filesystem probes and auto-mount partitions.
|
||||||
// When no ramdisk, disk partitions start at drive 0 so init.elf is found there.
|
// When no ramdisk, disk partitions start at drive 0 so init.elf is found there.
|
||||||
Fs::Fat32::RegisterProbe();
|
Fs::Fat32::RegisterProbe();
|
||||||
|
Fs::Ext2::RegisterProbe();
|
||||||
Fs::FsProbe::MountPartitions(hasRamdisk ? 1 : 0);
|
Fs::FsProbe::MountPartitions(hasRamdisk ? 1 : 0);
|
||||||
|
|
||||||
Hal::LoadTSS();
|
Hal::LoadTSS();
|
||||||
|
|||||||
@@ -49,11 +49,14 @@ namespace Memory::VMM {
|
|||||||
PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[virtualAddress.GetIndex(level)]);
|
PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[virtualAddress.GetIndex(level)]);
|
||||||
|
|
||||||
if (!entry->Present) {
|
if (!entry->Present) {
|
||||||
|
void* newPage = Memory::g_pfa->AllocateZeroed();
|
||||||
|
if (newPage == nullptr) {
|
||||||
|
Panic("OOM in Paging::HandleLevel (kernel page table allocation)", nullptr);
|
||||||
|
}
|
||||||
|
uint64_t downLevelAddr = Memory::SubHHDM((uint64_t)newPage);
|
||||||
|
|
||||||
entry->Present = true;
|
entry->Present = true;
|
||||||
entry->Writable = true;
|
entry->Writable = true;
|
||||||
|
|
||||||
uint64_t downLevelAddr = Memory::SubHHDM((uint64_t)Memory::g_pfa->AllocateZeroed());
|
|
||||||
|
|
||||||
entry->Address = downLevelAddr >> 12;
|
entry->Address = downLevelAddr >> 12;
|
||||||
|
|
||||||
return (PageTable*)downLevelAddr;
|
return (PageTable*)downLevelAddr;
|
||||||
@@ -66,12 +69,15 @@ namespace Memory::VMM {
|
|||||||
PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[virtualAddress.GetIndex(level)]);
|
PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[virtualAddress.GetIndex(level)]);
|
||||||
|
|
||||||
if (!entry->Present) {
|
if (!entry->Present) {
|
||||||
|
void* newPage = Memory::g_pfa->AllocateZeroed();
|
||||||
|
if (newPage == nullptr) {
|
||||||
|
Panic("OOM in Paging::HandleLevelUser (page table allocation)", nullptr);
|
||||||
|
}
|
||||||
|
uint64_t downLevelAddr = Memory::SubHHDM((uint64_t)newPage);
|
||||||
|
|
||||||
entry->Present = true;
|
entry->Present = true;
|
||||||
entry->Writable = true;
|
entry->Writable = true;
|
||||||
entry->Supervisor = 1; // User-accessible
|
entry->Supervisor = 1; // User-accessible
|
||||||
|
|
||||||
uint64_t downLevelAddr = Memory::SubHHDM((uint64_t)Memory::g_pfa->AllocateZeroed());
|
|
||||||
|
|
||||||
entry->Address = downLevelAddr >> 12;
|
entry->Address = downLevelAddr >> 12;
|
||||||
|
|
||||||
return (PageTable*)downLevelAddr;
|
return (PageTable*)downLevelAddr;
|
||||||
@@ -177,21 +183,24 @@ namespace Memory::VMM {
|
|||||||
return newPml4Phys;
|
return newPml4Phys;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Paging::MapUserIn(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
|
bool Paging::MapUserIn(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
|
||||||
if (virtualAddress % 0x1000 != 0 || physicalAddress % 0x1000 != 0) {
|
if (virtualAddress % 0x1000 != 0 || physicalAddress % 0x1000 != 0) {
|
||||||
Panic("Non-aligned address in Paging::MapUserIn!", nullptr);
|
Panic("Non-aligned address in Paging::MapUserIn!", nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualAddress va(virtualAddress);
|
VirtualAddress va(virtualAddress);
|
||||||
|
|
||||||
// Walk/create page tables from the given PML4, setting User bit at each level
|
// Walk/create page tables from the given PML4, setting User bit at each level.
|
||||||
|
// Returns nullptr on OOM.
|
||||||
auto walkLevel = [](PageTable* table, uint64_t index) -> PageTable* {
|
auto walkLevel = [](PageTable* table, uint64_t index) -> PageTable* {
|
||||||
PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[index]);
|
PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[index]);
|
||||||
if (!entry->Present) {
|
if (!entry->Present) {
|
||||||
|
void* newPage = Memory::g_pfa->AllocateZeroed();
|
||||||
|
if (newPage == nullptr) return nullptr;
|
||||||
|
uint64_t newPhys = Memory::SubHHDM((uint64_t)newPage);
|
||||||
entry->Present = true;
|
entry->Present = true;
|
||||||
entry->Writable = true;
|
entry->Writable = true;
|
||||||
entry->Supervisor = 1; // User-accessible
|
entry->Supervisor = 1; // User-accessible
|
||||||
uint64_t newPhys = Memory::SubHHDM((uint64_t)Memory::g_pfa->AllocateZeroed());
|
|
||||||
entry->Address = newPhys >> 12;
|
entry->Address = newPhys >> 12;
|
||||||
return (PageTable*)newPhys;
|
return (PageTable*)newPhys;
|
||||||
} else {
|
} else {
|
||||||
@@ -202,17 +211,21 @@ namespace Memory::VMM {
|
|||||||
|
|
||||||
PageTable* pml4 = (PageTable*)pml4Phys;
|
PageTable* pml4 = (PageTable*)pml4Phys;
|
||||||
auto pml3 = walkLevel(pml4, va.GetL4Index());
|
auto pml3 = walkLevel(pml4, va.GetL4Index());
|
||||||
|
if (!pml3) return false;
|
||||||
auto pml2 = walkLevel(pml3, va.GetL3Index());
|
auto pml2 = walkLevel(pml3, va.GetL3Index());
|
||||||
|
if (!pml2) return false;
|
||||||
auto pml1 = walkLevel(pml2, va.GetL2Index());
|
auto pml1 = walkLevel(pml2, va.GetL2Index());
|
||||||
|
if (!pml1) return false;
|
||||||
|
|
||||||
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&pml1->entries[va.GetPageIndex()]);
|
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&pml1->entries[va.GetPageIndex()]);
|
||||||
pageEntry->Present = true;
|
pageEntry->Present = true;
|
||||||
pageEntry->Writable = true;
|
pageEntry->Writable = true;
|
||||||
pageEntry->Supervisor = 1;
|
pageEntry->Supervisor = 1;
|
||||||
pageEntry->Address = physicalAddress >> 12;
|
pageEntry->Address = physicalAddress >> 12;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Paging::MapUserInWC(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
|
bool Paging::MapUserInWC(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
|
||||||
if (virtualAddress % 0x1000 != 0 || physicalAddress % 0x1000 != 0) {
|
if (virtualAddress % 0x1000 != 0 || physicalAddress % 0x1000 != 0) {
|
||||||
Panic("Non-aligned address in Paging::MapUserInWC!", nullptr);
|
Panic("Non-aligned address in Paging::MapUserInWC!", nullptr);
|
||||||
}
|
}
|
||||||
@@ -222,10 +235,12 @@ namespace Memory::VMM {
|
|||||||
auto walkLevel = [](PageTable* table, uint64_t index) -> PageTable* {
|
auto walkLevel = [](PageTable* table, uint64_t index) -> PageTable* {
|
||||||
PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[index]);
|
PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[index]);
|
||||||
if (!entry->Present) {
|
if (!entry->Present) {
|
||||||
|
void* newPage = Memory::g_pfa->AllocateZeroed();
|
||||||
|
if (newPage == nullptr) return nullptr;
|
||||||
|
uint64_t newPhys = Memory::SubHHDM((uint64_t)newPage);
|
||||||
entry->Present = true;
|
entry->Present = true;
|
||||||
entry->Writable = true;
|
entry->Writable = true;
|
||||||
entry->Supervisor = 1;
|
entry->Supervisor = 1;
|
||||||
uint64_t newPhys = Memory::SubHHDM((uint64_t)Memory::g_pfa->AllocateZeroed());
|
|
||||||
entry->Address = newPhys >> 12;
|
entry->Address = newPhys >> 12;
|
||||||
return (PageTable*)newPhys;
|
return (PageTable*)newPhys;
|
||||||
} else {
|
} else {
|
||||||
@@ -236,8 +251,11 @@ namespace Memory::VMM {
|
|||||||
|
|
||||||
PageTable* pml4 = (PageTable*)pml4Phys;
|
PageTable* pml4 = (PageTable*)pml4Phys;
|
||||||
auto pml3 = walkLevel(pml4, va.GetL4Index());
|
auto pml3 = walkLevel(pml4, va.GetL4Index());
|
||||||
|
if (!pml3) return false;
|
||||||
auto pml2 = walkLevel(pml3, va.GetL3Index());
|
auto pml2 = walkLevel(pml3, va.GetL3Index());
|
||||||
|
if (!pml2) return false;
|
||||||
auto pml1 = walkLevel(pml2, va.GetL2Index());
|
auto pml1 = walkLevel(pml2, va.GetL2Index());
|
||||||
|
if (!pml1) return false;
|
||||||
|
|
||||||
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&pml1->entries[va.GetPageIndex()]);
|
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&pml1->entries[va.GetPageIndex()]);
|
||||||
pageEntry->Present = true;
|
pageEntry->Present = true;
|
||||||
@@ -245,6 +263,7 @@ namespace Memory::VMM {
|
|||||||
pageEntry->Supervisor = 1;
|
pageEntry->Supervisor = 1;
|
||||||
pageEntry->WriteThrough = true; // PWT=1, PCD=0 → PAT entry 1 = WC
|
pageEntry->WriteThrough = true; // PWT=1, PCD=0 → PAT entry 1 = WC
|
||||||
pageEntry->Address = physicalAddress >> 12;
|
pageEntry->Address = physicalAddress >> 12;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Paging::UnmapUserIn(std::uint64_t pml4Phys, std::uint64_t virtualAddress) {
|
void Paging::UnmapUserIn(std::uint64_t pml4Phys, std::uint64_t virtualAddress) {
|
||||||
|
|||||||
@@ -104,10 +104,10 @@ public:
|
|||||||
static std::uint64_t CreateUserPML4();
|
static std::uint64_t CreateUserPML4();
|
||||||
|
|
||||||
// Map a page into an arbitrary PML4 (specified by physical address) with User bit set.
|
// Map a page into an arbitrary PML4 (specified by physical address) with User bit set.
|
||||||
static void MapUserIn(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
static bool MapUserIn(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
||||||
|
|
||||||
// Map a page into an arbitrary PML4 with User + Write-Combining attributes.
|
// Map a page into an arbitrary PML4 with User + Write-Combining attributes.
|
||||||
static void MapUserInWC(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
static bool MapUserInWC(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress);
|
||||||
|
|
||||||
// Unmap a single page from an arbitrary PML4 (clears PTE + invalidates TLB).
|
// Unmap a single page from an arbitrary PML4 (clears PTE + invalidates TLB).
|
||||||
static void UnmapUserIn(std::uint64_t pml4Phys, std::uint64_t virtualAddress);
|
static void UnmapUserIn(std::uint64_t pml4Phys, std::uint64_t virtualAddress);
|
||||||
|
|||||||
@@ -115,7 +115,11 @@ namespace Sched {
|
|||||||
uint64_t virtAddr = segBase + p * 0x1000;
|
uint64_t virtAddr = segBase + p * 0x1000;
|
||||||
|
|
||||||
// Map into the process's PML4 with User bit set
|
// Map into the process's PML4 with User bit set
|
||||||
Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, virtAddr);
|
if (!Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, virtAddr)) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to map page";
|
||||||
|
Memory::g_heap->Free(fileData);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Copy file data that overlaps this page (via HHDM)
|
// Copy file data that overlaps this page (via HHDM)
|
||||||
uint64_t pageStart = virtAddr;
|
uint64_t pageStart = virtAddr;
|
||||||
|
|||||||
@@ -149,7 +149,10 @@ namespace Sched {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
|
||||||
Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, userStackBase + i * 0x1000);
|
if (!Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, userStackBase + i * 0x1000)) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "Sched") << "Failed to map user stack page";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if (i == UserStackPages - 1) topStackPagePhys = physAddr;
|
if (i == UserStackPages - 1) topStackPagePhys = physAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +165,10 @@ namespace Sched {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
uint64_t stubPhys = Memory::SubHHDM((uint64_t)stubPage);
|
uint64_t stubPhys = Memory::SubHHDM((uint64_t)stubPage);
|
||||||
Memory::VMM::Paging::MapUserIn(pml4Phys, stubPhys, ExitStubAddr);
|
if (!Memory::VMM::Paging::MapUserIn(pml4Phys, stubPhys, ExitStubAddr)) {
|
||||||
|
Kt::KernelLogStream(Kt::ERROR, "Sched") << "Failed to map exit stub";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Write: xor edi, edi; xor eax, eax; syscall
|
// Write: xor edi, edi; xor eax, eax; syscall
|
||||||
uint8_t* stub = (uint8_t*)stubPage;
|
uint8_t* stub = (uint8_t*)stubPage;
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ namespace Montauk {
|
|||||||
|
|
||||||
// Filesystem type IDs for SYS_FSFORMAT
|
// Filesystem type IDs for SYS_FSFORMAT
|
||||||
static constexpr int FS_TYPE_FAT32 = 1;
|
static constexpr int FS_TYPE_FAT32 = 1;
|
||||||
|
static constexpr int FS_TYPE_EXT2 = 2;
|
||||||
|
|
||||||
struct FsFormatParams {
|
struct FsFormatParams {
|
||||||
int32_t partIndex; // global partition index
|
int32_t partIndex; // global partition index
|
||||||
|
|||||||
@@ -695,6 +695,8 @@ static void filemanager_on_mouse(Window* win, MouseEvent& ev) {
|
|||||||
montauk::spawn("0:/apps/pdfviewer/pdfviewer.elf", fullpath);
|
montauk::spawn("0:/apps/pdfviewer/pdfviewer.elf", fullpath);
|
||||||
} else if (is_spreadsheet_file(fm->entry_names[clicked_idx])) {
|
} else if (is_spreadsheet_file(fm->entry_names[clicked_idx])) {
|
||||||
montauk::spawn("0:/apps/spreadsheet/spreadsheet.elf", fullpath);
|
montauk::spawn("0:/apps/spreadsheet/spreadsheet.elf", fullpath);
|
||||||
|
} else if (str_ends_with(fm->entry_names[clicked_idx], ".elf")) {
|
||||||
|
montauk::spawn(fullpath);
|
||||||
} else if (fm->desktop) {
|
} else if (fm->desktop) {
|
||||||
open_texteditor_with_file(fm->desktop, fullpath);
|
open_texteditor_with_file(fm->desktop, fullpath);
|
||||||
}
|
}
|
||||||
@@ -751,6 +753,8 @@ static void filemanager_on_mouse(Window* win, MouseEvent& ev) {
|
|||||||
montauk::spawn("0:/apps/pdfviewer/pdfviewer.elf", fullpath);
|
montauk::spawn("0:/apps/pdfviewer/pdfviewer.elf", fullpath);
|
||||||
} else if (is_spreadsheet_file(fm->entry_names[clicked_idx])) {
|
} else if (is_spreadsheet_file(fm->entry_names[clicked_idx])) {
|
||||||
montauk::spawn("0:/apps/spreadsheet/spreadsheet.elf", fullpath);
|
montauk::spawn("0:/apps/spreadsheet/spreadsheet.elf", fullpath);
|
||||||
|
} else if (str_ends_with(fm->entry_names[clicked_idx], ".elf")) {
|
||||||
|
montauk::spawn(fullpath);
|
||||||
} else if (fm->desktop) {
|
} else if (fm->desktop) {
|
||||||
open_texteditor_with_file(fm->desktop, fullpath);
|
open_texteditor_with_file(fm->desktop, fullpath);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ static void log_err(const char* msg) { log(LOG_ERR, msg); }
|
|||||||
|
|
||||||
// ---- Service runner ----
|
// ---- Service runner ----
|
||||||
|
|
||||||
static bool run_service(const char* path, const char* name) {
|
static bool run_service(const char* path, const char* name, bool wait = true) {
|
||||||
char msg[128];
|
char msg[128];
|
||||||
|
|
||||||
snprintf(msg, sizeof(msg), "Starting %s", name);
|
snprintf(msg, sizeof(msg), "Starting %s", name);
|
||||||
@@ -137,10 +137,14 @@ static bool run_service(const char* path, const char* name) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
montauk::waitpid(pid);
|
montauk::waitpid(pid);
|
||||||
|
|
||||||
snprintf(msg, sizeof(msg), "%s finished (pid %d)", name, pid);
|
snprintf(msg, sizeof(msg), "%s finished (pid %d)", name, pid);
|
||||||
log_ok(msg);
|
log_ok(msg);
|
||||||
|
} else {
|
||||||
|
snprintf(msg, sizeof(msg), "%s spawned in background (pid %d)", name, pid);
|
||||||
|
log_ok(msg);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,8 +154,8 @@ extern "C" void _start() {
|
|||||||
|
|
||||||
log_info("The Montauk Operating System");
|
log_info("The Montauk Operating System");
|
||||||
|
|
||||||
// ---- Stage 1: Network configuration ----
|
// ---- Stage 1: Network configuration (non-blocking) ----
|
||||||
run_service("0:/os/dhcp.elf", "dhcp");
|
run_service("0:/os/dhcp.elf", "dhcp", false);
|
||||||
|
|
||||||
// ---- Stage 2: Desktop environment (falls back to shell) ----
|
// ---- Stage 2: Desktop environment (falls back to shell) ----
|
||||||
if (!run_service("0:/os/desktop.elf", "desktop")) {
|
if (!run_service("0:/os/desktop.elf", "desktop")) {
|
||||||
|
|||||||
@@ -7,9 +7,10 @@
|
|||||||
#include "installer.h"
|
#include "installer.h"
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// EFI System Partition type GUID: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
|
// Partition type GUIDs
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
// EFI System Partition: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
|
||||||
static Montauk::PartGuid esp_type_guid() {
|
static Montauk::PartGuid esp_type_guid() {
|
||||||
Montauk::PartGuid g;
|
Montauk::PartGuid g;
|
||||||
g.Data1 = 0xC12A7328;
|
g.Data1 = 0xC12A7328;
|
||||||
@@ -22,10 +23,31 @@ static Montauk::PartGuid esp_type_guid() {
|
|||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Linux Filesystem: 0FC63DAF-8483-4772-8E79-3D69D8477DE4
|
||||||
|
static Montauk::PartGuid linux_fs_type_guid() {
|
||||||
|
Montauk::PartGuid g;
|
||||||
|
g.Data1 = 0x0FC63DAF;
|
||||||
|
g.Data2 = 0x8483;
|
||||||
|
g.Data3 = 0x4772;
|
||||||
|
g.Data4[0] = 0x8E; g.Data4[1] = 0x79;
|
||||||
|
g.Data4[2] = 0x3D; g.Data4[3] = 0x69;
|
||||||
|
g.Data4[4] = 0xD8; g.Data4[5] = 0x47;
|
||||||
|
g.Data4[6] = 0x7D; g.Data4[7] = 0xE4;
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Refresh disk list
|
// Refresh disk list
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
void installer_refresh_parts() {
|
||||||
|
auto& st = g_state;
|
||||||
|
st.part_count = montauk::partlist(st.parts, MAX_PARTS);
|
||||||
|
if (st.part_count < 0) st.part_count = 0;
|
||||||
|
if (st.selected_part < 0 || st.selected_part >= st.part_count)
|
||||||
|
st.selected_part = st.part_count > 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
void installer_refresh_disks() {
|
void installer_refresh_disks() {
|
||||||
auto& st = g_state;
|
auto& st = g_state;
|
||||||
st.disk_count = 0;
|
st.disk_count = 0;
|
||||||
@@ -138,11 +160,11 @@ static int g_files_copied;
|
|||||||
static int g_dirs_created;
|
static int g_dirs_created;
|
||||||
|
|
||||||
// Copy all entries from src_dir (on ramdisk) to dst_dir (on target drive).
|
// Copy all entries from src_dir (on ramdisk) to dst_dir (on target drive).
|
||||||
// src_dir: e.g. "0:/" or "0:/os"
|
// If skip_toplevel is non-null, skip that directory name at the top level.
|
||||||
// dst_dir: e.g. "15:/" or "15:/os"
|
static bool copy_recursive(const char* src_dir, const char* dst_dir,
|
||||||
static bool copy_recursive(const char* src_dir, const char* dst_dir) {
|
const char* skip_toplevel = nullptr) {
|
||||||
const char* names[64];
|
const char* names[256];
|
||||||
int count = montauk::readdir(src_dir, names, 64);
|
int count = montauk::readdir(src_dir, names, 256);
|
||||||
if (count < 0) return true; // not a directory or empty, skip
|
if (count < 0) return true; // not a directory or empty, skip
|
||||||
|
|
||||||
// Ramdisk readdir returns names relative to the drive root with the
|
// Ramdisk readdir returns names relative to the drive root with the
|
||||||
@@ -188,6 +210,13 @@ static bool copy_recursive(const char* src_dir, const char* dst_dir) {
|
|||||||
for (; j < blen - 1 && j < 255; j++) dir_name[j] = basename[j];
|
for (; j < blen - 1 && j < 255; j++) dir_name[j] = basename[j];
|
||||||
dir_name[j] = '\0';
|
dir_name[j] = '\0';
|
||||||
|
|
||||||
|
// Skip the installer app — no need on the installed system
|
||||||
|
if (strcmp(dir_name, "installer") == 0 &&
|
||||||
|
strcmp(src_local, "apps") == 0) continue;
|
||||||
|
|
||||||
|
// Skip requested top-level directory
|
||||||
|
if (skip_toplevel && strcmp(dir_name, skip_toplevel) == 0) continue;
|
||||||
|
|
||||||
// Create directory on target
|
// Create directory on target
|
||||||
char target_path[256];
|
char target_path[256];
|
||||||
path_join(target_path, sizeof(target_path), dst_dir, dir_name);
|
path_join(target_path, sizeof(target_path), dst_dir, dir_name);
|
||||||
@@ -237,32 +266,245 @@ static bool copy_recursive(const char* src_dir, const char* dst_dir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Install MontaukOS to the selected disk
|
// GPT + partition helper
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
void do_install() {
|
static void set_gpt_name(Montauk::GptAddParams& params, const char* name) {
|
||||||
auto& st = g_state;
|
int i = 0;
|
||||||
int disk = st.selected_disk;
|
for (; name[i] && i < 71; i++) params.name[i] = name[i];
|
||||||
|
params.name[i] = '\0';
|
||||||
if (disk < 0 || disk >= st.disk_count) {
|
|
||||||
st.step = STEP_ERROR;
|
|
||||||
add_log("No disk selected");
|
|
||||||
flush_ui();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_files_copied = 0;
|
// ============================================================================
|
||||||
g_dirs_created = 0;
|
// Install: EFI + ext2 scheme
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static void install_efi_ext2(int disk) {
|
||||||
|
auto& st = g_state;
|
||||||
|
char path_buf[64];
|
||||||
|
int r;
|
||||||
|
|
||||||
// Step 1: Initialize GPT
|
// Step 1: Initialize GPT
|
||||||
add_log("Initializing GPT...");
|
add_log("Initializing GPT...");
|
||||||
flush_ui();
|
flush_ui();
|
||||||
int r = montauk::gpt_init(disk);
|
r = montauk::gpt_init(disk);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
add_log("ERROR: Failed to initialize GPT");
|
add_log("ERROR: Failed to initialize GPT");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
add_log(" GPT initialized");
|
||||||
flush_ui();
|
flush_ui();
|
||||||
st.step = STEP_ERROR;
|
|
||||||
return;
|
// Step 2: Create EFI System Partition (128 MB)
|
||||||
|
// After gpt_init, FirstUsableLba = 34.
|
||||||
|
static constexpr uint64_t ESP_SECTORS = 128 * 1024 * 2; // 128 MB in 512-byte sectors
|
||||||
|
static constexpr uint64_t FIRST_USABLE_LBA = 34;
|
||||||
|
|
||||||
|
add_log("Creating EFI partition (128 MB)...");
|
||||||
|
flush_ui();
|
||||||
|
{
|
||||||
|
Montauk::GptAddParams params;
|
||||||
|
montauk::memset(¶ms, 0, sizeof(params));
|
||||||
|
params.blockDev = disk;
|
||||||
|
params.startLba = FIRST_USABLE_LBA;
|
||||||
|
params.endLba = FIRST_USABLE_LBA + ESP_SECTORS - 1;
|
||||||
|
params.typeGuid = esp_type_guid();
|
||||||
|
set_gpt_name(params, "EFI System");
|
||||||
|
|
||||||
|
r = montauk::gpt_add(¶ms);
|
||||||
|
if (r < 0) {
|
||||||
|
add_log("ERROR: Failed to create EFI partition");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_log(" EFI partition created");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
// Step 3: Create Linux partition (remaining space)
|
||||||
|
add_log("Creating ext2 partition...");
|
||||||
|
flush_ui();
|
||||||
|
{
|
||||||
|
Montauk::GptAddParams params;
|
||||||
|
montauk::memset(¶ms, 0, sizeof(params));
|
||||||
|
params.blockDev = disk;
|
||||||
|
params.startLba = 0; // auto-fill largest free region
|
||||||
|
params.endLba = 0;
|
||||||
|
params.typeGuid = linux_fs_type_guid();
|
||||||
|
set_gpt_name(params, "MontaukOS");
|
||||||
|
|
||||||
|
r = montauk::gpt_add(¶ms);
|
||||||
|
if (r < 0) {
|
||||||
|
add_log("ERROR: Failed to create Linux partition");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_log(" ext2 partition created");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
// Step 4: Find partition indices
|
||||||
|
Montauk::PartInfo parts[MAX_PARTS];
|
||||||
|
int part_count = montauk::partlist(parts, MAX_PARTS);
|
||||||
|
int efi_idx = -1, linux_idx = -1;
|
||||||
|
for (int p = 0; p < part_count; p++) {
|
||||||
|
if (parts[p].blockDev != disk) continue;
|
||||||
|
// EFI partition starts at FIRST_USABLE_LBA
|
||||||
|
if (parts[p].startLba == FIRST_USABLE_LBA)
|
||||||
|
efi_idx = p;
|
||||||
|
else
|
||||||
|
linux_idx = p;
|
||||||
|
}
|
||||||
|
if (efi_idx < 0 || linux_idx < 0) {
|
||||||
|
add_log("ERROR: Could not find partitions");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Format EFI partition as FAT32
|
||||||
|
add_log("Formatting EFI partition (FAT32)...");
|
||||||
|
flush_ui();
|
||||||
|
{
|
||||||
|
Montauk::FsFormatParams fmt;
|
||||||
|
montauk::memset(&fmt, 0, sizeof(fmt));
|
||||||
|
fmt.partIndex = efi_idx;
|
||||||
|
fmt.fsType = Montauk::FS_TYPE_FAT32;
|
||||||
|
r = montauk::fs_format(&fmt);
|
||||||
|
if (r < 0) {
|
||||||
|
add_log("ERROR: FAT32 format failed");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_log(" FAT32 formatted");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
// Step 6: Format Linux partition as ext2
|
||||||
|
add_log("Formatting root partition (ext2)...");
|
||||||
|
flush_ui();
|
||||||
|
{
|
||||||
|
Montauk::FsFormatParams fmt;
|
||||||
|
montauk::memset(&fmt, 0, sizeof(fmt));
|
||||||
|
fmt.partIndex = linux_idx;
|
||||||
|
fmt.fsType = Montauk::FS_TYPE_EXT2;
|
||||||
|
r = montauk::fs_format(&fmt);
|
||||||
|
if (r < 0) {
|
||||||
|
add_log("ERROR: ext2 format failed");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
add_log(" ext2 formatted");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
// Step 7: Mount both partitions
|
||||||
|
add_log("Mounting partitions...");
|
||||||
|
flush_ui();
|
||||||
|
int efi_drive = 14, root_drive = 15;
|
||||||
|
r = montauk::fs_mount(efi_idx, efi_drive);
|
||||||
|
if (r < 0) {
|
||||||
|
add_log("ERROR: EFI mount failed");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
r = montauk::fs_mount(linux_idx, root_drive);
|
||||||
|
if (r < 0) {
|
||||||
|
add_log("ERROR: ext2 mount failed");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
add_log(" Mounted");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
char efi_root[8], ext2_root[8];
|
||||||
|
snprintf(efi_root, sizeof(efi_root), "%d:/", efi_drive);
|
||||||
|
snprintf(ext2_root, sizeof(ext2_root), "%d:/", root_drive);
|
||||||
|
|
||||||
|
// Step 8: Create EFI boot directory structure
|
||||||
|
add_log("Creating boot directories...");
|
||||||
|
flush_ui();
|
||||||
|
snprintf(path_buf, sizeof(path_buf), "%d:/EFI", efi_drive);
|
||||||
|
montauk::fmkdir(path_buf);
|
||||||
|
snprintf(path_buf, sizeof(path_buf), "%d:/EFI/BOOT", efi_drive);
|
||||||
|
montauk::fmkdir(path_buf);
|
||||||
|
snprintf(path_buf, sizeof(path_buf), "%d:/boot", efi_drive);
|
||||||
|
montauk::fmkdir(path_buf);
|
||||||
|
|
||||||
|
// Step 9: Copy boot files to EFI partition
|
||||||
|
add_log("Copying boot files to EFI partition...");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
char efi_boot[16];
|
||||||
|
snprintf(efi_boot, sizeof(efi_boot), "%d:/boot", efi_drive);
|
||||||
|
if (!copy_recursive("0:/boot", efi_boot)) {
|
||||||
|
add_log("ERROR: Boot file copy failed");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 10: Copy root filesystem to ext2 partition (skip boot/)
|
||||||
|
add_log("Copying root filesystem to ext2...");
|
||||||
|
flush_ui();
|
||||||
|
if (!copy_recursive("0:/", ext2_root, "boot")) {
|
||||||
|
add_log("ERROR: Root filesystem copy failed");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char copy_msg[64];
|
||||||
|
snprintf(copy_msg, sizeof(copy_msg), " %d files, %d directories copied",
|
||||||
|
g_files_copied, g_dirs_created);
|
||||||
|
add_log(copy_msg);
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
// Step 11: Write limine.conf to EFI partition
|
||||||
|
add_log("Writing boot configuration...");
|
||||||
|
flush_ui();
|
||||||
|
snprintf(path_buf, sizeof(path_buf), "%d:/boot/limine/limine.conf", efi_drive);
|
||||||
|
{
|
||||||
|
static const char limine_conf[] =
|
||||||
|
"timeout: 0\n"
|
||||||
|
"\n"
|
||||||
|
"/montaukos\n"
|
||||||
|
" protocol: limine\n"
|
||||||
|
" path: boot():/boot/kernel\n";
|
||||||
|
|
||||||
|
int conf_fd = montauk::fcreate(path_buf);
|
||||||
|
if (conf_fd < 0) {
|
||||||
|
add_log("ERROR: Failed to write limine.conf");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
montauk::fwrite(conf_fd, (const uint8_t*)limine_conf, 0, sizeof(limine_conf) - 1);
|
||||||
|
montauk::close(conf_fd);
|
||||||
|
}
|
||||||
|
add_log(" limine.conf written");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
// Step 12: Copy BOOTX64.EFI to EFI/BOOT/
|
||||||
|
add_log("Installing EFI bootloader...");
|
||||||
|
flush_ui();
|
||||||
|
snprintf(path_buf, sizeof(path_buf), "%d:/EFI/BOOT/BOOTX64.EFI", efi_drive);
|
||||||
|
if (!copy_file("0:/boot/limine/BOOTX64.EFI", path_buf)) {
|
||||||
|
add_log("ERROR: Failed to install BOOTX64.EFI");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
add_log(" BOOTX64.EFI installed");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
// Done
|
||||||
|
add_log("Installation complete!");
|
||||||
|
st.step = STEP_DONE;
|
||||||
|
set_status("MontaukOS installed successfully");
|
||||||
|
flush_ui();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Install: Single FAT32 scheme (legacy)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static void install_single_fat32(int disk) {
|
||||||
|
auto& st = g_state;
|
||||||
|
char path_buf[64];
|
||||||
|
int r;
|
||||||
|
|
||||||
|
// Step 1: Initialize GPT
|
||||||
|
add_log("Initializing GPT...");
|
||||||
|
flush_ui();
|
||||||
|
r = montauk::gpt_init(disk);
|
||||||
|
if (r < 0) {
|
||||||
|
add_log("ERROR: Failed to initialize GPT");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
}
|
}
|
||||||
add_log(" GPT initialized");
|
add_log(" GPT initialized");
|
||||||
flush_ui();
|
flush_ui();
|
||||||
@@ -276,18 +518,12 @@ void do_install() {
|
|||||||
params.startLba = 0;
|
params.startLba = 0;
|
||||||
params.endLba = 0;
|
params.endLba = 0;
|
||||||
params.typeGuid = esp_type_guid();
|
params.typeGuid = esp_type_guid();
|
||||||
|
set_gpt_name(params, "EFI System");
|
||||||
const char* pname = "EFI System";
|
|
||||||
int i = 0;
|
|
||||||
for (; pname[i] && i < 71; i++) params.name[i] = pname[i];
|
|
||||||
params.name[i] = '\0';
|
|
||||||
|
|
||||||
r = montauk::gpt_add(¶ms);
|
r = montauk::gpt_add(¶ms);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
add_log("ERROR: Failed to create partition");
|
add_log("ERROR: Failed to create partition");
|
||||||
flush_ui();
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
st.step = STEP_ERROR;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
add_log(" Partition created");
|
add_log(" Partition created");
|
||||||
flush_ui();
|
flush_ui();
|
||||||
@@ -308,9 +544,7 @@ void do_install() {
|
|||||||
|
|
||||||
if (esp_index < 0) {
|
if (esp_index < 0) {
|
||||||
add_log("ERROR: Could not find partition");
|
add_log("ERROR: Could not find partition");
|
||||||
flush_ui();
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
st.step = STEP_ERROR;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Montauk::FsFormatParams fmt;
|
Montauk::FsFormatParams fmt;
|
||||||
@@ -321,9 +555,7 @@ void do_install() {
|
|||||||
r = montauk::fs_format(&fmt);
|
r = montauk::fs_format(&fmt);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
add_log("ERROR: FAT32 format failed");
|
add_log("ERROR: FAT32 format failed");
|
||||||
flush_ui();
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
st.step = STEP_ERROR;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
add_log(" FAT32 formatted");
|
add_log(" FAT32 formatted");
|
||||||
flush_ui();
|
flush_ui();
|
||||||
@@ -335,9 +567,7 @@ void do_install() {
|
|||||||
r = montauk::fs_mount(esp_index, drive_num);
|
r = montauk::fs_mount(esp_index, drive_num);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
add_log("ERROR: Mount failed");
|
add_log("ERROR: Mount failed");
|
||||||
flush_ui();
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
st.step = STEP_ERROR;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char drive_root[8];
|
char drive_root[8];
|
||||||
@@ -349,7 +579,6 @@ void do_install() {
|
|||||||
add_log("Creating boot directories...");
|
add_log("Creating boot directories...");
|
||||||
flush_ui();
|
flush_ui();
|
||||||
|
|
||||||
char path_buf[64];
|
|
||||||
snprintf(path_buf, sizeof(path_buf), "%d:/EFI", drive_num);
|
snprintf(path_buf, sizeof(path_buf), "%d:/EFI", drive_num);
|
||||||
montauk::fmkdir(path_buf);
|
montauk::fmkdir(path_buf);
|
||||||
snprintf(path_buf, sizeof(path_buf), "%d:/EFI/BOOT", drive_num);
|
snprintf(path_buf, sizeof(path_buf), "%d:/EFI/BOOT", drive_num);
|
||||||
@@ -361,9 +590,7 @@ void do_install() {
|
|||||||
|
|
||||||
if (!copy_recursive("0:/", drive_root)) {
|
if (!copy_recursive("0:/", drive_root)) {
|
||||||
add_log("ERROR: File copy failed");
|
add_log("ERROR: File copy failed");
|
||||||
flush_ui();
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
st.step = STEP_ERROR;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char copy_msg[64];
|
char copy_msg[64];
|
||||||
@@ -387,9 +614,7 @@ void do_install() {
|
|||||||
int conf_fd = montauk::fcreate(path_buf);
|
int conf_fd = montauk::fcreate(path_buf);
|
||||||
if (conf_fd < 0) {
|
if (conf_fd < 0) {
|
||||||
add_log("ERROR: Failed to write limine.conf");
|
add_log("ERROR: Failed to write limine.conf");
|
||||||
flush_ui();
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
st.step = STEP_ERROR;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
montauk::fwrite(conf_fd, (const uint8_t*)limine_conf, 0, sizeof(limine_conf) - 1);
|
montauk::fwrite(conf_fd, (const uint8_t*)limine_conf, 0, sizeof(limine_conf) - 1);
|
||||||
montauk::close(conf_fd);
|
montauk::close(conf_fd);
|
||||||
@@ -398,16 +623,12 @@ void do_install() {
|
|||||||
flush_ui();
|
flush_ui();
|
||||||
|
|
||||||
// Step 8: Copy BOOTX64.EFI to EFI/BOOT/
|
// Step 8: Copy BOOTX64.EFI to EFI/BOOT/
|
||||||
// (The recursive copy already put it at boot/limine/BOOTX64.EFI,
|
|
||||||
// but UEFI firmware requires it at /EFI/BOOT/BOOTX64.EFI)
|
|
||||||
add_log("Installing EFI bootloader...");
|
add_log("Installing EFI bootloader...");
|
||||||
flush_ui();
|
flush_ui();
|
||||||
snprintf(path_buf, sizeof(path_buf), "%d:/EFI/BOOT/BOOTX64.EFI", drive_num);
|
snprintf(path_buf, sizeof(path_buf), "%d:/EFI/BOOT/BOOTX64.EFI", drive_num);
|
||||||
if (!copy_file("0:/boot/limine/BOOTX64.EFI", path_buf)) {
|
if (!copy_file("0:/boot/limine/BOOTX64.EFI", path_buf)) {
|
||||||
add_log("ERROR: Failed to install BOOTX64.EFI");
|
add_log("ERROR: Failed to install BOOTX64.EFI");
|
||||||
flush_ui();
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
st.step = STEP_ERROR;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
add_log(" BOOTX64.EFI installed");
|
add_log(" BOOTX64.EFI installed");
|
||||||
flush_ui();
|
flush_ui();
|
||||||
@@ -418,3 +639,109 @@ void do_install() {
|
|||||||
set_status("MontaukOS installed successfully");
|
set_status("MontaukOS installed successfully");
|
||||||
flush_ui();
|
flush_ui();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Install MontaukOS to the selected disk
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
void do_install() {
|
||||||
|
auto& st = g_state;
|
||||||
|
int disk = st.selected_disk;
|
||||||
|
|
||||||
|
if (disk < 0 || disk >= st.disk_count) {
|
||||||
|
st.step = STEP_ERROR;
|
||||||
|
add_log("No disk selected");
|
||||||
|
flush_ui();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_files_copied = 0;
|
||||||
|
g_dirs_created = 0;
|
||||||
|
|
||||||
|
switch (st.partition_scheme) {
|
||||||
|
case SCHEME_EFI_EXT2:
|
||||||
|
install_efi_ext2(disk);
|
||||||
|
break;
|
||||||
|
case SCHEME_SINGLE_FAT32:
|
||||||
|
install_single_fat32(disk);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
st.step = STEP_ERROR;
|
||||||
|
add_log("Unknown partition scheme");
|
||||||
|
flush_ui();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Update OS and apps on an existing partition
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
void do_update() {
|
||||||
|
auto& st = g_state;
|
||||||
|
|
||||||
|
if (st.selected_part < 0 || st.selected_part >= st.part_count) {
|
||||||
|
st.step = STEP_ERROR;
|
||||||
|
add_log("No partition selected");
|
||||||
|
flush_ui();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_files_copied = 0;
|
||||||
|
g_dirs_created = 0;
|
||||||
|
|
||||||
|
int part_idx = st.selected_part;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
// Step 1: Mount the target partition
|
||||||
|
add_log("Mounting target partition...");
|
||||||
|
flush_ui();
|
||||||
|
int drive_num = 15;
|
||||||
|
r = montauk::fs_mount(part_idx, drive_num);
|
||||||
|
if (r < 0) {
|
||||||
|
add_log("ERROR: Mount failed");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char drive_root[8];
|
||||||
|
snprintf(drive_root, sizeof(drive_root), "%d:/", drive_num);
|
||||||
|
add_log(" Mounted");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
// Step 2: Update os/ — remove old and copy fresh
|
||||||
|
add_log("Updating os/...");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
char path_buf[64];
|
||||||
|
snprintf(path_buf, sizeof(path_buf), "%d:/os", drive_num);
|
||||||
|
montauk::fmkdir(path_buf); // ensure it exists
|
||||||
|
|
||||||
|
if (!copy_recursive("0:/os", path_buf)) {
|
||||||
|
add_log("ERROR: Failed to update os/");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Update apps/ — copy all, overwriting existing and adding new
|
||||||
|
add_log("Updating apps/...");
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
snprintf(path_buf, sizeof(path_buf), "%d:/apps", drive_num);
|
||||||
|
montauk::fmkdir(path_buf); // ensure it exists
|
||||||
|
|
||||||
|
if (!copy_recursive("0:/apps", path_buf)) {
|
||||||
|
add_log("ERROR: Failed to update apps/");
|
||||||
|
flush_ui(); st.step = STEP_ERROR; return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char copy_msg[64];
|
||||||
|
snprintf(copy_msg, sizeof(copy_msg), " %d files, %d directories updated",
|
||||||
|
g_files_copied, g_dirs_created);
|
||||||
|
add_log(copy_msg);
|
||||||
|
flush_ui();
|
||||||
|
|
||||||
|
// Done
|
||||||
|
add_log("Update complete!");
|
||||||
|
st.step = STEP_DONE;
|
||||||
|
set_status("MontaukOS updated successfully");
|
||||||
|
flush_ui();
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,13 +56,23 @@ static constexpr Color DISABLED_BG = Color::from_rgb(0xBB, 0xBB, 0xBB);
|
|||||||
// Install steps
|
// Install steps
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
enum InstallerMode {
|
||||||
|
MODE_INSTALL = 0,
|
||||||
|
MODE_UPDATE,
|
||||||
|
};
|
||||||
|
|
||||||
enum InstallStep {
|
enum InstallStep {
|
||||||
|
STEP_MODE_SELECT,
|
||||||
STEP_SELECT_DISK,
|
STEP_SELECT_DISK,
|
||||||
STEP_PARTITION_SCHEME,
|
STEP_PARTITION_SCHEME,
|
||||||
STEP_CONFIRM,
|
STEP_CONFIRM,
|
||||||
STEP_INSTALLING,
|
STEP_INSTALLING,
|
||||||
STEP_DONE,
|
STEP_DONE,
|
||||||
STEP_ERROR,
|
STEP_ERROR,
|
||||||
|
// Update flow
|
||||||
|
STEP_UPDATE_SELECT_PART,
|
||||||
|
STEP_UPDATE_CONFIRM,
|
||||||
|
STEP_UPDATING,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -70,15 +80,18 @@ enum InstallStep {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
enum PartScheme {
|
enum PartScheme {
|
||||||
SCHEME_SINGLE_FAT32 = 0,
|
SCHEME_EFI_EXT2 = 0,
|
||||||
|
SCHEME_SINGLE_FAT32,
|
||||||
SCHEME_COUNT,
|
SCHEME_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* scheme_names[] = {
|
static const char* scheme_names[] = {
|
||||||
|
"EFI + ext2 (recommended)",
|
||||||
"Single FAT32 partition (entire disk)",
|
"Single FAT32 partition (entire disk)",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* scheme_descs[] = {
|
static const char* scheme_descs[] = {
|
||||||
|
"EFI boot partition + ext2 root filesystem.",
|
||||||
"One partition for boot, OS, and data. Simple and compatible.",
|
"One partition for boot, OS, and data. Simple and compatible.",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,11 +103,19 @@ static constexpr int LOG_LINES = 16;
|
|||||||
static constexpr int LOG_LINE_LEN = 64;
|
static constexpr int LOG_LINE_LEN = 64;
|
||||||
|
|
||||||
struct InstallerState {
|
struct InstallerState {
|
||||||
|
int mode; // InstallerMode
|
||||||
|
|
||||||
|
// Install flow
|
||||||
Montauk::DiskInfo disks[MAX_DISKS];
|
Montauk::DiskInfo disks[MAX_DISKS];
|
||||||
int disk_count;
|
int disk_count;
|
||||||
int selected_disk;
|
int selected_disk;
|
||||||
int partition_scheme;
|
int partition_scheme;
|
||||||
|
|
||||||
|
// Update flow
|
||||||
|
Montauk::PartInfo parts[MAX_PARTS];
|
||||||
|
int part_count;
|
||||||
|
int selected_part;
|
||||||
|
|
||||||
InstallStep step;
|
InstallStep step;
|
||||||
char log[LOG_LINES][LOG_LINE_LEN];
|
char log[LOG_LINES][LOG_LINE_LEN];
|
||||||
int log_count;
|
int log_count;
|
||||||
@@ -111,6 +132,7 @@ extern int g_win_w, g_win_h;
|
|||||||
extern InstallerState g_state;
|
extern InstallerState g_state;
|
||||||
extern TrueTypeFont* g_font;
|
extern TrueTypeFont* g_font;
|
||||||
extern uint32_t* g_pixels;
|
extern uint32_t* g_pixels;
|
||||||
|
extern uint32_t* g_backbuf;
|
||||||
extern int g_win_id;
|
extern int g_win_id;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -134,4 +156,6 @@ void render(uint32_t* pixels);
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
void installer_refresh_disks();
|
void installer_refresh_disks();
|
||||||
|
void installer_refresh_parts();
|
||||||
void do_install();
|
void do_install();
|
||||||
|
void do_update();
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ int FONT_SM = 14;
|
|||||||
InstallerState g_state;
|
InstallerState g_state;
|
||||||
TrueTypeFont* g_font = nullptr;
|
TrueTypeFont* g_font = nullptr;
|
||||||
uint32_t* g_pixels = nullptr;
|
uint32_t* g_pixels = nullptr;
|
||||||
|
uint32_t* g_backbuf = nullptr;
|
||||||
int g_win_id = -1;
|
int g_win_id = -1;
|
||||||
|
|
||||||
void apply_scale(int scale) {
|
void apply_scale(int scale) {
|
||||||
@@ -55,8 +56,9 @@ void add_log(const char* msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void flush_ui() {
|
void flush_ui() {
|
||||||
if (g_pixels && g_win_id >= 0) {
|
if (g_pixels && g_backbuf && g_win_id >= 0) {
|
||||||
render(g_pixels);
|
render(g_backbuf);
|
||||||
|
montauk::memcpy(g_pixels, g_backbuf, g_win_w * g_win_h * 4);
|
||||||
montauk::win_present(g_win_id);
|
montauk::win_present(g_win_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,12 +86,34 @@ void format_disk_size(char* buf, int bufsize, uint64_t sectors, uint16_t sectorS
|
|||||||
static bool handle_click(int mx, int my) {
|
static bool handle_click(int mx, int my) {
|
||||||
auto& st = g_state;
|
auto& st = g_state;
|
||||||
int fh = g_font ? g_font->get_cache(FONT_SIZE)->ascent - g_font->get_cache(FONT_SIZE)->descent : 16;
|
int fh = g_font ? g_font->get_cache(FONT_SIZE)->ascent - g_font->get_cache(FONT_SIZE)->descent : 16;
|
||||||
|
int fh_sm = g_font ? g_font->get_cache(FONT_SM)->ascent - g_font->get_cache(FONT_SM)->descent : 12;
|
||||||
|
|
||||||
// Common bottom button area
|
// Common bottom button area
|
||||||
int btn_w = 120, btn_h = 34;
|
int btn_w = 120, btn_h = 34;
|
||||||
int btn_y = g_win_h - STATUS_H - btn_h - 12;
|
int btn_y = g_win_h - STATUS_H - btn_h - 12;
|
||||||
|
|
||||||
if (st.step == STEP_SELECT_DISK) {
|
if (st.step == STEP_MODE_SELECT) {
|
||||||
|
// Two mode cards
|
||||||
|
int y = CONTENT_TOP + 16 + fh + 4 + fh_sm + 16;
|
||||||
|
int card_h = 52, card_x = 16, card_w = g_win_w - 32;
|
||||||
|
|
||||||
|
// Install card
|
||||||
|
if (mx >= card_x && mx < card_x + card_w && my >= y && my < y + card_h) {
|
||||||
|
st.mode = MODE_INSTALL;
|
||||||
|
installer_refresh_disks();
|
||||||
|
st.step = STEP_SELECT_DISK;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
y += card_h + 8;
|
||||||
|
|
||||||
|
// Update card
|
||||||
|
if (mx >= card_x && mx < card_x + card_w && my >= y && my < y + card_h) {
|
||||||
|
st.mode = MODE_UPDATE;
|
||||||
|
installer_refresh_parts();
|
||||||
|
st.step = STEP_UPDATE_SELECT_PART;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (st.step == STEP_SELECT_DISK) {
|
||||||
// Disk list items
|
// Disk list items
|
||||||
int y = CONTENT_TOP + 40 + fh + 12;
|
int y = CONTENT_TOP + 40 + fh + 12;
|
||||||
for (int i = 0; i < st.disk_count; i++) {
|
for (int i = 0; i < st.disk_count; i++) {
|
||||||
@@ -171,6 +195,54 @@ static bool handle_click(int mx, int my) {
|
|||||||
my >= btn_y && my < btn_y + btn_h) {
|
my >= btn_y && my < btn_y + btn_h) {
|
||||||
return false; // signal quit
|
return false; // signal quit
|
||||||
}
|
}
|
||||||
|
} else if (st.step == STEP_UPDATE_SELECT_PART) {
|
||||||
|
// Partition list items
|
||||||
|
int y = CONTENT_TOP + 16 + fh + 4 + fh_sm + 12;
|
||||||
|
for (int i = 0; i < st.part_count; i++) {
|
||||||
|
int item_h = 48;
|
||||||
|
if (mx >= 16 && mx < g_win_w - 16 && my >= y && my < y + item_h) {
|
||||||
|
st.selected_part = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
y += item_h + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Next" button
|
||||||
|
int next_x = g_win_w - btn_w - 16;
|
||||||
|
if (st.selected_part >= 0 &&
|
||||||
|
mx >= next_x && mx < next_x + btn_w &&
|
||||||
|
my >= btn_y && my < btn_y + btn_h) {
|
||||||
|
st.step = STEP_UPDATE_CONFIRM;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Back" button
|
||||||
|
int back_x = next_x - btn_w - 8;
|
||||||
|
if (mx >= back_x && mx < back_x + btn_w &&
|
||||||
|
my >= btn_y && my < btn_y + btn_h) {
|
||||||
|
st.step = STEP_MODE_SELECT;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (st.step == STEP_UPDATE_CONFIRM) {
|
||||||
|
int center_x = g_win_w / 2;
|
||||||
|
int gap = 16;
|
||||||
|
|
||||||
|
// "Update" button
|
||||||
|
int upd_x = center_x - btn_w - gap / 2;
|
||||||
|
if (mx >= upd_x && mx < upd_x + btn_w &&
|
||||||
|
my >= btn_y && my < btn_y + btn_h) {
|
||||||
|
st.step = STEP_UPDATING;
|
||||||
|
st.log_count = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Back" button
|
||||||
|
int back_x = center_x + gap / 2;
|
||||||
|
if (mx >= back_x && mx < back_x + btn_w &&
|
||||||
|
my >= btn_y && my < btn_y + btn_h) {
|
||||||
|
st.step = STEP_UPDATE_SELECT_PART;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -183,8 +255,9 @@ static bool handle_click(int mx, int my) {
|
|||||||
extern "C" void _start() {
|
extern "C" void _start() {
|
||||||
montauk::memset(&g_state, 0, sizeof(g_state));
|
montauk::memset(&g_state, 0, sizeof(g_state));
|
||||||
g_state.selected_disk = -1;
|
g_state.selected_disk = -1;
|
||||||
g_state.partition_scheme = SCHEME_SINGLE_FAT32;
|
g_state.selected_part = -1;
|
||||||
g_state.step = STEP_SELECT_DISK;
|
g_state.partition_scheme = SCHEME_EFI_EXT2;
|
||||||
|
g_state.step = STEP_MODE_SELECT;
|
||||||
|
|
||||||
// Load font
|
// Load font
|
||||||
{
|
{
|
||||||
@@ -209,8 +282,10 @@ extern "C" void _start() {
|
|||||||
uint32_t* pixels = (uint32_t*)(uintptr_t)wres.pixelVa;
|
uint32_t* pixels = (uint32_t*)(uintptr_t)wres.pixelVa;
|
||||||
g_pixels = pixels;
|
g_pixels = pixels;
|
||||||
g_win_id = win_id;
|
g_win_id = win_id;
|
||||||
|
g_backbuf = (uint32_t*)montauk::malloc(g_win_w * g_win_h * 4);
|
||||||
|
|
||||||
render(pixels);
|
render(g_backbuf);
|
||||||
|
montauk::memcpy(pixels, g_backbuf, g_win_w * g_win_h * 4);
|
||||||
montauk::win_present(win_id);
|
montauk::win_present(win_id);
|
||||||
|
|
||||||
bool install_triggered = false;
|
bool install_triggered = false;
|
||||||
@@ -225,11 +300,15 @@ extern "C" void _start() {
|
|||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
if (g_state.step == STEP_INSTALLING && !install_triggered) {
|
if (g_state.step == STEP_INSTALLING && !install_triggered) {
|
||||||
install_triggered = true;
|
install_triggered = true;
|
||||||
render(pixels);
|
flush_ui();
|
||||||
montauk::win_present(win_id);
|
|
||||||
do_install();
|
do_install();
|
||||||
render(pixels);
|
flush_ui();
|
||||||
montauk::win_present(win_id);
|
}
|
||||||
|
if (g_state.step == STEP_UPDATING && !install_triggered) {
|
||||||
|
install_triggered = true;
|
||||||
|
flush_ui();
|
||||||
|
do_update();
|
||||||
|
flush_ui();
|
||||||
}
|
}
|
||||||
montauk::sleep_ms(16);
|
montauk::sleep_ms(16);
|
||||||
continue;
|
continue;
|
||||||
@@ -246,6 +325,9 @@ extern "C" void _start() {
|
|||||||
g_win_w = ev.resize.w;
|
g_win_w = ev.resize.w;
|
||||||
g_win_h = ev.resize.h;
|
g_win_h = ev.resize.h;
|
||||||
pixels = (uint32_t*)(uintptr_t)montauk::win_resize(win_id, g_win_w, g_win_h);
|
pixels = (uint32_t*)(uintptr_t)montauk::win_resize(win_id, g_win_w, g_win_h);
|
||||||
|
g_pixels = pixels;
|
||||||
|
montauk::mfree(g_backbuf);
|
||||||
|
g_backbuf = (uint32_t*)montauk::malloc(g_win_w * g_win_h * 4);
|
||||||
redraw = true;
|
redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +345,7 @@ extern "C" void _start() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redraw) { render(pixels); montauk::win_present(win_id); }
|
if (redraw) flush_ui();
|
||||||
}
|
}
|
||||||
|
|
||||||
montauk::win_destroy(win_id);
|
montauk::win_destroy(win_id);
|
||||||
|
|||||||
@@ -137,6 +137,36 @@ static void px_button_outline(uint32_t* px, int bw, int bh,
|
|||||||
px_text(px, bw, bh, x + (w - tw) / 2, y + (h - fh) / 2, label, fg);
|
px_text(px, bw, bh, x + (w - tw) / 2, y + (h - fh) / 2, label, fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Render: mode selection step
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static void render_mode_select(uint32_t* px) {
|
||||||
|
int fh = font_h();
|
||||||
|
int y = CONTENT_TOP + 16;
|
||||||
|
|
||||||
|
px_text(px, g_win_w, g_win_h, 16, y, "Welcome to MontaukOS Installer", TEXT_COLOR);
|
||||||
|
y += fh + 4;
|
||||||
|
px_text(px, g_win_w, g_win_h, 16, y, "Choose an action below.", DIM_TEXT, FONT_SM);
|
||||||
|
y += font_h(FONT_SM) + 16;
|
||||||
|
|
||||||
|
// Install card
|
||||||
|
int card_h = 52, card_x = 16, card_w = g_win_w - 32;
|
||||||
|
px_fill_rounded(px, g_win_w, g_win_h, card_x, y, card_w, card_h, 6,
|
||||||
|
Color::from_rgb(0xF4, 0xF4, 0xF4));
|
||||||
|
px_text(px, g_win_w, g_win_h, card_x + 16, y + 8, "Install", TEXT_COLOR);
|
||||||
|
px_text(px, g_win_w, g_win_h, card_x + 16, y + 8 + fh + 2,
|
||||||
|
"Erase a disk and install MontaukOS.", DIM_TEXT, FONT_SM);
|
||||||
|
y += card_h + 8;
|
||||||
|
|
||||||
|
// Update card
|
||||||
|
px_fill_rounded(px, g_win_w, g_win_h, card_x, y, card_w, card_h, 6,
|
||||||
|
Color::from_rgb(0xF4, 0xF4, 0xF4));
|
||||||
|
px_text(px, g_win_w, g_win_h, card_x + 16, y + 8, "Update", TEXT_COLOR);
|
||||||
|
px_text(px, g_win_w, g_win_h, card_x + 16, y + 8 + fh + 2,
|
||||||
|
"Update OS and apps on an existing partition.", DIM_TEXT, FONT_SM);
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Render: disk selection step
|
// Render: disk selection step
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -161,22 +191,24 @@ static void render_select_disk(uint32_t* px) {
|
|||||||
int item_w = g_win_w - 32;
|
int item_w = g_win_w - 32;
|
||||||
bool sel = (i == st.selected_disk);
|
bool sel = (i == st.selected_disk);
|
||||||
|
|
||||||
Color bg = sel
|
px_fill_rounded(px, g_win_w, g_win_h, item_x, y, item_w, item_h, 6,
|
||||||
? Color::from_rgb(0xD8, 0xE8, 0xF8)
|
Color::from_rgb(0xF4, 0xF4, 0xF4));
|
||||||
: Color::from_rgb(0xF4, 0xF4, 0xF4);
|
|
||||||
|
|
||||||
px_fill_rounded(px, g_win_w, g_win_h, item_x, y, item_w, item_h, 6, bg);
|
|
||||||
|
|
||||||
|
// Radio indicator
|
||||||
|
int rx = item_x + 14;
|
||||||
|
int ry = y + item_h / 2;
|
||||||
|
px_fill_rounded(px, g_win_w, g_win_h, rx - 6, ry - 6, 12, 12, 6, DIM_TEXT);
|
||||||
|
px_fill_rounded(px, g_win_w, g_win_h, rx - 5, ry - 5, 10, 10, 5, BG_COLOR);
|
||||||
if (sel)
|
if (sel)
|
||||||
px_fill_rounded(px, g_win_w, g_win_h, item_x, y, 4, item_h, 2, ACCENT);
|
px_fill_rounded(px, g_win_w, g_win_h, rx - 3, ry - 3, 6, 6, 3, ACCENT);
|
||||||
|
|
||||||
px_text(px, g_win_w, g_win_h, item_x + 12, y + 6, st.disks[i].model, TEXT_COLOR);
|
px_text(px, g_win_w, g_win_h, item_x + 32, y + 6, st.disks[i].model, TEXT_COLOR);
|
||||||
|
|
||||||
char info[64], sz[24];
|
char info[64], sz[24];
|
||||||
format_disk_size(sz, sizeof(sz), st.disks[i].sectorCount, st.disks[i].sectorSizeLog);
|
format_disk_size(sz, sizeof(sz), st.disks[i].sectorCount, st.disks[i].sectorSizeLog);
|
||||||
const char* dtype = st.disks[i].rpm == 1 ? "SSD" : "HDD";
|
const char* dtype = st.disks[i].rpm == 1 ? "SSD" : "HDD";
|
||||||
snprintf(info, sizeof(info), "%s %s (Disk %d)", sz, dtype, i);
|
snprintf(info, sizeof(info), "%s %s (Disk %d)", sz, dtype, i);
|
||||||
px_text(px, g_win_w, g_win_h, item_x + 12, y + 6 + fh + 2, info, DIM_TEXT, FONT_SM);
|
px_text(px, g_win_w, g_win_h, item_x + 32, y + 6 + fh + 2, info, DIM_TEXT, FONT_SM);
|
||||||
|
|
||||||
y += item_h + 4;
|
y += item_h + 4;
|
||||||
}
|
}
|
||||||
@@ -217,14 +249,9 @@ static void render_partition_scheme(uint32_t* px) {
|
|||||||
int item_w = g_win_w - 32;
|
int item_w = g_win_w - 32;
|
||||||
|
|
||||||
bool selected = (i == st.partition_scheme);
|
bool selected = (i == st.partition_scheme);
|
||||||
Color bg = selected
|
|
||||||
? Color::from_rgb(0xD8, 0xE8, 0xF8)
|
|
||||||
: Color::from_rgb(0xF4, 0xF4, 0xF4);
|
|
||||||
|
|
||||||
px_fill_rounded(px, g_win_w, g_win_h, item_x, y, item_w, item_h, 6, bg);
|
px_fill_rounded(px, g_win_w, g_win_h, item_x, y, item_w, item_h, 6,
|
||||||
|
Color::from_rgb(0xF4, 0xF4, 0xF4));
|
||||||
if (selected)
|
|
||||||
px_fill_rounded(px, g_win_w, g_win_h, item_x, y, 4, item_h, 2, ACCENT);
|
|
||||||
|
|
||||||
// Radio indicator
|
// Radio indicator
|
||||||
int rx = item_x + 14;
|
int rx = item_x + 14;
|
||||||
@@ -299,6 +326,112 @@ static void render_confirm(uint32_t* px) {
|
|||||||
"Back", BORDER_COLOR, DIM_TEXT, TB_BTN_RAD);
|
"Back", BORDER_COLOR, DIM_TEXT, TB_BTN_RAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Render: update partition selection step
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static void render_update_select_part(uint32_t* px) {
|
||||||
|
auto& st = g_state;
|
||||||
|
int fh = font_h();
|
||||||
|
int fh_sm = font_h(FONT_SM);
|
||||||
|
|
||||||
|
int y = CONTENT_TOP + 16;
|
||||||
|
px_text(px, g_win_w, g_win_h, 16, y, "Select a root partition", TEXT_COLOR);
|
||||||
|
y += fh + 4;
|
||||||
|
px_text(px, g_win_w, g_win_h, 16, y,
|
||||||
|
"OS and apps will be updated on this partition.", DIM_TEXT, FONT_SM);
|
||||||
|
y += fh_sm + 12;
|
||||||
|
|
||||||
|
if (st.part_count == 0) {
|
||||||
|
px_text(px, g_win_w, g_win_h, 16, y, "No partitions detected", FAINT_TEXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < st.part_count; i++) {
|
||||||
|
int item_h = 48;
|
||||||
|
int item_x = 16;
|
||||||
|
int item_w = g_win_w - 32;
|
||||||
|
bool sel = (i == st.selected_part);
|
||||||
|
|
||||||
|
px_fill_rounded(px, g_win_w, g_win_h, item_x, y, item_w, item_h, 6,
|
||||||
|
Color::from_rgb(0xF4, 0xF4, 0xF4));
|
||||||
|
|
||||||
|
// Radio indicator
|
||||||
|
int rx = item_x + 14;
|
||||||
|
int ry = y + item_h / 2;
|
||||||
|
px_fill_rounded(px, g_win_w, g_win_h, rx - 6, ry - 6, 12, 12, 6, DIM_TEXT);
|
||||||
|
px_fill_rounded(px, g_win_w, g_win_h, rx - 5, ry - 5, 10, 10, 5, BG_COLOR);
|
||||||
|
if (sel)
|
||||||
|
px_fill_rounded(px, g_win_w, g_win_h, rx - 3, ry - 3, 6, 6, 3, ACCENT);
|
||||||
|
|
||||||
|
// Partition name
|
||||||
|
const char* pname = st.parts[i].name[0] ? st.parts[i].name : "Unnamed";
|
||||||
|
px_text(px, g_win_w, g_win_h, item_x + 32, y + 6, pname, TEXT_COLOR);
|
||||||
|
|
||||||
|
// Info line: type + size
|
||||||
|
char info[64], sz[24];
|
||||||
|
format_disk_size(sz, sizeof(sz), st.parts[i].sectorCount, 512);
|
||||||
|
snprintf(info, sizeof(info), "%s %s (Disk %d)",
|
||||||
|
sz, st.parts[i].typeName, st.parts[i].blockDev);
|
||||||
|
px_text(px, g_win_w, g_win_h, item_x + 32, y + 6 + fh + 2, info, DIM_TEXT, FONT_SM);
|
||||||
|
|
||||||
|
y += item_h + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
int btn_w = 120, btn_h = 34;
|
||||||
|
int btn_y = g_win_h - STATUS_H - btn_h - 12;
|
||||||
|
int next_x = g_win_w - btn_w - 16;
|
||||||
|
|
||||||
|
Color next_bg = (st.selected_part >= 0) ? ACCENT : DISABLED_BG;
|
||||||
|
px_button(px, g_win_w, g_win_h, next_x, btn_y, btn_w, btn_h,
|
||||||
|
"Next", next_bg, WHITE, TB_BTN_RAD);
|
||||||
|
|
||||||
|
int back_x = next_x - btn_w - 8;
|
||||||
|
px_button_outline(px, g_win_w, g_win_h, back_x, btn_y, btn_w, btn_h,
|
||||||
|
"Back", BORDER_COLOR, DIM_TEXT, TB_BTN_RAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Render: update confirmation step
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static void render_update_confirm(uint32_t* px) {
|
||||||
|
auto& st = g_state;
|
||||||
|
int fh = font_h();
|
||||||
|
int y = CONTENT_TOP + 24;
|
||||||
|
|
||||||
|
const char* title = "Confirm Update";
|
||||||
|
px_text(px, g_win_w, g_win_h, (g_win_w - text_w(title)) / 2, y, title, TEXT_COLOR);
|
||||||
|
y += fh + 16;
|
||||||
|
|
||||||
|
const char* warn1 = "This will overwrite os/ and apps/ on:";
|
||||||
|
px_text(px, g_win_w, g_win_h, (g_win_w - text_w(warn1)) / 2, y, warn1, ACCENT);
|
||||||
|
y += fh + 8;
|
||||||
|
|
||||||
|
char desc[128], sz[24];
|
||||||
|
format_disk_size(sz, sizeof(sz), st.parts[st.selected_part].sectorCount, 512);
|
||||||
|
const char* pname = st.parts[st.selected_part].name[0]
|
||||||
|
? st.parts[st.selected_part].name : "Unnamed";
|
||||||
|
snprintf(desc, sizeof(desc), "%s (%s, Disk %d)",
|
||||||
|
pname, sz, st.parts[st.selected_part].blockDev);
|
||||||
|
px_text(px, g_win_w, g_win_h, (g_win_w - text_w(desc)) / 2, y, desc, TEXT_COLOR);
|
||||||
|
y += fh + 16;
|
||||||
|
|
||||||
|
const char* info = "Existing user data will not be affected.";
|
||||||
|
px_text(px, g_win_w, g_win_h, (g_win_w - text_w(info, FONT_SM)) / 2, y, info, DIM_TEXT, FONT_SM);
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
int btn_w = 120, btn_h = 34;
|
||||||
|
int center_x = g_win_w / 2;
|
||||||
|
int btn_y = g_win_h - STATUS_H - btn_h - 12;
|
||||||
|
int gap = 16;
|
||||||
|
|
||||||
|
px_button(px, g_win_w, g_win_h, center_x - btn_w - gap / 2, btn_y, btn_w, btn_h,
|
||||||
|
"Update", ACCENT, WHITE, TB_BTN_RAD);
|
||||||
|
px_button_outline(px, g_win_w, g_win_h, center_x + gap / 2, btn_y, btn_w, btn_h,
|
||||||
|
"Back", BORDER_COLOR, DIM_TEXT, TB_BTN_RAD);
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Render: installing / done / error steps
|
// Render: installing / done / error steps
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -311,14 +444,14 @@ static void render_progress(uint32_t* px) {
|
|||||||
|
|
||||||
const char* title;
|
const char* title;
|
||||||
Color title_color;
|
Color title_color;
|
||||||
if (st.step == STEP_INSTALLING) {
|
if (st.step == STEP_INSTALLING || st.step == STEP_UPDATING) {
|
||||||
title = "Installing...";
|
title = (st.mode == MODE_UPDATE) ? "Updating..." : "Installing...";
|
||||||
title_color = TEXT_COLOR;
|
title_color = TEXT_COLOR;
|
||||||
} else if (st.step == STEP_DONE) {
|
} else if (st.step == STEP_DONE) {
|
||||||
title = "Installation Complete";
|
title = (st.mode == MODE_UPDATE) ? "Update Complete" : "Installation Complete";
|
||||||
title_color = SUCCESS_COLOR;
|
title_color = SUCCESS_COLOR;
|
||||||
} else {
|
} else {
|
||||||
title = "Installation Failed";
|
title = (st.mode == MODE_UPDATE) ? "Update Failed" : "Installation Failed";
|
||||||
title_color = DANGER;
|
title_color = DANGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,14 +508,29 @@ static void render_step_bar(uint32_t* px) {
|
|||||||
Color::from_rgb(0xFA, 0xFA, 0xFA));
|
Color::from_rgb(0xFA, 0xFA, 0xFA));
|
||||||
px_hline(px, g_win_w, g_win_h, 0, bar_y + STEP_BAR_H - 1, g_win_w, BORDER_COLOR);
|
px_hline(px, g_win_w, g_win_h, 0, bar_y + STEP_BAR_H - 1, g_win_w, BORDER_COLOR);
|
||||||
|
|
||||||
static const char* step_labels[] = { "Disk", "Partition", "Confirm", "Install" };
|
// No step bar for mode selection
|
||||||
static constexpr int STEP_COUNT = 4;
|
if (g_state.step == STEP_MODE_SELECT) return;
|
||||||
|
|
||||||
// Map current state to step index (0-3)
|
static const char* install_labels[] = { "Disk", "Partition", "Confirm", "Install" };
|
||||||
|
static const char* update_labels[] = { "Partition", "Confirm", "Update" };
|
||||||
|
|
||||||
|
const char** step_labels;
|
||||||
|
int STEP_COUNT;
|
||||||
int cur = 0;
|
int cur = 0;
|
||||||
|
|
||||||
|
if (g_state.mode == MODE_UPDATE) {
|
||||||
|
step_labels = update_labels;
|
||||||
|
STEP_COUNT = 3;
|
||||||
|
if (g_state.step == STEP_UPDATE_CONFIRM) cur = 1;
|
||||||
|
else if (g_state.step >= STEP_UPDATING) cur = 2;
|
||||||
|
else if (g_state.step == STEP_DONE || g_state.step == STEP_ERROR) cur = 2;
|
||||||
|
} else {
|
||||||
|
step_labels = install_labels;
|
||||||
|
STEP_COUNT = 4;
|
||||||
if (g_state.step == STEP_PARTITION_SCHEME) cur = 1;
|
if (g_state.step == STEP_PARTITION_SCHEME) cur = 1;
|
||||||
else if (g_state.step == STEP_CONFIRM) cur = 2;
|
else if (g_state.step == STEP_CONFIRM) cur = 2;
|
||||||
else if (g_state.step >= STEP_INSTALLING) cur = 3;
|
else if (g_state.step >= STEP_INSTALLING) cur = 3;
|
||||||
|
}
|
||||||
|
|
||||||
int fh = font_h();
|
int fh = font_h();
|
||||||
|
|
||||||
@@ -423,10 +571,17 @@ static void render_status(uint32_t* px) {
|
|||||||
px_fill(px, g_win_w, g_win_h, 0, sy, g_win_w, STATUS_H, Color::from_rgb(0xF0, 0xF0, 0xF0));
|
px_fill(px, g_win_w, g_win_h, 0, sy, g_win_w, STATUS_H, Color::from_rgb(0xF0, 0xF0, 0xF0));
|
||||||
px_hline(px, g_win_w, g_win_h, 0, sy, g_win_w, BORDER_COLOR);
|
px_hline(px, g_win_w, g_win_h, 0, sy, g_win_w, BORDER_COLOR);
|
||||||
if (st.status[0]) {
|
if (st.status[0]) {
|
||||||
|
Color sc;
|
||||||
|
if (st.step == STEP_DONE)
|
||||||
|
sc = TEXT_COLOR;
|
||||||
|
else if (st.step == STEP_ERROR)
|
||||||
|
sc = DANGER;
|
||||||
|
else {
|
||||||
uint64_t age = montauk::get_milliseconds() - st.status_time;
|
uint64_t age = montauk::get_milliseconds() - st.status_time;
|
||||||
Color sc = (age < 5000)
|
sc = (age < 5000)
|
||||||
? Color::from_rgb(0x33, 0x33, 0x33)
|
? Color::from_rgb(0x33, 0x33, 0x33)
|
||||||
: Color::from_rgb(0xAA, 0xAA, 0xAA);
|
: Color::from_rgb(0xAA, 0xAA, 0xAA);
|
||||||
|
}
|
||||||
px_text(px, g_win_w, g_win_h, 8, sy + (STATUS_H - fh) / 2, st.status, sc);
|
px_text(px, g_win_w, g_win_h, 8, sy + (STATUS_H - fh) / 2, st.status, sc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,10 +596,14 @@ void render(uint32_t* pixels) {
|
|||||||
render_step_bar(pixels);
|
render_step_bar(pixels);
|
||||||
|
|
||||||
switch (g_state.step) {
|
switch (g_state.step) {
|
||||||
|
case STEP_MODE_SELECT: render_mode_select(pixels); break;
|
||||||
case STEP_SELECT_DISK: render_select_disk(pixels); break;
|
case STEP_SELECT_DISK: render_select_disk(pixels); break;
|
||||||
case STEP_PARTITION_SCHEME: render_partition_scheme(pixels); break;
|
case STEP_PARTITION_SCHEME: render_partition_scheme(pixels); break;
|
||||||
case STEP_CONFIRM: render_confirm(pixels); break;
|
case STEP_CONFIRM: render_confirm(pixels); break;
|
||||||
|
case STEP_UPDATE_SELECT_PART: render_update_select_part(pixels); break;
|
||||||
|
case STEP_UPDATE_CONFIRM: render_update_confirm(pixels); break;
|
||||||
case STEP_INSTALLING:
|
case STEP_INSTALLING:
|
||||||
|
case STEP_UPDATING:
|
||||||
case STEP_DONE:
|
case STEP_DONE:
|
||||||
case STEP_ERROR: render_progress(pixels); break;
|
case STEP_ERROR: render_progress(pixels); break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user