feat: disconnect Bluetooth devices, flush disks on shutdown/reboot, display progress in login.elf window
This commit is contained in:
@@ -12,4 +12,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MONTAUK_BUILD_NUMBER 57
|
||||
#define MONTAUK_BUILD_NUMBER 64
|
||||
|
||||
@@ -13,8 +13,28 @@
|
||||
#include <ACPI/AcpiShutdown.hpp>
|
||||
#include <ACPI/AcpiSleep.hpp>
|
||||
|
||||
#include "Syscall.hpp"
|
||||
|
||||
namespace Montauk {
|
||||
|
||||
// Pending graceful power-off request, set by the desktop and consumed by
|
||||
// login.elf. Kernel-global IPC channel (one outstanding request system-wide).
|
||||
// `inline` so the single definition is shared across translation units.
|
||||
inline int g_pendingPowerAction = POWER_REQ_QUERY;
|
||||
|
||||
// SYS_POWER_REQUEST. action == POWER_REQ_QUERY reads and clears the pending
|
||||
// action; any other value records it as the pending action. Returns the
|
||||
// pending action for queries, or 0 when recording one.
|
||||
static int64_t Sys_PowerRequest(int action) {
|
||||
if (action == POWER_REQ_QUERY) {
|
||||
int pending = g_pendingPowerAction;
|
||||
g_pendingPowerAction = POWER_REQ_QUERY;
|
||||
return (int64_t)pending;
|
||||
}
|
||||
g_pendingPowerAction = action;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void Sys_Reset() {
|
||||
if (Efi::g_ResetSystem) {
|
||||
/* Switch to kernel PML4 which has identity-mapped UEFI runtime regions */
|
||||
|
||||
@@ -109,6 +109,12 @@ namespace Montauk {
|
||||
return (int64_t)Fs::FsProbe::MountPartition(partIndex, driveNum);
|
||||
}
|
||||
|
||||
// Flush all block-device write caches and cleanly unmount disk-backed
|
||||
// volumes ahead of power-off. Returns the number of volumes unmounted.
|
||||
static int64_t Sys_FsSync() {
|
||||
return (int64_t)Fs::FsProbe::SyncAndUnmountAll();
|
||||
}
|
||||
|
||||
// Format a partition with a filesystem. Returns 0 on success, -1 on error.
|
||||
static int64_t Sys_FsFormat(const FsFormatParams* params) {
|
||||
if (params == nullptr) return -1;
|
||||
|
||||
@@ -145,6 +145,8 @@ namespace Montauk {
|
||||
case SYS_SHUTDOWN:
|
||||
Sys_Shutdown();
|
||||
return 0;
|
||||
case SYS_POWER_REQUEST:
|
||||
return Sys_PowerRequest((int)frame->arg1);
|
||||
case SYS_GETTIME:
|
||||
if (!UserMemory::Writable<DateTime>(frame->arg1)) return -1;
|
||||
Sys_GetTime((DateTime*)frame->arg1);
|
||||
@@ -322,6 +324,8 @@ namespace Montauk {
|
||||
return (int64_t)Sys_GptAdd((const GptAddParams*)frame->arg1);
|
||||
case SYS_FSMOUNT:
|
||||
return (int64_t)Sys_FsMount((int)frame->arg1, (int)frame->arg2);
|
||||
case SYS_FS_SYNC:
|
||||
return Sys_FsSync();
|
||||
case SYS_FSFORMAT:
|
||||
if (!UserMemory::Readable<FsFormatParams>(frame->arg1)) return -1;
|
||||
return (int64_t)Sys_FsFormat((const FsFormatParams*)frame->arg1);
|
||||
|
||||
@@ -249,6 +249,21 @@ namespace Montauk {
|
||||
static constexpr uint64_t SYS_THREAD_JOIN = 132;
|
||||
static constexpr uint64_t SYS_THREAD_SELF = 133;
|
||||
|
||||
/* Storage.hpp -- flush + unmount persistent volumes for power-off */
|
||||
static constexpr uint64_t SYS_FS_SYNC = 134;
|
||||
|
||||
/* Power.hpp -- cross-process graceful power-off request channel */
|
||||
static constexpr uint64_t SYS_POWER_REQUEST = 135;
|
||||
|
||||
// Graceful power-off request actions (SYS_POWER_REQUEST). The desktop posts
|
||||
// a pending action and exits; login.elf reads it, runs the shutdown stages,
|
||||
// then issues the matching SYS_SHUTDOWN / SYS_RESET.
|
||||
enum PowerRequestAction : int {
|
||||
POWER_REQ_QUERY = 0, // read-and-clear the pending action
|
||||
POWER_REQ_SHUTDOWN = 1,
|
||||
POWER_REQ_REBOOT = 2,
|
||||
};
|
||||
|
||||
static constexpr uint32_t CLIPBOARD_MAX_TEXT_BYTES = 256 * 1024;
|
||||
|
||||
static constexpr uint32_t IPC_SIGNAL_READABLE = 1u << 0;
|
||||
|
||||
@@ -745,6 +745,9 @@ namespace Drivers::Storage::Ahci {
|
||||
bdev.WriteSectors = [](void* ctx, uint64_t lba, uint32_t count, const void* buffer) -> bool {
|
||||
return WriteSectors((int)(uintptr_t)ctx, lba, count, buffer);
|
||||
};
|
||||
bdev.Flush = [](void* ctx) -> bool {
|
||||
return FlushCache((int)(uintptr_t)ctx);
|
||||
};
|
||||
bdev.Ctx = (void*)(uintptr_t)i;
|
||||
bdev.SectorCount = g_ports[i].SectorCount;
|
||||
bdev.SectorSize = g_ports[i].SectorSizeLog;
|
||||
@@ -826,6 +829,37 @@ namespace Drivers::Storage::Ahci {
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool FlushCache(int port) {
|
||||
if (!g_initialized || port < 0 || port >= MAX_PORTS || !g_ports[port].Active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int slot = FindFreeSlot(port);
|
||||
if (slot < 0) {
|
||||
KernelLogStream(ERROR, "AHCI") << "FlushCache: no free command slot";
|
||||
return false;
|
||||
}
|
||||
|
||||
CommandHeader* hdr = &g_ports[port].CmdList[slot];
|
||||
CommandTable* tbl = g_ports[port].CmdTables[slot];
|
||||
|
||||
// FLUSH CACHE EXT is a non-data command: no PRDT, no transfer direction.
|
||||
memset(tbl, 0, sizeof(CommandTable) + sizeof(PrdtEntry) * MAX_PRDT_ENTRIES);
|
||||
|
||||
FisRegH2D* fis = (FisRegH2D*)tbl->CommandFis;
|
||||
fis->FisType = (uint8_t)FisType::RegH2D;
|
||||
fis->CmdCtl = 1; // Command
|
||||
fis->Command = ATA_CMD_FLUSH_CACHE_EX;
|
||||
fis->Device = (1 << 6); // LBA mode
|
||||
|
||||
hdr->CflPmpA = 5; // CFL = FIS length in dwords (FisRegH2D = 20 bytes)
|
||||
hdr->Flags = 0; // read direction (no data either way)
|
||||
hdr->PrdtLength = 0;
|
||||
hdr->PrdByteCount = 0;
|
||||
|
||||
return IssueCommand(port, slot);
|
||||
}
|
||||
|
||||
bool WriteSectors(int port, uint64_t lba, uint32_t count, const void* buffer) {
|
||||
if (!g_initialized || port < 0 || port >= MAX_PORTS || !g_ports[port].Active) {
|
||||
return false;
|
||||
|
||||
@@ -167,9 +167,10 @@ namespace Drivers::Storage::Ahci {
|
||||
// ATA commands
|
||||
// =========================================================================
|
||||
|
||||
constexpr uint8_t ATA_CMD_IDENTIFY = 0xEC;
|
||||
constexpr uint8_t ATA_CMD_READ_DMA_EX = 0x25; // READ DMA EXT (48-bit LBA)
|
||||
constexpr uint8_t ATA_CMD_WRITE_DMA_EX = 0x35; // WRITE DMA EXT (48-bit LBA)
|
||||
constexpr uint8_t ATA_CMD_IDENTIFY = 0xEC;
|
||||
constexpr uint8_t ATA_CMD_READ_DMA_EX = 0x25; // READ DMA EXT (48-bit LBA)
|
||||
constexpr uint8_t ATA_CMD_WRITE_DMA_EX = 0x35; // WRITE DMA EXT (48-bit LBA)
|
||||
constexpr uint8_t ATA_CMD_FLUSH_CACHE_EX = 0xEA; // FLUSH CACHE EXT (48-bit)
|
||||
|
||||
// =========================================================================
|
||||
// Constants
|
||||
@@ -252,6 +253,10 @@ namespace Drivers::Storage::Ahci {
|
||||
// Write sectors to a SATA device
|
||||
bool WriteSectors(int port, uint64_t lba, uint32_t count, const void* buffer);
|
||||
|
||||
// Flush the device's write cache to media (FLUSH CACHE EXT). Returns true on
|
||||
// success. Used during graceful shutdown to make pending writes durable.
|
||||
bool FlushCache(int port);
|
||||
|
||||
// Get info about a specific port
|
||||
const PortInfo* GetPortInfo(int port);
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ namespace Drivers::Storage {
|
||||
struct BlockDevice {
|
||||
bool (*ReadSectors)(void* ctx, uint64_t lba, uint32_t count, void* buffer);
|
||||
bool (*WriteSectors)(void* ctx, uint64_t lba, uint32_t count, const void* buffer);
|
||||
// Flush the device's volatile write cache to non-volatile media. Optional:
|
||||
// a null pointer means the device has no write-back cache to flush (so a
|
||||
// completed WriteSectors is already durable). Returns true on success.
|
||||
bool (*Flush)(void* ctx);
|
||||
void* Ctx;
|
||||
uint64_t SectorCount;
|
||||
uint16_t SectorSize;
|
||||
|
||||
@@ -665,6 +665,9 @@ namespace Drivers::Storage::Nvme {
|
||||
bdev.WriteSectors = [](void* ctx, uint64_t lba, uint32_t count, const void* buffer) -> bool {
|
||||
return WriteSectors((int)(uintptr_t)ctx, lba, count, buffer);
|
||||
};
|
||||
bdev.Flush = [](void* ctx) -> bool {
|
||||
return Flush((int)(uintptr_t)ctx);
|
||||
};
|
||||
bdev.Ctx = (void*)(uintptr_t)i;
|
||||
bdev.SectorCount = g_namespaces[i].SectorCount;
|
||||
bdev.SectorSize = (uint16_t)g_namespaces[i].SectorSize;
|
||||
@@ -770,6 +773,22 @@ namespace Drivers::Storage::Nvme {
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Flush(int ns) {
|
||||
if (!g_initialized || ns < 0 || ns >= g_nsCount || !g_namespaces[ns].Active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// NVM Flush: commits the namespace's volatile write cache to media.
|
||||
SqEntry cmd = {};
|
||||
cmd.Opcode = IO_CMD_FLUSH;
|
||||
cmd.Nsid = g_namespaces[ns].Nsid;
|
||||
|
||||
SubmitIoCommand(cmd);
|
||||
|
||||
CqEntry cqe;
|
||||
return WaitIoCompletion(cqe);
|
||||
}
|
||||
|
||||
bool WriteSectors(int ns, uint64_t lba, uint32_t count, const void* buffer) {
|
||||
if (!g_initialized || ns < 0 || ns >= g_nsCount || !g_namespaces[ns].Active) {
|
||||
return false;
|
||||
|
||||
@@ -120,6 +120,7 @@ namespace Drivers::Storage::Nvme {
|
||||
// NVM I/O opcodes
|
||||
// =========================================================================
|
||||
|
||||
constexpr uint8_t IO_CMD_FLUSH = 0x00;
|
||||
constexpr uint8_t IO_CMD_READ = 0x02;
|
||||
constexpr uint8_t IO_CMD_WRITE = 0x01;
|
||||
|
||||
@@ -187,6 +188,10 @@ namespace Drivers::Storage::Nvme {
|
||||
// Write sectors to an NVMe namespace
|
||||
bool WriteSectors(int ns, uint64_t lba, uint32_t count, const void* buffer);
|
||||
|
||||
// Flush the namespace's volatile write cache to media (NVM Flush command).
|
||||
// Returns true on success. Used during graceful shutdown.
|
||||
bool Flush(int ns);
|
||||
|
||||
// Get info about a specific namespace
|
||||
const NamespaceInfo* GetNamespaceInfo(int ns);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "FsProbe.hpp"
|
||||
#include <Drivers/Storage/Gpt.hpp>
|
||||
#include <Drivers/Storage/BlockDevice.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
|
||||
@@ -195,4 +196,43 @@ namespace Fs::FsProbe {
|
||||
return unmounted;
|
||||
}
|
||||
|
||||
int SyncAndUnmountAll() {
|
||||
// 1. Flush every block device's volatile write cache to media. FAT32 and
|
||||
// ext2 are write-through, so file data already reached the device; the
|
||||
// flush makes the device commit it to non-volatile storage.
|
||||
int devCount = Drivers::Storage::GetBlockDeviceCount();
|
||||
for (int i = 0; i < devCount; i++) {
|
||||
const auto* dev = Drivers::Storage::GetBlockDevice(i);
|
||||
if (dev && dev->Flush) {
|
||||
dev->Flush(dev->Ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Cleanly unmount every disk-backed VFS drive. The ramdisk has no
|
||||
// backing block device (GetBlockDeviceForDrive < 0) and is left
|
||||
// mounted so the rest of userspace keeps running until power-off.
|
||||
int drives[Vfs::MaxDrives];
|
||||
int n = Vfs::VfsDriveList(drives, Vfs::MaxDrives);
|
||||
int unmounted = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
int driveNum = drives[i];
|
||||
if (GetBlockDeviceForDrive(driveNum) < 0) continue;
|
||||
|
||||
if (Vfs::UnregisterDrive(driveNum) == 0) {
|
||||
unmounted++;
|
||||
for (int p = 0; p < Drivers::Storage::Gpt::MaxPartitions; p++) {
|
||||
if (g_mounted[p] && g_driveForPart[p] == driveNum) {
|
||||
g_mounted[p] = false;
|
||||
g_driveForPart[p] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kt::KernelLogStream(Kt::OK, "FsProbe") << "Synced and unmounted "
|
||||
<< unmounted << " disk-backed volume(s) for power-off";
|
||||
return unmounted;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -38,4 +38,10 @@ namespace Fs::FsProbe {
|
||||
// drive isn't backed by a partition (e.g. ramdisk).
|
||||
int GetBlockDeviceForDrive(int driveNum);
|
||||
|
||||
// Prepare persistent storage for power-off: flush every block device's
|
||||
// write cache to media, then cleanly unmount all disk-backed VFS drives.
|
||||
// The ramdisk (which has no backing block device) is left mounted. Returns
|
||||
// the number of disk-backed volumes that were unmounted.
|
||||
int SyncAndUnmountAll();
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user