Compare commits
31
Commits
821543238d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd397ac41d | ||
|
|
fdbb233cd3 | ||
|
|
21828990c4 | ||
|
|
70fa2016d1 | ||
|
|
f69f08acbb | ||
|
|
f0bea7736d | ||
|
|
3e596f2bad | ||
|
|
615ca7308a | ||
|
|
e7646bbbdb | ||
|
|
9051b8a16e | ||
|
|
5cd5c2e6be | ||
|
|
b1f1cfe32b | ||
|
|
0d23db8a0e | ||
|
|
dac12c22cd | ||
|
|
c8264b92a9 | ||
|
|
8afadb44cb | ||
|
|
ffc9756e67 | ||
|
|
8a74b771e1 | ||
|
|
903218168d | ||
|
|
cc90b34fdb | ||
|
|
788b662d44 | ||
|
|
f7677ac3f1 | ||
|
|
af7d096969 | ||
|
|
c917af0629 | ||
|
|
f6be9e2563 | ||
|
|
39a56153b4 | ||
|
|
724029cacb | ||
|
|
8e45d43116 | ||
|
|
0f81276efc | ||
|
|
f8ded3c30e | ||
|
|
e3ef1b81ad |
@@ -1,7 +1,7 @@
|
||||
# The Montauk Operating System
|
||||
MontaukOS is an operating system written in modern C++. It runs on bare metal and supports various applications, including DOOM, a Wikipedia client, and standard desktop utilities.
|
||||
|
||||

|
||||

|
||||
|
||||
## Features
|
||||
* Modern preemptive multitasking kernel
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ usable diagnostics once the desktop is up**, pending the unified syslog.
|
||||
Worth understanding, because it catches out anything written as a daemon:
|
||||
`montauk::print` is `SYS_PRINT`, which writes the kernel *terminal*, not the
|
||||
kernel *log*. Only in-kernel `KernelLogStream` writes raise `g_kernelLogDepth`,
|
||||
and only those append to the ring buffer `SYS_KLOG` reads -- so daemon output
|
||||
and only those append to the ring buffer `SYS_LOG` reads -- so daemon output
|
||||
never shows up in `klog`. On top of that, `Sys_Print` returns early once
|
||||
`g_suppressKernelLog` is set, which the desktop does at startup, so the output is
|
||||
discarded outright from then on. `init` spawns services with `spawn` rather than
|
||||
|
||||
@@ -22,7 +22,22 @@ namespace Hal {
|
||||
|
||||
// ============================================================================
|
||||
// ACPI encodes PNP IDs as compressed 32-bit EISAIDs.
|
||||
static constexpr uint32_t ByteSwap32(uint32_t value) {
|
||||
return ((value & 0x000000FFu) << 24) |
|
||||
((value & 0x0000FF00u) << 8) |
|
||||
((value & 0x00FF0000u) >> 8) |
|
||||
((value & 0xFF000000u) >> 24);
|
||||
}
|
||||
|
||||
static_assert(ByteSwap32(0x0301D041u) == 0x41D00103u); // PNP0103
|
||||
static_assert(ByteSwap32(0x090CD041u) == 0x41D00C09u); // PNP0C09
|
||||
|
||||
static void DecodeEisaId(uint32_t id, char* out) {
|
||||
// AML exposes the EISAID integer in little-endian byte order, while
|
||||
// the compressed manufacturer and product fields are defined in
|
||||
// display order. Convert it before extracting either field.
|
||||
id = ByteSwap32(id);
|
||||
|
||||
// EISA ID encoding:
|
||||
// Bits 31-16: 3 compressed letters (5 bits each, '@' based)
|
||||
// Bits 15-0: 4 hex digits (product number)
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MONTAUK_BUILD_NUMBER 116
|
||||
#define MONTAUK_BUILD_NUMBER 184
|
||||
|
||||
@@ -17,8 +17,14 @@
|
||||
#include <Ipc/Ipc.hpp>
|
||||
#include <Timekeeping/Time.hpp>
|
||||
#include "Path.hpp"
|
||||
#include <Fs/ProtectedPaths.hpp>
|
||||
|
||||
namespace montauk::abi {
|
||||
static bool CanModifyFilePath(const char* resolved) {
|
||||
uint64_t required = Fs::RequiredFileWriteCapability(resolved);
|
||||
return required == 0 || Sched::HasCapability(required);
|
||||
}
|
||||
|
||||
static int Sys_Open(const char* path) {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
@@ -62,9 +68,9 @@ namespace montauk::abi {
|
||||
if (proc == nullptr) return -1;
|
||||
|
||||
// Use a rotating ring of scratch pages below the heap instead of
|
||||
// bumping heapNext on every call. This keeps repeated directory scans
|
||||
// from leaking user heap space while still allowing nested callers to
|
||||
// hold multiple readdir results at once.
|
||||
// extending the heap high-water mark on every call. This keeps repeated
|
||||
// directory scans from consuming user heap address space while still
|
||||
// allowing nested callers to hold multiple readdir results at once.
|
||||
uint32_t slot = proc->readdirCursor % Sched::UserReadDirSlots;
|
||||
proc->readdirCursor = (slot + 1) % Sched::UserReadDirSlots;
|
||||
|
||||
@@ -108,12 +114,14 @@ namespace montauk::abi {
|
||||
static int Sys_FCreate(const char* path) {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION;
|
||||
return Ipc::CreateFileHandle(resolved);
|
||||
}
|
||||
|
||||
static int Sys_FDelete(const char* path) {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION;
|
||||
return Fs::Vfs::VfsDelete(resolved);
|
||||
}
|
||||
|
||||
@@ -138,6 +146,9 @@ namespace montauk::abi {
|
||||
bool useCurrent) {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
// Timestamps are file state like any other: a protected path must not
|
||||
// be mutable through a side door that skips the write check.
|
||||
if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION;
|
||||
if (useCurrent) {
|
||||
int64_t now = Timekeeping::GetUnixTimestamp();
|
||||
atime = now;
|
||||
@@ -149,6 +160,7 @@ namespace montauk::abi {
|
||||
static int Sys_FMkdir(const char* path) {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
if (!CanModifyFilePath(resolved)) return SYS_ERR_PERMISSION;
|
||||
return Fs::Vfs::VfsMkdir(resolved);
|
||||
}
|
||||
|
||||
@@ -157,6 +169,8 @@ namespace montauk::abi {
|
||||
char resolvedNew[256];
|
||||
if (!ResolveProcessPath(oldPath, resolvedOld, sizeof(resolvedOld))) return -1;
|
||||
if (!ResolveProcessPath(newPath, resolvedNew, sizeof(resolvedNew))) return -1;
|
||||
if (!CanModifyFilePath(resolvedOld) || !CanModifyFilePath(resolvedNew))
|
||||
return SYS_ERR_PERMISSION;
|
||||
return Fs::Vfs::VfsRename(resolvedOld, resolvedNew);
|
||||
}
|
||||
|
||||
|
||||
+43
-4
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Memory/UserRange.hpp>
|
||||
#include <cstdint>
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
@@ -44,7 +45,12 @@ namespace montauk::abi {
|
||||
static constexpr uint64_t VmProtWrite = 2;
|
||||
static constexpr uint64_t VmProtExec = 4;
|
||||
|
||||
inline uint64_t Sys_MapAnonymous(uint64_t size, uint64_t prot) {
|
||||
// Sys_MapAnonymous flags. Populate commits the whole range at mapping
|
||||
// time; without it every page is materialized on first touch.
|
||||
static constexpr uint64_t VmFlagPopulate = 1;
|
||||
|
||||
inline uint64_t Sys_MapAnonymous(uint64_t size, uint64_t prot,
|
||||
uint64_t flags = 0) {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc == nullptr) return 0;
|
||||
int slot = GetCurrentSlot();
|
||||
@@ -82,6 +88,33 @@ namespace montauk::abi {
|
||||
g_heapAllocs[slot] = new HeapAlloc { userVa, numPages, prot, allocationId,
|
||||
g_heapAllocs[slot] };
|
||||
|
||||
// Populate is best effort: commit as much of the range as the frame
|
||||
// allocator will give up front, and leave the remainder to the fault
|
||||
// path. A caller that is about to touch every page (a decode buffer,
|
||||
// a heap slab) then pays one loop instead of one trap, one mutex
|
||||
// acquire and one VMA walk per 4 KiB.
|
||||
if ((flags & VmFlagPopulate) != 0) {
|
||||
bool writable = (prot & VmProtWrite) != 0;
|
||||
bool executable = (prot & VmProtExec) != 0;
|
||||
// Bounded so one syscall cannot pin an unbounded amount of memory
|
||||
// with the slot's heap lock held. Anything past the cap faults in.
|
||||
static constexpr uint64_t MaxPopulatePages = 64 * 1024 * 1024 / 0x1000;
|
||||
uint64_t populate = numPages < MaxPopulatePages ? numPages
|
||||
: MaxPopulatePages;
|
||||
for (uint64_t i = 0; i < populate; i++) {
|
||||
uint64_t pageVa = userVa + i * 0x1000ULL;
|
||||
void* page = Memory::g_pfa->AllocateZeroed();
|
||||
if (page == nullptr) break;
|
||||
uint64_t phys = Memory::SubHHDM((uint64_t)page);
|
||||
if (!Memory::VMM::Paging::MapUserInPermissions(
|
||||
proc->pml4Phys, phys, pageVa, writable, executable)) {
|
||||
Memory::g_pfa->Free(page);
|
||||
break;
|
||||
}
|
||||
Sched::g_allocatedPages[slot]++;
|
||||
}
|
||||
}
|
||||
|
||||
g_heapLocks[slot].Release();
|
||||
return userVa;
|
||||
}
|
||||
@@ -90,6 +123,12 @@ namespace montauk::abi {
|
||||
return Sys_MapAnonymous(size, VmProtRead | VmProtWrite);
|
||||
}
|
||||
|
||||
// As Sys_Alloc, but commits the pages immediately instead of faulting them
|
||||
// in one at a time.
|
||||
inline uint64_t Sys_AllocEager(uint64_t size) {
|
||||
return Sys_MapAnonymous(size, VmProtRead | VmProtWrite, VmFlagPopulate);
|
||||
}
|
||||
|
||||
// Reset heap allocation tracking for a process slot.
|
||||
// The actual physical pages are freed by Paging::FreeUserHalf() during process cleanup.
|
||||
inline void CleanupHeapForSlot(int slot, uint64_t /*pml4Phys*/) {
|
||||
@@ -155,7 +194,7 @@ namespace montauk::abi {
|
||||
// user TLB entry can otherwise corrupt the frame's next owner.
|
||||
while (released != nullptr) {
|
||||
HeapAlloc* next = released->next;
|
||||
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, released->va,
|
||||
Memory::UnmapAndFreeUserRange(proc->pml4Phys, released->va,
|
||||
released->numPages);
|
||||
Sched::ReleaseUserHeapRange(slot, released->va,
|
||||
released->numPages * 0x1000ULL);
|
||||
@@ -218,7 +257,7 @@ namespace montauk::abi {
|
||||
resident++;
|
||||
g_heapLocks[slot].Release();
|
||||
|
||||
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, addr, pages);
|
||||
Memory::UnmapAndFreeUserRange(proc->pml4Phys, addr, pages);
|
||||
Sched::ReleaseUserHeapRange(slot, addr, size);
|
||||
g_heapLocks[slot].Acquire();
|
||||
Sched::g_allocatedPages[slot] -= resident;
|
||||
@@ -308,7 +347,7 @@ namespace montauk::abi {
|
||||
}
|
||||
}
|
||||
g_heapLocks[slot].Release();
|
||||
Ipc::ShootdownUserRange(proc->pml4Phys, addr, (uint32_t)pages);
|
||||
Memory::ShootdownUserRange(proc->pml4Phys, addr, (uint32_t)pages);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace montauk::abi {
|
||||
for (int i = 0; ver[i]; i++) outInfo->osVersion[i] = ver[i];
|
||||
outInfo->osVersion[5] = '\0';
|
||||
|
||||
outInfo->apiVersion = 10;
|
||||
outInfo->apiVersion = 11;
|
||||
outInfo->maxProcesses = Sched::MaxProcesses;
|
||||
outInfo->buildNumber = MONTAUK_BUILD_NUMBER;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,15 @@ namespace montauk::abi {
|
||||
|
||||
static constexpr uint32_t RedirOutputStreamCapacity = 64 * 1024;
|
||||
|
||||
static int Sys_SpawnRedir(const char* path, const char* args) {
|
||||
static int Sys_SpawnRedirInternal(
|
||||
const char* path, const char* args,
|
||||
const SpawnCapabilities* capabilities) {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
|
||||
int parentSlot = Ipc::CurrentSlot();
|
||||
int childPid = Sched::Spawn(resolved, args, false);
|
||||
int childPid = Sched::Spawn(resolved, args, false, nullptr, 0,
|
||||
capabilities);
|
||||
if (childPid < 0) return -1;
|
||||
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
@@ -83,28 +86,50 @@ namespace montauk::abi {
|
||||
return childPid;
|
||||
}
|
||||
|
||||
static int Sys_SpawnRedir(const char* path, const char* args) {
|
||||
return Sys_SpawnRedirInternal(path, args, nullptr);
|
||||
}
|
||||
|
||||
static int Sys_SpawnRedirCaps(const char* path, const char* args,
|
||||
const SpawnCapabilities* requested) {
|
||||
auto* parent = Sched::GetCurrentProcessPtr();
|
||||
if (parent == nullptr || requested == nullptr) return -1;
|
||||
|
||||
SpawnCapabilities copy = *requested;
|
||||
if (!ValidCapabilityDelegation(copy, parent->delegableCaps)) {
|
||||
return SYS_ERR_PERMISSION;
|
||||
}
|
||||
return Sys_SpawnRedirInternal(path, args, ©);
|
||||
}
|
||||
|
||||
static int Sys_ChildIoRead(int childPid, char* buf, int maxLen) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
|
||||
return SYS_ERR_PERMISSION;
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Stream* stream = GetRedirOutStream(child, snapshot);
|
||||
if (child == nullptr || !child->redirected || stream == nullptr) return -1;
|
||||
if (!child->redirected || stream == nullptr) return -1;
|
||||
return Ipc::StreamRead(stream, (uint8_t*)buf, maxLen, true);
|
||||
}
|
||||
|
||||
static int Sys_ChildIoWrite(int childPid, const char* data, int len) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
|
||||
return SYS_ERR_PERMISSION;
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Stream* stream = GetRedirInStream(child, snapshot);
|
||||
if (child == nullptr || !child->redirected || stream == nullptr) return -1;
|
||||
if (!child->redirected || stream == nullptr) return -1;
|
||||
return WriteAllToStream(stream, (const uint8_t*)data, len);
|
||||
}
|
||||
|
||||
static int Sys_ChildIoWriteKey(int childPid, const KeyEvent* key) {
|
||||
if (key == nullptr) return -1;
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
|
||||
return SYS_ERR_PERMISSION;
|
||||
Ipc::HandleSnapshot snapshot;
|
||||
Ipc::Mailbox* mailbox = GetRedirKeyMailbox(child, snapshot);
|
||||
if (child == nullptr || !child->redirected || mailbox == nullptr) return -1;
|
||||
if (!child->redirected || mailbox == nullptr) return -1;
|
||||
|
||||
for (;;) {
|
||||
uint64_t observedWake = Sched::ObserveObjectWake(mailbox);
|
||||
@@ -120,7 +145,9 @@ namespace montauk::abi {
|
||||
|
||||
static int Sys_ChildIoSetTermsz(int childPid, int cols, int rows) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
if (child == nullptr || !child->redirected) return -1;
|
||||
if (child == nullptr || child->parentPid != Sched::GetCurrentPid())
|
||||
return SYS_ERR_PERMISSION;
|
||||
if (!child->redirected) return -1;
|
||||
child->termCols = cols;
|
||||
child->termRows = rows;
|
||||
return 0;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include <Memory/UserRange.hpp>
|
||||
#include <cstdint>
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Sched/ElfLoader.hpp>
|
||||
@@ -133,7 +134,7 @@ namespace montauk::abi {
|
||||
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc != nullptr) {
|
||||
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, libBase,
|
||||
Memory::UnmapAndFreeUserRange(proc->pml4Phys, libBase,
|
||||
(libEnd - libBase) / 0x1000ULL);
|
||||
}
|
||||
|
||||
@@ -202,7 +203,7 @@ namespace montauk::abi {
|
||||
uint64_t libBase = GetLibSlotBase(i);
|
||||
uint64_t libEnd = libBase + Sched::LIB_MAX_SIZE;
|
||||
|
||||
Ipc::UnmapAndFreeUserRange(proc->pml4Phys, libBase,
|
||||
Memory::UnmapAndFreeUserRange(proc->pml4Phys, libBase,
|
||||
(libEnd - libBase) / 0x1000ULL);
|
||||
|
||||
g_libTable[slot][i].inUse = false;
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace montauk::abi {
|
||||
// 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) {
|
||||
// Polled by the session leader every second; deliberately silent and
|
||||
// non-destructive, so it neither floods the log nor races login for
|
||||
// the request it is about to hand over by exiting.
|
||||
if (action == POWER_REQ_PEEK) return (int64_t)g_pendingPowerAction;
|
||||
|
||||
if (action == POWER_REQ_QUERY) {
|
||||
int pending = g_pendingPowerAction;
|
||||
g_pendingPowerAction = POWER_REQ_QUERY;
|
||||
|
||||
+102
-32
@@ -43,6 +43,43 @@ namespace montauk::abi {
|
||||
return Sched::LookupExitCode(pid);
|
||||
}
|
||||
|
||||
// Hand a freshly spawned child the parent's redirected console. Both spawn
|
||||
// syscalls need this: a console tool launched from a GUI terminal must read
|
||||
// its keys from the terminal's mailbox and write its output back up the
|
||||
// stream, whether or not it also carries a capability grant. The child is
|
||||
// created suspended (startReady == false) so its first instruction cannot
|
||||
// run before the channels exist, and is started here once they do.
|
||||
// Returns childPid, or kills the child and returns -1 on failure.
|
||||
static int InheritRedirection(int childPid, Sched::Process* parent, int parentSlot) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
int childSlot = Ipc::SlotForPid(childPid);
|
||||
if (child == nullptr || childSlot < 0 || parentSlot < 0) {
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
child->ioOutHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioOutHandle, childSlot);
|
||||
child->ioInHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioInHandle, childSlot);
|
||||
child->ioKeyHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioKeyHandle, childSlot);
|
||||
|
||||
if (child->ioOutHandle < 0 || child->ioInHandle < 0 || child->ioKeyHandle < 0 ||
|
||||
!ConfigureRedirWaitsetForSlot(childSlot, child)) {
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
child->redirected = true;
|
||||
child->parentPid = parent->pid;
|
||||
child->termCols = parent->termCols;
|
||||
child->termRows = parent->termRows;
|
||||
if (Sched::StartProcess(childPid) < 0) {
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return childPid;
|
||||
}
|
||||
|
||||
static int Sys_Spawn(const char* path, const char* args,
|
||||
const char* environment = nullptr, uint32_t environmentLength = 0) {
|
||||
char resolved[256];
|
||||
@@ -55,34 +92,51 @@ namespace montauk::abi {
|
||||
environment, environmentLength);
|
||||
if (childPid < 0) return childPid;
|
||||
|
||||
if (inheritRedirection) {
|
||||
auto* child = Sched::GetProcessByPid(childPid);
|
||||
int childSlot = Ipc::SlotForPid(childPid);
|
||||
if (child == nullptr || childSlot < 0 || parentSlot < 0) {
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
}
|
||||
if (inheritRedirection)
|
||||
return InheritRedirection(childPid, parent, parentSlot);
|
||||
|
||||
child->ioOutHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioOutHandle, childSlot);
|
||||
child->ioInHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioInHandle, childSlot);
|
||||
child->ioKeyHandle = DuplicateHandleBetweenSlots(parentSlot, parent->ioKeyHandle, childSlot);
|
||||
return childPid;
|
||||
}
|
||||
|
||||
if (child->ioOutHandle < 0 || child->ioInHandle < 0 || child->ioKeyHandle < 0 ||
|
||||
!ConfigureRedirWaitsetForSlot(childSlot, child)) {
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
}
|
||||
static int Sys_SpawnCaps(const char* path, const char* args,
|
||||
const char* user, const SpawnCapabilities* requested) {
|
||||
auto* parent = Sched::GetCurrentProcessPtr();
|
||||
if (parent == nullptr || requested == nullptr) return -1;
|
||||
|
||||
child->redirected = true;
|
||||
child->parentPid = parent->pid;
|
||||
child->termCols = parent->termCols;
|
||||
child->termRows = parent->termRows;
|
||||
if (Sched::StartProcess(childPid) < 0) {
|
||||
Sched::KillProcess(childPid);
|
||||
return -1;
|
||||
}
|
||||
// Snapshot all security-sensitive userspace inputs before evaluating
|
||||
// them. This prevents another thread from changing a mask or owner
|
||||
// name between validation and process creation.
|
||||
SpawnCapabilities copy = *requested;
|
||||
char childUser[32];
|
||||
const char* userOverride = nullptr;
|
||||
if (user != nullptr) {
|
||||
if (!Sched::HasCapability(CAP_USER_ADMIN))
|
||||
return SYS_ERR_PERMISSION;
|
||||
int i = 0;
|
||||
for (; i < 31 && user[i]; i++) childUser[i] = user[i];
|
||||
childUser[i] = '\0';
|
||||
userOverride = childUser;
|
||||
}
|
||||
|
||||
// Authority may only diminish down the process tree. In particular,
|
||||
// possessing a capability is insufficient to pass it: the parent must
|
||||
// also hold it in its delegable set.
|
||||
if (!ValidCapabilityDelegation(copy, parent->delegableCaps)) {
|
||||
return SYS_ERR_PERMISSION;
|
||||
}
|
||||
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
|
||||
int parentSlot = Ipc::CurrentSlot();
|
||||
bool inheritRedirection = parent->redirected;
|
||||
int childPid = Sched::Spawn(resolved, args, !inheritRedirection,
|
||||
nullptr, 0, ©, userOverride);
|
||||
if (childPid < 0) return childPid;
|
||||
|
||||
if (inheritRedirection)
|
||||
return InheritRedirection(childPid, parent, parentSlot);
|
||||
|
||||
return childPid;
|
||||
}
|
||||
|
||||
@@ -150,15 +204,40 @@ namespace montauk::abi {
|
||||
}
|
||||
buf[count].heapUsed = Sched::g_allocatedPages[i] * 0x1000;
|
||||
buf[count].cpuTimeMs = proc->cpuTimeMs;
|
||||
buf[count].permittedCaps = proc->permittedCaps;
|
||||
buf[count].effectiveCaps = proc->effectiveCaps;
|
||||
buf[count].delegableCaps = proc->delegableCaps;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int Sys_Kill(int pid) {
|
||||
if (!Sched::HasCapability(CAP_PROCESS_ADMIN)) {
|
||||
int ancestor = pid;
|
||||
bool descendant = false;
|
||||
for (int depth = 0; depth < Sched::MaxProcesses; depth++) {
|
||||
auto* target = Sched::GetProcessByPid(ancestor);
|
||||
if (target == nullptr || target->parentPid < 0) break;
|
||||
if (target->parentPid == Sched::GetCurrentPid()) {
|
||||
descendant = true;
|
||||
break;
|
||||
}
|
||||
ancestor = target->parentPid;
|
||||
}
|
||||
if (!descendant) return SYS_ERR_PERMISSION;
|
||||
}
|
||||
return Sched::KillProcess(pid);
|
||||
}
|
||||
|
||||
static int Sys_SetSession() {
|
||||
return Sched::CreateSession();
|
||||
}
|
||||
|
||||
static int Sys_KillSession(int sessionId) {
|
||||
return Sched::KillSession(sessionId);
|
||||
}
|
||||
|
||||
static int Sys_SetUser(int pid, const char* name) {
|
||||
if (name == nullptr) return -1;
|
||||
auto* target = Sched::GetProcessByPid(pid);
|
||||
@@ -222,15 +301,6 @@ namespace montauk::abi {
|
||||
char resolved[256];
|
||||
if (!ResolveProcessPath(path, resolved, sizeof(resolved))) return -1;
|
||||
|
||||
bool isDriveRoot = false;
|
||||
{
|
||||
int prefixLen = 0;
|
||||
if (ParseDrivePrefix(resolved, &prefixLen) >= 0 &&
|
||||
resolved[prefixLen] == '/' && resolved[prefixLen + 1] == '\0') {
|
||||
isDriveRoot = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ReadDir doubles as the directory-existence probe: it fails
|
||||
// for nonexistent paths and for regular files (directories are
|
||||
// not openable as files anymore, so the old open-based check
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Sdr.hpp
|
||||
* Software-defined radio receive syscalls.
|
||||
* SYS_SDR_COUNT / INFO / OPEN / CLOSE / START / STOP / READ / SETPARAM / GETPARAM
|
||||
* Thin syscall layer over the generic SDR subsystem (Drivers::Radio::Sdr).
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <Drivers/Radio/Sdr.hpp>
|
||||
|
||||
#include "Syscall.hpp"
|
||||
|
||||
namespace montauk::abi {
|
||||
|
||||
static int64_t Sys_SdrCount() {
|
||||
return (int64_t)Drivers::Radio::Sdr::Count();
|
||||
}
|
||||
|
||||
static int64_t Sys_SdrInfo(int index, SdrDeviceInfo* out) {
|
||||
if (!out) return -1;
|
||||
return Drivers::Radio::Sdr::GetInfo(index, out) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int64_t Sys_SdrOpen(int index) {
|
||||
return (int64_t)Drivers::Radio::Sdr::Open(index);
|
||||
}
|
||||
|
||||
static int64_t Sys_SdrClose(int handle) {
|
||||
return (int64_t)Drivers::Radio::Sdr::Close(handle);
|
||||
}
|
||||
|
||||
static int64_t Sys_SdrStart(int handle) {
|
||||
return (int64_t)Drivers::Radio::Sdr::Start(handle);
|
||||
}
|
||||
|
||||
static int64_t Sys_SdrStop(int handle) {
|
||||
return (int64_t)Drivers::Radio::Sdr::Stop(handle);
|
||||
}
|
||||
|
||||
static int64_t Sys_SdrRead(int handle, uint8_t* buf, uint32_t len) {
|
||||
if (!buf) return -1;
|
||||
return (int64_t)Drivers::Radio::Sdr::Read(handle, buf, len);
|
||||
}
|
||||
|
||||
static int64_t Sys_SdrSetParam(int handle, int param, uint64_t value) {
|
||||
return (int64_t)Drivers::Radio::Sdr::SetParam(handle, param, value);
|
||||
}
|
||||
|
||||
static int64_t Sys_SdrGetParam(int handle, int param) {
|
||||
return (int64_t)Drivers::Radio::Sdr::GetParam(handle, param);
|
||||
}
|
||||
|
||||
}
|
||||
+122
-24
@@ -33,7 +33,7 @@
|
||||
#include "Window.hpp" // SYS_WINCREATE, SYS_WINDESTROY, SYS_WINPRESENT, SYS_WINPOLL, SYS_WINENUM, SYS_WINMAP, SYS_WINSENDEVENT, SYS_WINRESIZE, SYS_WINSETCURSOR, SYS_WINSETFLAGS, SYS_WINSETSCALE, SYS_WINGETSCALE
|
||||
#include "Audio.hpp" // SYS_AUDIOOPEN, SYS_AUDIOCLOSE, SYS_AUDIOWRITE, SYS_AUDIOCTL
|
||||
#include "BluetoothSyscall.hpp" // SYS_BTSCAN, SYS_BTCONNECT, SYS_BTDISCONNECT, SYS_BTLIST, SYS_BTINFO
|
||||
#include "Sdr.hpp" // SYS_SDR_COUNT, SYS_SDR_INFO, SYS_SDR_OPEN, SYS_SDR_CLOSE, SYS_SDR_START, SYS_SDR_STOP, SYS_SDR_READ, SYS_SDR_SETPARAM, SYS_SDR_GETPARAM
|
||||
#include "Usb.hpp" // generic process-owned USB interface access
|
||||
#include "WifiSyscall.hpp" // SYS_WIFI_SCAN, SYS_WIFI_INFO, SYS_WIFI_CONNECT, SYS_WIFI_DISCONNECT
|
||||
#include "IpcSyscall.hpp" // SYS_DUPHANDLE, SYS_WAIT_HANDLE, SYS_STREAM_CREATE, SYS_STREAM_READ, SYS_STREAM_WRITE, SYS_MAILBOX_CREATE, SYS_MAILBOX_SEND, SYS_MAILBOX_RECV, SYS_WAITSET_CREATE, SYS_WAITSET_ADD, SYS_WAITSET_REMOVE, SYS_WAITSET_WAIT, SYS_PROC_OPEN, SYS_SURFACE_CREATE, SYS_SURFACE_MAP, SYS_SURFACE_RESIZE
|
||||
#include "LibSyscall.hpp" // SYS_LOAD_LIB, SYS_UNLOAD_LIB, SYS_DLSYM
|
||||
@@ -53,6 +53,7 @@ namespace montauk::abi {
|
||||
static constexpr uint64_t kMaxWindowTitleBytes = 256;
|
||||
static constexpr uint64_t kMaxHostnameBytes = 256;
|
||||
static constexpr uint64_t kMaxUserNameBytes = 32;
|
||||
static constexpr uint64_t kMaxUserspaceLogEntryBytes = 1024;
|
||||
|
||||
// ---- Dispatch ----
|
||||
|
||||
@@ -115,6 +116,8 @@ namespace montauk::abi {
|
||||
(int)frame->arg4);
|
||||
case SYS_ALLOC:
|
||||
return (int64_t)Sys_Alloc(frame->arg1);
|
||||
case SYS_ALLOC_EAGER:
|
||||
return (int64_t)Sys_AllocEager(frame->arg1);
|
||||
case SYS_FREE:
|
||||
Sys_Free(frame->arg1);
|
||||
return 0;
|
||||
@@ -149,6 +152,16 @@ namespace montauk::abi {
|
||||
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1;
|
||||
return (int64_t)Sys_Spawn((const char*)frame->arg1,
|
||||
UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr);
|
||||
case SYS_SPAWN_CAPS:
|
||||
if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1;
|
||||
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1;
|
||||
if (frame->arg3 != 0 && !UserMemory::String(frame->arg3, 32)) return -1;
|
||||
if (!UserMemory::Readable<SpawnCapabilities>(frame->arg4)) return -1;
|
||||
return (int64_t)Sys_SpawnCaps(
|
||||
(const char*)frame->arg1,
|
||||
frame->arg2 ? (const char*)frame->arg2 : nullptr,
|
||||
frame->arg3 ? (const char*)frame->arg3 : nullptr,
|
||||
(const SpawnCapabilities*)frame->arg4);
|
||||
case SYS_SPAWN_ENV:
|
||||
if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1;
|
||||
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1;
|
||||
@@ -187,8 +200,11 @@ namespace montauk::abi {
|
||||
(uint64_t)frame->arg2 * sizeof(DisplayModeInfo), true)) return -1;
|
||||
return Sys_DisplayModes((DisplayModeInfo*)frame->arg1, (int)frame->arg2);
|
||||
case SYS_DISPLAYSETMODE:
|
||||
if (!Sched::HasCapability(CAP_DISPLAY_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
return Sys_DisplaySetMode((int)frame->arg1);
|
||||
case SYS_DISPLAYBRIGHTNESS:
|
||||
if ((int64_t)frame->arg1 >= 0 &&
|
||||
!Sched::HasCapability(CAP_DISPLAY_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
return Sys_DisplayBrightness((int)frame->arg1);
|
||||
case SYS_GETEXECPATH:
|
||||
if (!UserMemory::Range(frame->arg2 ? frame->arg1 : frame->arg1, frame->arg2, true)) return -1;
|
||||
@@ -209,18 +225,30 @@ namespace montauk::abi {
|
||||
if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1;
|
||||
return (int64_t)Sys_GetArgs((char*)frame->arg1, frame->arg2);
|
||||
case SYS_RESET:
|
||||
if (!Sched::HasCapability(CAP_POWER_CONTROL)) return SYS_ERR_PERMISSION;
|
||||
Sys_Reset();
|
||||
return 0;
|
||||
case SYS_SHUTDOWN:
|
||||
if (!Sched::HasCapability(CAP_POWER_CONTROL)) return SYS_ERR_PERMISSION;
|
||||
Sys_Shutdown();
|
||||
return 0;
|
||||
case SYS_POWER_REQUEST:
|
||||
if (frame->arg1 != POWER_REQ_QUERY &&
|
||||
frame->arg1 != POWER_REQ_SHUTDOWN &&
|
||||
frame->arg1 != POWER_REQ_REBOOT &&
|
||||
frame->arg1 != POWER_REQ_PEEK) return -1;
|
||||
if (frame->arg1 == POWER_REQ_QUERY) {
|
||||
if (!Sched::HasCapability(CAP_POWER_CONTROL)) return SYS_ERR_PERMISSION;
|
||||
} else if (!Sched::HasCapability(CAP_POWER_REQUEST)) {
|
||||
return SYS_ERR_PERMISSION;
|
||||
}
|
||||
return Sys_PowerRequest((int)frame->arg1);
|
||||
case SYS_GETTIME:
|
||||
if (!UserMemory::Writable<DateTime>(frame->arg1)) return -1;
|
||||
Sys_GetTime((DateTime*)frame->arg1);
|
||||
return 0;
|
||||
case SYS_SETUNIXTIME:
|
||||
if (!Sched::HasCapability(CAP_SET_TIME)) return SYS_ERR_PERMISSION;
|
||||
return Sys_SetUnixTime((int64_t)frame->arg1);
|
||||
case SYS_SOCKET:
|
||||
return (int64_t)Sys_Socket((int)frame->arg1);
|
||||
@@ -246,6 +274,7 @@ namespace montauk::abi {
|
||||
Sys_GetNetCfg((NetCfg*)frame->arg1);
|
||||
return 0;
|
||||
case SYS_SETNETCFG:
|
||||
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::Readable<NetCfg>(frame->arg1)) return -1;
|
||||
return (int64_t)Sys_SetNetCfg((const NetCfg*)frame->arg1);
|
||||
case SYS_NETSTATUS:
|
||||
@@ -300,9 +329,10 @@ namespace montauk::abi {
|
||||
case SYS_GETRANDOM:
|
||||
if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1;
|
||||
return Sys_GetRandom((uint8_t*)frame->arg1, frame->arg2);
|
||||
case SYS_KLOG:
|
||||
case SYS_LOG:
|
||||
if (!Sched::HasCapability(CAP_LOG_READ)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::Range(frame->arg1, frame->arg2, true)) return -1;
|
||||
return Kt::ReadKernelLog((char*)frame->arg1, frame->arg2);
|
||||
return Kt::ReadKernelLogBuffer((char*)frame->arg1, frame->arg2);
|
||||
case SYS_MOUSESTATE:
|
||||
if (!UserMemory::Writable<MouseState>(frame->arg1)) return -1;
|
||||
Sys_MouseState((MouseState*)frame->arg1);
|
||||
@@ -315,6 +345,14 @@ namespace montauk::abi {
|
||||
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1;
|
||||
return (int64_t)Sys_SpawnRedir((const char*)frame->arg1,
|
||||
UserMemory::IsUserPtr(frame->arg2) ? (const char*)frame->arg2 : nullptr);
|
||||
case SYS_SPAWN_REDIR_CAPS:
|
||||
if (!UserMemory::String(frame->arg1, kMaxPathBytes)) return -1;
|
||||
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, kMaxArgsBytes)) return -1;
|
||||
if (!UserMemory::Readable<SpawnCapabilities>(frame->arg3)) return -1;
|
||||
return (int64_t)Sys_SpawnRedirCaps(
|
||||
(const char*)frame->arg1,
|
||||
frame->arg2 ? (const char*)frame->arg2 : nullptr,
|
||||
(const SpawnCapabilities*)frame->arg3);
|
||||
case SYS_CHILDIO_READ:
|
||||
if ((int64_t)frame->arg3 < 0) return -1;
|
||||
if (!UserMemory::Range(frame->arg2, (uint64_t)frame->arg3, true)) return -1;
|
||||
@@ -359,6 +397,11 @@ namespace montauk::abi {
|
||||
return (int64_t)Sys_ProcList((ProcInfo*)frame->arg1, (int)frame->arg2);
|
||||
case SYS_KILL:
|
||||
return (int64_t)Sys_Kill((int)frame->arg1);
|
||||
case SYS_SETSESSION:
|
||||
return (int64_t)Sys_SetSession();
|
||||
case SYS_KILLSESSION:
|
||||
if (!Sched::HasCapability(CAP_PROCESS_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
return (int64_t)Sys_KillSession((int)frame->arg1);
|
||||
case SYS_DEVLIST:
|
||||
if ((int64_t)frame->arg2 < 0) return -1;
|
||||
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(DevInfo), true)) return -1;
|
||||
@@ -383,21 +426,28 @@ namespace montauk::abi {
|
||||
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(PartInfo), true)) return -1;
|
||||
return (int64_t)Sys_PartList((PartInfo*)frame->arg1, (int)frame->arg2);
|
||||
case SYS_DISKREAD:
|
||||
if (!Sched::HasCapability(CAP_RAW_STORAGE)) return SYS_ERR_PERMISSION;
|
||||
return (int64_t)Sys_DiskRead((int)frame->arg1, frame->arg2,
|
||||
(uint32_t)frame->arg3, (void*)frame->arg4);
|
||||
case SYS_DISKWRITE:
|
||||
if (!Sched::HasCapability(CAP_RAW_STORAGE)) return SYS_ERR_PERMISSION;
|
||||
return (int64_t)Sys_DiskWrite((int)frame->arg1, frame->arg2,
|
||||
(uint32_t)frame->arg3, (const void*)frame->arg4);
|
||||
case SYS_GPTINIT:
|
||||
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
return (int64_t)Sys_GptInit((int)frame->arg1);
|
||||
case SYS_GPTADD:
|
||||
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::Readable<GptAddParams>(frame->arg1)) return -1;
|
||||
return (int64_t)Sys_GptAdd((const GptAddParams*)frame->arg1);
|
||||
case SYS_FSMOUNT:
|
||||
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
return (int64_t)Sys_FsMount((int)frame->arg1, (int)frame->arg2);
|
||||
case SYS_FS_SYNC:
|
||||
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
return Sys_FsSync();
|
||||
case SYS_FSFORMAT:
|
||||
if (!Sched::HasCapability(CAP_STORAGE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::Readable<FsFormatParams>(frame->arg1)) return -1;
|
||||
return (int64_t)Sys_FsFormat((const FsFormatParams*)frame->arg1);
|
||||
case SYS_AUDIOOPEN:
|
||||
@@ -415,26 +465,42 @@ namespace montauk::abi {
|
||||
return Sys_AudioList((AudioStreamInfo*)frame->arg1, (int)frame->arg2);
|
||||
case SYS_AUDIOWAIT:
|
||||
return Sys_AudioWait(frame->arg1, frame->arg2);
|
||||
case SYS_SDR_COUNT:
|
||||
return Sys_SdrCount();
|
||||
case SYS_SDR_INFO:
|
||||
if (!UserMemory::Writable<SdrDeviceInfo>(frame->arg2)) return -1;
|
||||
return Sys_SdrInfo((int)frame->arg1, (SdrDeviceInfo*)frame->arg2);
|
||||
case SYS_SDR_OPEN:
|
||||
return Sys_SdrOpen((int)frame->arg1);
|
||||
case SYS_SDR_CLOSE:
|
||||
return Sys_SdrClose((int)frame->arg1);
|
||||
case SYS_SDR_START:
|
||||
return Sys_SdrStart((int)frame->arg1);
|
||||
case SYS_SDR_STOP:
|
||||
return Sys_SdrStop((int)frame->arg1);
|
||||
case SYS_SDR_READ:
|
||||
if (!UserMemory::Range(frame->arg2, frame->arg3, true)) return -1;
|
||||
return Sys_SdrRead((int)frame->arg1, (uint8_t*)frame->arg2, (uint32_t)frame->arg3);
|
||||
case SYS_SDR_SETPARAM:
|
||||
return Sys_SdrSetParam((int)frame->arg1, (int)frame->arg2, frame->arg3);
|
||||
case SYS_SDR_GETPARAM:
|
||||
return Sys_SdrGetParam((int)frame->arg1, (int)frame->arg2);
|
||||
case SYS_USB_LIST: {
|
||||
if ((int64_t)frame->arg2 < 0) return USB_ERR_INVALID;
|
||||
uint64_t maxCount = frame->arg2 > 16 ? 16 : frame->arg2;
|
||||
if (!UserMemory::Range(frame->arg1,
|
||||
maxCount * sizeof(UsbInterfaceInfo), true)) return USB_ERR_INVALID;
|
||||
return Sys_UsbList((UsbInterfaceInfo*)frame->arg1, (int)maxCount);
|
||||
}
|
||||
case SYS_USB_CLAIM:
|
||||
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (frame->arg1 == 0 || frame->arg1 > 16 || frame->arg2 > 255)
|
||||
return USB_ERR_INVALID;
|
||||
return Sys_UsbClaim((uint8_t)frame->arg1, (uint8_t)frame->arg2);
|
||||
case SYS_USB_CLOSE:
|
||||
return Sys_UsbClose((int)frame->arg1);
|
||||
case SYS_USB_CONTROL: {
|
||||
if (!UserMemory::Readable<UsbControlRequest>(frame->arg2)) return USB_ERR_INVALID;
|
||||
UsbControlRequest request = *(const UsbControlRequest*)frame->arg2;
|
||||
if (frame->arg4 > 4096 || frame->arg4 != request.length) return USB_ERR_INVALID;
|
||||
bool deviceToHost = (request.requestType & 0x80) != 0;
|
||||
if (frame->arg4 != 0 &&
|
||||
!UserMemory::Range(frame->arg3, frame->arg4, deviceToHost)) return USB_ERR_INVALID;
|
||||
return Sys_UsbControl((int)frame->arg1, &request,
|
||||
(void*)frame->arg3, (uint32_t)frame->arg4);
|
||||
}
|
||||
case SYS_USB_BULK_IN_START:
|
||||
if (frame->arg2 > 0xffffffffULL || frame->arg3 > 0xffffffffULL)
|
||||
return USB_ERR_INVALID;
|
||||
return Sys_UsbBulkInStart((int)frame->arg1, (uint32_t)frame->arg2,
|
||||
(uint32_t)frame->arg3);
|
||||
case SYS_USB_BULK_IN_STOP:
|
||||
return Sys_UsbBulkInStop((int)frame->arg1);
|
||||
case SYS_USB_BULK_IN_READ:
|
||||
if (frame->arg3 > 0xffffffffULL) return USB_ERR_INVALID;
|
||||
if (!UserMemory::Range(frame->arg2, frame->arg3, true)) return USB_ERR_INVALID;
|
||||
return Sys_UsbBulkInRead((int)frame->arg1, (uint8_t*)frame->arg2,
|
||||
(uint32_t)frame->arg3);
|
||||
case SYS_POWERINFO:
|
||||
if (!UserMemory::Writable<PowerInfo>(frame->arg1)) return -1;
|
||||
return Sys_PowerInfo((PowerInfo*)frame->arg1);
|
||||
@@ -449,16 +515,20 @@ namespace montauk::abi {
|
||||
case SYS_THREAD_SELF:
|
||||
return Sys_ThreadSelf();
|
||||
case SYS_BTSCAN:
|
||||
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if ((int64_t)frame->arg2 < 0) return -1;
|
||||
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtScanResult), true)) return -1;
|
||||
return Sys_BtScan((BtScanResult*)frame->arg1, (int)frame->arg2, (uint32_t)frame->arg3);
|
||||
case SYS_BTCONNECT:
|
||||
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::Range(frame->arg1, 6, false)) return -1;
|
||||
return Sys_BtConnect((const uint8_t*)frame->arg1);
|
||||
case SYS_BTDISCONNECT:
|
||||
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::Range(frame->arg1, 6, false)) return -1;
|
||||
return Sys_BtDisconnect((const uint8_t*)frame->arg1);
|
||||
case SYS_BTSETADDR:
|
||||
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::Range(frame->arg1, 6, false)) return -1;
|
||||
return Sys_BtSetAddr((const uint8_t*)frame->arg1);
|
||||
case SYS_BTBONDS:
|
||||
@@ -466,6 +536,7 @@ namespace montauk::abi {
|
||||
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(BtBondInfo), true)) return -1;
|
||||
return Sys_BtBonds((BtBondInfo*)frame->arg1, (int)frame->arg2);
|
||||
case SYS_BTFORGET:
|
||||
if (!Sched::HasCapability(CAP_DEVICE_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::Range(frame->arg1, 6, false)) return -1;
|
||||
return Sys_BtForget((const uint8_t*)frame->arg1);
|
||||
case SYS_BTLIST:
|
||||
@@ -476,6 +547,7 @@ namespace montauk::abi {
|
||||
if (!UserMemory::Writable<BtAdapterInfo>(frame->arg1)) return -1;
|
||||
return Sys_BtInfo((BtAdapterInfo*)frame->arg1);
|
||||
case SYS_WIFI_SCAN:
|
||||
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if ((int64_t)frame->arg2 < 0) return -1;
|
||||
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(WifiNetwork), true)) return -1;
|
||||
return Sys_WifiScan((WifiNetwork*)frame->arg1, (int)frame->arg2, (uint32_t)frame->arg3);
|
||||
@@ -483,18 +555,22 @@ namespace montauk::abi {
|
||||
if (!UserMemory::Writable<WifiInfo>(frame->arg1)) return -1;
|
||||
return Sys_WifiInfo((WifiInfo*)frame->arg1);
|
||||
case SYS_WIFI_CONNECT:
|
||||
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::String(frame->arg1, 64)) return -1;
|
||||
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, 128)) return -1;
|
||||
return Sys_WifiConnect((const char*)frame->arg1, (const char*)frame->arg2);
|
||||
case SYS_WIFI_DISCONNECT:
|
||||
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
return Sys_WifiDisconnect();
|
||||
case SYS_WIFI_SCAN_START:
|
||||
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
return Sys_WifiScanStart((uint32_t)frame->arg1);
|
||||
case SYS_WIFI_RESULTS:
|
||||
if ((int64_t)frame->arg2 < 0) return -1;
|
||||
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(WifiNetwork), true)) return -1;
|
||||
return Sys_WifiResults((WifiNetwork*)frame->arg1, (int)frame->arg2);
|
||||
case SYS_WIFI_CONNECT_ASYNC:
|
||||
if (!Sched::HasCapability(CAP_NETWORK_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::String(frame->arg1, 64)) return -1;
|
||||
if (frame->arg2 != 0 && !UserMemory::String(frame->arg2, 128)) return -1;
|
||||
return Sys_WifiConnectAsync((const char*)frame->arg1, (const char*)frame->arg2);
|
||||
@@ -503,12 +579,15 @@ namespace montauk::abi {
|
||||
if (!UserMemory::Range(frame->arg1, (uint64_t)frame->arg2 * sizeof(NetIfInfo), true)) return -1;
|
||||
return Sys_NetIfs((NetIfInfo*)frame->arg1, (int)frame->arg2);
|
||||
case SYS_SUSPEND:
|
||||
if (!Sched::HasCapability(CAP_SUSPEND)) return SYS_ERR_PERMISSION;
|
||||
return Sys_Suspend();
|
||||
case SYS_SETTZ:
|
||||
if (!Sched::HasCapability(CAP_SET_TIME)) return SYS_ERR_PERMISSION;
|
||||
return Sys_SetTZ((int32_t)frame->arg1);
|
||||
case SYS_GETTZ:
|
||||
return Sys_GetTZ();
|
||||
case SYS_SETUSER:
|
||||
if (!Sched::HasCapability(CAP_USER_ADMIN)) return SYS_ERR_PERMISSION;
|
||||
if (!UserMemory::String(frame->arg2, kMaxUserNameBytes)) return -1;
|
||||
return Sys_SetUser((int)frame->arg1, (const char*)frame->arg2);
|
||||
case SYS_GETUSER:
|
||||
@@ -605,6 +684,24 @@ namespace montauk::abi {
|
||||
return Sys_ClipboardClear();
|
||||
case SYS_INPUT_WAIT:
|
||||
return (int64_t)Sys_InputWait(frame->arg1, frame->arg2);
|
||||
case SYS_LOG_WRITE: {
|
||||
if (!UserMemory::String(frame->arg1, kMaxUserspaceLogEntryBytes)) return -1;
|
||||
|
||||
auto* process = Sched::GetCurrentProcessPtr();
|
||||
const char* username = process != nullptr && process->user[0] != '\0'
|
||||
? process->user
|
||||
: "unknown";
|
||||
const char* imageName = process != nullptr && process->name[0] != '\0'
|
||||
? process->name
|
||||
: "unknown";
|
||||
|
||||
Kt::UserspaceLogStream(imageName, username)
|
||||
<< (const char*)frame->arg1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
case SYS_TERMINAL_ATTACHED:
|
||||
return Sys_TerminalAttached();
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@@ -631,7 +728,8 @@ namespace montauk::abi {
|
||||
Hal::WriteMSR(Hal::IA32_FMASK, 0x200);
|
||||
|
||||
Kt::KernelLogStream(Kt::OK, "Syscall") << "SYSCALL/SYSRET initialized (LSTAR="
|
||||
<< kcp::hex << (uint64_t)SyscallEntry << kcp::dec << ", 171 syscall slots)";
|
||||
<< kcp::hex << (uint64_t)SyscallEntry << kcp::dec << ", "
|
||||
<< (SYS_SPAWN_REDIR_CAPS + 1) << " syscall slots)";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+145
-39
@@ -103,7 +103,7 @@ namespace montauk::abi {
|
||||
/* Random.hpp */
|
||||
static constexpr uint64_t SYS_GETRANDOM = 45;
|
||||
|
||||
static constexpr uint64_t SYS_KLOG = 46;
|
||||
static constexpr uint64_t SYS_LOG = 46;
|
||||
|
||||
/* Mouse.hpp */
|
||||
static constexpr uint64_t SYS_MOUSESTATE = 47;
|
||||
@@ -157,6 +157,12 @@ namespace montauk::abi {
|
||||
static constexpr uint64_t SYS_AUDIOWRITE = 82;
|
||||
static constexpr uint64_t SYS_AUDIOCTL = 83;
|
||||
|
||||
/* Userspace log */
|
||||
static constexpr uint64_t SYS_LOG_WRITE = 176;
|
||||
|
||||
/* Process terminal attachment */
|
||||
static constexpr uint64_t SYS_TERMINAL_ATTACHED = 177;
|
||||
|
||||
// Audio control commands (for SYS_AUDIOCTL).
|
||||
//
|
||||
// Commands 0..3 act on the stream named by the handle argument.
|
||||
@@ -194,6 +200,8 @@ namespace montauk::abi {
|
||||
/* Process.hpp */
|
||||
static constexpr uint64_t SYS_SETUSER = 92;
|
||||
static constexpr uint64_t SYS_GETUSER = 93;
|
||||
static constexpr uint64_t SYS_SETSESSION = 174;
|
||||
static constexpr uint64_t SYS_KILLSESSION = 175;
|
||||
|
||||
/* Filesystem.hpp */
|
||||
static constexpr uint64_t SYS_FRENAME = 94;
|
||||
@@ -267,16 +275,16 @@ namespace montauk::abi {
|
||||
static constexpr uint64_t SYS_BTBONDS = 138;
|
||||
static constexpr uint64_t SYS_BTFORGET = 139;
|
||||
|
||||
/* Sdr.hpp -- software-defined radio receive API */
|
||||
static constexpr uint64_t SYS_SDR_COUNT = 140; // number of receivers
|
||||
static constexpr uint64_t SYS_SDR_INFO = 141; // (index, SdrDeviceInfo*)
|
||||
static constexpr uint64_t SYS_SDR_OPEN = 142; // (index) -> handle
|
||||
static constexpr uint64_t SYS_SDR_CLOSE = 143; // (handle)
|
||||
static constexpr uint64_t SYS_SDR_START = 144; // (handle) begin streaming
|
||||
static constexpr uint64_t SYS_SDR_STOP = 145; // (handle) stop streaming
|
||||
static constexpr uint64_t SYS_SDR_READ = 146; // (handle, buf, len) -> bytes
|
||||
static constexpr uint64_t SYS_SDR_SETPARAM = 147; // (handle, param, value)
|
||||
static constexpr uint64_t SYS_SDR_GETPARAM = 148; // (handle, param) -> value
|
||||
/* Reserved: former SDR API. Kept unavailable to preserve ABI numbering. */
|
||||
static constexpr uint64_t SYS_RESERVED_140 = 140;
|
||||
static constexpr uint64_t SYS_RESERVED_141 = 141;
|
||||
static constexpr uint64_t SYS_RESERVED_142 = 142;
|
||||
static constexpr uint64_t SYS_RESERVED_143 = 143;
|
||||
static constexpr uint64_t SYS_RESERVED_144 = 144;
|
||||
static constexpr uint64_t SYS_RESERVED_145 = 145;
|
||||
static constexpr uint64_t SYS_RESERVED_146 = 146;
|
||||
static constexpr uint64_t SYS_RESERVED_147 = 147;
|
||||
static constexpr uint64_t SYS_RESERVED_148 = 148;
|
||||
|
||||
/* Power.hpp -- CPU power/thermal status */
|
||||
static constexpr uint64_t SYS_POWERINFO = 149; // (PowerInfo*) -> 0, -1 unsupported
|
||||
@@ -319,25 +327,103 @@ namespace montauk::abi {
|
||||
static constexpr uint64_t SYS_SETENVIRON = 172;
|
||||
static constexpr uint64_t SYS_SPAWN_ENV = 173;
|
||||
|
||||
// Tunable parameters (for SYS_SDR_SETPARAM / SYS_SDR_GETPARAM).
|
||||
static constexpr int SDR_PARAM_FREQ = 0; // center frequency, Hz
|
||||
static constexpr int SDR_PARAM_SAMPLE_RATE = 1; // sample rate, Hz
|
||||
static constexpr int SDR_PARAM_GAIN_MODE = 2; // 0 = auto/AGC, 1 = manual
|
||||
static constexpr int SDR_PARAM_GAIN = 3; // tuner gain, tenths of dB
|
||||
static constexpr int SDR_PARAM_FREQ_CORR = 4; // frequency correction, ppm
|
||||
static constexpr int SDR_PARAM_AGC = 5; // demod digital AGC, 0/1
|
||||
static constexpr int SDR_PARAM_DIRECT_SAMP = 6; // direct sampling: 0=off,1=I,2=Q
|
||||
/* Generic userspace USB interface access */
|
||||
static constexpr uint64_t SYS_USB_LIST = 178; // (UsbInterfaceInfo*, max) -> count
|
||||
static constexpr uint64_t SYS_USB_CLAIM = 179; // (slot, interface) -> owned handle
|
||||
static constexpr uint64_t SYS_USB_CLOSE = 180; // (handle)
|
||||
static constexpr uint64_t SYS_USB_CONTROL = 181; // (handle, UsbControlRequest*, data, len)
|
||||
static constexpr uint64_t SYS_USB_BULK_IN_START = 182; // (handle, transferBytes, buffers)
|
||||
static constexpr uint64_t SYS_USB_BULK_IN_STOP = 183; // (handle)
|
||||
static constexpr uint64_t SYS_USB_BULK_IN_READ = 184; // (handle, data, len) -> bytes
|
||||
static constexpr uint64_t SYS_SPAWN_CAPS = 185;
|
||||
static constexpr uint64_t SYS_SPAWN_REDIR_CAPS = 186;
|
||||
|
||||
// Sample formats reported in SdrDeviceInfo.sampleFormat.
|
||||
static constexpr uint8_t SDR_FORMAT_CU8 = 0; // 8-bit unsigned interleaved I/Q
|
||||
/* Heap.hpp -- as SYS_ALLOC, but commits every page up front instead of
|
||||
faulting them in one at a time. For buffers the caller is about to
|
||||
touch in full (image decode, heap slabs). */
|
||||
static constexpr uint64_t SYS_ALLOC_EAGER = 187; // (bytes) -> va, 0 on failure
|
||||
|
||||
/* Kernel-owned process capabilities. User identities may namespace
|
||||
per-user resources, but never participate in authorization decisions. */
|
||||
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
|
||||
static constexpr uint64_t CAP_POWER_REQUEST = 1ULL << 1;
|
||||
static constexpr uint64_t CAP_POWER_CONTROL = 1ULL << 2;
|
||||
static constexpr uint64_t CAP_SUSPEND = 1ULL << 3;
|
||||
static constexpr uint64_t CAP_STORAGE_ADMIN = 1ULL << 4;
|
||||
static constexpr uint64_t CAP_RAW_STORAGE = 1ULL << 5;
|
||||
static constexpr uint64_t CAP_NETWORK_ADMIN = 1ULL << 6;
|
||||
static constexpr uint64_t CAP_SET_TIME = 1ULL << 7;
|
||||
static constexpr uint64_t CAP_USER_ADMIN = 1ULL << 8;
|
||||
static constexpr uint64_t CAP_DISPLAY_ADMIN = 1ULL << 9;
|
||||
static constexpr uint64_t CAP_DEVICE_ADMIN = 1ULL << 10;
|
||||
static constexpr uint64_t CAP_LOG_READ = 1ULL << 11;
|
||||
/* Write to the program images the system boots and runs (0:/os,
|
||||
0:/apps). Deliberately separate from CAP_STORAGE_ADMIN: grants are
|
||||
keyed on binary path, so writing an image is equivalent to acquiring
|
||||
whatever that image is granted at its next launch. Formatting a data
|
||||
volume must not carry that authority with it. */
|
||||
static constexpr uint64_t CAP_SYSTEM_IMAGE = 1ULL << 12;
|
||||
static constexpr uint64_t CAP_ALL = (1ULL << 13) - 1;
|
||||
static constexpr uint64_t CAP_STANDARD_SESSION = CAP_POWER_REQUEST | CAP_SUSPEND;
|
||||
static constexpr uint64_t CAP_ADMIN_SESSION =
|
||||
CAP_STANDARD_SESSION | CAP_PROCESS_ADMIN | CAP_STORAGE_ADMIN |
|
||||
CAP_RAW_STORAGE | CAP_NETWORK_ADMIN | CAP_SET_TIME | CAP_USER_ADMIN |
|
||||
CAP_DISPLAY_ADMIN | CAP_DEVICE_ADMIN | CAP_LOG_READ;
|
||||
static_assert((CAP_STANDARD_SESSION & ~CAP_ADMIN_SESSION) == 0);
|
||||
static_assert((CAP_ADMIN_SESSION & CAP_POWER_CONTROL) == 0,
|
||||
"final power control belongs only to the session supervisor");
|
||||
static_assert((CAP_ADMIN_SESSION & CAP_SYSTEM_IMAGE) == 0,
|
||||
"an admin session must not imply authority to rewrite the "
|
||||
"programs it launches; grant CAP_SYSTEM_IMAGE per binary");
|
||||
static constexpr int SYS_ERR_PERMISSION = -13;
|
||||
|
||||
struct SpawnCapabilities {
|
||||
uint64_t permitted;
|
||||
uint64_t effective;
|
||||
uint64_t delegable;
|
||||
};
|
||||
|
||||
constexpr bool ValidCapabilityDelegation(const SpawnCapabilities& child,
|
||||
uint64_t parentDelegable) {
|
||||
return (child.permitted & ~CAP_ALL) == 0 &&
|
||||
(child.effective & ~child.permitted) == 0 &&
|
||||
(child.delegable & ~child.permitted) == 0 &&
|
||||
(child.permitted & ~parentDelegable) == 0 &&
|
||||
(child.delegable & ~parentDelegable) == 0;
|
||||
}
|
||||
static_assert(ValidCapabilityDelegation(
|
||||
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN, 0}, CAP_NETWORK_ADMIN));
|
||||
static_assert(!ValidCapabilityDelegation(
|
||||
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN}, 0));
|
||||
static_assert(!ValidCapabilityDelegation(
|
||||
{CAP_NETWORK_ADMIN, CAP_NETWORK_ADMIN | CAP_SET_TIME, 0}, CAP_ALL));
|
||||
|
||||
|
||||
// Generic USB errors. Claims are restricted to interfaces without a
|
||||
// bound in-kernel class driver and are owned by the claiming process.
|
||||
static constexpr int USB_ERR_INVALID = -1;
|
||||
static constexpr int USB_ERR_BUSY = -2;
|
||||
static constexpr int USB_ERR_DISCONNECTED = -3;
|
||||
static constexpr int USB_ERR_UNSUPPORTED = -4;
|
||||
static constexpr int USB_ERR_IO = -5;
|
||||
static constexpr int USB_ERR_NO_RESOURCES = -6;
|
||||
static constexpr int USB_ERR_NOT_FOUND = -7;
|
||||
static constexpr int USB_ERR_KERNEL_BOUND = -8;
|
||||
|
||||
// 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.
|
||||
//
|
||||
// A request can also be posted from inside the session -- the shell's
|
||||
// shutdown builtin does. login only looks at it once the session leader
|
||||
// exits, so the leader has to notice and stand down: POWER_REQ_PEEK is the
|
||||
// non-destructive read it polls with. Only login consumes (QUERY), so a
|
||||
// leader that peeks cannot swallow the request it is meant to act on.
|
||||
enum PowerRequestAction : int {
|
||||
POWER_REQ_QUERY = 0, // read-and-clear the pending action
|
||||
POWER_REQ_SHUTDOWN = 1,
|
||||
POWER_REQ_REBOOT = 2,
|
||||
POWER_REQ_PEEK = 3, // read the pending action without clearing it
|
||||
};
|
||||
|
||||
static constexpr uint32_t CLIPBOARD_MAX_TEXT_BYTES = 256 * 1024;
|
||||
@@ -612,8 +698,11 @@ namespace montauk::abi {
|
||||
uint8_t state; // 0=Free, 1=Ready, 2=Running, 3=Blocked, 4=Terminated
|
||||
uint8_t _pad[3];
|
||||
char name[64];
|
||||
uint64_t heapUsed; // heapNext - UserHeapBase (bytes)
|
||||
uint64_t heapUsed; // Distance from UserHeapBase to high-water mark
|
||||
uint64_t cpuTimeMs; // accumulated scheduler runtime
|
||||
uint64_t permittedCaps;
|
||||
uint64_t effectiveCaps;
|
||||
uint64_t delegableCaps;
|
||||
};
|
||||
|
||||
// Bluetooth scan result (returned by SYS_BTSCAN)
|
||||
@@ -650,23 +739,40 @@ namespace montauk::abi {
|
||||
uint8_t _pad[2];
|
||||
};
|
||||
|
||||
// Software-defined radio receiver description (returned by SYS_SDR_INFO).
|
||||
struct SdrDeviceInfo {
|
||||
char name[64]; // e.g. "Realtek RTL2832U"
|
||||
char tuner[32]; // e.g. "Rafael Micro R820T2"
|
||||
char serial[32]; // device serial / bus location
|
||||
uint64_t freqMin; // minimum tunable center frequency, Hz
|
||||
uint64_t freqMax; // maximum tunable center frequency, Hz
|
||||
uint32_t sampleRateMin; // minimum sample rate, Hz
|
||||
uint32_t sampleRateMax; // maximum sample rate, Hz
|
||||
uint32_t numGains; // number of discrete tuner gain steps
|
||||
int32_t gains[32]; // available gains, tenths of dB
|
||||
uint8_t sampleFormat; // SDR_FORMAT_*
|
||||
uint8_t present; // 1 if the underlying hardware is connected
|
||||
uint8_t streaming; // 1 if currently delivering samples
|
||||
uint8_t _pad;
|
||||
uint32_t _pad2;
|
||||
};
|
||||
// One USB interface currently represented by the xHCI device table. A
|
||||
// nonzero kernelDriverBound interface cannot be claimed by userspace.
|
||||
struct UsbInterfaceInfo {
|
||||
uint8_t slotId;
|
||||
uint8_t portId;
|
||||
uint8_t speed; // xHCI speed ID
|
||||
uint8_t interfaceNumber;
|
||||
uint16_t vendorId;
|
||||
uint16_t productId;
|
||||
uint8_t deviceClass;
|
||||
uint8_t interfaceClass;
|
||||
uint8_t interfaceSubClass;
|
||||
uint8_t interfaceProtocol;
|
||||
uint8_t bulkInEndpoint; // USB address, including direction bit
|
||||
uint8_t bulkOutEndpoint;
|
||||
uint16_t bulkInMaxPacket;
|
||||
uint16_t bulkOutMaxPacket;
|
||||
uint8_t kernelDriverBound;
|
||||
uint8_t claimed;
|
||||
uint8_t _reserved[4];
|
||||
} __attribute__((packed));
|
||||
|
||||
// Standard USB setup packet fields. requestType bit 7 determines the data
|
||||
// direction. length must match the data length passed to SYS_USB_CONTROL.
|
||||
struct UsbControlRequest {
|
||||
uint8_t requestType;
|
||||
uint8_t request;
|
||||
uint16_t value;
|
||||
uint16_t index;
|
||||
uint16_t length;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(UsbInterfaceInfo) == 24);
|
||||
static_assert(sizeof(UsbControlRequest) == 8);
|
||||
|
||||
|
||||
// Wi-Fi security suites reported in WifiNetwork.security.
|
||||
static constexpr uint8_t WIFI_SEC_OPEN = 0;
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
|
||||
namespace montauk::abi {
|
||||
|
||||
static int Sys_TerminalAttached() {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
return proc != nullptr && proc->redirected && proc->ioOutHandle >= 0;
|
||||
}
|
||||
|
||||
static void Sys_Print(const char* text) {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc && proc->redirected) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Usb.hpp
|
||||
* Generic userspace USB interface syscall layer.
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Drivers/USB/UserUsb.hpp>
|
||||
|
||||
namespace montauk::abi {
|
||||
|
||||
static int64_t Sys_UsbList(UsbInterfaceInfo* out, int maxCount) {
|
||||
return Drivers::USB::UserUsb::List(out, maxCount);
|
||||
}
|
||||
|
||||
static int64_t Sys_UsbClaim(uint8_t slotId, uint8_t interfaceNumber) {
|
||||
return Drivers::USB::UserUsb::Claim(slotId, interfaceNumber);
|
||||
}
|
||||
|
||||
static int64_t Sys_UsbClose(int handle) {
|
||||
return Drivers::USB::UserUsb::Close(handle);
|
||||
}
|
||||
|
||||
static int64_t Sys_UsbControl(int handle, const UsbControlRequest* request,
|
||||
void* data, uint32_t dataLen) {
|
||||
if (!request) return USB_ERR_INVALID;
|
||||
return Drivers::USB::UserUsb::Control(handle, *request, data, dataLen);
|
||||
}
|
||||
|
||||
static int64_t Sys_UsbBulkInStart(int handle, uint32_t transferBytes,
|
||||
uint32_t bufferCount) {
|
||||
return Drivers::USB::UserUsb::StartBulkIn(handle, transferBytes, bufferCount);
|
||||
}
|
||||
|
||||
static int64_t Sys_UsbBulkInStop(int handle) {
|
||||
return Drivers::USB::UserUsb::StopBulkIn(handle);
|
||||
}
|
||||
|
||||
static int64_t Sys_UsbBulkInRead(int handle, uint8_t* out, uint32_t maxLen) {
|
||||
return Drivers::USB::UserUsb::ReadBulkIn(handle, out, maxLen);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
/*
|
||||
* main.cpp
|
||||
* Kernel entry point
|
||||
* Copyright (c) 2025 Daniel Hammer, Limine Contributors (via Limine C++ example)
|
||||
* Copyright (c) 2025 Daniel Hammer.
|
||||
* Further copyright information and third party notices can be found at https://montaukos.org/license.txt.
|
||||
*/
|
||||
|
||||
#include <Fs/ProtectedPaths.hpp>
|
||||
#include <Memory/UserRange.hpp>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <Boot/Boot.hpp>
|
||||
@@ -29,7 +32,6 @@
|
||||
#include <Drivers/PS2/Keyboard.hpp>
|
||||
#include <Drivers/PS2/Mouse.hpp>
|
||||
#include <Drivers/Init.hpp>
|
||||
#include <Drivers/USB/Bluetooth/Bluetooth.hpp>
|
||||
#include <Graphics/Framebuffer.hpp>
|
||||
#include <Hal/MSR.hpp>
|
||||
#include <Hal/Cpu.hpp>
|
||||
@@ -62,12 +64,7 @@ extern "C" void kmain() {
|
||||
for (std::size_t i = 0; &__init_array[i] != __init_array_end; i++) {
|
||||
__init_array[i]();
|
||||
}
|
||||
|
||||
// Acquire the boot environment through the Montauk Boot Contract. The
|
||||
// active bootloader adapter (see Boot/Protocols/) translates its native
|
||||
// handoff into this bootloader-agnostic structure. A false return means
|
||||
// we cannot even bring up a console (unsupported loader, no HHDM, or no
|
||||
// framebuffer) -- there is nothing to do but halt.
|
||||
|
||||
if (!montauk::boot::Initialize()) {
|
||||
Hal::Halt();
|
||||
}
|
||||
@@ -119,20 +116,13 @@ extern "C" void kmain() {
|
||||
Memory::VMM::g_paging = &g_paging;
|
||||
g_paging.Init((uint64_t)&KernelStartSymbol, ((uint64_t)&KernelEndSymbol - (uint64_t)&KernelStartSymbol), boot.memoryMap, framebuffer);
|
||||
|
||||
// Reprogram PAT so entry 1 = Write-Combining (default is Write-Through).
|
||||
// Must be done after paging init and before any WC mappings.
|
||||
Hal::InitializePAT();
|
||||
Kt::KernelLogStream(OK, "Hal") << "PAT reprogrammed (entry 1 = WC)";
|
||||
|
||||
#endif
|
||||
|
||||
// Initialize the framebuffer early so we can WC-map it before
|
||||
// the bulk of boot logging begins (ACPI, PCI, drivers, etc.)
|
||||
Graphics::Framebuffer::Initialize(framebuffer);
|
||||
|
||||
#if defined (__x86_64__)
|
||||
// Map framebuffer as Write-Combining immediately for faster screen writes.
|
||||
// All subsequent log output benefits from WC burst transfers.
|
||||
Graphics::Framebuffer::MapWriteCombining();
|
||||
#endif
|
||||
|
||||
@@ -145,19 +135,12 @@ extern "C" void kmain() {
|
||||
|
||||
Hal::ApicInitialize(g_acpi.GetXSDT());
|
||||
|
||||
// Set up BSP per-CPU data (GS base) before enabling interrupts.
|
||||
// ISR stubs use SWAPGS which requires GS base to point to CpuData.
|
||||
Smp::InitBsp();
|
||||
|
||||
// Enable hardware P-state scaling and the thermal governor.
|
||||
// Needs GS base (per-CPU data) set up, and must run before the
|
||||
// APs boot so they inherit the shared policy in ApEntry.
|
||||
Hal::CpuPower::InitializeBsp();
|
||||
|
||||
// Now safe to enable interrupts (SWAPGS-aware ISR stubs are installed)
|
||||
asm volatile("sti");
|
||||
|
||||
// Initialize ACPI events (SCI, power button) after APIC is ready
|
||||
Hal::AcpiEvents::Initialize(g_acpi.GetXSDT());
|
||||
|
||||
Pci::Initialize(g_acpi.GetXSDT());
|
||||
@@ -186,29 +169,34 @@ extern "C" void kmain() {
|
||||
|
||||
Fs::InitializeBootFilesystems(boot.modules);
|
||||
|
||||
|
||||
#if defined (__x86_64__)
|
||||
Hal::LoadTSS();
|
||||
#endif
|
||||
|
||||
montauk::abi::InitializeSyscalls();
|
||||
|
||||
Sched::Initialize();
|
||||
Memory::InitUserRange();
|
||||
Fs::LogProtectedPaths();
|
||||
Ipc::Initialize();
|
||||
|
||||
// Boot Application Processors (all subsystems ready, APs can schedule)
|
||||
#if defined (__x86_64__)
|
||||
Smp::BootAPs(boot.smp);
|
||||
#endif
|
||||
|
||||
// Flush any stale PS/2 mouse bytes that accumulated during boot
|
||||
// (edge-triggered IRQs can be lost while spinlocks disable interrupts)
|
||||
#if defined (__x86_64__)
|
||||
Drivers::PS2::Mouse::FlushState();
|
||||
#endif
|
||||
|
||||
Kt::SuppressKernelLog();
|
||||
Sched::Spawn("0:/os/init.elf");
|
||||
|
||||
// Enable preemptive scheduling via the APIC timer
|
||||
Timekeeping::EnableSchedulerTick();
|
||||
|
||||
// Main loop: idle until next interrupt.
|
||||
#if defined (__x86_64__)
|
||||
// Use MWAIT for deeper C-states if available, otherwise HLT.
|
||||
auto* bspCpu = Smp::GetCpuData(0);
|
||||
|
||||
if (bspCpu && bspCpu->hasMwait) {
|
||||
static volatile uint64_t s_bspIdleMonitor = 0;
|
||||
for (;;) {
|
||||
@@ -219,4 +207,9 @@ extern "C" void kmain() {
|
||||
Timekeeping::IdleOnce(false);
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (;;) {
|
||||
Timekeeping::IdleOnce(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -440,6 +440,29 @@ namespace Drivers::Net::Wifi {
|
||||
static void ServiceAsync();
|
||||
static void ServiceRecovery();
|
||||
|
||||
bool HasDeferredWork() {
|
||||
if (g_initPending.load(std::memory_order_acquire) &&
|
||||
!g_initialized && Fs::Vfs::IsDriveRegistered(0)) {
|
||||
return true;
|
||||
}
|
||||
if (!g_iwx.Mmio) return false;
|
||||
if (g_iwx.WorkPending) return true;
|
||||
|
||||
uint64_t now = Timekeeping::GetMilliseconds();
|
||||
if (g_scanDeadline != 0 && now >= g_scanDeadline) return true;
|
||||
// The MLME/WPA state machine owns sub-second retransmission timers in
|
||||
// addition to the overall async deadline. Service it until ServiceAsync
|
||||
// observes Connected/Failed/Idle and clears this flag.
|
||||
if (g_asyncConnect) return true;
|
||||
if (g_iwx.State == IwxFwState::Error && g_initialized &&
|
||||
!g_recoveryGaveUp &&
|
||||
(g_lastRecoveryMs == 0 ||
|
||||
now - g_lastRecoveryMs >= RECOVERY_BACKOFF_MS)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ServiceEvents() {
|
||||
if (!g_iwx.Mmio) return;
|
||||
if (g_iwx.WorkPending) IwxProcessEvents();
|
||||
|
||||
@@ -21,6 +21,10 @@ namespace Drivers::Net::Wifi {
|
||||
// Steady-state event pump (RX ring, notifications). Idle-loop callback.
|
||||
void ServiceEvents();
|
||||
|
||||
// True when firmware initialization, an RX notification, an expired async
|
||||
// deadline, or a due recovery attempt needs idle-context servicing.
|
||||
bool HasDeferredWork();
|
||||
|
||||
bool IsInitialized();
|
||||
bool IsPresent();
|
||||
|
||||
|
||||
@@ -1,359 +0,0 @@
|
||||
/*
|
||||
* Sdr.cpp
|
||||
* Generic software-defined radio receive subsystem.
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "Sdr.hpp"
|
||||
#include <Memory/Heap.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Drivers::Radio::Sdr {
|
||||
|
||||
// I/Q ring size per receiver. 256 KiB is ~62 ms of jitter buffer at
|
||||
// 2.048 Msps (2 bytes/sample), which comfortably absorbs scheduling gaps
|
||||
// between a userspace reader's polls.
|
||||
static constexpr uint32_t RING_BYTES = 256 * 1024;
|
||||
|
||||
struct Receiver {
|
||||
bool used;
|
||||
bool opened;
|
||||
bool streaming;
|
||||
|
||||
char name[64];
|
||||
char tuner[32];
|
||||
char serial[32];
|
||||
uint64_t freqMin, freqMax;
|
||||
uint32_t sampleRateMin, sampleRateMax;
|
||||
int gains[MAX_GAINS];
|
||||
uint32_t numGains;
|
||||
uint8_t format;
|
||||
|
||||
ReceiverOps ops;
|
||||
void* ctx;
|
||||
|
||||
// Last-requested configuration (cached for GETPARAM readback).
|
||||
uint64_t freq;
|
||||
uint32_t sampleRate;
|
||||
int gainMode; // 0 = auto, 1 = manual
|
||||
int gain; // tenths of dB
|
||||
int ppm;
|
||||
int agc;
|
||||
int directSamp;
|
||||
|
||||
// I/Q ring buffer (byte FIFO).
|
||||
uint8_t* ring;
|
||||
uint32_t head; // write position
|
||||
uint32_t count; // bytes currently queued
|
||||
uint64_t totalBytes; // lifetime sample bytes delivered
|
||||
uint64_t droppedBytes; // bytes dropped on overflow
|
||||
kcp::Spinlock lock;
|
||||
};
|
||||
|
||||
static Receiver g_rx[MAX_RECEIVERS];
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static void CopyStr(char* dst, uint32_t cap, const char* src) {
|
||||
uint32_t i = 0;
|
||||
if (src) {
|
||||
for (; i < cap - 1 && src[i]; i++) dst[i] = src[i];
|
||||
}
|
||||
dst[i] = '\0';
|
||||
}
|
||||
|
||||
static Receiver* Lookup(int handle, bool needOpen) {
|
||||
if (handle < 0 || handle >= MAX_RECEIVERS) return nullptr;
|
||||
Receiver& r = g_rx[handle];
|
||||
if (!r.used) return nullptr;
|
||||
if (needOpen && !r.opened) return nullptr;
|
||||
return &r;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Driver-facing API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
int Register(const ReceiverDesc& desc) {
|
||||
for (int i = 0; i < MAX_RECEIVERS; i++) {
|
||||
if (g_rx[i].used) continue;
|
||||
Receiver& r = g_rx[i];
|
||||
|
||||
// Reset everything except the (non-copyable) spinlock instance.
|
||||
r.opened = false;
|
||||
r.streaming = false;
|
||||
CopyStr(r.name, sizeof(r.name), desc.name);
|
||||
CopyStr(r.tuner, sizeof(r.tuner), desc.tuner);
|
||||
CopyStr(r.serial, sizeof(r.serial), desc.serial);
|
||||
r.freqMin = desc.freqMin;
|
||||
r.freqMax = desc.freqMax;
|
||||
r.sampleRateMin = desc.sampleRateMin;
|
||||
r.sampleRateMax = desc.sampleRateMax;
|
||||
r.numGains = desc.numGains > MAX_GAINS ? MAX_GAINS : desc.numGains;
|
||||
for (uint32_t g = 0; g < r.numGains; g++) r.gains[g] = desc.gains[g];
|
||||
r.format = desc.format;
|
||||
r.ops = desc.ops;
|
||||
r.ctx = desc.ctx;
|
||||
|
||||
r.freq = (desc.freqMin + desc.freqMax) / 2;
|
||||
r.sampleRate = desc.sampleRateMax;
|
||||
r.gainMode = 0;
|
||||
r.gain = 0;
|
||||
r.ppm = 0;
|
||||
r.agc = 0;
|
||||
r.directSamp = 0;
|
||||
|
||||
r.ring = nullptr;
|
||||
r.head = r.count = 0;
|
||||
r.totalBytes = r.droppedBytes = 0;
|
||||
|
||||
r.used = true; // publish last
|
||||
KernelLogStream(OK, "SDR") << "Registered receiver " << (uint64_t)i
|
||||
<< ": " << r.name << " / " << r.tuner;
|
||||
return i;
|
||||
}
|
||||
KernelLogStream(WARNING, "SDR") << "No free receiver slot for " << desc.name;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Unregister(int idx) {
|
||||
if (idx < 0 || idx >= MAX_RECEIVERS) return;
|
||||
Receiver& r = g_rx[idx];
|
||||
if (!r.used) return;
|
||||
|
||||
if (r.streaming && r.ops.Stop) r.ops.Stop(r.ctx);
|
||||
|
||||
r.lock.Acquire();
|
||||
r.streaming = false;
|
||||
r.opened = false;
|
||||
r.used = false;
|
||||
uint8_t* ring = r.ring;
|
||||
r.ring = nullptr;
|
||||
r.head = r.count = 0;
|
||||
r.lock.Release();
|
||||
|
||||
if (ring) Memory::g_heap->Free(ring);
|
||||
KernelLogStream(INFO, "SDR") << "Unregistered receiver " << (uint64_t)idx;
|
||||
}
|
||||
|
||||
void PushSamples(int idx, const uint8_t* data, uint32_t len) {
|
||||
if (idx < 0 || idx >= MAX_RECEIVERS || !data || len == 0) return;
|
||||
Receiver& r = g_rx[idx];
|
||||
|
||||
r.lock.Acquire();
|
||||
// Re-validate under the lock: Unregister() clears these and frees the
|
||||
// ring while holding the same lock, so an in-flight USB completion can
|
||||
// never write into a freed buffer.
|
||||
if (!r.used || !r.ring) { r.lock.Release(); return; }
|
||||
|
||||
uint32_t space = RING_BYTES - r.count;
|
||||
uint32_t n = len;
|
||||
uint32_t dropped = 0;
|
||||
if (n > space) {
|
||||
// Truncate to a whole number of I/Q byte pairs: dropping an odd
|
||||
// count would swap I and Q for the rest of the stream.
|
||||
n = space & ~1u;
|
||||
dropped = len - n;
|
||||
}
|
||||
|
||||
uint32_t first = RING_BYTES - r.head;
|
||||
if (first > n) first = n;
|
||||
memcpy(r.ring + r.head, data, first);
|
||||
if (n > first) memcpy(r.ring, data + first, n - first);
|
||||
|
||||
r.head = (r.head + n) % RING_BYTES;
|
||||
r.count += n;
|
||||
r.totalBytes += n;
|
||||
r.droppedBytes += dropped;
|
||||
r.lock.Release();
|
||||
}
|
||||
|
||||
bool IsStreaming(int idx) {
|
||||
if (idx < 0 || idx >= MAX_RECEIVERS) return false;
|
||||
return g_rx[idx].used && g_rx[idx].streaming;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Syscall-facing API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
int Count() {
|
||||
int n = 0;
|
||||
for (int i = 0; i < MAX_RECEIVERS; i++) if (g_rx[i].used) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
bool GetInfo(int idx, montauk::abi::SdrDeviceInfo* out) {
|
||||
Receiver* r = Lookup(idx, false);
|
||||
if (!r || !out) return false;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
CopyStr(out->name, sizeof(out->name), r->name);
|
||||
CopyStr(out->tuner, sizeof(out->tuner), r->tuner);
|
||||
CopyStr(out->serial, sizeof(out->serial), r->serial);
|
||||
out->freqMin = r->freqMin;
|
||||
out->freqMax = r->freqMax;
|
||||
out->sampleRateMin = r->sampleRateMin;
|
||||
out->sampleRateMax = r->sampleRateMax;
|
||||
out->numGains = r->numGains;
|
||||
for (uint32_t g = 0; g < r->numGains && g < 32; g++) out->gains[g] = r->gains[g];
|
||||
out->sampleFormat = r->format;
|
||||
out->present = 1;
|
||||
out->streaming = r->streaming ? 1 : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int Open(int idx) {
|
||||
Receiver* r = Lookup(idx, false);
|
||||
if (!r) return -1;
|
||||
|
||||
// Single-user OS: an Open always claims the device, reclaiming it from a
|
||||
// previous owner that exited without closing.
|
||||
if (r->streaming && r->ops.Stop) r->ops.Stop(r->ctx);
|
||||
|
||||
if (!r->ring) {
|
||||
r->ring = (uint8_t*)Memory::g_heap->Request(RING_BYTES);
|
||||
if (!r->ring) {
|
||||
KernelLogStream(ERROR, "SDR") << "Ring alloc failed for receiver "
|
||||
<< (uint64_t)idx;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
r->lock.Acquire();
|
||||
r->head = r->count = 0;
|
||||
r->lock.Release();
|
||||
r->streaming = false;
|
||||
r->opened = true;
|
||||
return idx; // handle == index
|
||||
}
|
||||
|
||||
int Close(int handle) {
|
||||
Receiver* r = Lookup(handle, true);
|
||||
if (!r) return -1;
|
||||
if (r->streaming && r->ops.Stop) r->ops.Stop(r->ctx);
|
||||
r->streaming = false;
|
||||
r->opened = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Start(int handle) {
|
||||
Receiver* r = Lookup(handle, true);
|
||||
if (!r) return -1;
|
||||
// Already streaming: a second Start must not re-arm the driver's
|
||||
// transfer pool (it would double-queue every buffer).
|
||||
if (r->streaming) return 0;
|
||||
|
||||
r->lock.Acquire();
|
||||
r->head = r->count = 0; // discard stale samples before (re)starting
|
||||
r->lock.Release();
|
||||
|
||||
int rc = r->ops.Start ? r->ops.Start(r->ctx) : -1;
|
||||
if (rc == 0) r->streaming = true;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int Stop(int handle) {
|
||||
Receiver* r = Lookup(handle, true);
|
||||
if (!r) return -1;
|
||||
int rc = r->ops.Stop ? r->ops.Stop(r->ctx) : 0;
|
||||
r->streaming = false;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int Read(int handle, uint8_t* buf, uint32_t len) {
|
||||
Receiver* r = Lookup(handle, true);
|
||||
if (!r || !buf || !r->ring) return -1;
|
||||
if (len == 0) return 0;
|
||||
|
||||
// Give the driver a process-context tick (e.g. USB stall recovery)
|
||||
// before draining; do this outside the ring lock since it may issue
|
||||
// blocking USB commands.
|
||||
if (r->streaming && r->ops.Service) r->ops.Service(r->ctx);
|
||||
|
||||
r->lock.Acquire();
|
||||
uint32_t n = r->count < len ? r->count : len;
|
||||
uint32_t tail = (r->head + RING_BYTES - r->count) % RING_BYTES;
|
||||
uint32_t first = RING_BYTES - tail;
|
||||
if (first > n) first = n;
|
||||
memcpy(buf, r->ring + tail, first);
|
||||
if (n > first) memcpy(buf + first, r->ring, n - first);
|
||||
r->count -= n;
|
||||
r->lock.Release();
|
||||
return (int)n;
|
||||
}
|
||||
|
||||
uint32_t Available(int handle) {
|
||||
Receiver* r = Lookup(handle, true);
|
||||
if (!r) return 0;
|
||||
return r->count;
|
||||
}
|
||||
|
||||
int64_t SetParam(int handle, int param, uint64_t value) {
|
||||
Receiver* r = Lookup(handle, true);
|
||||
if (!r) return -1;
|
||||
|
||||
switch (param) {
|
||||
case montauk::abi::SDR_PARAM_FREQ:
|
||||
if (!r->ops.SetFreq) return -1;
|
||||
if (r->ops.SetFreq(r->ctx, value) != 0) return -1;
|
||||
r->freq = value;
|
||||
return 0;
|
||||
case montauk::abi::SDR_PARAM_SAMPLE_RATE:
|
||||
if (!r->ops.SetSampleRate) return -1;
|
||||
if (r->ops.SetSampleRate(r->ctx, (uint32_t)value) != 0) return -1;
|
||||
r->sampleRate = (uint32_t)value;
|
||||
return 0;
|
||||
case montauk::abi::SDR_PARAM_GAIN_MODE:
|
||||
if (!r->ops.SetGainMode) return -1;
|
||||
if (r->ops.SetGainMode(r->ctx, (int)value) != 0) return -1;
|
||||
r->gainMode = (int)value ? 1 : 0;
|
||||
return 0;
|
||||
case montauk::abi::SDR_PARAM_GAIN:
|
||||
if (!r->ops.SetGain) return -1;
|
||||
if (r->ops.SetGain(r->ctx, (int)(int64_t)value) != 0) return -1;
|
||||
r->gain = (int)(int64_t)value;
|
||||
return 0;
|
||||
case montauk::abi::SDR_PARAM_FREQ_CORR:
|
||||
if (!r->ops.SetFreqCorrection) return -1;
|
||||
if (r->ops.SetFreqCorrection(r->ctx, (int)(int64_t)value) != 0) return -1;
|
||||
r->ppm = (int)(int64_t)value;
|
||||
return 0;
|
||||
case montauk::abi::SDR_PARAM_AGC:
|
||||
if (!r->ops.SetAgc) return -1;
|
||||
if (r->ops.SetAgc(r->ctx, (int)value) != 0) return -1;
|
||||
r->agc = (int)value ? 1 : 0;
|
||||
return 0;
|
||||
case montauk::abi::SDR_PARAM_DIRECT_SAMP:
|
||||
if (!r->ops.SetDirectSampling) return -1;
|
||||
if (r->ops.SetDirectSampling(r->ctx, (int)value) != 0) return -1;
|
||||
r->directSamp = (int)value;
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t GetParam(int handle, int param) {
|
||||
Receiver* r = Lookup(handle, true);
|
||||
if (!r) return -1;
|
||||
|
||||
switch (param) {
|
||||
case montauk::abi::SDR_PARAM_FREQ: return (int64_t)r->freq;
|
||||
case montauk::abi::SDR_PARAM_SAMPLE_RATE: return (int64_t)r->sampleRate;
|
||||
case montauk::abi::SDR_PARAM_GAIN_MODE: return r->gainMode;
|
||||
case montauk::abi::SDR_PARAM_GAIN: return r->gain;
|
||||
case montauk::abi::SDR_PARAM_FREQ_CORR: return r->ppm;
|
||||
case montauk::abi::SDR_PARAM_AGC: return r->agc;
|
||||
case montauk::abi::SDR_PARAM_DIRECT_SAMP: return r->directSamp;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Sdr.hpp
|
||||
* Generic software-defined radio (SDR) receive subsystem.
|
||||
*
|
||||
* Hardware-agnostic registry of radio receivers. A concrete driver (e.g. the
|
||||
* RTL-SDR USB driver) registers itself as a receiver by supplying an ops table
|
||||
* and a private context pointer; it then pushes demodulated baseband I/Q
|
||||
* samples into a per-receiver ring buffer via PushSamples(). Userspace reaches
|
||||
* this layer through the SYS_SDR_* syscalls and drains the ring with Read().
|
||||
*
|
||||
* The native sample format is CU8 -- 8-bit unsigned interleaved I/Q -- which is
|
||||
* what the RTL2832U produces; other formats can be advertised per receiver via
|
||||
* SdrDeviceInfo.sampleFormat.
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <CppLib/Spinlock.hpp>
|
||||
#include <Api/Syscall.hpp>
|
||||
|
||||
namespace Drivers::Radio::Sdr {
|
||||
|
||||
static constexpr int MAX_RECEIVERS = 4;
|
||||
static constexpr int MAX_GAINS = 32;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Receiver ops table -- implemented by a concrete driver.
|
||||
// All calls happen in process/syscall context (never from the sample
|
||||
// callback), so they may block on USB control transfers. Each returns 0 on
|
||||
// success, negative on error. ctx is the receiver's private pointer.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
struct ReceiverOps {
|
||||
int (*SetFreq)(void* ctx, uint64_t hz);
|
||||
int (*SetSampleRate)(void* ctx, uint32_t hz);
|
||||
int (*SetGainMode)(void* ctx, int manual); // 0 = auto/AGC, 1 = manual
|
||||
int (*SetGain)(void* ctx, int tenthsDb);
|
||||
int (*SetFreqCorrection)(void* ctx, int ppm);
|
||||
int (*SetAgc)(void* ctx, int on); // demod digital AGC
|
||||
int (*SetDirectSampling)(void* ctx, int mode); // 0=off,1=I,2=Q
|
||||
int (*Start)(void* ctx); // arm streaming
|
||||
int (*Stop)(void* ctx); // halt streaming
|
||||
// Optional: process-context housekeeping invoked from Read() while
|
||||
// streaming (e.g. USB stall recovery that cannot run in the ISR). May
|
||||
// be null.
|
||||
void (*Service)(void* ctx);
|
||||
};
|
||||
|
||||
// Static description a driver supplies at registration time.
|
||||
struct ReceiverDesc {
|
||||
const char* name; // e.g. "Realtek RTL2832U"
|
||||
const char* tuner; // e.g. "Rafael Micro R820T2"
|
||||
const char* serial; // bus location / serial string (may be null)
|
||||
uint64_t freqMin; // Hz
|
||||
uint64_t freqMax; // Hz
|
||||
uint32_t sampleRateMin;
|
||||
uint32_t sampleRateMax;
|
||||
const int* gains; // table of tenths-of-dB gain steps (may be null)
|
||||
uint32_t numGains;
|
||||
uint8_t format; // montauk::abi::SDR_FORMAT_*
|
||||
ReceiverOps ops;
|
||||
void* ctx;
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Driver-facing API
|
||||
// =========================================================================
|
||||
|
||||
// Register a receiver. Returns its index [0, MAX_RECEIVERS) or -1 if full.
|
||||
int Register(const ReceiverDesc& desc);
|
||||
|
||||
// Remove a receiver (e.g. on USB unplug). Stops streaming and frees the
|
||||
// ring. Safe to call with an out-of-range / already-removed index.
|
||||
void Unregister(int idx);
|
||||
|
||||
// Push baseband sample bytes into a receiver's ring buffer. Called from the
|
||||
// driver's USB completion callback (possibly interrupt context); never
|
||||
// allocates or blocks. Bytes that do not fit are dropped (counted).
|
||||
void PushSamples(int idx, const uint8_t* data, uint32_t len);
|
||||
|
||||
// True if the receiver is currently in the streaming state (used by drivers
|
||||
// to decide whether to re-arm USB transfers).
|
||||
bool IsStreaming(int idx);
|
||||
|
||||
// =========================================================================
|
||||
// Syscall-facing API
|
||||
// =========================================================================
|
||||
|
||||
// Number of registered receivers.
|
||||
int Count();
|
||||
|
||||
// Fill out an info struct for receiver idx. Returns false if idx invalid.
|
||||
bool GetInfo(int idx, montauk::abi::SdrDeviceInfo* out);
|
||||
|
||||
// Claim a receiver for use. Returns a handle (== idx) or -1 on failure.
|
||||
int Open(int idx);
|
||||
|
||||
// Release a receiver (stops streaming). Returns 0 on success.
|
||||
int Close(int handle);
|
||||
|
||||
// Begin / end sample delivery. Returns 0 on success, negative on error.
|
||||
int Start(int handle);
|
||||
int Stop(int handle);
|
||||
|
||||
// Copy up to len bytes of buffered I/Q out of the ring. Non-blocking;
|
||||
// returns the number of bytes copied (0 when nothing is queued).
|
||||
int Read(int handle, uint8_t* buf, uint32_t len);
|
||||
|
||||
// Number of sample bytes currently queued in the ring.
|
||||
uint32_t Available(int handle);
|
||||
|
||||
// Set / get a tunable parameter (montauk::abi::SDR_PARAM_*). SetParam
|
||||
// returns 0 on success; GetParam returns the cached value or negative on
|
||||
// error.
|
||||
int64_t SetParam(int handle, int param, uint64_t value);
|
||||
int64_t GetParam(int handle, int param);
|
||||
|
||||
}
|
||||
@@ -419,6 +419,14 @@ namespace Drivers::USB::Bluetooth {
|
||||
// ServiceEvents — steady-state event pump (idle loop)
|
||||
// =========================================================================
|
||||
|
||||
bool HasDeferredWork() {
|
||||
if (g_initPending.load(std::memory_order_acquire) &&
|
||||
!g_initialized && Fs::Vfs::IsDriveRegistered(0)) {
|
||||
return true;
|
||||
}
|
||||
return g_initialized && Hci::HasPendingCommands();
|
||||
}
|
||||
|
||||
void ServiceEvents() {
|
||||
if (!g_initialized) return;
|
||||
if (Xhci::InPollContext()) return; // never nest under PollEvents
|
||||
|
||||
@@ -27,6 +27,11 @@ namespace Drivers::USB::Bluetooth {
|
||||
// (PollEvents/DrainEvents/ProcessPendingCommands all self-serialize).
|
||||
void ServiceEvents();
|
||||
|
||||
// True when boot-deferred initialization or queued HCI control work needs
|
||||
// an idle-context service pass. USB receive events are signaled separately
|
||||
// by xHCI and cause the dispatcher to service Bluetooth in the same pass.
|
||||
bool HasDeferredWork();
|
||||
|
||||
// Query adapter state
|
||||
bool IsInitialized();
|
||||
uint8_t GetSlotId();
|
||||
|
||||
@@ -1719,6 +1719,11 @@ namespace Drivers::USB::Bluetooth::Hci {
|
||||
s_active.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool HasPendingCommands() {
|
||||
return g_pendingTail.load(std::memory_order_acquire) !=
|
||||
g_pendingHead.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
bool WaitSecureSendResult(uint32_t timeoutMs, uint8_t* outResult, uint8_t* outStatus) {
|
||||
uint64_t start = Timekeeping::GetMilliseconds();
|
||||
while (Timekeeping::GetMilliseconds() - start < timeoutMs) {
|
||||
|
||||
@@ -331,6 +331,7 @@ namespace Drivers::USB::Bluetooth::Hci {
|
||||
// real confirmed transfers. Call from top-level (e.g. the connect loop),
|
||||
// NOT from an event handler -- event handlers only enqueue.
|
||||
void ProcessPendingCommands();
|
||||
bool HasPendingCommands();
|
||||
|
||||
// ACL TX flow control: outstanding (un-acked) ACL packets, and the
|
||||
// controller's ACL buffer count (Number-Of-Completed-Packets credits). The
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* R820t.hpp
|
||||
* Rafael Micro R820T / R820T2 silicon tuner.
|
||||
*
|
||||
* The tuner sits on the RTL2832U's I2C bus; all register traffic is carried
|
||||
* by the demod's I2C repeater (managed by the RtlSdr layer, which enables the
|
||||
* repeater around every call here). This module owns the tuner-side logic:
|
||||
* the init register array, the PLL/VCO frequency synthesis, the RF tracking
|
||||
* filter / mux band selection, and the LNA/Mixer/VGA gain stages.
|
||||
*
|
||||
* Register/algorithm facts follow the publicly documented R820T programming
|
||||
* model (as used by osmocom rtl-sdr); the implementation here is original.
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Drivers::USB::Radio {
|
||||
|
||||
// I2C bus address of the tuner on the RTL2832U (8-bit form).
|
||||
static constexpr uint8_t R820T_I2C_ADDR = 0x34;
|
||||
// Chip-id register (reg 0) reads back this value for an R820T/R820T2.
|
||||
static constexpr uint8_t R820T_CHECK_VAL = 0x69;
|
||||
|
||||
// First writable register; the 27-entry shadow covers regs 0x05..0x1f.
|
||||
static constexpr uint8_t R820T_REG_SHADOW_START = 5;
|
||||
static constexpr uint8_t R820T_NUM_REGS = 27;
|
||||
|
||||
struct R820tDev {
|
||||
uint8_t slotId;
|
||||
uint32_t xtal; // reference crystal, Hz (28.8 MHz on RTL-SDR)
|
||||
uint32_t intFreq; // IF the demod expects the signal at, Hz (3.57 MHz)
|
||||
uint8_t regs[32]; // register shadow (index == register number)
|
||||
bool hasLock; // PLL lock state after the last tune
|
||||
bool inited;
|
||||
};
|
||||
|
||||
// Detect an R820T/R820T2 on the demod I2C bus. The caller must have the
|
||||
// demod's I2C repeater enabled. Returns true if the chip id matches.
|
||||
bool R820tDetect(uint8_t slotId);
|
||||
|
||||
// Initialise the tuner (writes the init register array + base setup). The
|
||||
// caller must have the I2C repeater enabled. Returns true on success.
|
||||
bool R820tInit(R820tDev& d, uint8_t slotId, uint32_t xtal, uint32_t intFreq);
|
||||
|
||||
// Tune to an RF center frequency (Hz). Programs the RF mux band and the PLL
|
||||
// for an LO of rfHz + intFreq. Updates d.hasLock. Repeater must be on.
|
||||
bool R820tSetFreq(R820tDev& d, uint64_t rfHz);
|
||||
|
||||
// Configure gain. manual==0 puts LNA/mixer in AGC; manual!=0 selects the
|
||||
// closest fixed gain to tenthsDb from the LNA+mixer step tables.
|
||||
bool R820tSetGain(R820tDev& d, int manual, int tenthsDb);
|
||||
|
||||
// Put the tuner into standby (mute / power down).
|
||||
void R820tStandby(R820tDev& d);
|
||||
|
||||
// The discrete gain table (tenths of dB), for advertising to userspace.
|
||||
const int* R820tGainTable(int* count);
|
||||
|
||||
}
|
||||
@@ -1,575 +0,0 @@
|
||||
/*
|
||||
* RtlSdr.cpp
|
||||
* Realtek RTL2832U + R820T2 SDR receiver driver.
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "RtlSdr.hpp"
|
||||
#include "R820t.hpp"
|
||||
#include <Drivers/Radio/Sdr.hpp>
|
||||
#include <Drivers/USB/Xhci.hpp>
|
||||
#include <Drivers/USB/UsbDevice.hpp>
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <CppLib/Spinlock.hpp>
|
||||
#include <Api/Syscall.hpp>
|
||||
#include <atomic>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Drivers::USB::Radio {
|
||||
|
||||
// =========================================================================
|
||||
// Constants
|
||||
// =========================================================================
|
||||
|
||||
// Vendor control-transfer request types (vendor, host<->device).
|
||||
static constexpr uint8_t CTRL_OUT = 0x40; // host-to-device, vendor
|
||||
static constexpr uint8_t CTRL_IN = 0xC0; // device-to-host, vendor
|
||||
|
||||
// RTL2832U register blocks (high byte of wIndex; OR 0x10 to write).
|
||||
static constexpr uint8_t BLOCK_USB = 1;
|
||||
static constexpr uint8_t BLOCK_SYS = 2;
|
||||
static constexpr uint8_t BLOCK_IIC = 6;
|
||||
|
||||
// USB / system register addresses.
|
||||
static constexpr uint16_t USB_EPA_CTL = 0x2148;
|
||||
static constexpr uint16_t USB_EPA_MAXPKT = 0x2158;
|
||||
static constexpr uint16_t USB_SYSCTL = 0x2000;
|
||||
static constexpr uint16_t SYS_DEMOD_CTL = 0x3000;
|
||||
static constexpr uint16_t SYS_DEMOD_CTL1 = 0x300b;
|
||||
|
||||
static constexpr uint32_t RTL_XTAL = 28800000; // 28.8 MHz reference
|
||||
static constexpr uint32_t R82XX_IF = 3570000; // IF the demod expects
|
||||
static constexpr uint32_t TWO_POW22 = 1u << 22;
|
||||
|
||||
// =========================================================================
|
||||
// Driver state (single instance -- the common RTL-SDR case)
|
||||
// =========================================================================
|
||||
|
||||
static bool g_present = false;
|
||||
static bool g_hwInited = false;
|
||||
static bool g_streaming = false;
|
||||
static uint8_t g_slotId = 0;
|
||||
static int g_rxIndex = -1;
|
||||
|
||||
static uint8_t* g_ctlBuf = nullptr; // HHDM page for control transfers
|
||||
static kcp::Mutex g_ctlLock; // serialises register access
|
||||
|
||||
static R820tDev g_tuner{};
|
||||
static uint32_t g_rtlXtal = RTL_XTAL; // adjusted by ppm correction
|
||||
static int g_ppm = 0;
|
||||
static int g_manual = 0; // tuner gain mode (0=auto)
|
||||
static int g_gain = 0; // tuner gain, tenths of dB
|
||||
static int g_directSamp = 0; // 0=tuner path, 1=I ADC, 2=Q ADC
|
||||
static uint64_t g_lastFreq = 0; // last successfully tuned freq (Hz)
|
||||
|
||||
// Bulk-IN streaming geometry. We keep BULK_POOL_BUFS transfers of
|
||||
// BULK_XFER_LEN bytes outstanding at once (multi-URB), so the RTL2832U FIFO
|
||||
// always has a TRB to DMA into and never overflows in the window between a
|
||||
// completion and its re-arm -- the single-outstanding scheme dropped ~88% of
|
||||
// samples at 2.048 Msps for exactly that reason. 4 KiB == one DMA page;
|
||||
// 16 x 4 KiB == 64 KiB in flight, ~16 ms of slack at 4 MB/s.
|
||||
static constexpr uint32_t BULK_XFER_LEN = 4096;
|
||||
static constexpr uint32_t BULK_POOL_BUFS = 16;
|
||||
|
||||
// Set by the bulk-IN completion callback when the endpoint halts (cc=6
|
||||
// STALL etc.). Recovery (Reset Endpoint) needs a command wait and so must
|
||||
// run in process context -- serviced from Read() via OpService().
|
||||
static std::atomic<bool> g_bulkStalled{false};
|
||||
|
||||
// =========================================================================
|
||||
// Low-level register access (control transfers via EP0)
|
||||
// =========================================================================
|
||||
|
||||
static bool RegWrite(uint8_t block, uint16_t addr, uint16_t val, uint8_t len) {
|
||||
if (!g_ctlBuf) return false;
|
||||
g_ctlBuf[0] = (len == 1) ? (uint8_t)(val & 0xff) : (uint8_t)(val >> 8);
|
||||
g_ctlBuf[1] = (uint8_t)(val & 0xff);
|
||||
uint16_t index = (uint16_t)((block << 8) | 0x10);
|
||||
return Xhci::ControlTransfer(g_slotId, CTRL_OUT, 0, addr, index, len,
|
||||
g_ctlBuf, false) == Xhci::CC_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8_t DemodRead(uint8_t page, uint16_t addr) {
|
||||
if (!g_ctlBuf) return 0;
|
||||
uint16_t raddr = (uint16_t)((addr << 8) | 0x20);
|
||||
g_ctlBuf[0] = 0;
|
||||
Xhci::ControlTransfer(g_slotId, CTRL_IN, 0, raddr, page, 1, g_ctlBuf, true);
|
||||
return g_ctlBuf[0];
|
||||
}
|
||||
|
||||
static bool DemodWrite(uint8_t page, uint16_t addr, uint16_t val, uint8_t len) {
|
||||
if (!g_ctlBuf) return false;
|
||||
uint16_t waddr = (uint16_t)((addr << 8) | 0x20);
|
||||
uint16_t index = (uint16_t)(0x10 | page);
|
||||
g_ctlBuf[0] = (len == 1) ? (uint8_t)(val & 0xff) : (uint8_t)(val >> 8);
|
||||
g_ctlBuf[1] = (uint8_t)(val & 0xff);
|
||||
bool ok = Xhci::ControlTransfer(g_slotId, CTRL_OUT, 0, waddr, index, len,
|
||||
g_ctlBuf, false) == Xhci::CC_SUCCESS;
|
||||
// Dummy status read after every demod write (reference behaviour);
|
||||
// acts as a write barrier so the register latches before the next op.
|
||||
DemodRead(0x0a, 0x01);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void SetI2cRepeater(bool on) {
|
||||
DemodWrite(1, 0x01, on ? 0x18 : 0x10, 1);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// I2C facade for the tuner module
|
||||
// =========================================================================
|
||||
|
||||
bool RtlI2cWrite(uint8_t slotId, uint8_t i2cAddr, const uint8_t* buf, uint8_t len) {
|
||||
if (!g_ctlBuf || len == 0 || len > 64) return false;
|
||||
memcpy(g_ctlBuf, buf, len);
|
||||
uint16_t index = (uint16_t)((BLOCK_IIC << 8) | 0x10);
|
||||
uint32_t cc = Xhci::ControlTransfer(slotId, CTRL_OUT, 0, i2cAddr, index, len,
|
||||
g_ctlBuf, false);
|
||||
if (cc != Xhci::CC_SUCCESS)
|
||||
KernelLogStream(WARNING, "RTL-SDR") << "I2C write cc=" << (uint64_t)cc
|
||||
<< " reg=0x" << base::hex << (uint64_t)buf[0]
|
||||
<< " len=" << base::dec << (uint64_t)len;
|
||||
return cc == Xhci::CC_SUCCESS;
|
||||
}
|
||||
|
||||
bool RtlI2cRead(uint8_t slotId, uint8_t i2cAddr, uint8_t* buf, uint8_t len) {
|
||||
if (!g_ctlBuf || len == 0 || len > 64) return false;
|
||||
uint16_t index = (uint16_t)(BLOCK_IIC << 8);
|
||||
uint32_t cc = Xhci::ControlTransfer(slotId, CTRL_IN, 0, i2cAddr, index, len,
|
||||
g_ctlBuf, true);
|
||||
if (cc != Xhci::CC_SUCCESS) {
|
||||
KernelLogStream(WARNING, "RTL-SDR") << "I2C read cc=" << (uint64_t)cc
|
||||
<< " len=" << (uint64_t)len;
|
||||
return false;
|
||||
}
|
||||
memcpy(buf, g_ctlBuf, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Demodulator bring-up
|
||||
// =========================================================================
|
||||
|
||||
// The 16-tap default FIR (8x int8 then 8x int12) used for the SDR/FM path.
|
||||
static void SetFir() {
|
||||
static const int fir[16] = {
|
||||
-54, -36, -41, -40, -32, -14, 14, 53,
|
||||
101, 156, 215, 273, 327, 372, 404, 421,
|
||||
};
|
||||
uint8_t buf[20];
|
||||
for (int i = 0; i < 8; i++) buf[i] = (uint8_t)(fir[i] & 0xff);
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
int v0 = fir[8 + i];
|
||||
int v1 = fir[8 + i + 1];
|
||||
buf[8 + i * 3 / 2] = (uint8_t)((v0 >> 4) & 0xff);
|
||||
buf[8 + i * 3 / 2 + 1] = (uint8_t)(((v0 << 4) | ((v1 >> 8) & 0x0f)) & 0xff);
|
||||
buf[8 + i * 3 / 2 + 2] = (uint8_t)(v1 & 0xff);
|
||||
}
|
||||
for (int i = 0; i < 20; i++) DemodWrite(1, (uint16_t)(0x1c + i), buf[i], 1);
|
||||
}
|
||||
|
||||
static bool BasebandInit() {
|
||||
// USB FIFO / endpoint A setup.
|
||||
RegWrite(BLOCK_USB, USB_SYSCTL, 0x09, 1);
|
||||
RegWrite(BLOCK_USB, USB_EPA_MAXPKT, 0x0002, 2);
|
||||
RegWrite(BLOCK_USB, USB_EPA_CTL, 0x1002, 2);
|
||||
|
||||
// Power on the demod.
|
||||
RegWrite(BLOCK_SYS, SYS_DEMOD_CTL1, 0x22, 1);
|
||||
RegWrite(BLOCK_SYS, SYS_DEMOD_CTL, 0xe8, 1);
|
||||
|
||||
// Soft-reset the demod state machine.
|
||||
DemodWrite(1, 0x01, 0x14, 1);
|
||||
DemodWrite(1, 0x01, 0x10, 1);
|
||||
|
||||
// Disable spectrum inversion + clear DDC shift / IF registers.
|
||||
DemodWrite(1, 0x15, 0x00, 1);
|
||||
DemodWrite(1, 0x16, 0x0000, 2);
|
||||
for (int i = 0; i < 6; i++) DemodWrite(1, (uint16_t)(0x16 + i), 0x00, 1);
|
||||
|
||||
SetFir();
|
||||
|
||||
DemodWrite(0, 0x19, 0x05, 1); // enable SDR mode, disable DAGC
|
||||
DemodWrite(1, 0x93, 0xf0, 1);
|
||||
DemodWrite(1, 0x94, 0x0f, 1);
|
||||
DemodWrite(1, 0x11, 0x00, 1); // disable AGC loop
|
||||
DemodWrite(1, 0x04, 0x00, 1);
|
||||
DemodWrite(0, 0x61, 0x60, 1); // disable PID filter
|
||||
DemodWrite(0, 0x06, 0x80, 1); // default ADC I/Q datapath
|
||||
DemodWrite(1, 0xb1, 0x1b, 1); // zero-IF + DC cancel + IQ comp/est
|
||||
DemodWrite(0, 0x0d, 0x83, 1); // disable clock output on TP_CK0
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the digital downconversion IF frequency the demod searches at.
|
||||
static void SetIfFreq(uint32_t freq) {
|
||||
int32_t ifv = (int32_t)(-(int64_t)((uint64_t)freq * TWO_POW22 / g_rtlXtal));
|
||||
DemodWrite(1, 0x19, (uint16_t)((ifv >> 16) & 0x3f), 1);
|
||||
DemodWrite(1, 0x1a, (uint16_t)((ifv >> 8) & 0xff), 1);
|
||||
DemodWrite(1, 0x1b, (uint16_t)(ifv & 0xff), 1);
|
||||
}
|
||||
|
||||
static void ApplySampleFreqCorrection() {
|
||||
int32_t offs = (int32_t)(-(int64_t)g_ppm * (1 << 24) / 1000000);
|
||||
DemodWrite(1, 0x3f, (uint16_t)(offs & 0xff), 1);
|
||||
DemodWrite(1, 0x3e, (uint16_t)((offs >> 8) & 0x3f), 1);
|
||||
}
|
||||
|
||||
static bool TunerInit() {
|
||||
SetI2cRepeater(true);
|
||||
// Retry detection a few times: an I2C read can transiently come back
|
||||
// wrong if it raced another core's USB activity around bring-up.
|
||||
bool detected = false;
|
||||
for (int attempt = 0; attempt < 4 && !detected; attempt++)
|
||||
detected = R820tDetect(g_slotId);
|
||||
bool ok = detected && R820tInit(g_tuner, g_slotId, g_rtlXtal, R82XX_IF);
|
||||
SetI2cRepeater(false);
|
||||
if (!detected) {
|
||||
KernelLogStream(WARNING, "RTL-SDR") << "no R820T2 tuner found on I2C";
|
||||
return false;
|
||||
}
|
||||
if (!ok) return false;
|
||||
|
||||
// Demod path for the R820T2 low-IF tuner.
|
||||
DemodWrite(1, 0xb1, 0x1a, 1); // disable zero-IF mode
|
||||
DemodWrite(0, 0x08, 0x4d, 1); // enable In-phase ADC input only
|
||||
SetIfFreq(R82XX_IF);
|
||||
DemodWrite(1, 0x15, 0x01, 1); // enable spectrum inversion
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool EnsureInit() {
|
||||
if (g_hwInited) return true;
|
||||
if (!g_present || !g_ctlBuf) return false;
|
||||
if (!BasebandInit()) return false;
|
||||
if (!TunerInit()) return false;
|
||||
g_hwInited = true;
|
||||
KernelLogStream(OK, "RTL-SDR") << "Demod + tuner brought up on slot "
|
||||
<< (uint64_t)g_slotId;
|
||||
return true;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Tuning / configuration (each holds g_ctlLock via the op wrappers)
|
||||
// =========================================================================
|
||||
|
||||
static int DoSetFreq(uint64_t hz) {
|
||||
if (!EnsureInit()) return -1;
|
||||
if (g_directSamp) {
|
||||
// Tuner is bypassed: tuning is the demod's digital downconverter.
|
||||
SetIfFreq((uint32_t)hz);
|
||||
g_lastFreq = hz;
|
||||
return 0;
|
||||
}
|
||||
SetI2cRepeater(true);
|
||||
bool ok = R820tSetFreq(g_tuner, hz);
|
||||
SetI2cRepeater(false);
|
||||
if (ok) g_lastFreq = hz;
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
static int DoSetSampleRate(uint32_t rate) {
|
||||
if (!EnsureInit()) return -1;
|
||||
// The RTL2832 resampler does not cover 300k..900k.
|
||||
if (rate <= 225000 || rate > 3200000 ||
|
||||
(rate > 300000 && rate <= 900000)) return -1;
|
||||
|
||||
// The ratio uses the NOMINAL crystal frequency: ppm correction is
|
||||
// applied by the demod's sample-frequency-offset registers below, so
|
||||
// baking it into the ratio too would correct the rate twice.
|
||||
uint32_t ratio = (uint32_t)(((uint64_t)RTL_XTAL * TWO_POW22) / rate);
|
||||
ratio &= 0x0ffffffc;
|
||||
DemodWrite(1, 0x9f, (uint16_t)((ratio >> 16) & 0xffff), 2);
|
||||
DemodWrite(1, 0xa1, (uint16_t)(ratio & 0xffff), 2);
|
||||
|
||||
ApplySampleFreqCorrection();
|
||||
DemodWrite(1, 0x01, 0x14, 1); // soft reset
|
||||
DemodWrite(1, 0x01, 0x10, 1);
|
||||
SetIfFreq(g_directSamp ? (uint32_t)g_lastFreq : R82XX_IF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DoSetGainMode(int manual) {
|
||||
if (!EnsureInit()) return -1;
|
||||
g_manual = manual ? 1 : 0;
|
||||
SetI2cRepeater(true);
|
||||
bool ok = R820tSetGain(g_tuner, g_manual, g_gain);
|
||||
SetI2cRepeater(false);
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
static int DoSetGain(int tenths) {
|
||||
if (!EnsureInit()) return -1;
|
||||
g_gain = tenths;
|
||||
g_manual = 1; // selecting an explicit gain implies manual mode
|
||||
SetI2cRepeater(true);
|
||||
bool ok = R820tSetGain(g_tuner, 1, g_gain);
|
||||
SetI2cRepeater(false);
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
static int DoSetFreqCorrection(int ppm) {
|
||||
if (!EnsureInit()) return -1;
|
||||
g_ppm = ppm;
|
||||
g_rtlXtal = (uint32_t)((int64_t)RTL_XTAL + (int64_t)RTL_XTAL * ppm / 1000000);
|
||||
g_tuner.xtal = g_rtlXtal;
|
||||
ApplySampleFreqCorrection();
|
||||
// The tuner PLL (and, in direct mode, the DDC) derive from the xtal;
|
||||
// retune so the new correction actually takes effect.
|
||||
if (g_lastFreq) return DoSetFreq(g_lastFreq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DoSetAgc(int on) {
|
||||
if (!EnsureInit()) return -1;
|
||||
return DemodWrite(0, 0x19, on ? 0x25 : 0x05, 1) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int DoSetDirectSampling(int mode) {
|
||||
if (!EnsureInit()) return -1;
|
||||
if (mode) {
|
||||
// Bypass the tuner and digitise the ADC input directly.
|
||||
SetI2cRepeater(true);
|
||||
R820tStandby(g_tuner);
|
||||
SetI2cRepeater(false);
|
||||
DemodWrite(1, 0xb1, 0x1a, 1); // disable zero-IF
|
||||
DemodWrite(1, 0x15, 0x00, 1); // no spectrum inversion
|
||||
DemodWrite(0, 0x08, 0x4d, 1); // In-phase ADC input
|
||||
DemodWrite(0, 0x06, (mode == 2) ? 0x90 : 0x80, 1); // Q vs I ADC
|
||||
g_directSamp = mode;
|
||||
// Tuning now happens in the DDC; carry the current frequency over.
|
||||
SetIfFreq((uint32_t)g_lastFreq);
|
||||
} else {
|
||||
// Restore the R820T2 low-IF receive path. Standby powered the
|
||||
// tuner down, so it needs a full re-initialisation.
|
||||
SetI2cRepeater(true);
|
||||
bool ok = R820tInit(g_tuner, g_slotId, g_rtlXtal, R82XX_IF);
|
||||
SetI2cRepeater(false);
|
||||
if (!ok) return -1;
|
||||
SetIfFreq(R82XX_IF);
|
||||
DemodWrite(1, 0x15, 0x01, 1); // enable spectrum inversion
|
||||
DemodWrite(0, 0x06, 0x80, 1); // default ADC I/Q datapath
|
||||
g_directSamp = 0;
|
||||
if (g_lastFreq) return DoSetFreq(g_lastFreq);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Streaming
|
||||
// =========================================================================
|
||||
|
||||
static void TransferCallback(uint8_t slotId, uint8_t epDci,
|
||||
const uint8_t* data, uint32_t length,
|
||||
uint32_t /*completionCode*/) {
|
||||
if (slotId != g_slotId) return;
|
||||
auto* dev = Xhci::GetDevice(slotId);
|
||||
if (!dev) return;
|
||||
|
||||
uint8_t bulkInDci = dev->BulkInEpNum ? (uint8_t)(dev->BulkInEpNum * 2 + 1) : 0;
|
||||
if (epDci != bulkInDci || !g_streaming) return;
|
||||
|
||||
if (data) {
|
||||
// Deliver samples only. The xHCI layer owns the multi-buffer pool
|
||||
// (StartBulkInStream) and re-arms this very buffer automatically once
|
||||
// we return; re-queuing here would double-arm the pool and lap the
|
||||
// ring. PushSamples copies out synchronously, so the buffer is free
|
||||
// to be re-armed the instant this returns.
|
||||
if (length > 0)
|
||||
Drivers::Radio::Sdr::PushSamples(g_rxIndex, data, length);
|
||||
} else {
|
||||
// Error (data==nullptr), e.g. cc=6 STALL: the endpoint is halted.
|
||||
// Clearing it requires a Reset Endpoint command that waits on the
|
||||
// event ring, which is unsafe here (we are inside PollEvents).
|
||||
// Flag it for process-context recovery in OpService(), which resets
|
||||
// the endpoint and re-primes the whole pool.
|
||||
g_bulkStalled.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
static int DoStart() {
|
||||
if (!EnsureInit()) return -1;
|
||||
|
||||
// Reset endpoint-A FIFO so streaming starts on a clean boundary.
|
||||
RegWrite(BLOCK_USB, USB_EPA_CTL, 0x1002, 2); // hold + reset
|
||||
RegWrite(BLOCK_USB, USB_EPA_CTL, 0x0000, 2); // release
|
||||
|
||||
g_bulkStalled.store(false, std::memory_order_relaxed);
|
||||
g_streaming = true;
|
||||
Xhci::RegisterTransferCallback(g_slotId, TransferCallback);
|
||||
|
||||
auto* dev = Xhci::GetDevice(g_slotId);
|
||||
if (dev && dev->BulkInEpNum)
|
||||
Xhci::StartBulkInStream(g_slotId, BULK_XFER_LEN, BULK_POOL_BUFS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DoStop() {
|
||||
g_streaming = false;
|
||||
g_bulkStalled.store(false, std::memory_order_relaxed);
|
||||
// Disarm the multi-buffer rotation so no further transfers re-arm, then
|
||||
// hold/reset the FIFO so the device stops producing samples.
|
||||
Xhci::StopBulkInStream(g_slotId);
|
||||
if (g_ctlBuf) RegWrite(BLOCK_USB, USB_EPA_CTL, 0x1002, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Process-context housekeeping called from Read(): recover a halted bulk-IN
|
||||
// endpoint (Reset Endpoint + Set TR Dequeue) and re-arm reception.
|
||||
static void DoService() {
|
||||
if (!g_bulkStalled.load(std::memory_order_relaxed)) return;
|
||||
g_ctlLock.Acquire();
|
||||
if (g_streaming) {
|
||||
Xhci::ResetBulkInEndpoint(g_slotId);
|
||||
Xhci::PrimeBulkInStream(g_slotId);
|
||||
}
|
||||
g_bulkStalled.store(false, std::memory_order_relaxed);
|
||||
g_ctlLock.Release();
|
||||
|
||||
static uint32_t recoveries = 0;
|
||||
if (recoveries < 5) {
|
||||
recoveries++;
|
||||
KernelLogStream(INFO, "RTL-SDR") << "bulk IN stall recovered ("
|
||||
<< (uint64_t)recoveries << ")";
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Ops table wrappers (lock the control path)
|
||||
// =========================================================================
|
||||
|
||||
static int OpSetFreq(void*, uint64_t hz) {
|
||||
g_ctlLock.Acquire(); int r = DoSetFreq(hz); g_ctlLock.Release(); return r;
|
||||
}
|
||||
static int OpSetSampleRate(void*, uint32_t hz) {
|
||||
g_ctlLock.Acquire(); int r = DoSetSampleRate(hz); g_ctlLock.Release(); return r;
|
||||
}
|
||||
static int OpSetGainMode(void*, int manual) {
|
||||
g_ctlLock.Acquire(); int r = DoSetGainMode(manual); g_ctlLock.Release(); return r;
|
||||
}
|
||||
static int OpSetGain(void*, int tenths) {
|
||||
g_ctlLock.Acquire(); int r = DoSetGain(tenths); g_ctlLock.Release(); return r;
|
||||
}
|
||||
static int OpSetFreqCorrection(void*, int ppm) {
|
||||
g_ctlLock.Acquire(); int r = DoSetFreqCorrection(ppm); g_ctlLock.Release(); return r;
|
||||
}
|
||||
static int OpSetAgc(void*, int on) {
|
||||
g_ctlLock.Acquire(); int r = DoSetAgc(on); g_ctlLock.Release(); return r;
|
||||
}
|
||||
static int OpSetDirectSampling(void*, int mode) {
|
||||
g_ctlLock.Acquire(); int r = DoSetDirectSampling(mode); g_ctlLock.Release(); return r;
|
||||
}
|
||||
static int OpStart(void*) {
|
||||
g_ctlLock.Acquire(); int r = DoStart(); g_ctlLock.Release(); return r;
|
||||
}
|
||||
static int OpStop(void*) {
|
||||
g_ctlLock.Acquire(); int r = DoStop(); g_ctlLock.Release(); return r;
|
||||
}
|
||||
// DoService does its own locking (around the reset), so OpService must not
|
||||
// take g_ctlLock here.
|
||||
static void OpService(void*) { DoService(); }
|
||||
|
||||
// =========================================================================
|
||||
// USB enumeration hooks
|
||||
// =========================================================================
|
||||
|
||||
bool IsRtlSdr(uint16_t vid, uint16_t pid) {
|
||||
if (vid != 0x0bda) return false; // Realtek Semiconductor
|
||||
switch (pid) {
|
||||
// Only the two known RTL2832U ids. In particular 0x2831 is the
|
||||
// RTL2831U, a DIFFERENT demod this driver cannot program.
|
||||
case 0x2832: // RTL2832U (generic)
|
||||
case 0x2838: // RTL2838 (most RTL-SDR.com dongles)
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterDevice(uint8_t slotId) {
|
||||
if (g_present) {
|
||||
KernelLogStream(WARNING, "RTL-SDR")
|
||||
<< "second RTL-SDR ignored (single instance), slot " << (uint64_t)slotId;
|
||||
return;
|
||||
}
|
||||
|
||||
g_slotId = slotId;
|
||||
g_present = true;
|
||||
g_hwInited = false;
|
||||
g_streaming = false;
|
||||
g_ppm = 0;
|
||||
g_rtlXtal = RTL_XTAL;
|
||||
g_manual = 0;
|
||||
g_gain = 0;
|
||||
g_directSamp = 0;
|
||||
g_lastFreq = 0;
|
||||
g_tuner = R820tDev{};
|
||||
|
||||
g_ctlBuf = (uint8_t*)Memory::g_pfa->AllocateZeroed();
|
||||
if (!g_ctlBuf) {
|
||||
KernelLogStream(ERROR, "RTL-SDR") << "control buffer alloc failed";
|
||||
g_present = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int gainCount = 0;
|
||||
const int* gains = R820tGainTable(&gainCount);
|
||||
|
||||
Drivers::Radio::Sdr::ReceiverDesc desc{};
|
||||
desc.name = "Realtek RTL2832U";
|
||||
desc.tuner = "Rafael Micro R820T2";
|
||||
desc.serial = "USB RTL-SDR";
|
||||
desc.freqMin = 24000000ull;
|
||||
desc.freqMax = 1766000000ull;
|
||||
desc.sampleRateMin = 225001;
|
||||
desc.sampleRateMax = 3200000;
|
||||
desc.gains = gains;
|
||||
desc.numGains = (uint32_t)gainCount;
|
||||
desc.format = montauk::abi::SDR_FORMAT_CU8;
|
||||
desc.ops.SetFreq = OpSetFreq;
|
||||
desc.ops.SetSampleRate = OpSetSampleRate;
|
||||
desc.ops.SetGainMode = OpSetGainMode;
|
||||
desc.ops.SetGain = OpSetGain;
|
||||
desc.ops.SetFreqCorrection = OpSetFreqCorrection;
|
||||
desc.ops.SetAgc = OpSetAgc;
|
||||
desc.ops.SetDirectSampling = OpSetDirectSampling;
|
||||
desc.ops.Start = OpStart;
|
||||
desc.ops.Stop = OpStop;
|
||||
desc.ops.Service = OpService;
|
||||
desc.ctx = nullptr;
|
||||
|
||||
g_rxIndex = Drivers::Radio::Sdr::Register(desc);
|
||||
if (g_rxIndex < 0) {
|
||||
KernelLogStream(ERROR, "RTL-SDR") << "SDR registration failed";
|
||||
Memory::g_pfa->Free(g_ctlBuf);
|
||||
g_ctlBuf = nullptr;
|
||||
g_present = false;
|
||||
return;
|
||||
}
|
||||
|
||||
KernelLogStream(OK, "RTL-SDR") << "RTL-SDR on slot " << (uint64_t)slotId
|
||||
<< " registered as receiver " << (uint64_t)g_rxIndex;
|
||||
}
|
||||
|
||||
void UnregisterDevice(uint8_t slotId) {
|
||||
if (!g_present || slotId != g_slotId) return;
|
||||
|
||||
g_streaming = false;
|
||||
if (g_rxIndex >= 0) Drivers::Radio::Sdr::Unregister(g_rxIndex);
|
||||
g_rxIndex = -1;
|
||||
g_present = false;
|
||||
g_hwInited = false;
|
||||
|
||||
if (g_ctlBuf) {
|
||||
Memory::g_pfa->Free(g_ctlBuf);
|
||||
g_ctlBuf = nullptr;
|
||||
}
|
||||
KernelLogStream(INFO, "RTL-SDR") << "RTL-SDR removed from slot " << (uint64_t)slotId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* RtlSdr.hpp
|
||||
* Realtek RTL2832U + R820T2 software-defined-radio receiver (RTL-SDR).
|
||||
*
|
||||
* The RTL2832U is a DVB-T demodulator that, in raw mode, streams 8-bit
|
||||
* unsigned I/Q samples over a USB bulk-IN endpoint. This driver brings up the
|
||||
* demodulator + R820T2 tuner, configures the resampler / IF, and feeds the
|
||||
* bulk-IN samples into the generic SDR receive subsystem (Drivers::Radio::Sdr),
|
||||
* which userspace reaches through the SYS_SDR_* syscalls.
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Drivers::USB::Radio {
|
||||
|
||||
// True if a USB VID:PID identifies a supported RTL2832U-based SDR dongle.
|
||||
bool IsRtlSdr(uint16_t vid, uint16_t pid);
|
||||
|
||||
// Called by USB enumeration once the bulk-IN endpoint has been configured.
|
||||
// Registers a receiver with the SDR subsystem; the demod/tuner are brought
|
||||
// up lazily on first use (in process context, never from the USB poll path).
|
||||
void RegisterDevice(uint8_t slotId);
|
||||
|
||||
// Tear down on unplug.
|
||||
void UnregisterDevice(uint8_t slotId);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// I2C facade used by the R820T2 tuner module. These carry the tuner's
|
||||
// register traffic over the demod's I2C block. The control-transfer mutex
|
||||
// is held by the calling op wrapper, so these do not lock themselves.
|
||||
// -------------------------------------------------------------------------
|
||||
bool RtlI2cWrite(uint8_t slotId, uint8_t i2cAddr, const uint8_t* buf, uint8_t len);
|
||||
bool RtlI2cRead(uint8_t slotId, uint8_t i2cAddr, uint8_t* buf, uint8_t len);
|
||||
|
||||
}
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "HidMouse.hpp"
|
||||
#include "MassStorage.hpp"
|
||||
#include "Bluetooth/Bluetooth.hpp"
|
||||
#include "Radio/RtlSdr.hpp"
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
@@ -351,11 +350,6 @@ namespace Drivers::USB::UsbDevice {
|
||||
bool foundBulkOut = false;
|
||||
uint16_t hidReportDescLen = 0;
|
||||
|
||||
// RTL-SDR dongles are vendor-class (0xFF) devices identified by VID:PID.
|
||||
// They expose a single bulk-IN endpoint that streams raw I/Q samples;
|
||||
// treat them like the other bulk-capable class drivers below.
|
||||
bool foundRadio = Drivers::USB::Radio::IsRtlSdr(devDesc.idVendor, devDesc.idProduct);
|
||||
|
||||
while (offset + 2 <= totalLen) {
|
||||
uint8_t len = cfgBuf[offset];
|
||||
uint8_t type = cfgBuf[offset + 1];
|
||||
@@ -423,8 +417,8 @@ namespace Drivers::USB::UsbDevice {
|
||||
foundEp = true;
|
||||
}
|
||||
|
||||
// Bluetooth, Mass Storage and RTL-SDR bulk endpoints
|
||||
if (foundBt || currentMsc || foundRadio) {
|
||||
// Bulk endpoints needed by in-kernel Bluetooth and storage drivers.
|
||||
if (foundBt || currentMsc) {
|
||||
if (isIn && xferType == EP_XFER_INTERRUPT && !foundEp) {
|
||||
// HCI event pipe (interrupt IN)
|
||||
dev->InterruptEpNum = ep->bEndpointAddress & 0x0F;
|
||||
@@ -457,6 +451,51 @@ namespace Drivers::USB::UsbDevice {
|
||||
foundBt = true;
|
||||
}
|
||||
|
||||
// No in-kernel class driver recognized this device. Preserve the first
|
||||
// interface and its bulk endpoints so a userspace driver can claim it.
|
||||
// The xHCI slot model currently stores one interface; a future model
|
||||
// can retain every alternate/interface without changing the userspace
|
||||
// claim ABI, which already names the interface number explicitly.
|
||||
bool knownInterface = foundBt || foundMsc ||
|
||||
dev->InterfaceClass == CLASS_HID;
|
||||
if (!knownInterface) {
|
||||
bool inFirstInterface = false;
|
||||
bool haveFirstInterface = false;
|
||||
offset = 0;
|
||||
while (offset + 2 <= totalLen) {
|
||||
uint8_t len = cfgBuf[offset];
|
||||
uint8_t type = cfgBuf[offset + 1];
|
||||
if (len == 0 || offset + len > totalLen) break;
|
||||
|
||||
if (type == DESC_INTERFACE &&
|
||||
offset + sizeof(InterfaceDescriptor) <= totalLen) {
|
||||
if (haveFirstInterface) break;
|
||||
auto* iface = (InterfaceDescriptor*)&cfgBuf[offset];
|
||||
dev->InterfaceClass = iface->bInterfaceClass;
|
||||
dev->InterfaceSubClass = iface->bInterfaceSubClass;
|
||||
dev->InterfaceProtocol = iface->bInterfaceProtocol;
|
||||
dev->InterfaceNumber = iface->bInterfaceNumber;
|
||||
haveFirstInterface = true;
|
||||
inFirstInterface = true;
|
||||
} else if (inFirstInterface && type == DESC_ENDPOINT &&
|
||||
offset + sizeof(EndpointDescriptor) <= totalLen) {
|
||||
auto* ep = (EndpointDescriptor*)&cfgBuf[offset];
|
||||
uint8_t xferType = ep->bmAttributes & EP_XFER_TYPE_MASK;
|
||||
bool isIn = (ep->bEndpointAddress & EP_DIR_IN) != 0;
|
||||
if (xferType == EP_XFER_BULK && isIn && !foundBulkIn) {
|
||||
dev->BulkInEpNum = ep->bEndpointAddress & 0x0F;
|
||||
dev->BulkInMaxPacket = ep->wMaxPacketSize & 0x7FF;
|
||||
foundBulkIn = true;
|
||||
} else if (xferType == EP_XFER_BULK && !isIn && !foundBulkOut) {
|
||||
dev->BulkOutEpNum = ep->bEndpointAddress & 0x0F;
|
||||
dev->BulkOutMaxPacket = ep->wMaxPacketSize & 0x7FF;
|
||||
foundBulkOut = true;
|
||||
}
|
||||
}
|
||||
offset += len;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 8: SET_CONFIGURATION
|
||||
// -----------------------------------------------------------------
|
||||
@@ -657,15 +696,20 @@ namespace Drivers::USB::UsbDevice {
|
||||
// -----------------------------------------------------------------
|
||||
// Step 13: Register with the appropriate class driver
|
||||
// -----------------------------------------------------------------
|
||||
if (dev->InterfaceClass == CLASS_HID && dev->InterfaceProtocol == PROTOCOL_KEYBOARD) {
|
||||
if (foundEp && dev->InterfaceClass == CLASS_HID &&
|
||||
dev->InterfaceProtocol == PROTOCOL_KEYBOARD) {
|
||||
dev->KernelDriverBound = true;
|
||||
HidKeyboard::RegisterDevice(slotId);
|
||||
KernelLogStream(OK, "USB") << "Slot " << (uint64_t)slotId << ": HID Boot Keyboard";
|
||||
} else if (dev->InterfaceClass == CLASS_HID && dev->InterfaceProtocol == PROTOCOL_MOUSE) {
|
||||
} else if (foundEp && dev->InterfaceClass == CLASS_HID &&
|
||||
dev->InterfaceProtocol == PROTOCOL_MOUSE) {
|
||||
dev->KernelDriverBound = true;
|
||||
HidMouse::RegisterDevice(slotId);
|
||||
KernelLogStream(OK, "USB") << "Slot " << (uint64_t)slotId << ": HID Boot Mouse";
|
||||
} else if (dev->InterfaceClass == CLASS_WIRELESS &&
|
||||
dev->InterfaceSubClass == SUBCLASS_RF &&
|
||||
dev->InterfaceProtocol == PROTOCOL_BLUETOOTH) {
|
||||
dev->KernelDriverBound = true;
|
||||
Bluetooth::RegisterAdapter(slotId);
|
||||
KernelLogStream(OK, "USB") << "Slot " << (uint64_t)slotId << ": Bluetooth Adapter"
|
||||
<< " VID:" << base::hex << (uint64_t)dev->VendorId
|
||||
@@ -674,15 +718,10 @@ namespace Drivers::USB::UsbDevice {
|
||||
dev->InterfaceSubClass == SUBCLASS_SCSI &&
|
||||
dev->InterfaceProtocol == PROTOCOL_BULK_ONLY &&
|
||||
foundMsc && foundBulkIn && foundBulkOut) {
|
||||
dev->KernelDriverBound = true;
|
||||
MassStorage::RegisterDevice(slotId);
|
||||
KernelLogStream(OK, "USB") << "Slot " << (uint64_t)slotId
|
||||
<< ": USB Mass Storage";
|
||||
} else if (foundRadio && foundBulkIn) {
|
||||
Drivers::USB::Radio::RegisterDevice(slotId);
|
||||
KernelLogStream(OK, "USB") << "Slot " << (uint64_t)slotId
|
||||
<< ": RTL-SDR receiver"
|
||||
<< " VID:" << base::hex << (uint64_t)dev->VendorId
|
||||
<< " PID:" << (uint64_t)dev->ProductId << base::dec;
|
||||
} else if (foundEp) {
|
||||
KernelLogStream(INFO, "USB") << "Slot " << (uint64_t)slotId
|
||||
<< ": USB device, class=" << (uint64_t)dev->InterfaceClass
|
||||
@@ -692,6 +731,9 @@ namespace Drivers::USB::UsbDevice {
|
||||
<< ": Non-HID device, class=" << (uint64_t)devDesc.bDeviceClass;
|
||||
}
|
||||
|
||||
// Publish to userspace only after endpoint configuration and kernel
|
||||
// class-driver binding decisions are complete.
|
||||
dev->Ready = true;
|
||||
return slotId;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* UserUsb.cpp
|
||||
* Process-owned access to unbound USB interfaces.
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "UserUsb.hpp"
|
||||
|
||||
#include "Xhci.hpp"
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Memory/Heap.hpp>
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <CppLib/Spinlock.hpp>
|
||||
|
||||
namespace Drivers::USB::UserUsb {
|
||||
|
||||
static constexpr int MaxClaims = Xhci::MAX_SLOTS;
|
||||
static constexpr uint32_t RingBytes = 256 * 1024;
|
||||
static constexpr uint32_t MaxControlBytes = 4096;
|
||||
|
||||
struct ClaimState {
|
||||
bool active;
|
||||
bool connected;
|
||||
bool streaming;
|
||||
uint8_t slotId;
|
||||
uint8_t interfaceNumber;
|
||||
uint16_t generation;
|
||||
int ownerPid;
|
||||
|
||||
uint8_t* ring;
|
||||
uint32_t head;
|
||||
uint32_t count;
|
||||
uint64_t droppedBytes;
|
||||
uint32_t lastCompletionCode;
|
||||
kcp::Spinlock ringLock;
|
||||
};
|
||||
|
||||
static ClaimState g_claims[MaxClaims];
|
||||
// Serializes process-context operations and prevents a sibling thread from
|
||||
// closing a claim while another syscall is using its backing state.
|
||||
static kcp::Mutex g_claimsLock;
|
||||
|
||||
static int MakeHandle(int index, uint16_t generation) {
|
||||
return ((int)generation << 8) | index;
|
||||
}
|
||||
|
||||
static ClaimState* LookupLocked(int handle, bool requireConnected = true) {
|
||||
int index = handle & 0xff;
|
||||
uint16_t generation = (uint16_t)((uint32_t)handle >> 8);
|
||||
if (index < 0 || index >= MaxClaims || generation == 0) return nullptr;
|
||||
|
||||
ClaimState& claim = g_claims[index];
|
||||
if (!claim.active || claim.generation != generation) return nullptr;
|
||||
if (claim.ownerPid != Sched::GetCurrentPid()) return nullptr;
|
||||
if (requireConnected && !claim.connected) return nullptr;
|
||||
return &claim;
|
||||
}
|
||||
|
||||
static bool SlotClaimedLocked(uint8_t slotId) {
|
||||
for (int i = 0; i < MaxClaims; i++) {
|
||||
if (g_claims[i].active && g_claims[i].connected &&
|
||||
g_claims[i].slotId == slotId) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void CopyInterfaceInfo(uint8_t slotId, const Xhci::UsbDeviceInfo& dev,
|
||||
montauk::abi::UsbInterfaceInfo& out) {
|
||||
memset(&out, 0, sizeof(out));
|
||||
out.slotId = slotId;
|
||||
out.portId = dev.PortId;
|
||||
out.speed = (uint8_t)dev.Speed;
|
||||
out.interfaceNumber = dev.InterfaceNumber;
|
||||
out.vendorId = dev.VendorId;
|
||||
out.productId = dev.ProductId;
|
||||
out.deviceClass = dev.DeviceClass;
|
||||
out.interfaceClass = dev.InterfaceClass;
|
||||
out.interfaceSubClass = dev.InterfaceSubClass;
|
||||
out.interfaceProtocol = dev.InterfaceProtocol;
|
||||
out.bulkInEndpoint = dev.BulkInEpNum ? (uint8_t)(0x80 | dev.BulkInEpNum) : 0;
|
||||
out.bulkOutEndpoint = dev.BulkOutEpNum;
|
||||
out.bulkInMaxPacket = dev.BulkInMaxPacket;
|
||||
out.bulkOutMaxPacket = dev.BulkOutMaxPacket;
|
||||
out.kernelDriverBound = dev.KernelDriverBound ? 1 : 0;
|
||||
out.claimed = SlotClaimedLocked(slotId) ? 1 : 0;
|
||||
}
|
||||
|
||||
int List(montauk::abi::UsbInterfaceInfo* out, int maxCount) {
|
||||
if (!out || maxCount <= 0) return 0;
|
||||
g_claimsLock.Acquire();
|
||||
int count = 0;
|
||||
for (uint8_t slot = 1; slot <= Xhci::MAX_SLOTS && count < maxCount; slot++) {
|
||||
Xhci::UsbDeviceInfo* dev = Xhci::GetDevice(slot);
|
||||
if (!dev || !dev->Active || !dev->Ready) continue;
|
||||
CopyInterfaceInfo(slot, *dev, out[count++]);
|
||||
}
|
||||
g_claimsLock.Release();
|
||||
return count;
|
||||
}
|
||||
|
||||
int Claim(uint8_t slotId, uint8_t interfaceNumber) {
|
||||
int ownerPid = Sched::GetCurrentPid();
|
||||
if (ownerPid < 0 || slotId == 0 || slotId > Xhci::MAX_SLOTS) return montauk::abi::USB_ERR_INVALID;
|
||||
|
||||
g_claimsLock.Acquire();
|
||||
Xhci::UsbDeviceInfo* dev = Xhci::GetDevice(slotId);
|
||||
if (!dev || !dev->Active || !dev->Ready ||
|
||||
dev->InterfaceNumber != interfaceNumber) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_NOT_FOUND;
|
||||
}
|
||||
if (dev->KernelDriverBound) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_KERNEL_BOUND;
|
||||
}
|
||||
if (SlotClaimedLocked(slotId)) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_BUSY;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MaxClaims; i++) {
|
||||
ClaimState& claim = g_claims[i];
|
||||
if (claim.active) continue;
|
||||
uint16_t generation = (uint16_t)(claim.generation + 1);
|
||||
// Keep the encoded int handle positive so conventional `h < 0`
|
||||
// error checks remain valid in userspace.
|
||||
if (generation == 0 || generation > 0x7fff) generation = 1;
|
||||
claim.active = true;
|
||||
claim.connected = true;
|
||||
claim.streaming = false;
|
||||
claim.slotId = slotId;
|
||||
claim.interfaceNumber = interfaceNumber;
|
||||
claim.generation = generation;
|
||||
claim.ownerPid = ownerPid;
|
||||
claim.ring = nullptr;
|
||||
claim.head = 0;
|
||||
claim.count = 0;
|
||||
claim.droppedBytes = 0;
|
||||
claim.lastCompletionCode = Xhci::CC_SUCCESS;
|
||||
int handle = MakeHandle(i, generation);
|
||||
g_claimsLock.Release();
|
||||
return handle;
|
||||
}
|
||||
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_NO_RESOURCES;
|
||||
}
|
||||
|
||||
static void TransferCallback(uint8_t slotId, uint8_t epDci,
|
||||
const uint8_t* data, uint32_t length,
|
||||
uint32_t completionCode) {
|
||||
for (int i = 0; i < MaxClaims; i++) {
|
||||
ClaimState& claim = g_claims[i];
|
||||
claim.ringLock.Acquire();
|
||||
if (!claim.active || !claim.connected || !claim.streaming ||
|
||||
claim.slotId != slotId) {
|
||||
claim.ringLock.Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
Xhci::UsbDeviceInfo* dev = Xhci::GetDevice(slotId);
|
||||
uint8_t expectedDci = (dev && dev->BulkInEpNum)
|
||||
? (uint8_t)(dev->BulkInEpNum * 2 + 1) : 0;
|
||||
claim.lastCompletionCode = completionCode;
|
||||
if (epDci != expectedDci || !data || length == 0 || !claim.ring) {
|
||||
claim.ringLock.Release();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t space = RingBytes - claim.count;
|
||||
uint32_t copied = length < space ? length : space;
|
||||
uint32_t first = RingBytes - claim.head;
|
||||
if (first > copied) first = copied;
|
||||
memcpy(claim.ring + claim.head, data, first);
|
||||
if (copied > first) memcpy(claim.ring, data + first, copied - first);
|
||||
claim.head = (claim.head + copied) % RingBytes;
|
||||
claim.count += copied;
|
||||
claim.droppedBytes += length - copied;
|
||||
claim.ringLock.Release();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void CloseLocked(ClaimState& claim) {
|
||||
claim.ringLock.Acquire();
|
||||
bool connected = claim.connected;
|
||||
bool wasStreaming = claim.streaming;
|
||||
uint8_t slotId = claim.slotId;
|
||||
claim.streaming = false;
|
||||
claim.active = false;
|
||||
claim.connected = false;
|
||||
claim.ownerPid = -1;
|
||||
uint8_t* ring = claim.ring;
|
||||
claim.ring = nullptr;
|
||||
claim.head = claim.count = 0;
|
||||
claim.ringLock.Release();
|
||||
|
||||
// Mark the claim inactive before stopping: PollEvents may dispatch a
|
||||
// late completion from StopBulkInStream, and the callback must drop it.
|
||||
if (connected && wasStreaming) Xhci::StopBulkInStream(slotId);
|
||||
if (connected) Xhci::RegisterTransferCallback(slotId, nullptr);
|
||||
if (ring) Memory::g_heap->Free(ring);
|
||||
}
|
||||
|
||||
int Close(int handle) {
|
||||
g_claimsLock.Acquire();
|
||||
ClaimState* claim = LookupLocked(handle, false);
|
||||
if (!claim) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_INVALID;
|
||||
}
|
||||
CloseLocked(*claim);
|
||||
g_claimsLock.Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Control(int handle, const montauk::abi::UsbControlRequest& request,
|
||||
void* data, uint32_t dataLen) {
|
||||
if (dataLen != request.length || dataLen > MaxControlBytes ||
|
||||
(dataLen != 0 && data == nullptr)) return montauk::abi::USB_ERR_INVALID;
|
||||
|
||||
g_claimsLock.Acquire();
|
||||
ClaimState* claim = LookupLocked(handle);
|
||||
if (!claim) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_DISCONNECTED;
|
||||
}
|
||||
|
||||
void* dma = nullptr;
|
||||
if (dataLen != 0) {
|
||||
dma = Memory::g_pfa->AllocateZeroed();
|
||||
if (!dma) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_NO_RESOURCES;
|
||||
}
|
||||
if ((request.requestType & 0x80) == 0) memcpy(dma, data, dataLen);
|
||||
}
|
||||
|
||||
uint32_t cc = Xhci::ControlTransfer(claim->slotId, request.requestType,
|
||||
request.request, request.value, request.index, request.length,
|
||||
dma, (request.requestType & 0x80) != 0);
|
||||
if ((cc == Xhci::CC_SUCCESS || cc == Xhci::CC_SHORT_PACKET) &&
|
||||
dataLen != 0 && (request.requestType & 0x80) != 0) {
|
||||
memcpy(data, dma, dataLen);
|
||||
}
|
||||
if (dma) Memory::g_pfa->Free(dma);
|
||||
g_claimsLock.Release();
|
||||
return (cc == Xhci::CC_SUCCESS || cc == Xhci::CC_SHORT_PACKET)
|
||||
? 0 : montauk::abi::USB_ERR_IO;
|
||||
}
|
||||
|
||||
int StartBulkIn(int handle, uint32_t transferBytes, uint32_t bufferCount) {
|
||||
if (transferBytes == 0 || transferBytes > 4096 ||
|
||||
bufferCount == 0 || bufferCount > 16) return montauk::abi::USB_ERR_INVALID;
|
||||
|
||||
g_claimsLock.Acquire();
|
||||
ClaimState* claim = LookupLocked(handle);
|
||||
if (!claim) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_DISCONNECTED;
|
||||
}
|
||||
Xhci::UsbDeviceInfo* dev = Xhci::GetDevice(claim->slotId);
|
||||
if (!dev || !dev->BulkInEpNum || !dev->BulkInRing) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_UNSUPPORTED;
|
||||
}
|
||||
if (claim->streaming) {
|
||||
g_claimsLock.Release();
|
||||
return 0;
|
||||
}
|
||||
if (!claim->ring) claim->ring = (uint8_t*)Memory::g_heap->Request(RingBytes);
|
||||
if (!claim->ring) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_NO_RESOURCES;
|
||||
}
|
||||
|
||||
claim->ringLock.Acquire();
|
||||
claim->head = claim->count = 0;
|
||||
claim->droppedBytes = 0;
|
||||
claim->lastCompletionCode = Xhci::CC_SUCCESS;
|
||||
claim->streaming = true;
|
||||
claim->ringLock.Release();
|
||||
Xhci::RegisterTransferCallback(claim->slotId, TransferCallback);
|
||||
Xhci::StartBulkInStream(claim->slotId, transferBytes, bufferCount);
|
||||
if (!Xhci::IsBulkInStreamActive(claim->slotId)) {
|
||||
claim->ringLock.Acquire();
|
||||
claim->streaming = false;
|
||||
claim->ringLock.Release();
|
||||
Xhci::RegisterTransferCallback(claim->slotId, nullptr);
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_NO_RESOURCES;
|
||||
}
|
||||
g_claimsLock.Release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int StopBulkIn(int handle) {
|
||||
g_claimsLock.Acquire();
|
||||
ClaimState* claim = LookupLocked(handle, false);
|
||||
if (!claim) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_INVALID;
|
||||
}
|
||||
claim->ringLock.Acquire();
|
||||
bool stop = claim->connected && claim->streaming;
|
||||
claim->streaming = false;
|
||||
claim->ringLock.Release();
|
||||
if (stop) Xhci::StopBulkInStream(claim->slotId);
|
||||
g_claimsLock.Release();
|
||||
return claim->connected ? 0 : montauk::abi::USB_ERR_DISCONNECTED;
|
||||
}
|
||||
|
||||
int ReadBulkIn(int handle, uint8_t* out, uint32_t maxLen) {
|
||||
if (!out && maxLen != 0) return montauk::abi::USB_ERR_INVALID;
|
||||
g_claimsLock.Acquire();
|
||||
ClaimState* claim = LookupLocked(handle, false);
|
||||
if (!claim) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_INVALID;
|
||||
}
|
||||
if (!claim->connected && claim->count == 0) {
|
||||
g_claimsLock.Release();
|
||||
return montauk::abi::USB_ERR_DISCONNECTED;
|
||||
}
|
||||
// Failed completions halt the endpoint and cannot be repaired from the
|
||||
// xHCI event callback. Recover in process context so userspace drivers
|
||||
// do not need a host-controller-specific reset API.
|
||||
claim->ringLock.Acquire();
|
||||
bool recover = claim->connected && claim->streaming &&
|
||||
claim->lastCompletionCode != Xhci::CC_SUCCESS &&
|
||||
claim->lastCompletionCode != Xhci::CC_SHORT_PACKET;
|
||||
if (recover) claim->lastCompletionCode = Xhci::CC_SUCCESS;
|
||||
claim->ringLock.Release();
|
||||
if (recover) {
|
||||
Xhci::ResetBulkInEndpoint(claim->slotId);
|
||||
Xhci::PrimeBulkInStream(claim->slotId);
|
||||
}
|
||||
if (!claim->ring || maxLen == 0) {
|
||||
g_claimsLock.Release();
|
||||
return 0;
|
||||
}
|
||||
claim->ringLock.Acquire();
|
||||
uint32_t copied = claim->count < maxLen ? claim->count : maxLen;
|
||||
uint32_t tail = (claim->head + RingBytes - claim->count) % RingBytes;
|
||||
uint32_t first = RingBytes - tail;
|
||||
if (first > copied) first = copied;
|
||||
memcpy(out, claim->ring + tail, first);
|
||||
if (copied > first) memcpy(out + first, claim->ring, copied - first);
|
||||
claim->count -= copied;
|
||||
claim->ringLock.Release();
|
||||
g_claimsLock.Release();
|
||||
return (int)copied;
|
||||
}
|
||||
|
||||
void ReleaseAllForPid(int pid) {
|
||||
if (pid < 0) return;
|
||||
g_claimsLock.Acquire();
|
||||
for (int i = 0; i < MaxClaims; i++) {
|
||||
if (g_claims[i].active && g_claims[i].ownerPid == pid) CloseLocked(g_claims[i]);
|
||||
}
|
||||
g_claimsLock.Release();
|
||||
}
|
||||
|
||||
void DeviceDisconnected(uint8_t slotId) {
|
||||
// Hot-unplug runs in deferred kernel context. Do not take the
|
||||
// process-operation mutex: a control syscall may be polling the same
|
||||
// event queue. The per-claim spinlock is enough to make callbacks and
|
||||
// reads observe the disconnect atomically.
|
||||
for (int i = 0; i < MaxClaims; i++) {
|
||||
ClaimState& claim = g_claims[i];
|
||||
claim.ringLock.Acquire();
|
||||
if (claim.active && claim.slotId == slotId) {
|
||||
claim.connected = false;
|
||||
claim.streaming = false;
|
||||
}
|
||||
claim.ringLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* UserUsb.hpp
|
||||
* Process-owned access to unbound USB interfaces.
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <Api/Syscall.hpp>
|
||||
|
||||
namespace Drivers::USB::UserUsb {
|
||||
|
||||
// Enumerate the interfaces represented by the xHCI device table.
|
||||
int List(montauk::abi::UsbInterfaceInfo* out, int maxCount);
|
||||
|
||||
// Exclusively claim an interface that has no in-kernel class driver.
|
||||
// The returned handle is generation checked and belongs to the calling
|
||||
// process. The current xHCI device model represents one interface per
|
||||
// slot, so a claim is presently exclusive for the whole device slot.
|
||||
int Claim(uint8_t slotId, uint8_t interfaceNumber);
|
||||
int Close(int handle);
|
||||
|
||||
// Issue an EP0 control request. bmRequestType supplies the direction;
|
||||
// dataLen is limited to one DMA page.
|
||||
int Control(int handle, const montauk::abi::UsbControlRequest& request,
|
||||
void* data, uint32_t dataLen);
|
||||
|
||||
// Continuous bulk-IN streaming into a kernel ring. Read is non-blocking.
|
||||
int StartBulkIn(int handle, uint32_t transferBytes, uint32_t bufferCount);
|
||||
int StopBulkIn(int handle);
|
||||
int ReadBulkIn(int handle, uint8_t* out, uint32_t maxLen);
|
||||
|
||||
// Lifetime hooks used by process teardown and USB hot-unplug.
|
||||
void ReleaseAllForPid(int pid);
|
||||
void DeviceDisconnected(uint8_t slotId);
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "HidKeyboard.hpp"
|
||||
#include "HidMouse.hpp"
|
||||
#include "MassStorage.hpp"
|
||||
#include "Radio/RtlSdr.hpp"
|
||||
#include "UserUsb.hpp"
|
||||
#include <Pci/Pci.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
#include <CppLib/Stream.hpp>
|
||||
@@ -167,8 +167,7 @@ namespace Drivers::USB::Xhci {
|
||||
// nesting (a callback invoked from THIS core's PollEvents); a different
|
||||
// core merely polling must not make a process-context transfer skip its
|
||||
// wait -- that returned CC_SUCCESS before the device filled the buffer
|
||||
// (observed as garbled RTL-SDR register reads while the BT firmware
|
||||
// download was polling on another core).
|
||||
// (observed as corrupted USB control reads while another core was polling).
|
||||
static std::atomic<int> g_pollOwnerCpu{-1};
|
||||
|
||||
// Serialises non-nested (waiting) control transfers so only one EP0
|
||||
@@ -198,7 +197,7 @@ namespace Drivers::USB::Xhci {
|
||||
// re-arms the SAME buffer at the ring tail, so the endpoint is never without
|
||||
// a place to DMA. This closes the gap that single-outstanding bulk IN leaves
|
||||
// between completion and re-arm, during which the device FIFO overflows
|
||||
// (the RTL-SDR ~88% sample-drop at 2.048 Msps). PoolCount==0 => the legacy
|
||||
// under sustained high-rate input. PoolCount==0 selects the legacy
|
||||
// single-buffer path above (used by Bluetooth ACL), unchanged.
|
||||
static constexpr uint32_t BULK_IN_POOL_MAX = 16;
|
||||
static uint8_t* g_bulkInPool[MAX_SLOTS + 1][BULK_IN_POOL_MAX] = {};
|
||||
@@ -206,6 +205,7 @@ namespace Drivers::USB::Xhci {
|
||||
static uint32_t g_bulkInPoolCount[MAX_SLOTS + 1] = {}; // outstanding URBs (0=off)
|
||||
static uint32_t g_bulkInPoolHead[MAX_SLOTS + 1] = {}; // next buffer to complete
|
||||
static uint32_t g_bulkInPoolXferLen[MAX_SLOTS + 1] = {}; // bytes per transfer
|
||||
static kcp::Spinlock g_bulkInPoolLocks[MAX_SLOTS + 1];
|
||||
|
||||
// Transfer callbacks for non-HID class drivers (per slot)
|
||||
static TransferCallback g_transferCallbacks[MAX_SLOTS + 1] = {};
|
||||
@@ -590,15 +590,19 @@ namespace Drivers::USB::Xhci {
|
||||
// buffer is safe -- it will not be DMA'd into again
|
||||
// until the other PoolCount-1 transfers ahead of it
|
||||
// complete (~PoolCount ms of slack).
|
||||
uint32_t i = g_bulkInPoolHead[slotId];
|
||||
uint32_t reqLen = g_bulkInPoolXferLen[slotId];
|
||||
uint32_t len = (residual < reqLen) ? (reqLen - residual) : 0;
|
||||
g_transferCallbacks[slotId](slotId, epDci,
|
||||
g_bulkInPool[slotId][i], len, completionCode);
|
||||
QueueBulkInTransfer(slotId, g_bulkInPool[slotId][i],
|
||||
g_bulkInPoolPhys[slotId][i], reqLen);
|
||||
g_bulkInPoolHead[slotId] =
|
||||
(i + 1) % g_bulkInPoolCount[slotId];
|
||||
g_bulkInPoolLocks[slotId].Acquire();
|
||||
uint32_t poolCount = g_bulkInPoolCount[slotId];
|
||||
if (poolCount > 0) {
|
||||
uint32_t i = g_bulkInPoolHead[slotId];
|
||||
uint32_t reqLen = g_bulkInPoolXferLen[slotId];
|
||||
uint32_t len = (residual < reqLen) ? (reqLen - residual) : 0;
|
||||
g_transferCallbacks[slotId](slotId, epDci,
|
||||
g_bulkInPool[slotId][i], len, completionCode);
|
||||
QueueBulkInTransfer(slotId, g_bulkInPool[slotId][i],
|
||||
g_bulkInPoolPhys[slotId][i], reqLen);
|
||||
g_bulkInPoolHead[slotId] = (i + 1) % poolCount;
|
||||
}
|
||||
g_bulkInPoolLocks[slotId].Release();
|
||||
} else if (epDci == bulkInDci && g_transferCallbacks[slotId]) {
|
||||
// Bulk IN — dispatch via registered callback.
|
||||
// len = actually-transferred bytes (requested -
|
||||
@@ -1149,13 +1153,18 @@ namespace Drivers::USB::Xhci {
|
||||
// single-buffer start relies on.
|
||||
void PrimeBulkInStream(uint8_t slotId) {
|
||||
if (slotId == 0 || slotId > MAX_SLOTS || !g_devices[slotId].Active) return;
|
||||
g_bulkInPoolLocks[slotId].Acquire();
|
||||
uint32_t n = g_bulkInPoolCount[slotId];
|
||||
if (n == 0) return;
|
||||
if (n == 0) {
|
||||
g_bulkInPoolLocks[slotId].Release();
|
||||
return;
|
||||
}
|
||||
g_bulkInPoolHead[slotId] = 0;
|
||||
uint32_t len = g_bulkInPoolXferLen[slotId];
|
||||
for (uint32_t i = 0; i < n; i++)
|
||||
QueueBulkInTransfer(slotId, g_bulkInPool[slotId][i],
|
||||
g_bulkInPoolPhys[slotId][i], len);
|
||||
g_bulkInPoolLocks[slotId].Release();
|
||||
}
|
||||
|
||||
void StartBulkInStream(uint8_t slotId, uint32_t xferLen, uint32_t numBuffers) {
|
||||
@@ -1195,15 +1204,27 @@ namespace Drivers::USB::Xhci {
|
||||
for (uint32_t i = 0; i < numBuffers; i++)
|
||||
QueueBulkInTransfer(slotId, g_bulkInPool[slotId][i],
|
||||
g_bulkInPoolPhys[slotId][i], xferLen);
|
||||
g_bulkInPoolLocks[slotId].Acquire();
|
||||
g_bulkInPoolCount[slotId] = numBuffers;
|
||||
g_bulkInPoolLocks[slotId].Release();
|
||||
}
|
||||
|
||||
bool IsBulkInStreamActive(uint8_t slotId) {
|
||||
if (slotId == 0 || slotId > MAX_SLOTS) return false;
|
||||
g_bulkInPoolLocks[slotId].Acquire();
|
||||
bool active = g_bulkInPoolCount[slotId] != 0;
|
||||
g_bulkInPoolLocks[slotId].Release();
|
||||
return active;
|
||||
}
|
||||
|
||||
void StopBulkInStream(uint8_t slotId) {
|
||||
if (slotId == 0 || slotId > MAX_SLOTS) return;
|
||||
// Disarm the rotation; any late completion now takes the (no-op for SDR)
|
||||
// legacy path and is not re-armed. Buffers are retained for reuse.
|
||||
g_bulkInPoolLocks[slotId].Acquire();
|
||||
bool wasArmed = g_bulkInPoolCount[slotId] != 0;
|
||||
g_bulkInPoolCount[slotId] = 0;
|
||||
g_bulkInPoolLocks[slotId].Release();
|
||||
if (!wasArmed) return;
|
||||
|
||||
// Flush the up-to-PoolCount TRBs still pending on the ring: Stop
|
||||
@@ -1437,9 +1458,7 @@ namespace Drivers::USB::Xhci {
|
||||
}
|
||||
|
||||
static void UnregisterClassDriver(uint8_t slotId, const UsbDeviceInfo& dev) {
|
||||
if (Radio::IsRtlSdr(dev.VendorId, dev.ProductId)) {
|
||||
Radio::UnregisterDevice(slotId);
|
||||
} else if (dev.InterfaceClass == UsbDevice::CLASS_MASS_STORAGE) {
|
||||
if (dev.InterfaceClass == UsbDevice::CLASS_MASS_STORAGE) {
|
||||
MassStorage::UnregisterDevice(slotId);
|
||||
} else if (dev.InterfaceClass == UsbDevice::CLASS_HID &&
|
||||
dev.InterfaceProtocol == UsbDevice::PROTOCOL_KEYBOARD) {
|
||||
@@ -1559,6 +1578,7 @@ namespace Drivers::USB::Xhci {
|
||||
// Device disconnected — deactivate its slot
|
||||
for (uint8_t s = 1; s <= MAX_SLOTS; s++) {
|
||||
if (g_devices[s].Active && g_devices[s].PortId == port + 1) {
|
||||
UserUsb::DeviceDisconnected(s);
|
||||
UnregisterClassDriver(s, g_devices[s]);
|
||||
g_devices[s].Active = false;
|
||||
g_transferCallbacks[s] = nullptr;
|
||||
|
||||
@@ -233,6 +233,7 @@ namespace Drivers::USB::Xhci {
|
||||
|
||||
struct UsbDeviceInfo {
|
||||
bool Active;
|
||||
bool Ready; // descriptors/endpoints and binding are complete
|
||||
uint8_t PortId;
|
||||
uint32_t Speed;
|
||||
uint16_t VendorId;
|
||||
@@ -242,6 +243,7 @@ namespace Drivers::USB::Xhci {
|
||||
uint8_t InterfaceProtocol;
|
||||
uint8_t InterfaceNumber;
|
||||
uint8_t DeviceClass; // bDeviceClass from device descriptor
|
||||
bool KernelDriverBound; // unavailable to a userspace interface claim
|
||||
|
||||
// Interrupt IN endpoint
|
||||
uint8_t InterruptEpNum; // Endpoint number (1-15)
|
||||
@@ -329,7 +331,7 @@ namespace Drivers::USB::Xhci {
|
||||
// Clear a halted bulk IN endpoint (Reset Endpoint + Set TR Dequeue) without
|
||||
// re-arming. Must be called from process context (it issues commands that
|
||||
// wait on the event ring); the caller re-arms with QueueBulkInTransfer.
|
||||
// Used for SDR stream stall recovery (RTL2832 bulk IN can STALL on start).
|
||||
// Used by generic process-owned bulk streams after a transfer stall.
|
||||
void ResetBulkInEndpoint(uint8_t slotId);
|
||||
|
||||
// Clear a halted bulk OUT endpoint and discard the errored/queued TRBs.
|
||||
@@ -352,10 +354,11 @@ namespace Drivers::USB::Xhci {
|
||||
// as it completes. The slot's registered transfer callback receives every
|
||||
// buffer's data but must NOT re-arm itself (the event handler does). This
|
||||
// eliminates the FIFO-overflow gap of single-outstanding bulk IN. Use for
|
||||
// sustained high-rate sources (RTL-SDR I/Q). PrimeBulkInStream re-queues the
|
||||
// sustained high-rate sources. PrimeBulkInStream re-queues the
|
||||
// whole pool after a stall reset; StopBulkInStream disarms the rotation.
|
||||
// All three are process-context calls.
|
||||
void StartBulkInStream(uint8_t slotId, uint32_t xferLen, uint32_t numBuffers);
|
||||
bool IsBulkInStreamActive(uint8_t slotId);
|
||||
void PrimeBulkInStream(uint8_t slotId);
|
||||
void StopBulkInStream(uint8_t slotId);
|
||||
|
||||
|
||||
@@ -1202,6 +1202,13 @@ namespace Fs::Ext2 {
|
||||
Inode inode;
|
||||
if (!TraversePath(self, path, &inodeNum, &inode)) return -1;
|
||||
|
||||
// Directories are not openable as files, matching the ramdisk. A
|
||||
// handle on one would fail every read/write anyway, and userspace
|
||||
// stat() falls back to open() when it cannot get real metadata:
|
||||
// succeeding here classified every directory as a regular file and
|
||||
// GCC's include-path setup then rejected them as "not a directory".
|
||||
if ((inode.i_mode & IMODE_TYPE_MASK) == IMODE_DIR) return -1;
|
||||
|
||||
for (int i = 0; i < MaxFilesPerInstance; i++) {
|
||||
if (!self.files[i].inUse) {
|
||||
self.files[i].inUse = true;
|
||||
|
||||
@@ -955,6 +955,10 @@ namespace Fs::Fat32 {
|
||||
ParsedEntry entry;
|
||||
if (!TraversePath(inst, path, &entry)) return -1;
|
||||
|
||||
// Directories are not openable as files; see the matching note in
|
||||
// Ext2::OpenImpl (userspace stat() falls back to open()).
|
||||
if ((entry.attributes & ATTR_DIRECTORY) != 0) return -1;
|
||||
|
||||
// Find a free file handle
|
||||
auto& self = g_instances[inst];
|
||||
for (int i = 0; i < MaxFilesPerInstance; i++) {
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* ProtectedPaths.cpp
|
||||
* Capability required to modify paths on the booted system volume
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*
|
||||
* Split out of Ipc.cpp: this is filesystem security policy, not IPC. It
|
||||
* lived there only because OpenFileHandleForSlot was its first caller.
|
||||
*/
|
||||
|
||||
#include "ProtectedPaths.hpp"
|
||||
|
||||
#include <Api/Syscall.hpp>
|
||||
#include <Terminal/Terminal.hpp>
|
||||
|
||||
namespace Fs {
|
||||
|
||||
// ==== Protected system paths ====
|
||||
// The capability required to modify (create, write, delete or rename) a
|
||||
// path. The kernel only ever enumerates paths here; it never parses a
|
||||
// policy file. Userspace grant policy lives in 0:/config/capabilities.toml
|
||||
// and can only ever narrow what the kernel already delegated, so no input
|
||||
// to that file can produce authority this table does not already allow.
|
||||
//
|
||||
// Rules apply ONLY to the system volume. Drive 0 is always the boot
|
||||
// ramdisk (Fs/Boot.cpp registers it unconditionally); every other drive is
|
||||
// a partition discovered at probe time, in probe order. A user data disk
|
||||
// that happens to contain an "apps" or "config" directory must not inherit
|
||||
// system protection, and an installed system's files on another volume are
|
||||
// inert data until that disk is booted -- at which point its contents are
|
||||
// themselves the drive-0 ramdisk.
|
||||
// Anything the kernel itself reads belongs here:
|
||||
// guarding only the syscall leaves the file as an unguarded second path to
|
||||
// the same state (bluetooth.toml feeds the BD_ADDR override at bring-up).
|
||||
struct ProtectedPath {
|
||||
const char* pattern; // drive-relative, leading '/'
|
||||
bool prefix; // also match everything beneath the pattern
|
||||
uint64_t capability;
|
||||
};
|
||||
|
||||
static constexpr ProtectedPath g_protectedPaths[] = {
|
||||
// Authentication, first-boot administrator creation, trusted service
|
||||
// activation and capability grants. Readable by anyone; writable only
|
||||
// with administrative authority. The bare directory is listed so it
|
||||
// cannot be renamed or deleted out from under the files inside it.
|
||||
{"/config", false, montauk::abi::CAP_USER_ADMIN},
|
||||
{"/config/users.toml", false, montauk::abi::CAP_USER_ADMIN},
|
||||
{"/config/setup.toml", false, montauk::abi::CAP_USER_ADMIN},
|
||||
{"/config/init.toml", false, montauk::abi::CAP_USER_ADMIN},
|
||||
{"/config/ssh.toml", false, montauk::abi::CAP_USER_ADMIN},
|
||||
{"/config/capabilities.toml",false, montauk::abi::CAP_USER_ADMIN},
|
||||
// Pre-scaled wallpaper the login screen blits before it has decoded
|
||||
// anything. It is drawn on a screen that is about to take a password,
|
||||
// so it must not be plantable by an unprivileged process.
|
||||
{"/config/wallpaper.cache", false, montauk::abi::CAP_USER_ADMIN},
|
||||
// Read by the Bluetooth driver at controller bring-up.
|
||||
{"/config/bluetooth.toml", false, montauk::abi::CAP_DEVICE_ADMIN},
|
||||
// Program images. Capability grants are keyed on binary path, so a
|
||||
// writable image would let an unprivileged process substitute a binary
|
||||
// and inherit the grant the next time a privileged launcher runs it.
|
||||
// This is CAP_SYSTEM_IMAGE and not CAP_STORAGE_ADMIN precisely because
|
||||
// it is the trusted computing base: partitioning and formatting a data
|
||||
// volume is an ordinary administrative act, while replacing the image
|
||||
// of login.elf is a route to every capability the system can issue.
|
||||
{"/apps", true, montauk::abi::CAP_SYSTEM_IMAGE},
|
||||
{"/os", true, montauk::abi::CAP_SYSTEM_IMAGE},
|
||||
};
|
||||
|
||||
static char LowerAscii(char c) {
|
||||
return (c >= 'A' && c <= 'Z') ? (char)(c + ('a' - 'A')) : c;
|
||||
}
|
||||
|
||||
// The volume the running system was booted from.
|
||||
static constexpr uint64_t SystemDrive = 0;
|
||||
|
||||
// Strip the "<digits>:" prefix, but only for the system volume. Returns
|
||||
// nullptr for any other drive, meaning no rule applies to it.
|
||||
static const char* SystemRelativePath(const char* path) {
|
||||
if (path == nullptr) return nullptr;
|
||||
|
||||
const char* p = path;
|
||||
if (*p < '0' || *p > '9') return nullptr;
|
||||
|
||||
uint64_t drive = 0;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
drive = drive * 10 + (uint64_t)(*p - '0');
|
||||
if (drive > 0xFFFF) return nullptr; // absurd; cannot be a drive
|
||||
p++;
|
||||
}
|
||||
if (*p != ':' || drive != SystemDrive) return nullptr;
|
||||
return p + 1;
|
||||
}
|
||||
|
||||
// Case-insensitive: FAT32 resolves differing cases to the same file, so a
|
||||
// case-sensitive rule would be trivially sidestepped.
|
||||
static bool ProtectedPathMatches(const char* path, const ProtectedPath& rule) {
|
||||
const char* p = path;
|
||||
const char* q = rule.pattern;
|
||||
while (*q) {
|
||||
if (LowerAscii(*p) != LowerAscii(*q)) return false;
|
||||
p++;
|
||||
q++;
|
||||
}
|
||||
if (*p == '\0') return true; // the pattern itself
|
||||
return rule.prefix && *p == '/'; // something beneath it
|
||||
}
|
||||
|
||||
uint64_t RequiredFileWriteCapability(const char* path) {
|
||||
const char* relative = SystemRelativePath(path);
|
||||
if (relative == nullptr) return 0; // not the system volume
|
||||
|
||||
// Overlapping rules accumulate: HasCapability() requires every bit, so
|
||||
// a path covered by two rules demands both.
|
||||
uint64_t required = 0;
|
||||
for (const auto& rule : g_protectedPaths) {
|
||||
if (ProtectedPathMatches(relative, rule)) required |= rule.capability;
|
||||
}
|
||||
return required;
|
||||
}
|
||||
|
||||
void LogProtectedPaths() {
|
||||
for (const auto& rule : g_protectedPaths) {
|
||||
Kt::KernelLogStream(Kt::INFO, "IPC") << "Protected path "
|
||||
<< rule.pattern << (rule.prefix ? "/* " : " ")
|
||||
<< "requires capability mask "
|
||||
<< kcp::hex << rule.capability << kcp::dec;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* ProtectedPaths.hpp
|
||||
* Capability required to modify paths on the booted system volume
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Fs {
|
||||
|
||||
// Capability a process must hold to create, write, delete, rename or
|
||||
// re-timestamp `path`. Returns 0 when the path is unprotected.
|
||||
uint64_t RequiredFileWriteCapability(const char* path);
|
||||
|
||||
// Log the rule table at boot, so a path that should be protected and is
|
||||
// not is visible rather than silently missing.
|
||||
void LogProtectedPaths();
|
||||
|
||||
}
|
||||
@@ -355,10 +355,18 @@ namespace Fs::Ramdisk {
|
||||
uint64_t newCap = entry.size;
|
||||
if (endOffset > newCap) newCap = endOffset;
|
||||
if (newCap < 256) newCap = 256;
|
||||
// Round up to next power of 2 for growth
|
||||
uint64_t rounded = 256;
|
||||
while (rounded < newCap) rounded *= 2;
|
||||
newCap = rounded;
|
||||
// Small files round to the next power of 2, so an appender grows
|
||||
// in a few steps. Large ones round to a page instead: the kernel
|
||||
// heap grows in physically contiguous runs, and doubling an 8 MiB
|
||||
// write into a 16 MiB block asks the frame allocator for twice the
|
||||
// contiguous span the file actually needs.
|
||||
if (newCap < 64 * 1024) {
|
||||
uint64_t rounded = 256;
|
||||
while (rounded < newCap) rounded *= 2;
|
||||
newCap = rounded;
|
||||
} else {
|
||||
newCap = (newCap + 0xFFFULL) & ~0xFFFULL;
|
||||
}
|
||||
|
||||
uint8_t* newBuf = (uint8_t*)Memory::g_heap->Request(newCap);
|
||||
if (newBuf == nullptr) return -1;
|
||||
@@ -374,8 +382,14 @@ namespace Fs::Ramdisk {
|
||||
|
||||
// Grow buffer if needed
|
||||
if (endOffset > entry.capacity) {
|
||||
uint64_t newCap = entry.capacity;
|
||||
while (newCap < endOffset) newCap *= 2;
|
||||
// Double while small, then grow in fixed 1 MiB steps. Doubling all
|
||||
// the way keeps growth amortized but overshoots badly on multi-MiB
|
||||
// files, and every byte of overshoot is a physically contiguous
|
||||
// kernel-heap run this file holds for the rest of the boot.
|
||||
static constexpr uint64_t MaxGrowStep = 1024 * 1024;
|
||||
uint64_t newCap = entry.capacity < 256 ? 256 : entry.capacity;
|
||||
while (newCap < endOffset)
|
||||
newCap += (newCap < MaxGrowStep) ? newCap : MaxGrowStep;
|
||||
|
||||
uint8_t* newBuf = (uint8_t*)Memory::g_heap->Request(newCap);
|
||||
if (newBuf == nullptr) return -1;
|
||||
|
||||
@@ -218,6 +218,10 @@ namespace Smp {
|
||||
for (;;) {
|
||||
// Pick up thermal-governor frequency changes decided by the BSP.
|
||||
Hal::CpuPower::ApplyPolicyIfChanged();
|
||||
// Runnable work sends this AP a reschedule IPI. Keep its periodic
|
||||
// scheduler tick masked for the entire idle-context pass so long
|
||||
// firmware waits do not keep generating useless timer interrupts.
|
||||
Timekeeping::ApicTimerEnterApIdle();
|
||||
// Any idle core may run bounded USB/NIC bottom halves. Preserve
|
||||
// the AP's ACPI/MWAIT idle selection after servicing them.
|
||||
Timekeeping::ServiceDeferredWork();
|
||||
|
||||
+72
-148
@@ -8,11 +8,13 @@
|
||||
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Fs/Vfs.hpp>
|
||||
#include <Fs/ProtectedPaths.hpp>
|
||||
#include <Net/Tcp.hpp>
|
||||
#include <Net/Udp.hpp>
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
#include <Memory/UserRange.hpp>
|
||||
#include <Libraries/Memory.hpp>
|
||||
#include <CppLib/Spinlock.hpp>
|
||||
#include <Hal/Apic/Apic.hpp>
|
||||
@@ -82,6 +84,7 @@ namespace Ipc {
|
||||
|
||||
struct File : Object {
|
||||
Fs::Vfs::BackendFile backend;
|
||||
uint64_t writeCapability;
|
||||
};
|
||||
|
||||
struct UdpDgramHeader {
|
||||
@@ -178,149 +181,11 @@ namespace Ipc {
|
||||
|
||||
static void ReleaseRawObject(Object* object);
|
||||
|
||||
// MUST be a Mutex, never a Spinlock. ShootdownUserRange holds this while
|
||||
// waiting for remote CPUs to acknowledge the shootdown IPI, so a CPU that
|
||||
// is queued behind the holder has to stay interruptible long enough to
|
||||
// service that IPI itself. An interrupt-disabling Spinlock here deadlocks
|
||||
// every CPU contending for the lock, and the bounded-retry logic below
|
||||
// then reports it as a "target failed to acknowledge" Panic -- which reads
|
||||
// like a hardware fault rather than a lock-type regression.
|
||||
static kcp::Mutex g_tlbShootdownLock;
|
||||
static volatile uint64_t g_tlbShootdownSeq = 0;
|
||||
static volatile uint64_t g_tlbShootdownPml4 = 0;
|
||||
static volatile uint64_t g_tlbShootdownStartVa = 0;
|
||||
static volatile uint32_t g_tlbShootdownPages = 0;
|
||||
static volatile uint64_t g_tlbShootdownDone[Smp::MaxCPUs] = {};
|
||||
|
||||
static bool CpuCurrentlyUsesPml4(Smp::CpuData* cpu, uint64_t pml4Phys) {
|
||||
if (cpu == nullptr || pml4Phys == 0 || cpu->currentSlot < 0) return false;
|
||||
|
||||
Sched::Process* proc = Sched::GetProcessSlot(cpu->currentSlot);
|
||||
if (proc == nullptr) return false;
|
||||
if (proc->state == Sched::ProcessState::Free) return false;
|
||||
return proc->pml4Phys == pml4Phys;
|
||||
}
|
||||
|
||||
static void InvalidateLocalUserRange(uint64_t startVa, uint32_t pages) {
|
||||
if (pages == 0) return;
|
||||
|
||||
if (pages > 1024) {
|
||||
Memory::VMM::FlushTLB();
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t p = 0; p < pages; p++) {
|
||||
uint64_t va = startVa + (uint64_t)p * 0x1000ULL;
|
||||
asm volatile("invlpg (%0)" :: "r"(va) : "memory");
|
||||
}
|
||||
}
|
||||
|
||||
static void TlbShootdownIpiHandler(uint8_t, bool) {
|
||||
Smp::CpuData* cpu = Smp::GetCurrentCpuData();
|
||||
uint64_t seq = g_tlbShootdownSeq;
|
||||
uint64_t pml4 = g_tlbShootdownPml4;
|
||||
uint64_t startVa = g_tlbShootdownStartVa;
|
||||
uint32_t pages = g_tlbShootdownPages;
|
||||
|
||||
if (CpuCurrentlyUsesPml4(cpu, pml4)) {
|
||||
InvalidateLocalUserRange(startVa, pages);
|
||||
}
|
||||
|
||||
if (cpu != nullptr && cpu->cpuIndex >= 0 && cpu->cpuIndex < Smp::MaxCPUs) {
|
||||
asm volatile("" ::: "memory");
|
||||
g_tlbShootdownDone[cpu->cpuIndex] = seq;
|
||||
}
|
||||
}
|
||||
|
||||
void ShootdownUserRange(uint64_t pml4Phys, uint64_t startVa, uint32_t pages) {
|
||||
if (pml4Phys == 0 || pages == 0) return;
|
||||
|
||||
bool targets[Smp::MaxCPUs] = {};
|
||||
Smp::CpuData* currentCpu = Smp::GetCurrentCpuData();
|
||||
int currentCpuIndex = currentCpu ? currentCpu->cpuIndex : -1;
|
||||
|
||||
g_tlbShootdownLock.Acquire();
|
||||
uint64_t seq = g_tlbShootdownSeq + 1;
|
||||
g_tlbShootdownPml4 = pml4Phys;
|
||||
g_tlbShootdownStartVa = startVa;
|
||||
g_tlbShootdownPages = pages;
|
||||
asm volatile("" ::: "memory");
|
||||
g_tlbShootdownSeq = seq;
|
||||
|
||||
for (int i = 0; i < Smp::GetCpuCount(); i++) {
|
||||
Smp::CpuData* cpu = Smp::GetCpuData(i);
|
||||
if (cpu == nullptr || !cpu->started) continue;
|
||||
|
||||
if (i == currentCpuIndex) {
|
||||
if (CpuCurrentlyUsesPml4(cpu, pml4Phys)) {
|
||||
InvalidateLocalUserRange(startVa, pages);
|
||||
}
|
||||
g_tlbShootdownDone[i] = seq;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CpuCurrentlyUsesPml4(cpu, pml4Phys)) {
|
||||
g_tlbShootdownDone[i] = seq;
|
||||
continue;
|
||||
}
|
||||
|
||||
targets[i] = true;
|
||||
(void)Hal::LocalApic::SendFixedIpi(cpu->lapicId,
|
||||
Hal::IRQ_VECTOR_BASE + Hal::IRQ_TLB_SHOOTDOWN);
|
||||
}
|
||||
|
||||
for (int i = 0; i < Smp::GetCpuCount(); i++) {
|
||||
if (!targets[i]) continue;
|
||||
uint32_t spins = 0;
|
||||
uint32_t retries = 0;
|
||||
while (g_tlbShootdownDone[i] != seq) {
|
||||
asm volatile("pause");
|
||||
if (++spins < 1000000) continue;
|
||||
|
||||
// Delivery normally completes in a handful of cycles. Retry a
|
||||
// bounded number of times in case the first IPI was lost while
|
||||
// the target changed interrupt state. Continuing without an
|
||||
// acknowledgement would let the caller free frames still
|
||||
// reachable through a remote stale TLB entry, so fail loudly
|
||||
// instead of either corrupting memory or spinning forever.
|
||||
spins = 0;
|
||||
if (++retries > 4) {
|
||||
Panic("TLB shootdown target failed to acknowledge", nullptr);
|
||||
}
|
||||
Smp::CpuData* cpu = Smp::GetCpuData(i);
|
||||
if (cpu != nullptr && cpu->started) {
|
||||
(void)Hal::LocalApic::SendFixedIpi(cpu->lapicId,
|
||||
Hal::IRQ_VECTOR_BASE + Hal::IRQ_TLB_SHOOTDOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_tlbShootdownLock.Release();
|
||||
}
|
||||
|
||||
void UnmapAndFreeUserRange(uint64_t pml4Phys, uint64_t startVa, uint64_t pages) {
|
||||
static constexpr uint32_t PagesPerChunk = 64;
|
||||
uint64_t physPages[PagesPerChunk];
|
||||
|
||||
for (uint64_t base = 0; base < pages; base += PagesPerChunk) {
|
||||
uint32_t count = (uint32_t)((pages - base > PagesPerChunk)
|
||||
? PagesPerChunk : pages - base);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uint64_t pageVa = startVa + (base + i) * 0x1000ULL;
|
||||
physPages[i] = Memory::VMM::Paging::GetPhysAddr(pml4Phys, pageVa);
|
||||
Memory::VMM::Paging::UnmapUserIn(pml4Phys, pageVa);
|
||||
}
|
||||
|
||||
ShootdownUserRange(pml4Phys, startVa + base * 0x1000ULL, count);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (physPages[i] != 0) {
|
||||
Memory::g_pfa->Free((void*)Memory::HHDM(physPages[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ==========================================================================
|
||||
// Object lifetime
|
||||
// Pool allocation, refcounting and type-dispatched teardown.
|
||||
// Every object type routes through this layer.
|
||||
// ==========================================================================
|
||||
|
||||
static void InitObject(Object& object, HandleType type) {
|
||||
object.type = type;
|
||||
@@ -568,6 +433,11 @@ namespace Ipc {
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Handle table
|
||||
// Per-process handle installation, rights, duplication and close.
|
||||
// ==========================================================================
|
||||
|
||||
int CurrentSlot() {
|
||||
auto* proc = Sched::GetCurrentProcessPtr();
|
||||
if (proc == nullptr) return -1;
|
||||
@@ -817,6 +687,11 @@ namespace Ipc {
|
||||
return InstallHandleForSlot(slot, snapshot.object, snapshot.type, snapshot.rights);
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Streams
|
||||
// Byte pipes.
|
||||
// ==========================================================================
|
||||
|
||||
Stream* CreateStream(uint32_t capacity) {
|
||||
if (capacity == 0) capacity = DefaultStreamCapacity;
|
||||
|
||||
@@ -1000,6 +875,11 @@ namespace Ipc {
|
||||
return hasData;
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Mailboxes
|
||||
// Discrete message queues.
|
||||
// ==========================================================================
|
||||
|
||||
Mailbox* CreateMailbox() {
|
||||
g_mailboxPoolLock.Acquire();
|
||||
for (int i = 0; i < MaxMailboxes; i++) {
|
||||
@@ -1260,9 +1140,22 @@ namespace Ipc {
|
||||
return hasMsg;
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Files
|
||||
// Write authority is re-checked against the calling process on every
|
||||
// write, so passing a writable handle to a less privileged process does
|
||||
// not transfer the ability to use it. See Fs/ProtectedPaths.cpp.
|
||||
// ==========================================================================
|
||||
|
||||
int OpenFileHandleForSlot(int slot, const char* path, bool create) {
|
||||
if (slot < 0 || slot >= Sched::MaxProcesses || path == nullptr) return -1;
|
||||
|
||||
uint64_t writeCapability = Fs::RequiredFileWriteCapability(path);
|
||||
if (create && writeCapability != 0 &&
|
||||
!Sched::HasCapability(writeCapability)) {
|
||||
return montauk::abi::SYS_ERR_PERMISSION;
|
||||
}
|
||||
|
||||
Fs::Vfs::BackendFile backend = {-1, -1, 0};
|
||||
int result = create ? Fs::Vfs::CreateBackendFile(path, backend)
|
||||
: Fs::Vfs::OpenBackendFile(path, backend);
|
||||
@@ -1273,6 +1166,7 @@ namespace Ipc {
|
||||
if (g_files[i].active || g_files[i].destroying) continue;
|
||||
InitObject(g_files[i], HandleType::File);
|
||||
g_files[i].backend = backend;
|
||||
g_files[i].writeCapability = writeCapability;
|
||||
g_filePoolLock.Release();
|
||||
|
||||
uint32_t rights = RightRead | RightWait | RightDup;
|
||||
@@ -1318,7 +1212,12 @@ namespace Ipc {
|
||||
HandleSnapshot snapshot;
|
||||
if (!snapshot.Capture(CurrentSlot(), handle)) return -1;
|
||||
if (snapshot.type != HandleType::File || (snapshot.rights & RightWrite) == 0) return -1;
|
||||
return Fs::Vfs::WriteBackendFile(((File*)snapshot.object)->backend, buffer, offset, size);
|
||||
File* file = (File*)snapshot.object;
|
||||
if (file->writeCapability != 0 &&
|
||||
!Sched::HasCapability(file->writeCapability)) {
|
||||
return montauk::abi::SYS_ERR_PERMISSION;
|
||||
}
|
||||
return Fs::Vfs::WriteBackendFile(file->backend, buffer, offset, size);
|
||||
}
|
||||
|
||||
uint64_t FileGetSizeHandle(int handle) {
|
||||
@@ -1328,6 +1227,11 @@ namespace Ipc {
|
||||
return Fs::Vfs::GetBackendFileSize(((File*)snapshot.object)->backend);
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Sockets
|
||||
// TCP and UDP endpoints.
|
||||
// ==========================================================================
|
||||
|
||||
static Socket* AllocateSocketObject(int type) {
|
||||
g_socketPoolLock.Acquire();
|
||||
for (int i = 0; i < MaxSockets; i++) {
|
||||
@@ -1663,6 +1567,13 @@ namespace Ipc {
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Surfaces
|
||||
// Shared pixel buffers mapped into a client address space.
|
||||
// Pages MUST be unmapped from the owner before being freed, or
|
||||
// FreeUserHalf() double-frees them on process exit.
|
||||
// ==========================================================================
|
||||
|
||||
Surface* CreateSurface(uint64_t byteSize) {
|
||||
if (byteSize == 0) byteSize = 0x1000;
|
||||
uint32_t numPages = (uint32_t)((byteSize + 0xFFFu) / 0x1000u);
|
||||
@@ -1748,7 +1659,7 @@ namespace Ipc {
|
||||
for (uint32_t p = m.numPages; p < newPages; p++) {
|
||||
Memory::VMM::Paging::UnmapUserIn(pml4, m.va + (uint64_t)p * 0x1000ULL);
|
||||
}
|
||||
ShootdownUserRange(pml4, startVa, rollbackPages);
|
||||
Memory::ShootdownUserRange(pml4, startVa, rollbackPages);
|
||||
}
|
||||
g_surfaceMapLocks[s].Release();
|
||||
}
|
||||
@@ -1900,7 +1811,7 @@ namespace Ipc {
|
||||
Memory::VMM::Paging::UnmapUserIn(pml4, va);
|
||||
}
|
||||
|
||||
ShootdownUserRange(pml4, baseVa, flushPages);
|
||||
Memory::ShootdownUserRange(pml4, baseVa, flushPages);
|
||||
m.numPages = newPages;
|
||||
}
|
||||
g_surfaceMapLocks[s].Release();
|
||||
@@ -2111,7 +2022,7 @@ namespace Ipc {
|
||||
// releasing the mapping reference can then destroy the surface
|
||||
// and recycle its frames while that sibling writes through its
|
||||
// stale TLB entry. Quiesce every CPU using this PML4 first.
|
||||
ShootdownUserRange(pml4Phys, baseVa, numPages);
|
||||
Memory::ShootdownUserRange(pml4Phys, baseVa, numPages);
|
||||
|
||||
g_surfaceMaps[slot][i].used = false;
|
||||
g_surfaceMaps[slot][i].surface = nullptr;
|
||||
@@ -2127,6 +2038,11 @@ namespace Ipc {
|
||||
return unmapped > 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Process handles
|
||||
// Wait-only references to a live process.
|
||||
// ==========================================================================
|
||||
|
||||
int OpenProcessHandle(int pid) {
|
||||
g_processPoolLock.Acquire();
|
||||
for (int i = 0; i < MaxProcessObjects; i++) {
|
||||
@@ -2183,6 +2099,11 @@ namespace Ipc {
|
||||
return exited;
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Signals and waitsets
|
||||
// Readiness computation and multiplexed waiting.
|
||||
// ==========================================================================
|
||||
|
||||
static uint32_t CurrentSocketSignals(Socket* socket, uint32_t rights) {
|
||||
if (socket == nullptr) return SignalNone;
|
||||
|
||||
@@ -2513,6 +2434,10 @@ namespace Ipc {
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Teardown and init
|
||||
// ==========================================================================
|
||||
|
||||
void CleanupProcessSlot(int slot, int /*pid*/, uint64_t pml4Phys) {
|
||||
if (slot < 0 || slot >= Sched::MaxProcesses) return;
|
||||
|
||||
@@ -2548,7 +2473,6 @@ namespace Ipc {
|
||||
for (int i = 0; i < Sched::MaxProcesses; i++) {
|
||||
g_processObjectsBySlot[i] = nullptr;
|
||||
}
|
||||
Hal::RegisterIrqHandler(Hal::IRQ_TLB_SHOOTDOWN, TlbShootdownIpiHandler);
|
||||
Kt::KernelLogStream(Kt::OK, "IPC") << "Initialized ("
|
||||
<< (uint64_t)MaxHandlesPerProcess << " handles/process, "
|
||||
<< (uint64_t)MaxStreams << " streams, "
|
||||
|
||||
@@ -171,11 +171,6 @@ namespace Ipc {
|
||||
int WaitsetWaitHandle(int waitsetHandle, WaitsetReady* outReady, uint64_t timeoutMs);
|
||||
|
||||
void NotifyObjectChanged(Object* object);
|
||||
// Invalidate a user range on every CPU currently running the address
|
||||
// space. Call this after removing PTEs and before releasing their frames.
|
||||
void ShootdownUserRange(uint64_t pml4Phys, uint64_t startVa, uint32_t pages);
|
||||
// Safely remove ordinary PFA-backed user mappings and release their frames.
|
||||
void UnmapAndFreeUserRange(uint64_t pml4Phys, uint64_t startVa, uint64_t pages);
|
||||
void CleanupProcessSlot(int slot, int pid, uint64_t pml4Phys);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* UserRange.cpp
|
||||
* Cross-CPU invalidation and teardown of user address-space mappings
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*
|
||||
* Split out of Ipc.cpp: this is paging and SMP work with no dependency on
|
||||
* any IPC object or handle pool, and it lived there only by history.
|
||||
*/
|
||||
|
||||
#include "UserRange.hpp"
|
||||
|
||||
#include <Sched/Scheduler.hpp>
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
#include <Memory/HHDM.hpp>
|
||||
#include <Memory/Paging.hpp>
|
||||
#include <CppLib/Spinlock.hpp>
|
||||
#include <Hal/Apic/Apic.hpp>
|
||||
#include <Hal/Apic/Interrupts.hpp>
|
||||
#include <Hal/SmpBoot.hpp>
|
||||
#include <Common/Panic.hpp>
|
||||
|
||||
namespace Memory {
|
||||
|
||||
// MUST be a Mutex, never a Spinlock. ShootdownUserRange holds this while
|
||||
// waiting for remote CPUs to acknowledge the shootdown IPI, so a CPU that
|
||||
// is queued behind the holder has to stay interruptible long enough to
|
||||
// service that IPI itself. An interrupt-disabling Spinlock here deadlocks
|
||||
// every CPU contending for the lock, and the bounded-retry logic below
|
||||
// then reports it as a "target failed to acknowledge" Panic -- which reads
|
||||
// like a hardware fault rather than a lock-type regression.
|
||||
static kcp::Mutex g_tlbShootdownLock;
|
||||
static volatile uint64_t g_tlbShootdownSeq = 0;
|
||||
static volatile uint64_t g_tlbShootdownPml4 = 0;
|
||||
static volatile uint64_t g_tlbShootdownStartVa = 0;
|
||||
static volatile uint32_t g_tlbShootdownPages = 0;
|
||||
static volatile uint64_t g_tlbShootdownDone[Smp::MaxCPUs] = {};
|
||||
|
||||
static bool CpuCurrentlyUsesPml4(Smp::CpuData* cpu, uint64_t pml4Phys) {
|
||||
if (cpu == nullptr || pml4Phys == 0 || cpu->currentSlot < 0) return false;
|
||||
|
||||
Sched::Process* proc = Sched::GetProcessSlot(cpu->currentSlot);
|
||||
if (proc == nullptr) return false;
|
||||
if (proc->state == Sched::ProcessState::Free) return false;
|
||||
return proc->pml4Phys == pml4Phys;
|
||||
}
|
||||
|
||||
static void InvalidateLocalUserRange(uint64_t startVa, uint32_t pages) {
|
||||
if (pages == 0) return;
|
||||
|
||||
if (pages > 1024) {
|
||||
Memory::VMM::FlushTLB();
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t p = 0; p < pages; p++) {
|
||||
uint64_t va = startVa + (uint64_t)p * 0x1000ULL;
|
||||
asm volatile("invlpg (%0)" :: "r"(va) : "memory");
|
||||
}
|
||||
}
|
||||
|
||||
static void TlbShootdownIpiHandler(uint8_t, bool) {
|
||||
Smp::CpuData* cpu = Smp::GetCurrentCpuData();
|
||||
uint64_t seq = g_tlbShootdownSeq;
|
||||
uint64_t pml4 = g_tlbShootdownPml4;
|
||||
uint64_t startVa = g_tlbShootdownStartVa;
|
||||
uint32_t pages = g_tlbShootdownPages;
|
||||
|
||||
if (CpuCurrentlyUsesPml4(cpu, pml4)) {
|
||||
InvalidateLocalUserRange(startVa, pages);
|
||||
}
|
||||
|
||||
if (cpu != nullptr && cpu->cpuIndex >= 0 && cpu->cpuIndex < Smp::MaxCPUs) {
|
||||
asm volatile("" ::: "memory");
|
||||
g_tlbShootdownDone[cpu->cpuIndex] = seq;
|
||||
}
|
||||
}
|
||||
|
||||
void ShootdownUserRange(uint64_t pml4Phys, uint64_t startVa, uint32_t pages) {
|
||||
if (pml4Phys == 0 || pages == 0) return;
|
||||
|
||||
bool targets[Smp::MaxCPUs] = {};
|
||||
Smp::CpuData* currentCpu = Smp::GetCurrentCpuData();
|
||||
int currentCpuIndex = currentCpu ? currentCpu->cpuIndex : -1;
|
||||
|
||||
g_tlbShootdownLock.Acquire();
|
||||
uint64_t seq = g_tlbShootdownSeq + 1;
|
||||
g_tlbShootdownPml4 = pml4Phys;
|
||||
g_tlbShootdownStartVa = startVa;
|
||||
g_tlbShootdownPages = pages;
|
||||
asm volatile("" ::: "memory");
|
||||
g_tlbShootdownSeq = seq;
|
||||
|
||||
for (int i = 0; i < Smp::GetCpuCount(); i++) {
|
||||
Smp::CpuData* cpu = Smp::GetCpuData(i);
|
||||
if (cpu == nullptr || !cpu->started) continue;
|
||||
|
||||
if (i == currentCpuIndex) {
|
||||
if (CpuCurrentlyUsesPml4(cpu, pml4Phys)) {
|
||||
InvalidateLocalUserRange(startVa, pages);
|
||||
}
|
||||
g_tlbShootdownDone[i] = seq;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CpuCurrentlyUsesPml4(cpu, pml4Phys)) {
|
||||
g_tlbShootdownDone[i] = seq;
|
||||
continue;
|
||||
}
|
||||
|
||||
targets[i] = true;
|
||||
(void)Hal::LocalApic::SendFixedIpi(cpu->lapicId,
|
||||
Hal::IRQ_VECTOR_BASE + Hal::IRQ_TLB_SHOOTDOWN);
|
||||
}
|
||||
|
||||
for (int i = 0; i < Smp::GetCpuCount(); i++) {
|
||||
if (!targets[i]) continue;
|
||||
uint32_t spins = 0;
|
||||
uint32_t retries = 0;
|
||||
while (g_tlbShootdownDone[i] != seq) {
|
||||
asm volatile("pause");
|
||||
if (++spins < 1000000) continue;
|
||||
|
||||
// Delivery normally completes in a handful of cycles. Retry a
|
||||
// bounded number of times in case the first IPI was lost while
|
||||
// the target changed interrupt state. Continuing without an
|
||||
// acknowledgement would let the caller free frames still
|
||||
// reachable through a remote stale TLB entry, so fail loudly
|
||||
// instead of either corrupting memory or spinning forever.
|
||||
spins = 0;
|
||||
if (++retries > 4) {
|
||||
Panic("TLB shootdown target failed to acknowledge", nullptr);
|
||||
}
|
||||
Smp::CpuData* cpu = Smp::GetCpuData(i);
|
||||
if (cpu != nullptr && cpu->started) {
|
||||
(void)Hal::LocalApic::SendFixedIpi(cpu->lapicId,
|
||||
Hal::IRQ_VECTOR_BASE + Hal::IRQ_TLB_SHOOTDOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_tlbShootdownLock.Release();
|
||||
}
|
||||
|
||||
void UnmapAndFreeUserRange(uint64_t pml4Phys, uint64_t startVa, uint64_t pages) {
|
||||
static constexpr uint32_t PagesPerChunk = 64;
|
||||
uint64_t physPages[PagesPerChunk];
|
||||
|
||||
for (uint64_t base = 0; base < pages; base += PagesPerChunk) {
|
||||
uint32_t count = (uint32_t)((pages - base > PagesPerChunk)
|
||||
? PagesPerChunk : pages - base);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uint64_t pageVa = startVa + (base + i) * 0x1000ULL;
|
||||
physPages[i] = Memory::VMM::Paging::GetPhysAddr(pml4Phys, pageVa);
|
||||
Memory::VMM::Paging::UnmapUserIn(pml4Phys, pageVa);
|
||||
}
|
||||
|
||||
ShootdownUserRange(pml4Phys, startVa + base * 0x1000ULL, count);
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if (physPages[i] != 0) {
|
||||
Memory::g_pfa->Free((void*)Memory::HHDM(physPages[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitUserRange() {
|
||||
Hal::RegisterIrqHandler(Hal::IRQ_TLB_SHOOTDOWN, TlbShootdownIpiHandler);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* UserRange.hpp
|
||||
* Cross-CPU invalidation and teardown of user address-space mappings
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Memory {
|
||||
|
||||
// Register the TLB-shootdown IPI handler. Must run before any AP is
|
||||
// booted, since a shootdown targets every CPU running the address space.
|
||||
void InitUserRange();
|
||||
|
||||
// Invalidate a user range on every CPU currently running the address
|
||||
// space. Call this after removing PTEs and before releasing their frames.
|
||||
void ShootdownUserRange(uint64_t pml4Phys, uint64_t startVa, uint32_t pages);
|
||||
|
||||
// Safely remove ordinary PFA-backed user mappings and release their frames.
|
||||
void UnmapAndFreeUserRange(uint64_t pml4Phys, uint64_t startVa, uint64_t pages);
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2025-2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include <Memory/UserRange.hpp>
|
||||
#include "Scheduler.hpp"
|
||||
#include "ElfLoader.hpp"
|
||||
#include <Memory/PageFrameAllocator.hpp>
|
||||
@@ -27,6 +28,7 @@
|
||||
#include <Drivers/Audio/Mixer.hpp>
|
||||
#include <Drivers/Graphics/IntelGPU.hpp>
|
||||
#include <Ipc/Ipc.hpp>
|
||||
#include <Drivers/USB/UserUsb.hpp>
|
||||
|
||||
// Assembly: context switch with CR3 and FPU state parameters
|
||||
extern "C" void SchedContextSwitch(uint64_t* oldRsp, uint64_t newRsp, uint64_t newCR3,
|
||||
@@ -297,6 +299,10 @@ namespace Sched {
|
||||
processTable[i].environment[0] = '\0';
|
||||
processTable[i].environmentLength = 1;
|
||||
processTable[i].user[0] = '\0';
|
||||
processTable[i].permittedCaps = 0;
|
||||
processTable[i].effectiveCaps = 0;
|
||||
processTable[i].delegableCaps = 0;
|
||||
processTable[i].sessionId = -1;
|
||||
processTable[i].cwd[0] = '\0';
|
||||
processTable[i].runningOnCpu = -1;
|
||||
processTable[i].killPending = false;
|
||||
@@ -340,7 +346,9 @@ namespace Sched {
|
||||
}
|
||||
|
||||
int Spawn(const char* vfsPath, const char* args, bool startReady,
|
||||
const char* environment, uint32_t environmentLength) {
|
||||
const char* environment, uint32_t environmentLength,
|
||||
const montauk::abi::SpawnCapabilities* capabilities,
|
||||
const char* userOverride) {
|
||||
schedLock.Acquire();
|
||||
|
||||
int slot = -1;
|
||||
@@ -545,9 +553,37 @@ namespace Sched {
|
||||
proc.environmentLength = 1;
|
||||
}
|
||||
|
||||
// Inherit user string from parent, or default to "system" if no parent
|
||||
// Capabilities are kernel-owned and never inferred from the user name.
|
||||
// A normal userspace spawn receives no privileged authority; callers
|
||||
// must use SYS_SPAWN_CAPS for an explicit, kernel-validated delegation.
|
||||
if (parentPrimarySlot >= 0) {
|
||||
if (capabilities != nullptr) {
|
||||
proc.permittedCaps = capabilities->permitted;
|
||||
proc.effectiveCaps = capabilities->effective;
|
||||
proc.delegableCaps = capabilities->delegable;
|
||||
} else {
|
||||
proc.permittedCaps = 0;
|
||||
proc.effectiveCaps = 0;
|
||||
proc.delegableCaps = 0;
|
||||
}
|
||||
} else {
|
||||
// The kernel-created init process is the root of the capability
|
||||
// delegation tree. No userspace pathname or PID receives this
|
||||
// treatment; it is reached only with no current parent process.
|
||||
proc.permittedCaps = montauk::abi::CAP_ALL;
|
||||
proc.effectiveCaps = montauk::abi::CAP_ALL;
|
||||
proc.delegableCaps = montauk::abi::CAP_ALL;
|
||||
}
|
||||
|
||||
// Inherit user string from parent, or default to "system" if no parent.
|
||||
// An explicit override is accepted only through the validated
|
||||
// SYS_SPAWN_CAPS path.
|
||||
{
|
||||
if (parentSlot >= 0) {
|
||||
if (userOverride != nullptr) {
|
||||
int i = 0;
|
||||
for (; i < 31 && userOverride[i]; i++) proc.user[i] = userOverride[i];
|
||||
proc.user[i] = '\0';
|
||||
} else if (parentSlot >= 0) {
|
||||
int i = 0;
|
||||
for (; i < 31 && processTable[parentSlot].user[i]; i++)
|
||||
proc.user[i] = processTable[parentSlot].user[i];
|
||||
@@ -560,6 +596,13 @@ namespace Sched {
|
||||
}
|
||||
}
|
||||
|
||||
// Process sessions are explicit groups used by supervisors such as
|
||||
// login.elf. A session leader opts in with CreateSession(); all of its
|
||||
// subsequently spawned descendants inherit the same stable ID.
|
||||
proc.sessionId = (parentPrimarySlot >= 0)
|
||||
? processTable[parentPrimarySlot].sessionId
|
||||
: -1;
|
||||
|
||||
{
|
||||
if (parentSlot >= 0 && processTable[parentSlot].cwd[0]) {
|
||||
int i = 0;
|
||||
@@ -675,7 +718,7 @@ namespace Sched {
|
||||
mappedPages++;
|
||||
}
|
||||
if (!ok) {
|
||||
Ipc::UnmapAndFreeUserRange(sharedPml4, base, mappedPages);
|
||||
Memory::UnmapAndFreeUserRange(sharedPml4, base, mappedPages);
|
||||
ReleaseUserHeapRange(primarySlot_, base, numPages * 0x1000ULL);
|
||||
Kt::KernelLogStream(Kt::ERROR, "Sched")
|
||||
<< "Thread TLS allocation failed";
|
||||
@@ -702,7 +745,7 @@ namespace Sched {
|
||||
void* stackMem = Memory::g_pfa->ReallocConsecutive(nullptr, StackPages);
|
||||
if (stackMem == nullptr) {
|
||||
if (threadTlsPages != 0) {
|
||||
Ipc::UnmapAndFreeUserRange(sharedPml4, threadTlsBase, threadTlsPages);
|
||||
Memory::UnmapAndFreeUserRange(sharedPml4, threadTlsBase, threadTlsPages);
|
||||
ReleaseUserHeapRange(primarySlot_, threadTlsBase,
|
||||
threadTlsPages * 0x1000ULL);
|
||||
}
|
||||
@@ -728,7 +771,7 @@ namespace Sched {
|
||||
schedLock.Release();
|
||||
Memory::g_pfa->Free(stackMem, StackPages);
|
||||
if (threadTlsPages != 0) {
|
||||
Ipc::UnmapAndFreeUserRange(sharedPml4, threadTlsBase, threadTlsPages);
|
||||
Memory::UnmapAndFreeUserRange(sharedPml4, threadTlsBase, threadTlsPages);
|
||||
ReleaseUserHeapRange(primarySlot_, threadTlsBase,
|
||||
threadTlsPages * 0x1000ULL);
|
||||
}
|
||||
@@ -876,7 +919,7 @@ namespace Sched {
|
||||
uint64_t base = thr.fsBase - blockSize;
|
||||
uint64_t pages = (blockSize + 16 + 0xFFF) / 0x1000;
|
||||
thr.fsBase = 0;
|
||||
Ipc::UnmapAndFreeUserRange(primary.pml4Phys, base, pages);
|
||||
Memory::UnmapAndFreeUserRange(primary.pml4Phys, base, pages);
|
||||
ReleaseUserHeapRange(primarySlot_, base, pages * 0x1000ULL);
|
||||
}
|
||||
|
||||
@@ -1276,6 +1319,15 @@ namespace Sched {
|
||||
uint8_t* oldFpu = (oldSlot >= 0) ? processTable[oldSlot].fpuState : nullptr;
|
||||
uint8_t* newFpu = processTable[next].fpuState;
|
||||
|
||||
if (oldSlot < 0) {
|
||||
// AP idle loops mask their local periodic timer. Rearm it before
|
||||
// dispatching user work so preemption resumes with the process.
|
||||
// Also pick up a thermal-governor policy epoch that may have
|
||||
// changed while this CPU remained asleep without timer ticks.
|
||||
Hal::CpuPower::ApplyPolicyIfChanged();
|
||||
Timekeeping::ApicTimerLeaveApIdle();
|
||||
}
|
||||
|
||||
LoadUserFsBase(cpu, processTable[next].fsBase);
|
||||
|
||||
// DO NOT release schedLock here! It is held across the context
|
||||
@@ -1400,6 +1452,11 @@ namespace Sched {
|
||||
return &processTable[primary];
|
||||
}
|
||||
|
||||
bool HasCapability(uint64_t capability) {
|
||||
Process* proc = GetCurrentProcessPtr();
|
||||
return proc != nullptr && (proc->effectiveCaps & capability) == capability;
|
||||
}
|
||||
|
||||
Process* GetCurrentThreadPtr() {
|
||||
auto* cpu = Smp::GetCurrentCpuData();
|
||||
int slot = cpu->currentSlot;
|
||||
@@ -1590,6 +1647,11 @@ namespace Sched {
|
||||
// never stranded on the invisible buffer (no-op for non-owners).
|
||||
Drivers::Graphics::IntelGPU::OnProcessExit(exitingPid);
|
||||
|
||||
// USB interface claims are process-owned capabilities. Closing them
|
||||
// here stops DMA streaming and releases exclusivity even when an app
|
||||
// exits without calling usb_close().
|
||||
Drivers::USB::UserUsb::ReleaseAllForPid(exitingPid);
|
||||
|
||||
// Release process-scoped IPC handles/mappings before tearing down the address space.
|
||||
Ipc::CleanupProcessSlot(slot, exitingPid, proc.pml4Phys);
|
||||
montauk::abi::CleanupHeapForSlot(slot, proc.pml4Phys);
|
||||
@@ -1747,6 +1809,68 @@ namespace Sched {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CreateSession() {
|
||||
schedLock.Acquire();
|
||||
Process* proc = GetCurrentProcessPtr();
|
||||
if (proc == nullptr) {
|
||||
schedLock.Release();
|
||||
return -1;
|
||||
}
|
||||
proc->sessionId = proc->pid;
|
||||
int sessionId = proc->sessionId;
|
||||
schedLock.Release();
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
int KillSession(int sessionId) {
|
||||
if (sessionId < 0) return -1;
|
||||
|
||||
int callerPid = GetCurrentPid();
|
||||
int killed = 0;
|
||||
|
||||
// Mark the complete group in one scheduler-lock transaction. New
|
||||
// children inherit sessionId, so a supervisor can repeat this call
|
||||
// until zero is returned to close the small spawn/exit race cleanly.
|
||||
schedLock.Acquire();
|
||||
for (int i = 0; i < MaxProcesses; i++) {
|
||||
Process& primary = processTable[i];
|
||||
if (primary.primarySlot != i || primary.pid == callerPid ||
|
||||
primary.sessionId != sessionId) {
|
||||
continue;
|
||||
}
|
||||
auto state = primary.state;
|
||||
if (state != ProcessState::Ready && state != ProcessState::Running &&
|
||||
state != ProcessState::Blocked) {
|
||||
continue;
|
||||
}
|
||||
|
||||
primary.exitCode = 256 + 9; /* killed (SIGKILL) */
|
||||
primary.killPending = true;
|
||||
if (primary.state == ProcessState::Blocked) {
|
||||
primary.state = ProcessState::Ready;
|
||||
readyCount++;
|
||||
primary.waitingForPid = -1;
|
||||
primary.waitingOnObject = nullptr;
|
||||
primary.sleepUntilTick = 0;
|
||||
}
|
||||
|
||||
for (int j = 0; j < MaxProcesses; j++) {
|
||||
if (j == i || processTable[j].primarySlot != i) continue;
|
||||
if (processTable[j].state == ProcessState::Running) {
|
||||
processTable[j].killPending = true;
|
||||
}
|
||||
}
|
||||
killed++;
|
||||
}
|
||||
schedLock.Release();
|
||||
|
||||
if (killed > 0) {
|
||||
KickOneIdleCpu(Smp::GetCurrentCpuData()
|
||||
? Smp::GetCurrentCpuData()->cpuIndex : -1);
|
||||
}
|
||||
return killed;
|
||||
}
|
||||
|
||||
int LookupExitCode(int pid) {
|
||||
schedLock.Acquire();
|
||||
int code = 0;
|
||||
|
||||
@@ -80,12 +80,16 @@ namespace Sched {
|
||||
uint64_t pml4Phys; // Physical address of per-process PML4
|
||||
uint64_t kernelStackTop; // Top of kernel stack (for TSS RSP0 / SYSCALL)
|
||||
uint64_t userStackTop; // User-space stack top
|
||||
uint64_t heapNext; // Simple bump allocator for user heap
|
||||
uint64_t heapNext; // High-water mark of the user-heap address space
|
||||
uint32_t readdirCursor; // Next SYS_READDIR scratch slot
|
||||
char args[4096]; // Command-line arguments (set by parent via Spawn)
|
||||
char environment[EnvironmentBytes]; // NUL-separated NAME=VALUE entries
|
||||
uint32_t environmentLength;
|
||||
char user[32]; // Owner user name (inherited from parent on spawn)
|
||||
uint64_t permittedCaps; // Authority owned by this process
|
||||
uint64_t effectiveCaps; // Authority currently usable by syscalls
|
||||
uint64_t delegableCaps; // Authority this process may pass to children
|
||||
int sessionId; // Process-session leader PID (inherited on spawn)
|
||||
char cwd[256]; // Absolute current working directory
|
||||
|
||||
// Thread-local storage. fsBase is loaded into IA32_FS_BASE when
|
||||
@@ -143,7 +147,9 @@ namespace Sched {
|
||||
|
||||
void Initialize();
|
||||
int Spawn(const char* vfsPath, const char* args = nullptr, bool startReady = true,
|
||||
const char* environment = nullptr, uint32_t environmentLength = 0);
|
||||
const char* environment = nullptr, uint32_t environmentLength = 0,
|
||||
const montauk::abi::SpawnCapabilities* capabilities = nullptr,
|
||||
const char* userOverride = nullptr);
|
||||
int StartProcess(int pid);
|
||||
void Schedule();
|
||||
|
||||
@@ -171,6 +177,10 @@ namespace Sched {
|
||||
// Always returns the slot that owns per-process state -- never a sibling thread.
|
||||
Process* GetCurrentProcessPtr();
|
||||
|
||||
// Capability checks always consult kernel-owned process metadata. User
|
||||
// names are deliberately excluded from authorization.
|
||||
bool HasCapability(uint64_t capability);
|
||||
|
||||
// Get a pointer to the currently running thread's slot (may be a sibling).
|
||||
Process* GetCurrentThreadPtr();
|
||||
|
||||
@@ -252,6 +262,12 @@ namespace Sched {
|
||||
// Returns 0 on success, -1 on failure.
|
||||
int KillProcess(int pid);
|
||||
|
||||
// Start a new process session for the caller, or terminate every process
|
||||
// belonging to a session. KillSession returns the number of live members
|
||||
// it signalled; callers can repeat until it returns zero.
|
||||
int CreateSession();
|
||||
int KillSession(int sessionId);
|
||||
|
||||
// Find a process by PID (returns nullptr if not found or not alive)
|
||||
Process* GetProcessByPid(int pid);
|
||||
|
||||
@@ -266,8 +282,9 @@ namespace Sched {
|
||||
|
||||
// Per-process allocated page count (tracked by Heap syscalls, separate from Process struct)
|
||||
inline uint64_t g_allocatedPages[MaxProcesses] = {};
|
||||
// One bit per page in the bounded userspace heap. Unlike heapNext, this
|
||||
// makes virtual ranges reusable after unmap and failed reservations.
|
||||
// One bit per page in the bounded userspace heap. This is the authoritative
|
||||
// allocation state; unlike heapNext, it makes virtual ranges reusable after
|
||||
// unmap and failed reservations.
|
||||
inline uint64_t g_userHeapPageMap[MaxProcesses][UserHeapBitmapWords] = {};
|
||||
|
||||
}
|
||||
|
||||
@@ -205,12 +205,10 @@ namespace Kt {
|
||||
|
||||
void Putchar(char c) {
|
||||
if (g_kernelLogDepth > 0) {
|
||||
if (c == '\n') {
|
||||
RingBufferAppend('\r');
|
||||
RingBufferAppend('\n');
|
||||
} else {
|
||||
RingBufferAppend(c);
|
||||
}
|
||||
// Keep the log as canonical text. CRLF is only needed by the
|
||||
// framebuffer terminal below; storing it in the ring makes file
|
||||
// consumers treat one logical newline as two line breaks.
|
||||
RingBufferAppend(c);
|
||||
|
||||
if (g_suppressKernelLog) {
|
||||
return;
|
||||
@@ -250,7 +248,7 @@ namespace Kt {
|
||||
g_suppressKernelLog = false;
|
||||
}
|
||||
|
||||
int64_t ReadKernelLog(char* buf, uint64_t size) {
|
||||
int64_t ReadKernelLogBuffer(char* buf, uint64_t size) {
|
||||
if (buf == nullptr || size == 0) return 0;
|
||||
|
||||
uint64_t toRead = g_klogCount;
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace Kt
|
||||
// intentionally lock-free: a panic can occur while another CPU owns the
|
||||
// terminal mutex, and the system is about to halt.
|
||||
void EnablePanicOutput();
|
||||
int64_t ReadKernelLog(char* buf, uint64_t size);
|
||||
int64_t ReadKernelLogBuffer(char* buf, uint64_t size);
|
||||
|
||||
class KernelLogStream {
|
||||
KernelOutStream localStream{};
|
||||
@@ -151,7 +151,7 @@ public:
|
||||
g_kernelLogDepth++;
|
||||
componentName = desiredComponentName;
|
||||
|
||||
localStream << componentName << ": " << "[" << LogLevelToStringWithColor(level) << "] ";
|
||||
localStream << "[kernel/" << componentName << "] " << LogLevelToStringWithColor(level) << ": ";
|
||||
}
|
||||
|
||||
~KernelLogStream() {
|
||||
@@ -168,6 +168,36 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class UserspaceLogStream {
|
||||
KernelOutStream localStream{};
|
||||
const char* imageName = "";
|
||||
const char* username = "";
|
||||
|
||||
public:
|
||||
|
||||
UserspaceLogStream(const char* desiredImageName, const char* desiredUsername) {
|
||||
g_termLock.Acquire();
|
||||
g_kernelLogDepth++;
|
||||
imageName = desiredImageName;
|
||||
username = desiredUsername;
|
||||
|
||||
localStream << "[user " << username << "@" << imageName << "] ";
|
||||
}
|
||||
|
||||
~UserspaceLogStream() {
|
||||
localStream << newline;
|
||||
g_kernelLogDepth--;
|
||||
g_termLock.Release();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
UserspaceLogStream &operator<<(T item) {
|
||||
localStream << item;
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
extern Kt::KernelOutStream kout;
|
||||
|
||||
@@ -41,8 +41,8 @@ namespace Timekeeping {
|
||||
static constexpr uint32_t DIVIDE_BY_16 = 0x03;
|
||||
|
||||
// The BSP keeps a 1 ms tick for timekeeping and sleep deadlines.
|
||||
// APs use a coarser 10 ms scheduler tick to avoid waking idle cores
|
||||
// 1000 times per second with no useful work to do.
|
||||
// Running APs use a 10 ms scheduler tick. Idle APs mask it entirely and
|
||||
// rely on reschedule IPIs, avoiding periodic package wakeups.
|
||||
static constexpr uint32_t BSP_TICK_INTERVAL_MS = 1;
|
||||
static constexpr uint32_t BSP_TIMER_HZ = 1000 / BSP_TICK_INTERVAL_MS;
|
||||
static constexpr uint32_t AP_TICK_INTERVAL_MS = 10;
|
||||
@@ -220,8 +220,28 @@ namespace Timekeeping {
|
||||
// identical. This avoids PIT contention during AP boot.
|
||||
if (g_ticksPerMs == 0) return;
|
||||
|
||||
// Configure a coarser periodic timer on APs. The scheduler still gets
|
||||
// a 10 ms time slice, but idle APs stop taking 1000 timer interrupts/sec.
|
||||
// Configure the 10 ms scheduler timer for running APs. Their idle loop
|
||||
// masks it after initialization and rearms it when dispatching work.
|
||||
ProgramTimer(true, AP_TICK_INTERVAL_MS);
|
||||
}
|
||||
|
||||
void ApicTimerEnterApIdle() {
|
||||
auto* cpu = Smp::GetCurrentCpuData();
|
||||
if (cpu == nullptr || cpu->cpuIndex == 0 || g_ticksPerMs == 0) return;
|
||||
|
||||
uint32_t lvt = Hal::LocalApic::ReadRegister(Hal::LocalApic::REG_TIMER_LVT);
|
||||
if ((lvt & LVT_MASKED) == 0) {
|
||||
Hal::LocalApic::WriteRegister(Hal::LocalApic::REG_TIMER_LVT,
|
||||
lvt | LVT_MASKED);
|
||||
}
|
||||
}
|
||||
|
||||
void ApicTimerLeaveApIdle() {
|
||||
auto* cpu = Smp::GetCurrentCpuData();
|
||||
if (cpu == nullptr || cpu->cpuIndex == 0 || g_ticksPerMs == 0) return;
|
||||
|
||||
// Reprogram the initial count as well as unmasking. A deep idle state
|
||||
// may have stopped the local timer at an arbitrary point in its period.
|
||||
ProgramTimer(true, AP_TICK_INTERVAL_MS);
|
||||
}
|
||||
|
||||
@@ -239,19 +259,24 @@ namespace Timekeeping {
|
||||
bool wasReserved = cpu->reservedForKernelWork;
|
||||
cpu->reservedForKernelWork = true;
|
||||
|
||||
// Drain USB hot-plug deferred work from any idle core, not just the BSP.
|
||||
if (Drivers::USB::Xhci::HasDeferredWork()) {
|
||||
// Drain USB work only when the MSI path has actually queued something.
|
||||
// Bluetooth shares this controller, so service its protocol queues in
|
||||
// the same pass after xHCI has delivered completion callbacks.
|
||||
bool usbWork = Drivers::USB::Xhci::HasDeferredWork();
|
||||
if (usbWork) {
|
||||
Drivers::USB::Xhci::ProcessDeferredWork();
|
||||
}
|
||||
|
||||
// NIC hard IRQs only acknowledge/mask and queue RX work. Dispatching
|
||||
// Ethernet/TCP/UDP here keeps process-context IPC mutexes out of IRQs.
|
||||
Drivers::Net::E1000::ProcessDeferredWork();
|
||||
Drivers::Net::E1000E::ProcessDeferredWork();
|
||||
if (Drivers::Net::E1000::HasDeferredWork())
|
||||
Drivers::Net::E1000::ProcessDeferredWork();
|
||||
if (Drivers::Net::E1000E::HasDeferredWork())
|
||||
Drivers::Net::E1000E::ProcessDeferredWork();
|
||||
|
||||
// HDA completion IRQs only acknowledge/mask. Resampling and DMA-ring
|
||||
// refill are far too expensive for hard interrupt context.
|
||||
if (cpu->cpuIndex == 0) {
|
||||
if (cpu->cpuIndex == 0 && Drivers::Audio::IntelHda::HasDeferredWork()) {
|
||||
Drivers::Audio::IntelHda::ProcessDeferredWork();
|
||||
}
|
||||
|
||||
@@ -260,7 +285,10 @@ namespace Timekeeping {
|
||||
// seconds and used to stall kmain before the first process spawned.
|
||||
// Cheap no-op unless an adapter is waiting; self-claiming, and safe
|
||||
// to preempt (the scheduler saves/resumes the idle context).
|
||||
Drivers::USB::Bluetooth::ServiceDeferredInit();
|
||||
bool bluetoothWork = usbWork ||
|
||||
Drivers::USB::Bluetooth::HasDeferredWork();
|
||||
if (bluetoothWork)
|
||||
Drivers::USB::Bluetooth::ServiceDeferredInit();
|
||||
|
||||
// Service Bluetooth inbound traffic (the headset's SDP/AVRCP queries,
|
||||
// AVDTP commands, ACL flow-control credits) whenever a core idles.
|
||||
@@ -269,13 +297,16 @@ namespace Timekeeping {
|
||||
// writes got silence (observed: Bose re-dialing SDP during playback,
|
||||
// queries never answered). Self-serializing and a cheap no-op when
|
||||
// the adapter is down.
|
||||
Drivers::USB::Bluetooth::ServiceEvents();
|
||||
if (bluetoothWork)
|
||||
Drivers::USB::Bluetooth::ServiceEvents();
|
||||
|
||||
// Wi-Fi mirrors the Bluetooth split: the firmware load needs the
|
||||
// ramdisk, and the RX/notification ring must be drained outside hard
|
||||
// interrupt context (the MSI handler only latches a flag).
|
||||
Drivers::Net::Wifi::ServiceDeferredInit();
|
||||
Drivers::Net::Wifi::ServiceEvents();
|
||||
if (Drivers::Net::Wifi::HasDeferredWork()) {
|
||||
Drivers::Net::Wifi::ServiceDeferredInit();
|
||||
Drivers::Net::Wifi::ServiceEvents();
|
||||
}
|
||||
|
||||
// Thermal policy records transitions during BSP maintenance; print
|
||||
// them from this explicitly non-interrupt idle path.
|
||||
|
||||
@@ -11,9 +11,14 @@ namespace Timekeeping {
|
||||
// Initialize the APIC timer: calibrate against PIT, start periodic interrupts
|
||||
void ApicTimerInitialize();
|
||||
|
||||
// Initialize the APIC timer on an AP (calibrate + start, no IRQ handler registration)
|
||||
// Initialize the scheduler timer on an AP using the BSP calibration.
|
||||
void ApicTimerInitializeAP();
|
||||
|
||||
// Idle APs are woken for runnable work by the reschedule IPI, so their
|
||||
// periodic scheduler timer can remain masked until a process is dispatched.
|
||||
void ApicTimerEnterApIdle();
|
||||
void ApicTimerLeaveApIdle();
|
||||
|
||||
// Reinitialize the APIC timer after S3 resume using the previously
|
||||
// calibrated tick rate. Skips PIT calibration and IRQ registration
|
||||
// (both survive in RAM). Only reprograms the timer hardware registers.
|
||||
|
||||
@@ -70,14 +70,19 @@ static Timekeeping::DateTime EpochToDate(int64_t epoch) {
|
||||
void Timekeeping::Init(uint16_t Year, uint8_t Month, uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t Second) {
|
||||
g_bootEpoch = DateToEpoch(Year, Month, Day, Hour, Minute, Second);
|
||||
|
||||
int offH = g_tzOffsetMinutes / 60;
|
||||
int offM = g_tzOffsetMinutes % 60;
|
||||
if (offM < 0) offM = -offM;
|
||||
int64_t absoluteOffset = g_tzOffsetMinutes;
|
||||
char sign = '+';
|
||||
if (absoluteOffset < 0) {
|
||||
sign = '-';
|
||||
absoluteOffset = -absoluteOffset;
|
||||
}
|
||||
|
||||
Kt::KernelLogStream(INFO, "Timekeeping Service") << "Time zone: UTC"
|
||||
<< (offH >= 0 ? "+" : "") << offH
|
||||
<< (offM ? ":" : "") << (offM >= 10 ? "" : (offM ? "0" : ""))
|
||||
<< (offM ? offM : 0);
|
||||
int offH = (int)(absoluteOffset / 60);
|
||||
int offM = (int)(absoluteOffset % 60);
|
||||
auto log = Kt::KernelLogStream(INFO, "Timekeeping Service");
|
||||
log << "Time zone: UTC" << sign << offH;
|
||||
if (offM != 0)
|
||||
log << ":" << (offM < 10 ? "0" : "") << offM;
|
||||
}
|
||||
|
||||
int64_t Timekeeping::GetUnixTimestamp() {
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Montauk API">
|
||||
<title>Montauk API - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">Application Programming Manual</a></li>
|
||||
<li><a href="api.html" class="current">Montauk API</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Montauk API</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Application management">
|
||||
<title>Application management - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">Application Programming Manual</a></li>
|
||||
<li><a href="apps.html" class="current">Application management</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Application management</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 8.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
@@ -1,236 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS userspace configuration and TOML API">
|
||||
<title>Configuration and TOML - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.75em; }
|
||||
h2 { margin: 1.25em 0 0.5em; }
|
||||
h3 { margin: 1em 0 0.35em; }
|
||||
.sidebar { width: 160px; flex-shrink: 0; }
|
||||
.sidebar ul { list-style: none; padding: 0; }
|
||||
.sidebar li { margin: 0.5em 0; }
|
||||
.sidebar a { color: #0066CC; text-decoration: none; font-weight: 600; }
|
||||
.sidebar a:hover { color: #004499; text-decoration: underline; }
|
||||
.sidebar .current { color: #004499; }
|
||||
.sidebar hr { border: none; border-top: 1px solid #999; margin: 0.75em 0; }
|
||||
.main { flex: 1; min-width: 0; }
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
pre {
|
||||
background: #f4f4f4;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0.75em;
|
||||
overflow-x: auto;
|
||||
}
|
||||
code { font-family: monospace; }
|
||||
@media (max-width: 700px) {
|
||||
body { flex-direction: column; gap: 1em; padding: 1em 0.75em; }
|
||||
.sidebar { width: auto; }
|
||||
.sidebar ul { display: flex; flex-wrap: wrap; gap: 0 1em; }
|
||||
.sidebar hr { width: 100%; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">Application Programming Manual</a></li>
|
||||
<li><a href="config.html" class="current">Configuration and TOML</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Configuration and TOML</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>MontaukOS provides a small header-only C++ API for reading and writing
|
||||
TOML configuration files from userspace. Include
|
||||
<code><montauk/config.h></code> for file-backed configuration and
|
||||
<code><montauk/toml.h></code> for the in-memory TOML document model.</p>
|
||||
|
||||
<h2>Configuration locations</h2>
|
||||
|
||||
<p>System configuration is stored in <code>0:/config</code>. The API accepts a
|
||||
name without the <code>.toml</code> extension:</p>
|
||||
|
||||
<pre><code>0:/config/desktop.toml
|
||||
0:/config/network.toml</code></pre>
|
||||
|
||||
<p>Per-user configuration is stored below the user directory:</p>
|
||||
|
||||
<pre><code>0:/users/<username>/config/<name>.toml</code></pre>
|
||||
|
||||
<h2>Loading and saving</h2>
|
||||
|
||||
<pre><code>#include <montauk/config.h>
|
||||
|
||||
auto doc = montauk::config::load("desktop");
|
||||
const char* theme = doc.get_string("appearance.theme", "light");
|
||||
|
||||
montauk::config::set_string(&doc, "appearance.theme", "dark");
|
||||
int result = montauk::config::save("desktop", &doc);
|
||||
|
||||
doc.destroy();</code></pre>
|
||||
|
||||
<p><code>load()</code> returns an initialized empty document if the file does
|
||||
not exist. <code>save()</code> creates the configuration directory and returns
|
||||
<code>0</code> on success or a negative value on error. Saving rewrites the
|
||||
whole file.</p>
|
||||
|
||||
<h3>System configuration API</h3>
|
||||
|
||||
<pre><code>toml::Doc config::load(const char* name);
|
||||
int config::save(const char* name, toml::Doc* doc);
|
||||
int config::remove(const char* name);</code></pre>
|
||||
|
||||
<h3>Per-user configuration API</h3>
|
||||
|
||||
<pre><code>toml::Doc config::load_user(const char* username, const char* name);
|
||||
int config::save_user(const char* username,
|
||||
const char* name,
|
||||
toml::Doc* doc);</code></pre>
|
||||
|
||||
<p>For example:</p>
|
||||
|
||||
<pre><code>auto doc = montauk::config::load_user("alice", "desktop");
|
||||
bool clock24 = doc.get_bool("display.clock_24h", false);
|
||||
|
||||
montauk::config::set_bool(&doc, "display.clock_24h", true);
|
||||
montauk::config::save_user("alice", "desktop", &doc);
|
||||
doc.destroy();</code></pre>
|
||||
|
||||
<h2>Reading values</h2>
|
||||
|
||||
<p>Keys may use dotted paths corresponding to TOML tables:</p>
|
||||
|
||||
<pre><code>[server]
|
||||
host = "pool.ntp.org"
|
||||
port = 123
|
||||
enabled = true</code></pre>
|
||||
|
||||
<pre><code>const char* host = doc.get_string("server.host", "localhost");
|
||||
int64_t port = doc.get_int("server.port", 80);
|
||||
bool enabled = doc.get_bool("server.enabled", false);</code></pre>
|
||||
|
||||
<p>Typed getters return their default when the key is absent or has another
|
||||
type. The available value types are:</p>
|
||||
|
||||
<pre><code>toml::Type::String
|
||||
toml::Type::Int
|
||||
toml::Type::Bool
|
||||
toml::Type::Array
|
||||
toml::Type::Table</code></pre>
|
||||
|
||||
<p>Arrays and tables are returned as <code>toml::Value*</code>. Their children
|
||||
are available through <code>value->array.items</code> and
|
||||
<code>value->array.count</code>:</p>
|
||||
|
||||
<pre><code>auto* names = doc.get_array("server.names");
|
||||
if (names) {
|
||||
for (int i = 0; i < names->array.count; ++i) {
|
||||
auto* item = names->array.items[i];
|
||||
if (item->type == montauk::toml::Type::String)
|
||||
montauk::print(item->str);
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<h2>Modifying documents</h2>
|
||||
|
||||
<pre><code>void config::set_string(toml::Doc* doc,
|
||||
const char* key, const char* value);
|
||||
void config::set_int(toml::Doc* doc,
|
||||
const char* key, int64_t value);
|
||||
void config::set_bool(toml::Doc* doc,
|
||||
const char* key, bool value);
|
||||
bool config::unset(toml::Doc* doc, const char* key);</code></pre>
|
||||
|
||||
<p>The setters overwrite an existing value or append a new one. The
|
||||
<code>unset()</code> return value is <code>true</code> when a matching key was
|
||||
removed.</p>
|
||||
|
||||
<h2>Parsing and serialization</h2>
|
||||
|
||||
<p>Use <code>toml::parse()</code> when TOML is already available in memory:</p>
|
||||
|
||||
<pre><code>const char* text =
|
||||
"[server]\n"
|
||||
"port = 8080\n"
|
||||
"enabled = true\n";
|
||||
|
||||
auto doc = montauk::toml::parse(text);
|
||||
int64_t port = doc.get_int("server.port");
|
||||
doc.destroy();</code></pre>
|
||||
|
||||
<p>A document can be serialized to newly allocated TOML text:</p>
|
||||
|
||||
<pre><code>char* text = montauk::config::serialize(&doc);
|
||||
// Use text...
|
||||
montauk::mfree(text);</code></pre>
|
||||
|
||||
<p>Serialization produces normalized TOML and does not preserve comments or
|
||||
the original formatting.</p>
|
||||
|
||||
<h2>Memory ownership</h2>
|
||||
|
||||
<p><code>toml::Doc</code> owns its parsed values and strings. Every document
|
||||
returned by <code>load()</code>, <code>load_user()</code>, or
|
||||
<code>toml::parse()</code> must eventually be released with
|
||||
<code>doc.destroy()</code>.</p>
|
||||
|
||||
<p>When constructing a document manually, initialize it before using the
|
||||
mutation helpers:</p>
|
||||
|
||||
<pre><code>montauk::toml::Doc doc;
|
||||
doc.init();
|
||||
montauk::config::set_bool(&doc, "enabled", true);
|
||||
montauk::config::save("example", &doc);
|
||||
doc.destroy();</code></pre>
|
||||
|
||||
<h2>Supported TOML features</h2>
|
||||
|
||||
<p>The userspace parser supports strings, literal and multiline strings,
|
||||
integers (including hexadecimal, octal, and binary forms), booleans, arrays,
|
||||
tables, inline tables, dotted keys, and comments.</p>
|
||||
|
||||
<p>There are no typed float or datetime accessors. Callers should also treat
|
||||
configuration names and usernames as safe path components, since they are
|
||||
used to construct filesystem paths.</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="index.html">Back to Application Programming Manual</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,291 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS dialogs library API">
|
||||
<title>Dialogs Library - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #999;
|
||||
padding: 0.35em 0.5em;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
th { background: #f0f0f0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.figure {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.figure img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
.figure p {
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
margin: 0.25em 0 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">Application Programming Manual</a></li>
|
||||
<li><a href="dialogs.html" class="current">Dialogs library</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Dialogs</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Overview</h2>
|
||||
<p>
|
||||
The dialogs library (at 0:/os/dialogs.lib) provides the following system dialogs across apps:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>File selection (Open)</li>
|
||||
<li>File save location selection (Save)</li>
|
||||
<li>Print setup/submission</li>
|
||||
<li>Message Box popups</li>
|
||||
</ul>
|
||||
|
||||
<h2>Message Box</h2>
|
||||
<p>
|
||||
<i>message_box</i> displays a popup window with text and buttons.
|
||||
</p>
|
||||
|
||||
<pre><code>gui::dialogs::MessageBoxResult message_box(
|
||||
const char* title,
|
||||
const char* message,
|
||||
gui::dialogs::MessageBoxButtons buttons = gui::dialogs::MESSAGE_BOX_OK,
|
||||
char* out_message = nullptr,
|
||||
int out_message_len = 0);</code></pre>
|
||||
|
||||
<h3>Button Sets</h3>
|
||||
<table>
|
||||
<tr><th>Value</th><th>Buttons</th></tr>
|
||||
<tr><td>MESSAGE_BOX_OK</td><td>OK</td></tr>
|
||||
<tr><td>MESSAGE_BOX_OK_CANCEL</td><td>OK, Cancel</td></tr>
|
||||
<tr><td>MESSAGE_BOX_YES_NO</td><td>Yes, No</td></tr>
|
||||
<tr><td>MESSAGE_BOX_YES_NO_CANCEL</td><td>Yes, No, Cancel</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>Results</h3>
|
||||
<table>
|
||||
<tr><th>Value</th><th>Meaning</th></tr>
|
||||
<tr><td>MESSAGE_BOX_RESULT_OK</td><td>The user selected OK.</td></tr>
|
||||
<tr><td>MESSAGE_BOX_RESULT_CANCEL</td><td>The user selected Cancel or closed a cancelable dialog.</td></tr>
|
||||
<tr><td>MESSAGE_BOX_RESULT_YES</td><td>The user selected Yes.</td></tr>
|
||||
<tr><td>MESSAGE_BOX_RESULT_NO</td><td>The user selected No, or closed a Yes/No dialog.</td></tr>
|
||||
<tr><td>MESSAGE_BOX_RESULT_NONE</td><td>The dialog could not be loaded or invoked.</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>Example</h3>
|
||||
<pre><code>#include <gui/dialogs.hpp>
|
||||
|
||||
void show_confirm() {
|
||||
auto result = gui::dialogs::message_box(
|
||||
"Close Document",
|
||||
"Discard unsaved changes?",
|
||||
gui::dialogs::MESSAGE_BOX_YES_NO_CANCEL);
|
||||
|
||||
if (result == gui::dialogs::MESSAGE_BOX_RESULT_YES) {
|
||||
/* discard and close */
|
||||
}
|
||||
}</code></pre>
|
||||
|
||||
<div class="figure">
|
||||
<img src="assets/discard_dialog.png" width="337" height="181" alt="Message box asking whether to discard unsaved changes">
|
||||
<p>Example <i>MESSAGE_BOX_YES_NO_CANCEL</i> dialog.</p>
|
||||
</div>
|
||||
|
||||
<h2>File Dialogs</h2>
|
||||
<p>
|
||||
File dialog helpers allow applications to use graphical file selection views (similar to the Files app) to select paths for Open/Save operations.
|
||||
</p>
|
||||
|
||||
<pre><code>bool open_file(
|
||||
const char* title,
|
||||
const char* initial_path,
|
||||
char* out_path,
|
||||
int out_path_len,
|
||||
char* out_message = nullptr,
|
||||
int out_message_len = 0);
|
||||
|
||||
bool save_file(
|
||||
const char* title,
|
||||
const char* initial_path,
|
||||
const char* suggested_name,
|
||||
char* out_path,
|
||||
int out_path_len,
|
||||
char* out_message = nullptr,
|
||||
int out_message_len = 0);</code></pre>
|
||||
|
||||
<p>
|
||||
Use <code>initial_path</code> to select the starting directory or current file
|
||||
context. <code>save_file</code> also accepts a <code>suggested_name</code> for
|
||||
the filename field.
|
||||
</p>
|
||||
|
||||
<h3>Open Example</h3>
|
||||
<pre><code>char path[256];
|
||||
char message[160];
|
||||
|
||||
if (gui::dialogs::open_file("Open File", "", path, sizeof(path),
|
||||
message, sizeof(message))) {
|
||||
/* open path */
|
||||
}</code></pre>
|
||||
|
||||
<h3>Save Example</h3>
|
||||
<pre><code>char path[256];
|
||||
|
||||
if (gui::dialogs::save_file("Save File", "", "untitled.txt",
|
||||
path, sizeof(path))) {
|
||||
/* write path */
|
||||
}</code></pre>
|
||||
|
||||
<div class="figure">
|
||||
<img src="assets/save_dialog.png" width="749" height="581" alt="Save file dialog showing folders and a filename field">
|
||||
<p>Save-file dialog as used by the MontaukOS Word Processor app.</p>
|
||||
</div>
|
||||
|
||||
<h2>Print Dialogs</h2>
|
||||
<p>
|
||||
Print dialog helpers allow applications to expose printer configuration to the user, and submit a file for printing.
|
||||
</p>
|
||||
|
||||
<pre><code>bool configure_print(
|
||||
const char* title,
|
||||
const char* initial_printer_uri,
|
||||
const char* job_name,
|
||||
char* out_printer_uri,
|
||||
int out_printer_uri_len,
|
||||
char* out_printer_name,
|
||||
int out_printer_name_len,
|
||||
uint32_t* out_copies = nullptr,
|
||||
char* out_message = nullptr,
|
||||
int out_message_len = 0);
|
||||
|
||||
bool print_file(
|
||||
const char* title,
|
||||
const char* source_path,
|
||||
const char* job_name,
|
||||
char* out_job_id,
|
||||
int out_job_id_len,
|
||||
char* out_message = nullptr,
|
||||
int out_message_len = 0);</code></pre>
|
||||
|
||||
|
||||
<h2>Include</h2>
|
||||
<pre><code>#include <gui/dialogs.hpp></code></pre><br>
|
||||
|
||||
<hr>
|
||||
<p class="center">Copyright © 2026 Montauk Operating System Project. All rights reserved.<br><br>Page last revised 26 May 2026.</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,174 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS Application Programming Manual">
|
||||
<title>Application Programming Manual - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Application Programming Manual</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Application Programming Manual</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<h2>Pages</h2>
|
||||
<ul class="doc-list">
|
||||
<li>
|
||||
<a href="toolchains.html">Toolchains</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="api.html">Montauk API</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="config.html">Configuration and TOML</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="libc.html">C library</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="mtk.html">Montauk GUI Toolkit (MTK)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="apps.html">Application management</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="sharedlibs.html">Shared libraries</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="porting.html">Application porting guide</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="dialogs.html">Dialogs library</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - C library">
|
||||
<title>C library - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">Application Programming Manual</a></li>
|
||||
<li><a href="libc.html" class="current">C library</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>C library</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Montauk GUI Toolkit (MTK)">
|
||||
<title>Montauk GUI Toolkit (MTK) - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">Application Programming Manual</a></li>
|
||||
<li><a href="mtk.html" class="current">Montauk GUI Toolkit (MTK)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Montauk GUI Toolkit (MTK)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Application porting guide">
|
||||
<title>Application porting guide - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">Application Programming Manual</a></li>
|
||||
<li><a href="porting.html" class="current">Application porting guide</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Application porting guide</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Shared libraries">
|
||||
<title>Shared libraries - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">Application Programming Manual</a></li>
|
||||
<li><a href="sharedlibs.html" class="current">Shared libraries</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Shared libraries</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Toolchains">
|
||||
<title>Toolchains - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">Application Programming Manual</a></li>
|
||||
<li><a href="toolchains.html" class="current">Toolchains</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Toolchains</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,156 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS Documentation">
|
||||
<title>Documentation - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../index.html">Home</a></li>
|
||||
<li><a href="../downloads.html">Downloads</a></li>
|
||||
<li><a href="index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Documentation</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>User Documentation</h2>
|
||||
<ul class="doc-list">
|
||||
<li>
|
||||
<a href="usersmanual/index.html">Tutorials</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="man/index.html">Man pages</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Developer Documentation</h2>
|
||||
<ul class="doc-list">
|
||||
<li>
|
||||
<a href="apm/index.html">Application Programming Manual</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="osdev/index.html">Operating System Development Manual</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Home</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,166 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: dhcp(1) - obtain network configuration via DHCP">
|
||||
<title>dhcp(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>dhcp(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
dhcp - obtain network configuration via DHCP
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
dhcp
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
The DHCP client automatically obtains an IP address, subnet mask,
|
||||
default gateway, and other network parameters from a DHCP server
|
||||
on the local network using the Dynamic Host Configuration Protocol
|
||||
(RFC 2131).
|
||||
|
||||
On success the network configuration is applied immediately via
|
||||
set_netcfg(). On failure the original configuration is restored.
|
||||
|
||||
The client is run automatically by the init system at boot, but
|
||||
may also be invoked manually from the shell.
|
||||
|
||||
<strong>PROTOCOL</strong>
|
||||
The client performs the standard four-message DHCP exchange:
|
||||
|
||||
1. DHCPDISCOVER Broadcast to 255.255.255.255:67
|
||||
2. DHCPOFFER Server offers an IP address
|
||||
3. DHCPREQUEST Client accepts the offered address
|
||||
4. DHCPACK Server confirms the lease
|
||||
|
||||
The BROADCAST flag (0x8000) is set so that server replies are
|
||||
sent to the broadcast address, since the client has no IP yet.
|
||||
|
||||
Each step has a 10-second timeout. If no response is received
|
||||
the client exits with an error and restores the previous config.
|
||||
|
||||
<strong>OUTPUT</strong>
|
||||
On success the client prints the assigned configuration:
|
||||
|
||||
IP Address, Subnet Mask, Gateway, DNS Server, Lease Time
|
||||
|
||||
<strong>OPTIONS</strong>
|
||||
The DHCP client requests the following options from the server:
|
||||
|
||||
1 Subnet Mask
|
||||
3 Router (default gateway)
|
||||
6 DNS Server
|
||||
51 Lease Time
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
ifconfig(1), shell(1), syscalls(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,169 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: edit(1) - text editor for MontaukOS">
|
||||
<title>edit(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>edit(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
edit - text editor for MontaukOS
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
edit [filename]
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
edit is an interactive text editor. When invoked with a filename,
|
||||
it opens the file for editing. If the file does not exist, a new
|
||||
empty buffer is created and will be saved to that path on write.
|
||||
|
||||
When invoked without arguments, edit opens an empty buffer. You
|
||||
will be prompted for a filename when saving.
|
||||
|
||||
<strong>KEYBOARD SHORTCUTS</strong>
|
||||
|
||||
<strong>Navigation</strong>
|
||||
Arrow Keys Move cursor up/down/left/right
|
||||
Home Move to start of line
|
||||
End Move to end of line
|
||||
Page Up Scroll up one page
|
||||
Page Down Scroll down one page
|
||||
|
||||
<strong>Editing</strong>
|
||||
Backspace Delete character before cursor
|
||||
Delete Delete character at cursor
|
||||
Enter Insert new line
|
||||
Tab Insert 4 spaces
|
||||
|
||||
<strong>Commands</strong>
|
||||
Ctrl+S Save file
|
||||
Ctrl+Q Quit (warns if unsaved changes)
|
||||
Ctrl+F Search for text
|
||||
Ctrl+G Find next occurrence
|
||||
|
||||
<strong>DISPLAY</strong>
|
||||
The top line shows the filename, a modified indicator [+],
|
||||
and the current cursor position (Ln, Col).
|
||||
|
||||
The bottom line shows keyboard shortcuts or status messages.
|
||||
|
||||
Line numbers are displayed in a gutter on the left side.
|
||||
Lines past the end of the file are marked with ~.
|
||||
|
||||
<strong>EXAMPLES</strong>
|
||||
edit intro.1 Edit a file
|
||||
edit Open a new empty buffer
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
cat(1), shell(1)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,180 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: fetch(1) - HTTP/HTTPS client for MontaukOS">
|
||||
<title>fetch(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>fetch(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
fetch - HTTP/HTTPS client for MontaukOS
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
fetch [-v] <url>
|
||||
fetch [-v] <host> <port> [path]
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
fetch performs an HTTP/1.0 GET request and prints the response
|
||||
body to the terminal. Supports both plain HTTP and HTTPS (TLS 1.2)
|
||||
connections. By default only the body is printed.
|
||||
|
||||
In URL mode, the scheme (http:// or https://) determines whether
|
||||
TLS is used. The port defaults to 80 for HTTP and 443 for HTTPS.
|
||||
|
||||
In legacy mode, the host and port are specified as separate
|
||||
arguments and the connection is always plain HTTP.
|
||||
|
||||
The host may be an IP address or a hostname. Hostnames are
|
||||
resolved via the configured DNS server.
|
||||
|
||||
If no path is given, "/" is used.
|
||||
|
||||
<strong>OPTIONS</strong>
|
||||
<strong>-v</strong>
|
||||
Verbose mode. Print connection info, trust anchor count, TLS
|
||||
handshake progress, and the HTTP status/size header before
|
||||
the body.
|
||||
|
||||
<strong>EXAMPLES</strong>
|
||||
fetch https://icanhazip.com
|
||||
Print your public IP address over HTTPS.
|
||||
|
||||
fetch http://icanhazip.com
|
||||
Same, but over plain HTTP.
|
||||
|
||||
fetch -v https://example.com
|
||||
Fetch a page with verbose output showing:
|
||||
Connecting to example.com:443 (HTTPS)...
|
||||
Loaded 128 trust anchors
|
||||
TLS handshake...
|
||||
TLS connection established
|
||||
GET /
|
||||
HTTP 200 OK (1256 bytes)
|
||||
|
||||
fetch 10.0.68.1 80 /
|
||||
Fetch from a local server by IP (legacy syntax).
|
||||
|
||||
<strong>TLS SUPPORT</strong>
|
||||
HTTPS connections use BearSSL for TLS 1.2. Server certificates
|
||||
are validated against the system CA bundle at
|
||||
0:/os/certs/ca-certificates.crt.
|
||||
|
||||
Entropy for the TLS handshake is provided by RDTSC-seeded
|
||||
random data via the SYS_GETRANDOM syscall.
|
||||
|
||||
<strong>KEYBOARD</strong>
|
||||
Ctrl+Q Abort the request
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
ping(1), nslookup(1), tcpconnect(1), shell(1), syscalls(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,220 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: file(2) - file I/O system calls">
|
||||
<title>file(2) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>file(2)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
open, read, getsize, close, readdir - file I/O system calls
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
<strong> int montauk::open(const char* path);</strong>
|
||||
<strong> int montauk::read(int handle, uint8_t* buf, uint64_t offset, uint64_t size);</strong>
|
||||
<strong> uint64_t montauk::getsize(int handle);</strong>
|
||||
<strong> void montauk::close(int handle);</strong>
|
||||
<strong> int montauk::readdir(const char* path, const char** names, int max);</strong>
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
MontaukOS provides a Virtual File System (VFS) with read/write
|
||||
support. Drive 0 is the boot ramdisk; additional drives may be
|
||||
mounted from GPT partitions backed by FAT32 or ext2 (see
|
||||
syscalls(2), STORAGE section). Files are accessed via paths in
|
||||
the format "<drive>:/<path>".
|
||||
|
||||
<strong>open</strong>
|
||||
Opens a file and returns a non-negative handle on success, or a
|
||||
negative value on error (file not found, no free handles).
|
||||
|
||||
int h = montauk::open("0:/os/hello.elf");
|
||||
|
||||
<strong>read</strong>
|
||||
Reads up to 'size' bytes starting at 'offset' into 'buf'.
|
||||
Returns the number of bytes actually read, or negative on error.
|
||||
There is no implicit file position -- the offset is explicit on
|
||||
every call.
|
||||
|
||||
uint8_t buf[512];
|
||||
int n = montauk::read(h, buf, 0, 512);
|
||||
|
||||
<strong>getsize</strong>
|
||||
Returns the total size in bytes of the file.
|
||||
|
||||
uint64_t sz = montauk::getsize(h);
|
||||
|
||||
<strong>close</strong>
|
||||
Closes the file handle and frees kernel resources.
|
||||
|
||||
montauk::close(h);
|
||||
|
||||
<strong>readdir</strong>
|
||||
Lists entries in a directory. Up to 'max' entry names (VFS cap
|
||||
256, driver-backed listings such as 0:/os/ cap 128) are written
|
||||
to the 'names' array. The kernel allocates a user-accessible
|
||||
page for the string data automatically. Directory entries are
|
||||
returned with a trailing slash.
|
||||
|
||||
const char* entries[64];
|
||||
int count = montauk::readdir("0:/", entries, 64);
|
||||
// entries: "os/", "apps/", "man/", "www/", "users/", ...
|
||||
|
||||
For directories that may contain more entries than fit in one
|
||||
call, use montauk::readdir_at(path, names, max, startIndex) and
|
||||
advance startIndex by the returned count until it returns 0.
|
||||
|
||||
<strong>READING PATTERN</strong>
|
||||
The standard pattern for reading a file:
|
||||
|
||||
int h = montauk::open("0:/man/intro.1");
|
||||
uint64_t size = montauk::getsize(h);
|
||||
uint8_t buf[512];
|
||||
uint64_t off = 0;
|
||||
while (off < size) {
|
||||
uint64_t chunk = size - off;
|
||||
if (chunk > 511) chunk = 511;
|
||||
int n = montauk::read(h, buf, off, chunk);
|
||||
if (n <= 0) break;
|
||||
buf[n] = '\0';
|
||||
montauk::print((const char*)buf);
|
||||
off += n;
|
||||
}
|
||||
montauk::close(h);
|
||||
|
||||
<strong>WRITING, DELETING, RENAMING</strong>
|
||||
<strong> int montauk::fcreate(const char* path);</strong>
|
||||
<strong> int montauk::fwrite(int handle, const uint8_t* buf, uint64_t offset, uint64_t size);</strong>
|
||||
<strong> int montauk::fdelete(const char* path);</strong>
|
||||
<strong> int montauk::fmkdir(const char* path);</strong>
|
||||
<strong> int montauk::frename(const char* oldPath, const char* newPath);</strong>
|
||||
|
||||
fcreate creates a new file and returns a handle. fwrite writes
|
||||
bytes at the given offset. fdelete removes a file, fmkdir
|
||||
creates a directory, and frename renames or moves a file or
|
||||
directory (the basis for file manager move operations).
|
||||
|
||||
On drive 0 (the ramdisk), changes persist only until reboot --
|
||||
the ramdisk is reloaded from the USTAR archive on each boot. On
|
||||
disk-backed drives (FAT32/ext2 partitions mounted with
|
||||
montauk::fs_mount), changes are written through to storage; use
|
||||
montauk::fs_sync() to flush caches before power-off.
|
||||
|
||||
<strong>NOTES</strong>
|
||||
Drive 0 is loaded at boot from a USTAR tar archive into RAM.
|
||||
Other drives are mounted on demand from GPT partitions on
|
||||
SATA/NVMe/USB block devices; see syscalls(2), STORAGE and
|
||||
DEVICES sections.
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
syscalls(2), spawn(2), malloc(3)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,161 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: fontscale(1) - get or set terminal font scale">
|
||||
<title>fontscale(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>fontscale(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
fontscale - get or set terminal font scale
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
fontscale
|
||||
fontscale <n>
|
||||
fontscale <x> <y>
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
Controls the terminal font scale factor. The Flanterm terminal
|
||||
emulator renders text at a configurable scale multiplier.
|
||||
Increasing the scale makes text larger, which is useful on
|
||||
high-resolution displays or real hardware where text may be
|
||||
too small to read comfortably.
|
||||
|
||||
With no arguments, prints the current scale factor and terminal
|
||||
dimensions.
|
||||
|
||||
With one argument, sets both the horizontal and vertical scale
|
||||
to the same value.
|
||||
|
||||
With two arguments, sets asymmetric horizontal and vertical
|
||||
scale factors independently.
|
||||
|
||||
Valid scale values are 1 through 8. After rescaling, the screen
|
||||
is cleared.
|
||||
|
||||
<strong>OUTPUT</strong>
|
||||
fontscale
|
||||
Scale: 1x1 (160 cols x 50 rows)
|
||||
|
||||
fontscale 2
|
||||
Scale set to 2x2 (80 cols x 25 rows)
|
||||
|
||||
<strong>EXAMPLES</strong>
|
||||
fontscale Show current scale and dimensions
|
||||
fontscale 2 Double the font size
|
||||
fontscale 3 2 3x horizontal, 2x vertical
|
||||
fontscale 1 Reset to default size
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
shell(1), syscalls(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,180 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: framebuffer(2) - direct framebuffer access">
|
||||
<title>framebuffer(2) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>framebuffer(2)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
fb_info, fb_map - direct framebuffer access
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
<strong> void montauk::fb_info(montauk::abi::FbInfo* info);</strong>
|
||||
<strong> void* montauk::fb_map();</strong>
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
These syscalls allow userspace programs to access the linear
|
||||
framebuffer directly for graphical output.
|
||||
|
||||
<strong>fb_info</strong>
|
||||
Fills in an FbInfo structure with the framebuffer geometry:
|
||||
|
||||
montauk::abi::FbInfo fb;
|
||||
montauk::fb_info(&fb);
|
||||
// fb.width, fb.height, fb.pitch, fb.bpp
|
||||
|
||||
The pitch is the number of bytes per scanline (may be larger
|
||||
than width * 4 due to alignment). bpp is always 32.
|
||||
|
||||
<strong>fb_map</strong>
|
||||
Maps the physical framebuffer into the process address space at
|
||||
a fixed virtual address (0x50000000) and returns that address.
|
||||
|
||||
uint32_t* pixels = (uint32_t*)montauk::fb_map();
|
||||
|
||||
Each pixel is a 32-bit value in 0xAARRGGBB format (blue in the
|
||||
low byte). Writing to this memory directly updates the screen.
|
||||
|
||||
<strong>PIXEL FORMAT</strong>
|
||||
Bits 31-24: Alpha (unused, typically 0xFF)
|
||||
Bits 23-16: Red
|
||||
Bits 15-8: Green
|
||||
Bits 7-0: Blue
|
||||
|
||||
Example: red = 0x00FF0000, green = 0x0000FF00, blue = 0x000000FF
|
||||
|
||||
<strong>EXAMPLE</strong>
|
||||
Fill the screen with blue:
|
||||
|
||||
montauk::abi::FbInfo fb;
|
||||
montauk::fb_info(&fb);
|
||||
uint32_t* pixels = (uint32_t*)montauk::fb_map();
|
||||
|
||||
for (uint64_t y = 0; y < fb.height; y++) {
|
||||
uint32_t* row = (uint32_t*)((uint8_t*)pixels + y * fb.pitch);
|
||||
for (uint64_t x = 0; x < fb.width; x++) {
|
||||
row[x] = 0x000000FF;
|
||||
}
|
||||
}
|
||||
|
||||
<strong>NOTES</strong>
|
||||
After mapping, the cursor overlay is not composited. Programs
|
||||
that use the framebuffer take full control of screen output.
|
||||
|
||||
Only one mapping per process is supported. Calling fb_map()
|
||||
multiple times returns the same address.
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
syscalls(2), malloc(3)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,264 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS Man Pages">
|
||||
<title>Man Pages - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
font-family: monospace;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Man Pages</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<p>
|
||||
Manual pages for MontaukOS, viewable in-system with the <a href="man.html">man(1)</a> command.
|
||||
</p>
|
||||
|
||||
<h2>User Commands (Section 1)</h2>
|
||||
|
||||
<ul class="doc-list">
|
||||
|
||||
<li>
|
||||
<a href="intro.html">intro(1)</a>
|
||||
<p>introduction to MontaukOS userspace</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="shell.html">shell(1)</a>
|
||||
<p>MontaukOS interactive command shell</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="init.html">init(1)</a>
|
||||
<p>MontaukOS init system</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="dhcp.html">dhcp(1)</a>
|
||||
<p>obtain network configuration via DHCP</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="fetch.html">fetch(1)</a>
|
||||
<p>HTTP/HTTPS client for MontaukOS</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="ping.html">ping(1)</a>
|
||||
<p>send ICMP echo requests</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="nslookup.html">nslookup(1)</a>
|
||||
<p>DNS hostname lookup</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="fontscale.html">fontscale(1)</a>
|
||||
<p>get or set terminal font scale</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="edit.html">edit(1)</a>
|
||||
<p>text editor for MontaukOS</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="man.html">man(1)</a>
|
||||
<p>display manual pages</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="printctl.html">printctl(1)</a>
|
||||
<p>configure printers and submit print jobs</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="printd.html">printd(1)</a>
|
||||
<p>MontaukOS userspace print spooler daemon</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="wiki.html">wiki(1)</a>
|
||||
<p>Wikipedia article viewer for MontaukOS</p>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h2>System Calls (Section 2)</h2>
|
||||
|
||||
<ul class="doc-list">
|
||||
|
||||
<li>
|
||||
<a href="syscalls.html">syscalls(2)</a>
|
||||
<p>overview of MontaukOS system calls</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="spawn.html">spawn(2)</a>
|
||||
<p>create and wait for processes</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="file.html">file(2)</a>
|
||||
<p>file I/O system calls</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="framebuffer.html">framebuffer(2)</a>
|
||||
<p>direct framebuffer access</p>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h2>Library Functions (Section 3)</h2>
|
||||
|
||||
<ul class="doc-list">
|
||||
|
||||
<li>
|
||||
<a href="malloc.html">malloc(3)</a>
|
||||
<p>userspace heap allocation</p>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h2>File Formats / Reference (Section 5)</h2>
|
||||
|
||||
<ul class="doc-list">
|
||||
|
||||
<li>
|
||||
<a href="tls-errors.html">tls-errors(5)</a>
|
||||
<p>BearSSL TLS and X.509 error codes</p>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<h2>Miscellaneous (Section 7)</h2>
|
||||
|
||||
<ul class="doc-list">
|
||||
|
||||
<li>
|
||||
<a href="legal.html">legal(7)</a>
|
||||
<p>MontaukOS legal/copyright information</p>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,157 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: init(1) - MontaukOS init system">
|
||||
<title>init(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>init(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
init - MontaukOS init system
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
Spawned automatically by the kernel as PID 0.
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
init is the first userspace process started by the MontaukOS
|
||||
kernel. It chains system services in sequence, then launches
|
||||
the interactive shell.
|
||||
|
||||
Each service is spawned as a child process. init waits for it
|
||||
to exit before starting the next one. If a service fails to
|
||||
spawn, init logs an error and continues to the next stage.
|
||||
|
||||
Log output is timestamped and color-coded:
|
||||
|
||||
HH:MM:SS INFO init Starting dhcp
|
||||
HH:MM:SS OK init dhcp finished (pid 1)
|
||||
|
||||
<strong>BOOT SEQUENCE</strong>
|
||||
The following services are started in order:
|
||||
|
||||
1. 0:/os/dhcp.elf Obtain network configuration via DHCP
|
||||
2. 0:/os/shell.elf Launch the interactive shell
|
||||
|
||||
After the shell exits, init enters an idle loop.
|
||||
|
||||
<strong>LOG LEVELS</strong>
|
||||
init uses four log levels, each with a distinct color:
|
||||
|
||||
OK Green Service completed successfully
|
||||
INFO Cyan Informational (service starting, etc.)
|
||||
WARN Yellow Non-fatal warning
|
||||
FAIL Red Service failed to start
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
dhcp(1), shell(1), syscalls(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,212 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: intro(1) - introduction to MontaukOS userspace">
|
||||
<title>intro(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>intro(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
intro - introduction to MontaukOS userspace
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
MontaukOS is a hobbyist 64-bit operating system written in C++20,
|
||||
currently at version 0.1.7 (API version 8). Userspace programs
|
||||
run in Ring 3, are loaded as static ELF64 binaries, and
|
||||
communicate with the kernel through the x86-64 SYSCALL/SYSRET
|
||||
mechanism (150 syscalls -- see syscalls(2)).
|
||||
|
||||
Programs are compiled with a freestanding cross-compiler and
|
||||
linked at virtual address 0x400000. There is no standard C
|
||||
library for C++ programs -- all system interaction goes through
|
||||
the montauk:: syscall wrappers. A desktop environment with a
|
||||
window server, GUI apps, and Bluetooth/audio/networking stacks
|
||||
runs on top of the same syscall API.
|
||||
|
||||
<strong>GETTING STARTED</strong>
|
||||
To write a new system/CLI program, create a directory under
|
||||
programs/src/ with a main.cpp file. The entry point is:
|
||||
|
||||
extern "C" void _start() { ... }
|
||||
|
||||
There is no argc/argv. Use montauk::getargs() to retrieve any
|
||||
arguments passed by the parent process. Include <montauk/syscall.h>
|
||||
for the full typed syscall API. GUI apps additionally use
|
||||
win_create()/win_poll()/win_present() from montauk/Window.hpp
|
||||
(see framebuffer(2)).
|
||||
|
||||
Build with:
|
||||
|
||||
cd programs && make
|
||||
|
||||
System/CLI binaries appear in programs/bin/os/; GUI app bundles
|
||||
(ELF + manifest.toml + icon) appear under programs/bin/apps/<name>/.
|
||||
|
||||
<strong>RAMDISK LAYOUT</strong>
|
||||
The boot ramdisk is mounted as drive 0 with the following
|
||||
directory structure:
|
||||
|
||||
0:/os/ System/CLI binaries (shell, init, man, etc.),
|
||||
plus os-owned data: certs/, firmware/,
|
||||
licenses/, wallpapers/
|
||||
0:/apps/ GUI app bundles, one directory per app
|
||||
(<app>.elf + manifest.toml + icon)
|
||||
0:/config/ System-wide config TOMLs
|
||||
0:/users/<name>/ Per-user home directories (created at
|
||||
login), with Music/, Videos/, Pictures/,
|
||||
config/ subdirectories
|
||||
0:/fonts/ Shared fonts
|
||||
0:/icons/ Shared icons
|
||||
0:/man/ Manual pages
|
||||
0:/www/ Web server content
|
||||
0:/lib/ Lua and TinyCC toolchain payloads
|
||||
0:/boot/ Kernel, bootloader, ramdisk image
|
||||
|
||||
There is no 0:/games/, 0:/common/, 0:/home/, or 0:/etc/ --
|
||||
these were used by earlier single-user releases and no longer
|
||||
exist. Games and other GUI programs (including doom) ship as
|
||||
bundles under 0:/apps/.
|
||||
|
||||
<strong>SHELL</strong>
|
||||
The interactive shell is the primary way to interact with
|
||||
MontaukOS. Commands are resolved against the current directory
|
||||
first, then 0:/os/. Type 'help' at the shell prompt for a list
|
||||
of commands. Use 'man shell' for detailed shell documentation.
|
||||
|
||||
<strong>MAN PAGES</strong>
|
||||
The following man pages are available:
|
||||
|
||||
intro(1) This page
|
||||
shell(1) Shell commands reference
|
||||
init(1) Init system
|
||||
dhcp(1) DHCP client
|
||||
fetch(1) HTTP client
|
||||
ping(1) ICMP ping
|
||||
nslookup(1) DNS lookup
|
||||
fontscale(1) Terminal font scaling
|
||||
edit(1) Text editor
|
||||
man(1) The man command itself
|
||||
printctl(1) Printer control
|
||||
printd(1) Print spooler daemon
|
||||
wiki(1) Wikipedia article viewer
|
||||
legal(7) Copyright and legal information
|
||||
tls-errors(5) TLS/BearSSL error reference
|
||||
syscalls(2) Overview of all syscalls
|
||||
spawn(2) Process spawning
|
||||
file(2) File I/O syscalls
|
||||
framebuffer(2) Framebuffer access
|
||||
malloc(3) Memory allocation
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
shell(1), syscalls(2), malloc(3)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,177 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: legal(7) - MontaukOS legal/copyright information">
|
||||
<title>legal(7) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>legal(7)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
MontaukOS legal/copyright information
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
Copyright (c) 2025-2026 Daniel Hammer, et al.
|
||||
(includes contributors to other projects, i.e. The Limine Bootloader. Please refer to any other project's own license.)
|
||||
|
||||
MontaukOS is source-available software, provided under the terms of the
|
||||
MontaukOS Software License. The full license text is on this system at
|
||||
0:/os/licenses/LICENSE.txt.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
== License for the Limine C++ template (certain portions derive therefrom) ==
|
||||
Copyright (C) 2023-2026 Mintsuki and contributors.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
<strong>THIRD-PARTY COMPONENTS</strong>
|
||||
MontaukOS is distributed together with third-party components that remain
|
||||
under their own licenses, including:
|
||||
|
||||
* Flat Remix icon theme, Copyright (C) Daniel Ruiz de Alegria - GPLv3
|
||||
* DOOM engine (doom.elf, via doomgeneric), Copyright (C) id Software, Inc.
|
||||
and contributors - GPLv2
|
||||
* Limine bootloader, Copyright (C) Mintsuki and contributors - BSD 2-Clause
|
||||
* BearSSL, Copyright (c) Thomas Pornin - MIT
|
||||
* stb_image, Copyright (c) Sean Barrett - MIT
|
||||
* JetBrains Mono font, Copyright The JetBrains Mono Project Authors - OFL-1.1
|
||||
* Noto Serif font, Copyright The Noto Project Authors - OFL-1.1
|
||||
* Roboto font, Copyright The Roboto Project Authors - OFL-1.1
|
||||
* C059 font (URW Base 35), Copyright (C) (URW)++ Design and Development
|
||||
GmbH - AGPLv3 with font-embedding exception
|
||||
* Tiny C Compiler (tcc.elf, 0:/sdk/tcc), Copyright (c) Fabrice Bellard and
|
||||
contributors - LGPL-2.1
|
||||
* Lua (lua.elf, 0:/sdk/lua), Copyright (C) Lua.org, PUC-Rio - MIT
|
||||
* Mozilla CA certificate bundle (0:/os/certs), Mozilla CA Certificate
|
||||
Program - MPL-2.0
|
||||
* Intel Bluetooth firmware (0:/os/firmware/intel), Copyright (c) Intel
|
||||
Corporation - Intel redistributable firmware license
|
||||
* Default wallpaper photo (0:/os/wallpapers), by Nikhil Kumar -
|
||||
Unsplash License
|
||||
|
||||
Full license texts and notices are on this system in 0:/os/licenses/.</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,175 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: malloc(3) - userspace heap allocation">
|
||||
<title>malloc(3) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>malloc(3)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
malloc, mfree, realloc - userspace heap allocation
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
<strong> void* montauk::malloc(uint64_t size);</strong>
|
||||
<strong> void montauk::mfree(void* ptr);</strong>
|
||||
<strong> void* montauk::realloc(void* ptr, uint64_t size);</strong>
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
The userspace heap provides dynamic memory allocation on top of
|
||||
the kernel's page-mapping syscall (SYS_ALLOC). Include the
|
||||
header <montauk/heap.h> to use these functions.
|
||||
|
||||
<strong>malloc</strong>
|
||||
Allocates 'size' bytes from the free list. Returns a 16-byte
|
||||
aligned pointer, or nullptr on failure. When the free list is
|
||||
empty, it requests more pages from the kernel via SYS_ALLOC
|
||||
(minimum 16 KiB growth, initial seed of 64 KiB).
|
||||
|
||||
char* buf = (char*)montauk::malloc(1024);
|
||||
|
||||
<strong>mfree</strong>
|
||||
Returns the block to the userspace free list. No syscall is
|
||||
made -- the memory stays mapped and is immediately reusable.
|
||||
Passing nullptr is a safe no-op.
|
||||
|
||||
montauk::mfree(buf);
|
||||
|
||||
<strong>realloc</strong>
|
||||
Resizes the allocation to 'size' bytes. Allocates a new block,
|
||||
copies the smaller of old/new sizes, and frees the old block.
|
||||
If ptr is nullptr, behaves like malloc.
|
||||
|
||||
buf = (char*)montauk::realloc(buf, 2048);
|
||||
|
||||
<strong>IMPLEMENTATION</strong>
|
||||
The allocator uses a linked free-list with first-fit search.
|
||||
Blocks larger than needed are split. The allocation header is
|
||||
16 bytes (magic + size). All allocations are 16-byte aligned.
|
||||
|
||||
The heap grows by requesting pages from the kernel via
|
||||
SYS_ALLOC. These pages are never returned to the kernel (since
|
||||
SYS_FREE is currently a no-op), but mfree makes them available
|
||||
for future malloc calls within the process.
|
||||
|
||||
<strong>LOW-LEVEL PAGE API</strong>
|
||||
For large allocations or when direct page control is needed:
|
||||
|
||||
void* montauk::alloc(uint64_t size); // SYS_ALLOC
|
||||
void montauk::free(void* ptr); // SYS_FREE (no-op)
|
||||
|
||||
alloc() maps zeroed pages starting at 0x40000000 and growing
|
||||
upward. Size is rounded up to 4 KiB page boundaries.
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
syscalls(2), file(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,167 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: man(1) - display manual pages">
|
||||
<title>man(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>man(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
man - display manual pages
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
<strong> man topic</strong>
|
||||
<strong> man section topic</strong>
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
The man command displays manual pages from the ramdisk in a
|
||||
fullscreen pager. Pages are stored as plain text files with
|
||||
simple formatting directives.
|
||||
|
||||
If no section is specified, sections 1 through 7 are searched
|
||||
in order. If a section number is given, only that section is
|
||||
checked.
|
||||
|
||||
<strong>KEY BINDINGS</strong>
|
||||
|
||||
<strong>Navigation</strong>
|
||||
j, Down Arrow Scroll down one line
|
||||
k, Up Arrow Scroll up one line
|
||||
Space, Page Down Scroll down one page
|
||||
b, Page Up Scroll up one page
|
||||
g, Home Go to top
|
||||
G, End Go to bottom
|
||||
q Quit
|
||||
|
||||
<strong>SECTIONS</strong>
|
||||
1 User commands and programs
|
||||
2 System calls (kernel interface)
|
||||
3 Library functions (userspace libraries)
|
||||
7 Miscellaneous (legal, conventions)
|
||||
|
||||
<strong>FILES</strong>
|
||||
Man pages are stored on the ramdisk at:
|
||||
|
||||
0:/man/<topic>.<section>
|
||||
|
||||
For example, man intro reads 0:/man/intro.1
|
||||
|
||||
<strong>EXAMPLES</strong>
|
||||
man intro View the introduction
|
||||
man 2 syscalls View syscall overview (section 2)
|
||||
man malloc View malloc documentation
|
||||
man legal View copyright information
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
intro(1), shell(1), syscalls(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,154 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: nslookup(1) - DNS hostname lookup">
|
||||
<title>nslookup(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>nslookup(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
nslookup - DNS hostname lookup
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
nslookup <hostname>
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
Resolves a hostname to an IPv4 address using the configured
|
||||
DNS server and prints the result.
|
||||
|
||||
The kernel DNS resolver sends a UDP query to port 53 of the
|
||||
configured DNS server and waits up to 5 seconds for a reply.
|
||||
Results are cached in an 8-entry kernel cache with TTL support.
|
||||
|
||||
<strong>OUTPUT</strong>
|
||||
Server: 10.0.68.1
|
||||
Name: example.com
|
||||
Address: 93.184.216.34
|
||||
Time: 3ms
|
||||
|
||||
If the lookup fails:
|
||||
|
||||
Could not resolve: badhost.invalid
|
||||
|
||||
<strong>DNS CONFIGURATION</strong>
|
||||
The DNS server address is obtained automatically via DHCP.
|
||||
It can also be viewed and set with ifconfig. The default
|
||||
is 10.0.68.1 (QEMU user-mode networking).
|
||||
|
||||
<strong>EXAMPLES</strong>
|
||||
nslookup google.com
|
||||
nslookup icanhazip.com
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
ping(1), fetch(1), dhcp(1), ifconfig(1), syscalls(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,155 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: ping(1) - send ICMP echo requests">
|
||||
<title>ping(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>ping(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
ping - send ICMP echo requests
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
ping <host>
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
Sends 4 ICMP echo requests to the specified host and prints
|
||||
the round-trip time for each reply.
|
||||
|
||||
The host may be an IP address or a hostname. Hostnames are
|
||||
resolved via the configured DNS server.
|
||||
|
||||
Each request has a 3-second timeout. Requests are sent at
|
||||
1-second intervals.
|
||||
|
||||
<strong>OUTPUT</strong>
|
||||
PING example.com (93.184.216.34)
|
||||
Reply from 93.184.216.34: time=12ms
|
||||
Reply from 93.184.216.34: time=11ms
|
||||
Reply from 93.184.216.34: time=13ms
|
||||
Reply from 93.184.216.34: time=11ms
|
||||
|
||||
If a reply is not received within the timeout:
|
||||
|
||||
Request timed out
|
||||
|
||||
<strong>EXAMPLES</strong>
|
||||
ping 10.0.68.1
|
||||
Ping the gateway by IP address.
|
||||
|
||||
ping google.com
|
||||
Ping by hostname (requires DNS).
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
nslookup(1), ifconfig(1), shell(1), syscalls(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,150 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: printctl(1) - configure printers and submit print jobs">
|
||||
<title>printctl(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>printctl(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
printctl - configure printers and submit print jobs
|
||||
<strong>SYNOPSIS</strong>
|
||||
<strong>printctl</strong>
|
||||
<em>command</em>
|
||||
[<em>options</em>]
|
||||
<strong>DESCRIPTION</strong>
|
||||
<strong>printctl</strong>
|
||||
manages the MontaukOS userspace print spooler and submits print jobs to IPP printers.
|
||||
<strong>COMMANDS</strong>
|
||||
|
||||
<strong>set-printer <em>URI</em></strong>
|
||||
Store the default printer URI.
|
||||
|
||||
<strong>show-printer</strong>
|
||||
Print the configured default printer URI.
|
||||
|
||||
<strong>print <em>FILE</em> [--printer <em>URI</em>] [--name <em>JOB</em>] [--wait]</strong>
|
||||
Queue a file for printing.
|
||||
|
||||
<strong>test-page [--printer <em>URI</em>] [--wait] [--no-wait]</strong>
|
||||
Generate and queue a simple test page.
|
||||
|
||||
<strong>status [--verbose]</strong>
|
||||
Show daemon state and queued, active, completed, and failed jobs.
|
||||
|
||||
<strong>inspect <em>JOB-ID</em></strong>
|
||||
Show full metadata and debug details for a queued, active, completed, or failed job.
|
||||
|
||||
<strong>probe [<em>URI</em>]</strong>
|
||||
Probe the configured printer, print host and resolution details, and show IPP capability diagnostics.</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,130 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: printd(1) - MontaukOS userspace print spooler daemon">
|
||||
<title>printd(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>printd(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
printd - MontaukOS userspace print spooler daemon
|
||||
<strong>SYNOPSIS</strong>
|
||||
<strong>printd</strong>
|
||||
<strong>DESCRIPTION</strong>
|
||||
<strong>printd</strong>
|
||||
monitors the print spool directories, claims queued jobs, and delivers them to IPP printers.
|
||||
|
||||
It is normally launched automatically by
|
||||
<strong>init</strong>(1)
|
||||
and does not require direct user interaction.</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,313 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: shell(1) - MontaukOS interactive command shell">
|
||||
<title>shell(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>shell(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
shell - MontaukOS interactive command shell
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
The MontaukOS shell is a command interpreter launched by init
|
||||
after system services have started. It provides command
|
||||
execution, file navigation, shell variables, command chaining,
|
||||
tab completion, and command history.
|
||||
|
||||
Commands are either shell builtins or external programs. When
|
||||
a command is not a builtin, the shell searches for a matching
|
||||
ELF binary and executes it as a child process.
|
||||
|
||||
<strong>COMMAND RESOLUTION</strong>
|
||||
When a non-builtin command is entered, the shell searches for
|
||||
a matching binary in the following order:
|
||||
|
||||
1. <cwd>/<command> (exact name, e.g. "hello.elf")
|
||||
2. <cwd>/<command>.elf
|
||||
3. 0:/os/<command>.elf
|
||||
4. 0:/os/<command> (no extension)
|
||||
5. If on a non-zero drive, the drive root: <drive>:/<command>[.elf]
|
||||
|
||||
A command containing a "/" (or an explicit drive prefix, or a
|
||||
leading "." or "/") is instead treated as a direct path and
|
||||
resolved by the kernel against the process CWD, trying the
|
||||
path as-is and then with ".elf" appended.
|
||||
|
||||
The first match is spawned and the shell waits for it to exit.
|
||||
If no match is found, the shell prints:
|
||||
|
||||
<command>: command not found
|
||||
|
||||
Arguments after the command name are passed to the spawned
|
||||
process.
|
||||
|
||||
<strong>BUILTINS</strong>
|
||||
|
||||
<strong>help</strong>
|
||||
Display a categorized list of available commands.
|
||||
|
||||
<strong>ls [dir]</strong>
|
||||
List files in the current directory, or in the specified
|
||||
directory. Directory entries are shown with a trailing slash.
|
||||
Examples: ls, ls man, ls os
|
||||
|
||||
<strong>cd [dir]</strong>
|
||||
Change the working directory. With no argument, returns to the
|
||||
logged-in user's home directory (0:/users/<user>); with /,
|
||||
returns to the drive root. Use cd .. to go up one level.
|
||||
The shell prompt reflects the current directory.
|
||||
Examples: cd os, cd .., cd
|
||||
|
||||
<strong>pwd</strong>
|
||||
Print the current working directory as an absolute path
|
||||
(e.g. "0:/os").
|
||||
|
||||
<strong>echo [-n] ...</strong>
|
||||
Print the arguments. -n suppresses the trailing newline.
|
||||
|
||||
<strong>set [VAR=value]</strong>
|
||||
With no argument, list all shell variables (built-in and
|
||||
user-defined). With VAR=value, set a variable. With a bare
|
||||
name, print that variable's value.
|
||||
|
||||
<strong>unset VAR</strong>
|
||||
Remove a user-defined shell variable.
|
||||
|
||||
<strong>true / false</strong>
|
||||
Return exit status 0 / 1 without doing anything. Useful with
|
||||
&& and ||.
|
||||
|
||||
<strong>N:</strong>
|
||||
A bare "<number>:" (e.g. "1:") switches the current drive to
|
||||
drive N and resets the working directory to that drive's root.
|
||||
|
||||
<strong>exit</strong>
|
||||
Terminate the shell process (with the last command's exit code).
|
||||
|
||||
<strong>SYNTAX</strong>
|
||||
<strong>Variables</strong>
|
||||
NAME=value Set a shell variable (no leading $)
|
||||
$VAR or ${VAR} Expand a variable's value
|
||||
$? Exit status of the last command
|
||||
$USER, $HOME, $PWD Built-in dynamic variables (session user,
|
||||
home directory, current directory)
|
||||
\$ Escape a literal '$'
|
||||
|
||||
<strong>Tilde expansion</strong>
|
||||
A leading ~ expands to the session home directory
|
||||
(0:/users/<user>) when followed by end-of-string, '/', or a
|
||||
space.
|
||||
|
||||
<strong>Command chaining</strong>
|
||||
cmd1 ; cmd2 Run cmd2 unconditionally after cmd1
|
||||
cmd1 && cmd2 Run cmd2 only if cmd1 succeeded (exit 0)
|
||||
cmd1 || cmd2 Run cmd2 only if cmd1 failed (nonzero exit)
|
||||
|
||||
Single and double quotes protect ;, &&, and || from being
|
||||
treated as separators.
|
||||
|
||||
<strong>Comments</strong>
|
||||
A '#' outside of quotes starts a comment; the rest of the line
|
||||
is ignored.
|
||||
|
||||
<strong>EXTERNAL COMMANDS</strong>
|
||||
All external commands live in 0:/os/ (see COMMAND RESOLUTION).
|
||||
Where a dedicated man page exists it is noted below; run
|
||||
'man <command>' for details.
|
||||
|
||||
<strong>File commands</strong>
|
||||
cat <file> Display file contents
|
||||
edit [file] Text editor -- see edit(1)
|
||||
copy <src> <dst> Copy a file
|
||||
move <src> <dst> Move/rename a file
|
||||
rm <file> Remove a file
|
||||
touch <file> Create an empty file
|
||||
|
||||
<strong>System commands</strong>
|
||||
man <topic> View manual pages -- see man(1)
|
||||
whoami Print the current username
|
||||
info / mtkfetch Show system information
|
||||
date Show current date and time
|
||||
uptime Show system uptime
|
||||
proclist List running processes
|
||||
power CPU power/thermal status (power [watch [secs]])
|
||||
clear Clear the screen and framebuffer
|
||||
fontscale [n] Get or set terminal font scale -- see fontscale(1)
|
||||
lua Lua interpreter
|
||||
tcc TinyCC (in-system C compiler)
|
||||
reset Reboot the system
|
||||
shutdown Shut down the system
|
||||
|
||||
<strong>Network commands</strong>
|
||||
ping <host> Send ICMP echo requests -- see ping(1)
|
||||
nslookup <host> DNS lookup -- see nslookup(1)
|
||||
ifconfig Show/set network configuration
|
||||
tcpconnect <host> <port> Interactive TCP client
|
||||
irc IRC client
|
||||
dhcp DHCP client -- see dhcp(1)
|
||||
fetch <url> HTTP/HTTPS client (TLS 1.2) -- see fetch(1)
|
||||
wiki <title> Wikipedia article viewer -- see wiki(1)
|
||||
httpd HTTP server
|
||||
|
||||
Network commands accept both IP addresses and hostnames.
|
||||
Hostnames are resolved via the configured DNS server.
|
||||
|
||||
<strong>Bluetooth</strong>
|
||||
btlist List connected Bluetooth devices
|
||||
btbonds List bonded (paired) Bluetooth devices
|
||||
|
||||
<strong>Software-defined radio</strong>
|
||||
sdr [freqMHz [rateHz]] Receive and report basic signal
|
||||
statistics from an attached RTL-SDR dongle
|
||||
|
||||
GUI applications (window server programs, not run from the
|
||||
shell prompt as text commands) live under 0:/apps/, one bundle
|
||||
per app -- e.g. doom, terminal, texteditor, spreadsheet,
|
||||
wordprocessor, paint, calculator, network, bluetooth, audio,
|
||||
disks, devexplorer, procmgr, powermgr, printers, timezone,
|
||||
weather, wikipedia. There is no 0:/games/ directory.
|
||||
|
||||
<strong>TAB COMPLETION</strong>
|
||||
Pressing Tab completes the word under the cursor against, in
|
||||
order: executable names in 0:/os/, shell builtins, and file/
|
||||
directory entries in the current directory. A single match is
|
||||
completed inline; multiple matches are listed below the prompt.
|
||||
|
||||
<strong>INPUT</strong>
|
||||
The shell uses non-blocking keyboard input via SYS_GETKEY (with
|
||||
SYS_INPUT_WAIT to sleep between events) to support arrow key
|
||||
detection. Lines are limited to 255 characters.
|
||||
|
||||
<strong>Editing</strong>
|
||||
Backspace Delete character before cursor
|
||||
Tab Tab-complete the current word
|
||||
Enter Execute the command line
|
||||
|
||||
<strong>History</strong>
|
||||
The shell stores the last 32 unique commands. Duplicate
|
||||
consecutive entries are suppressed.
|
||||
|
||||
Up Arrow Recall previous command
|
||||
Down Arrow Recall next command (or clear line)
|
||||
|
||||
<strong>PROMPT</strong>
|
||||
The prompt displays the current drive and working directory:
|
||||
|
||||
0:/> _ (at root of drive 0)
|
||||
0:/os> _ (in os/ directory)
|
||||
1:/> _ (at root of drive 1)
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
man(1), intro(1), syscalls(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,187 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: spawn(2) - create and wait for processes">
|
||||
<title>spawn(2) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>spawn(2)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
spawn, waitpid - create and wait for processes
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
<strong> int montauk::spawn(const char* path, const char* args = nullptr);</strong>
|
||||
<strong> void montauk::waitpid(int pid);</strong>
|
||||
<strong> int montauk::getargs(char* buf, uint64_t maxLen);</strong>
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
|
||||
<strong>spawn</strong>
|
||||
Loads the ELF64 binary at the given VFS path and creates a new
|
||||
process. The path must include the drive prefix, for example:
|
||||
|
||||
int pid = montauk::spawn("0:/os/hello.elf");
|
||||
|
||||
An optional second argument passes a string to the child:
|
||||
|
||||
int pid = montauk::spawn("0:/os/man.elf", "intro");
|
||||
|
||||
The new process gets its own PML4 page table, a 32 KiB stack
|
||||
(at 0x7FFFFF7000-0x7FFFFFF000), and begins executing at the
|
||||
ELF entry point (_start).
|
||||
|
||||
Returns the new process's PID on success, or -1 on failure.
|
||||
Failure occurs when there are no free process slots (max 256),
|
||||
the file cannot be found, or the ELF is invalid.
|
||||
|
||||
<strong>waitpid</strong>
|
||||
Blocks the calling process until the process with the given PID
|
||||
has exited. Internally, this yields the CPU in a loop:
|
||||
|
||||
montauk::waitpid(pid);
|
||||
|
||||
This is how the shell implements foreground process execution --
|
||||
it spawns a child and waits for it to complete before showing
|
||||
the next prompt.
|
||||
|
||||
<strong>EXAMPLES</strong>
|
||||
Spawn a program and wait for it:
|
||||
|
||||
int pid = montauk::spawn("0:/os/hello.elf");
|
||||
if (pid < 0) {
|
||||
montauk::print("spawn failed\n");
|
||||
} else {
|
||||
montauk::waitpid(pid);
|
||||
montauk::print("child exited\n");
|
||||
}
|
||||
|
||||
<strong>getargs</strong>
|
||||
Copies the argument string into buf (up to maxLen bytes, always
|
||||
null-terminated). Returns the number of characters copied, or
|
||||
-1 on error.
|
||||
|
||||
char args[256];
|
||||
montauk::getargs(args, sizeof(args));
|
||||
|
||||
The argument string is set by the parent when calling spawn().
|
||||
If no arguments were provided, the buffer will be empty.
|
||||
|
||||
<strong>NOTES</strong>
|
||||
The _start() entry point receives no argc/argv. Use getargs()
|
||||
to retrieve the argument string passed by the parent process.
|
||||
|
||||
Process exit codes are not yet collected by waitpid.
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
syscalls(2), file(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,906 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: syscalls(2) - overview of MontaukOS system calls">
|
||||
<title>syscalls(2) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>syscalls(2)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
syscalls - overview of MontaukOS system calls
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
MontaukOS provides 150 system calls (numbers 0-149, sparsely
|
||||
assigned -- not every number in the range is in use) for
|
||||
userspace programs. Syscalls use the x86-64 SYSCALL instruction
|
||||
with the following register convention:
|
||||
|
||||
RAX Syscall number (in) / return value (out)
|
||||
RDI Argument 1
|
||||
RSI Argument 2
|
||||
RDX Argument 3
|
||||
R10 Argument 4
|
||||
R8 Argument 5
|
||||
R9 Argument 6
|
||||
|
||||
Include <Api/Syscall.hpp> for the numeric SYS_* constants and
|
||||
ABI structs, and <montauk/syscall.h> for typed wrappers in the
|
||||
montauk:: namespace. This page groups syscalls the same way the
|
||||
kernel source does (one subsystem header per group).
|
||||
|
||||
<strong>PROCESS MANAGEMENT</strong>
|
||||
<strong>SYS_EXIT (0)</strong>
|
||||
Terminate the calling process.
|
||||
[[noreturn]] void montauk::exit(int code = 0);
|
||||
|
||||
<strong>SYS_YIELD (1)</strong>
|
||||
Yield the remainder of the time slice.
|
||||
void montauk::yield();
|
||||
|
||||
<strong>SYS_SLEEP_MS (2)</strong>
|
||||
Sleep for at least the given number of milliseconds.
|
||||
void montauk::sleep_ms(uint64_t ms);
|
||||
|
||||
<strong>SYS_GETPID (3)</strong>
|
||||
Return the PID of the calling process.
|
||||
int montauk::getpid();
|
||||
|
||||
<strong>SYS_SPAWN (20)</strong>
|
||||
Spawn a new process from an ELF binary on the VFS.
|
||||
int montauk::spawn(const char* path, const char* args = nullptr);
|
||||
|
||||
<strong>SYS_WAITPID (23)</strong>
|
||||
Block until the given process has exited.
|
||||
void montauk::waitpid(int pid);
|
||||
|
||||
<strong>SYS_GETARGS (25)</strong>
|
||||
Get the argument string passed to this process at spawn time.
|
||||
int montauk::getargs(char* buf, uint64_t maxLen);
|
||||
|
||||
<strong>SYS_PROCLIST (61)</strong>
|
||||
List running processes (pid, parent, state, name, heap usage,
|
||||
accumulated CPU time).
|
||||
int montauk::proclist(montauk::abi::ProcInfo* buf, int max);
|
||||
|
||||
<strong>SYS_KILL (62)</strong>
|
||||
Terminate another process by PID.
|
||||
int montauk::kill(int pid);
|
||||
|
||||
<strong>SYS_CHDIR (96)</strong>
|
||||
Change the calling process's current working directory.
|
||||
int montauk::chdir(const char* path);
|
||||
|
||||
<strong>SYS_GETCWD (95)</strong>
|
||||
Get the calling process's current working directory.
|
||||
int montauk::getcwd(char* buf, uint64_t maxLen);
|
||||
|
||||
<strong>SYS_SETUSER (92)</strong>
|
||||
Associate a process with a logged-in user name (used by login/session
|
||||
management).
|
||||
int montauk::setuser(int pid, const char* name);
|
||||
|
||||
<strong>SYS_GETUSER (93)</strong>
|
||||
Get the user name associated with the calling process.
|
||||
int montauk::getuser(char* buf, uint64_t maxLen);
|
||||
|
||||
<strong>THREADING</strong>
|
||||
Threads share the spawning process's address space and heap
|
||||
(see montauk/heap.h for the heap lock). Declared in
|
||||
montauk/thread.h.
|
||||
|
||||
<strong>SYS_THREAD_SPAWN (130)</strong>
|
||||
Spawn a new thread in the calling process. Returns a positive
|
||||
TID on success, -1 on failure.
|
||||
int montauk::thread_spawn(ThreadEntry entry, void* arg,
|
||||
uint64_t stack_bytes = 0);
|
||||
|
||||
<strong>SYS_THREAD_EXIT (131)</strong>
|
||||
Terminate only the calling thread. If it is the main thread,
|
||||
the whole process exits.
|
||||
[[noreturn]] void montauk::thread_exit(int code = 0);
|
||||
|
||||
<strong>SYS_THREAD_JOIN (132)</strong>
|
||||
Block until the given TID exits, then reclaim its kernel state.
|
||||
int montauk::thread_join(int tid, int* out_code = nullptr);
|
||||
|
||||
<strong>SYS_THREAD_SELF (133)</strong>
|
||||
Return the calling thread's TID (equals getpid() for the main
|
||||
thread).
|
||||
int montauk::thread_self();
|
||||
|
||||
<strong>CONSOLE I/O</strong>
|
||||
<strong>SYS_PRINT (4)</strong>
|
||||
Write a null-terminated string to the terminal.
|
||||
void montauk::print(const char* text);
|
||||
|
||||
<strong>SYS_PUTCHAR (5)</strong>
|
||||
Write a single character to the terminal.
|
||||
void montauk::putchar(char c);
|
||||
|
||||
<strong>FILE I/O</strong>
|
||||
<strong>SYS_OPEN (6)</strong>
|
||||
Open a file. Returns a handle or negative on error.
|
||||
int montauk::open(const char* path);
|
||||
|
||||
<strong>SYS_READ (7)</strong>
|
||||
Read bytes from a file at a given offset.
|
||||
int montauk::read(int h, uint8_t* buf, uint64_t off, uint64_t sz);
|
||||
|
||||
<strong>SYS_GETSIZE (8)</strong>
|
||||
Get the size of an open file in bytes.
|
||||
uint64_t montauk::getsize(int handle);
|
||||
|
||||
<strong>SYS_CLOSE (9)</strong>
|
||||
Close a file handle.
|
||||
void montauk::close(int handle);
|
||||
|
||||
<strong>SYS_READDIR (10)</strong>
|
||||
List directory entries (max 256 per call for VFS directories,
|
||||
128 for driver-backed listings such as 0:/os/). For larger
|
||||
directories use SYS_READDIR_AT.
|
||||
int montauk::readdir(const char* path, const char** names, int max);
|
||||
|
||||
<strong>SYS_READDIR_AT (136)</strong>
|
||||
Paginated directory read. Returns entries starting at
|
||||
startIndex; call repeatedly with startIndex advanced by the
|
||||
returned count until it returns 0 to enumerate directories of
|
||||
any size.
|
||||
int montauk::readdir_at(const char* path, const char** names,
|
||||
int max, int startIndex);
|
||||
|
||||
<strong>SYS_FWRITE (41)</strong>
|
||||
Write bytes to a file at a given offset.
|
||||
int montauk::fwrite(int handle, const uint8_t* buf,
|
||||
uint64_t offset, uint64_t size);
|
||||
|
||||
<strong>SYS_FCREATE (42)</strong>
|
||||
Create a new file on the target volume. Returns a handle or
|
||||
negative on error.
|
||||
int montauk::fcreate(const char* path);
|
||||
|
||||
<strong>SYS_FDELETE (77)</strong>
|
||||
Delete a file.
|
||||
int montauk::fdelete(const char* path);
|
||||
|
||||
<strong>SYS_FMKDIR (78)</strong>
|
||||
Create a directory.
|
||||
int montauk::fmkdir(const char* path);
|
||||
|
||||
<strong>SYS_FRENAME (94)</strong>
|
||||
Rename or move a file/directory (used as the basis for file
|
||||
manager move operations).
|
||||
int montauk::frename(const char* oldPath, const char* newPath);
|
||||
|
||||
<strong>SYS_DRIVELIST (79)</strong>
|
||||
List mounted drive numbers.
|
||||
int montauk::drivelist(int* outDrives, int max);
|
||||
|
||||
<strong>SYS_DRIVELABEL (124)</strong>
|
||||
Get the volume label of a drive.
|
||||
int montauk::drivelabel(int drive, char* outLabel, int maxLen);
|
||||
|
||||
<strong>SYS_DRIVEKIND (127)</strong>
|
||||
Get the block device kind backing a drive: 0=unknown/ramdisk,
|
||||
1=SATA, 2=SATAPI, 3=NVMe, 4=USB mass storage.
|
||||
int montauk::drivekind(int drive);
|
||||
|
||||
<strong>MEMORY</strong>
|
||||
<strong>SYS_ALLOC (11)</strong>
|
||||
Map zeroed pages into the process address space.
|
||||
void* montauk::alloc(uint64_t size);
|
||||
|
||||
<strong>SYS_FREE (12)</strong>
|
||||
Reserved (currently a no-op).
|
||||
void montauk::free(void* ptr);
|
||||
|
||||
<strong>SYS_MEMSTATS (67)</strong>
|
||||
Get kernel-wide physical memory usage (total/free/used bytes,
|
||||
page size).
|
||||
void montauk::memstats(montauk::abi::MemStats* out);
|
||||
|
||||
<strong>TIMEKEEPING</strong>
|
||||
<strong>SYS_GETTICKS (13)</strong>
|
||||
Get APIC timer ticks since boot.
|
||||
uint64_t montauk::get_ticks();
|
||||
|
||||
<strong>SYS_GETMILLISECONDS (14)</strong>
|
||||
Get milliseconds elapsed since boot.
|
||||
uint64_t montauk::get_milliseconds();
|
||||
|
||||
<strong>SYS_GETTIME (28)</strong>
|
||||
Get the current wall-clock date and time (UTC).
|
||||
Fills a montauk::abi::DateTime struct with Year, Month, Day,
|
||||
Hour, Minute, and Second fields.
|
||||
void montauk::gettime(montauk::abi::DateTime* out);
|
||||
|
||||
<strong>SYS_SETTZ (90)</strong>
|
||||
Set the process/system timezone offset, in minutes from UTC.
|
||||
void montauk::settz(int offset_minutes);
|
||||
|
||||
<strong>SYS_GETTZ (91)</strong>
|
||||
Get the current timezone offset, in minutes from UTC.
|
||||
int montauk::gettz();
|
||||
|
||||
<strong>SYSTEM</strong>
|
||||
<strong>SYS_GETINFO (15)</strong>
|
||||
Get OS name, version string, API version, max process count,
|
||||
and the monotonic kernel build number.
|
||||
void montauk::get_info(montauk::abi::SysInfo* info);
|
||||
|
||||
<strong>KEYBOARD</strong>
|
||||
<strong>SYS_ISKEYAVAILABLE (16)</strong>
|
||||
Check if a key event is pending (non-blocking).
|
||||
bool montauk::is_key_available();
|
||||
|
||||
<strong>SYS_GETKEY (17)</strong>
|
||||
Get the next key event (press or release).
|
||||
void montauk::getkey(montauk::abi::KeyEvent* out);
|
||||
|
||||
<strong>SYS_GETCHAR (18)</strong>
|
||||
Block until a printable character is typed.
|
||||
char montauk::getchar();
|
||||
|
||||
<strong>SYS_INPUT_WAIT (123)</strong>
|
||||
Block until the input serial number differs from
|
||||
observedSerial or the timeout elapses; used to sleep
|
||||
efficiently between input-driven redraws.
|
||||
uint64_t montauk::input_wait(uint64_t observedSerial, uint64_t timeoutMs);
|
||||
|
||||
<strong>MOUSE</strong>
|
||||
<strong>SYS_MOUSESTATE (47)</strong>
|
||||
Get the current mouse position, scroll delta, and button mask.
|
||||
void montauk::mouse_state(montauk::abi::MouseState* out);
|
||||
|
||||
<strong>SYS_SETMOUSEBOUNDS (48)</strong>
|
||||
Set the maximum X/Y the mouse cursor may reach (e.g. framebuffer
|
||||
dimensions).
|
||||
void montauk::set_mouse_bounds(int32_t maxX, int32_t maxY);
|
||||
|
||||
<strong>NETWORKING</strong>
|
||||
<strong>SYS_PING (19)</strong>
|
||||
Send an ICMP echo request and wait for reply.
|
||||
int32_t montauk::ping(uint32_t ip, uint32_t timeoutMs = 3000);
|
||||
|
||||
<strong>SYS_RESOLVE (44)</strong>
|
||||
Resolve a hostname to an IPv4 address via DNS. Sends a UDP
|
||||
query to the configured DNS server and waits up to 5 seconds
|
||||
for a reply. Returns the IP in network byte order, or 0 on
|
||||
failure. IP address strings (e.g. "10.0.0.1") are detected
|
||||
and returned directly without a DNS query.
|
||||
uint32_t montauk::resolve(const char* hostname);
|
||||
|
||||
<strong>SYS_GETNETCFG (37)</strong>
|
||||
Get the current network configuration (IP, mask, gateway, MAC,
|
||||
DNS server).
|
||||
void montauk::get_netcfg(montauk::abi::NetCfg* out);
|
||||
|
||||
<strong>SYS_SETNETCFG (38)</strong>
|
||||
Set the network configuration (IP, mask, gateway, DNS server).
|
||||
int montauk::set_netcfg(const montauk::abi::NetCfg* cfg);
|
||||
|
||||
<strong>SYS_NETSTATUS (125)</strong>
|
||||
Get adapter status including driver name, link state, polling mode,
|
||||
and RX/TX packet counters.
|
||||
int montauk::net_status(montauk::abi::NetStatus* out);
|
||||
|
||||
<strong>SOCKETS</strong>
|
||||
<strong>SYS_SOCKET (29)</strong>
|
||||
Create a socket. type=SOCK_TCP (1) or SOCK_UDP (2).
|
||||
Returns fd or -1.
|
||||
int montauk::socket(int type);
|
||||
|
||||
<strong>SYS_CONNECT (30)</strong>
|
||||
Connect a TCP socket to a remote host.
|
||||
int montauk::connect(int fd, uint32_t ip, uint16_t port);
|
||||
|
||||
<strong>SYS_BIND (31)</strong>
|
||||
Bind a socket to a local port for listening.
|
||||
int montauk::bind(int fd, uint16_t port);
|
||||
|
||||
<strong>SYS_LISTEN (32)</strong>
|
||||
Start listening for incoming TCP connections.
|
||||
int montauk::listen(int fd);
|
||||
|
||||
<strong>SYS_ACCEPT (33)</strong>
|
||||
Accept an incoming connection on a listening socket.
|
||||
Returns a new socket fd for the client connection.
|
||||
int montauk::accept(int fd);
|
||||
|
||||
<strong>SYS_SEND (34)</strong>
|
||||
Send data on a connected socket. Returns bytes sent.
|
||||
int montauk::send(int fd, const void* data, uint32_t len);
|
||||
|
||||
<strong>SYS_RECV (35)</strong>
|
||||
Receive data from a connected socket. Returns bytes
|
||||
received, 0 if no data available, or -1 on close/error.
|
||||
int montauk::recv(int fd, void* buf, uint32_t maxLen);
|
||||
|
||||
<strong>SYS_CLOSESOCK (36)</strong>
|
||||
Close a socket and release its resources.
|
||||
int montauk::closesocket(int fd);
|
||||
|
||||
<strong>SYS_SENDTO (39)</strong>
|
||||
Send a UDP datagram to a specific destination.
|
||||
int montauk::sendto(int fd, const void* data, uint32_t len,
|
||||
uint32_t destIp, uint16_t destPort);
|
||||
|
||||
<strong>SYS_RECVFROM (40)</strong>
|
||||
Receive a UDP datagram. Returns the source address.
|
||||
int montauk::recvfrom(int fd, void* buf, uint32_t maxLen,
|
||||
uint32_t* srcIp, uint16_t* srcPort);
|
||||
|
||||
<strong>FRAMEBUFFER</strong>
|
||||
<strong>SYS_FBINFO (21)</strong>
|
||||
Get framebuffer dimensions and format.
|
||||
void montauk::fb_info(montauk::abi::FbInfo* info);
|
||||
|
||||
<strong>SYS_FBMAP (22)</strong>
|
||||
Map the framebuffer into process memory.
|
||||
void* montauk::fb_map();
|
||||
|
||||
<strong>TERMINAL</strong>
|
||||
<strong>SYS_TERMSIZE (24)</strong>
|
||||
Get terminal dimensions (columns and rows).
|
||||
void montauk::termsize(int* cols, int* rows);
|
||||
|
||||
<strong>SYS_TERMSCALE (43)</strong>
|
||||
Get or set the terminal font scale factor. When scale_x is 0,
|
||||
returns the current scale as (scale_y << 32 | scale_x). When
|
||||
scale_x is non-zero, sets the font scale and returns the new
|
||||
terminal dimensions as (rows << 32 | cols).
|
||||
void montauk::termscale(int scale_x, int scale_y);
|
||||
void montauk::get_termscale(int* scale_x, int* scale_y);
|
||||
|
||||
<strong>RANDOM</strong>
|
||||
<strong>SYS_GETRANDOM (45)</strong>
|
||||
Fill a buffer with random bytes using RDTSC-seeded entropy.
|
||||
Returns the number of bytes written.
|
||||
int64_t montauk::getrandom(void* buf, uint32_t len);
|
||||
|
||||
<strong>POWER MANAGEMENT</strong>
|
||||
<strong>SYS_RESET (26)</strong>
|
||||
Reboot the system.
|
||||
[[noreturn]] void montauk::reset();
|
||||
|
||||
<strong>SYS_SHUTDOWN (27)</strong>
|
||||
Shut down the system.
|
||||
[[noreturn]] void montauk::shutdown();
|
||||
|
||||
<strong>SYS_SUSPEND (89)</strong>
|
||||
Enter ACPI S3 sleep. Returns after wake, 0 on success.
|
||||
int montauk::suspend();
|
||||
|
||||
<strong>SYS_POWER_REQUEST (135)</strong>
|
||||
Cross-process graceful power-off request channel. The desktop
|
||||
posts a pending action (POWER_REQ_SHUTDOWN / POWER_REQ_REBOOT)
|
||||
then exits; login.elf reads it with POWER_REQ_QUERY
|
||||
(read-and-clear), runs the shutdown stages, and finally calls
|
||||
shutdown()/reset(). See montauk::abi::PowerRequestAction.
|
||||
int montauk::power_request(int action);
|
||||
|
||||
<strong>SYS_POWERINFO (149)</strong>
|
||||
Get the CPU power/thermal snapshot (HWP state, throttling,
|
||||
package temperature, base/max/effective frequency). Returns 0
|
||||
on success, -1 if unsupported by the running hardware.
|
||||
int montauk::syscall1(SYS_POWERINFO, (uint64_t)&out);
|
||||
// out: montauk::abi::PowerInfo*
|
||||
|
||||
<strong>KERNEL LOG</strong>
|
||||
<strong>SYS_KLOG (46)</strong>
|
||||
Read from the kernel ring log buffer.
|
||||
int64_t montauk::read_klog(char* buf, uint64_t size);
|
||||
|
||||
<strong>I/O REDIRECTION</strong>
|
||||
Used by the terminal app and similar programs to run a child
|
||||
process with its console I/O captured instead of going directly
|
||||
to the framebuffer console.
|
||||
|
||||
<strong>SYS_SPAWN_REDIR (49)</strong>
|
||||
Spawn a process with its console I/O redirected to the caller.
|
||||
int montauk::spawn_redir(const char* path, const char* args = nullptr);
|
||||
|
||||
<strong>SYS_CHILDIO_READ (50)</strong>
|
||||
Read buffered output produced by a redirected child.
|
||||
int montauk::childio_read(int childPid, char* buf, int maxLen);
|
||||
|
||||
<strong>SYS_CHILDIO_WRITE (51)</strong>
|
||||
Write text input to a redirected child's stdin.
|
||||
int montauk::childio_write(int childPid, const char* data, int len);
|
||||
|
||||
<strong>SYS_CHILDIO_WRITEKEY (52)</strong>
|
||||
Forward a raw key event to a redirected child.
|
||||
int montauk::childio_writekey(int childPid, const montauk::abi::KeyEvent* key);
|
||||
|
||||
<strong>SYS_CHILDIO_SETTERMSZ (53)</strong>
|
||||
Tell a redirected child its terminal dimensions changed.
|
||||
int montauk::childio_settermsz(int childPid, int cols, int rows);
|
||||
|
||||
<strong>WINDOW SERVER</strong>
|
||||
Window server syscalls are used by GUI programs to create and
|
||||
drive an on-screen window (see montauk/Window.hpp for the
|
||||
higher-level win_create/win_poll/win_present wrappers built on
|
||||
top of these).
|
||||
|
||||
<strong>SYS_WINCREATE (54)</strong>
|
||||
Create a window and get its pixel buffer.
|
||||
int montauk::win_create(const char* title, int w, int h,
|
||||
montauk::abi::WinCreateResult* result);
|
||||
|
||||
<strong>SYS_WINDESTROY (55)</strong>
|
||||
Destroy a window.
|
||||
int montauk::win_destroy(int id);
|
||||
|
||||
<strong>SYS_WINPRESENT (56)</strong>
|
||||
Flush the pixel buffer to the screen.
|
||||
uint64_t montauk::win_present(int id);
|
||||
|
||||
<strong>SYS_WINPOLL (57)</strong>
|
||||
Poll the next event (key, mouse, resize, close, scale) for a
|
||||
window.
|
||||
int montauk::win_poll(int id, montauk::abi::WinEvent* event);
|
||||
|
||||
<strong>SYS_WINENUM (58)</strong>
|
||||
Enumerate all windows currently managed by the window server.
|
||||
int montauk::win_enumerate(montauk::abi::WinInfo* info, int max);
|
||||
|
||||
<strong>SYS_WINMAP (59)</strong>
|
||||
Map (or re-map) a window's pixel buffer into the caller's
|
||||
address space.
|
||||
uint64_t montauk::win_map(int id);
|
||||
|
||||
<strong>SYS_WINUNMAP (97)</strong>
|
||||
Unmap a window's pixel buffer from the caller's address space.
|
||||
int montauk::win_unmap(int id);
|
||||
|
||||
<strong>SYS_WINSENDEVENT (60)</strong>
|
||||
Inject an event into a window's event queue.
|
||||
int montauk::win_sendevent(int id, const montauk::abi::WinEvent* event);
|
||||
|
||||
<strong>SYS_WINRESIZE (64)</strong>
|
||||
Resize a window and its pixel buffer.
|
||||
uint64_t montauk::win_resize(int id, int w, int h);
|
||||
|
||||
<strong>SYS_WINSETSCALE (65)</strong>
|
||||
Set the desktop-wide UI scale factor.
|
||||
int montauk::win_setscale(int scale);
|
||||
|
||||
<strong>SYS_WINGETSCALE (66)</strong>
|
||||
Get the desktop-wide UI scale factor.
|
||||
int montauk::win_getscale();
|
||||
|
||||
<strong>SYS_WINSETCURSOR (68)</strong>
|
||||
Set the mouse cursor shown while over a window (0=arrow,
|
||||
1=resize_h, 2=resize_v).
|
||||
int montauk::win_setcursor(int id, int cursor);
|
||||
|
||||
<strong>SYS_WINSETFLAGS (126)</strong>
|
||||
Set window flags (e.g. WIN_FLAG_FULLSCREEN).
|
||||
int montauk::win_setflags(int id, uint32_t flags);
|
||||
|
||||
<strong>DEVICES</strong>
|
||||
<strong>SYS_DEVLIST (63)</strong>
|
||||
Enumerate detected devices (CPU, interrupts, timers, input,
|
||||
USB, network, display, storage, PCI, audio, ACPI) for the
|
||||
device explorer app.
|
||||
int montauk::devlist(montauk::abi::DevInfo* buf, int max);
|
||||
|
||||
<strong>SYS_DISKINFO (69)</strong>
|
||||
Get detailed info for one block device (model, serial, sector
|
||||
size, NCQ/TRIM/SMART support, etc.).
|
||||
int montauk::diskinfo(montauk::abi::DiskInfo* buf, int port);
|
||||
|
||||
<strong>STORAGE</strong>
|
||||
<strong>SYS_PARTLIST (70)</strong>
|
||||
Enumerate GPT partitions across all block devices.
|
||||
int montauk::partlist(montauk::abi::PartInfo* buf, int max);
|
||||
|
||||
<strong>SYS_DISKREAD (71)</strong>
|
||||
Raw, driver-agnostic sector read from a block device.
|
||||
int64_t montauk::disk_read(int blockDev, uint64_t lba,
|
||||
uint32_t sectorCount, void* buf);
|
||||
|
||||
<strong>SYS_DISKWRITE (72)</strong>
|
||||
Raw, driver-agnostic sector write to a block device.
|
||||
int64_t montauk::disk_write(int blockDev, uint64_t lba,
|
||||
uint32_t sectorCount, const void* buf);
|
||||
|
||||
<strong>SYS_GPTINIT (73)</strong>
|
||||
Initialize a fresh GPT partition table on a block device.
|
||||
int montauk::gpt_init(int blockDev);
|
||||
|
||||
<strong>SYS_GPTADD (74)</strong>
|
||||
Add a partition to an existing GPT table.
|
||||
int montauk::gpt_add(const montauk::abi::GptAddParams* params);
|
||||
|
||||
<strong>SYS_FSMOUNT (75)</strong>
|
||||
Mount a partition's filesystem onto a drive number.
|
||||
int montauk::fs_mount(int partIndex, int driveNum);
|
||||
|
||||
<strong>SYS_FSFORMAT (76)</strong>
|
||||
Format a partition with a filesystem (FS_TYPE_FAT32 or
|
||||
FS_TYPE_EXT2).
|
||||
int montauk::fs_format(const montauk::abi::FsFormatParams* params);
|
||||
|
||||
<strong>SYS_FS_SYNC (134)</strong>
|
||||
Flush all block-device write caches and cleanly unmount
|
||||
disk-backed volumes ahead of power-off. Returns the number of
|
||||
volumes unmounted. Part of the graceful shutdown sequence
|
||||
(see SYS_POWER_REQUEST).
|
||||
int montauk::fs_sync();
|
||||
|
||||
<strong>AUDIO</strong>
|
||||
<strong>SYS_AUDIOOPEN (80)</strong>
|
||||
Open a mixer output stream at the given sample rate, channel
|
||||
count, and bit depth. Returns a stream handle.
|
||||
int montauk::audio_open(uint32_t sampleRate, uint8_t channels,
|
||||
uint8_t bitsPerSample);
|
||||
|
||||
<strong>SYS_AUDIOCLOSE (81)</strong>
|
||||
Close an audio stream.
|
||||
void montauk::audio_close(int handle);
|
||||
|
||||
<strong>SYS_AUDIOWRITE (82)</strong>
|
||||
Write PCM samples to an audio stream.
|
||||
int montauk::audio_write(int handle, const void* data, uint32_t size);
|
||||
|
||||
<strong>SYS_AUDIOCTL (83)</strong>
|
||||
Control an audio stream or the global mixer. Commands 0-3 act
|
||||
on the stream named by the handle argument; commands 4-12 act
|
||||
on that stream's routing/mute state or the global master and
|
||||
ignore or reuse the handle as documented below.
|
||||
int montauk::audio_ctl(int handle, int cmd, int value);
|
||||
|
||||
Convenience wrappers (all thin calls onto audio_ctl):
|
||||
audio_set_volume, audio_get_volume AUDIO_CTL_{SET,GET}_VOLUME (0/1)
|
||||
audio_get_pos AUDIO_CTL_GET_POS (2)
|
||||
audio_pause, audio_resume AUDIO_CTL_PAUSE (3)
|
||||
audio_get_output AUDIO_CTL_GET_OUTPUT (4): 0=HDA, 1=Bluetooth
|
||||
audio_set_output AUDIO_CTL_SET_OUTPUT (5): switch all streams
|
||||
(SET_OUTPUT, 5) switch a stream's output route
|
||||
audio_bt_status AUDIO_CTL_BT_STATUS (6)
|
||||
audio_set_master_volume, _get_ AUDIO_CTL_{SET,GET}_MASTER_VOLUME (7/8), 0-100
|
||||
audio_set_mute, audio_get_mute AUDIO_CTL_{SET,GET}_MUTE (9/10), per-stream
|
||||
audio_set_master_mute, _get_ AUDIO_CTL_{SET,GET}_MASTER_MUTE (11/12)
|
||||
|
||||
<strong>SYS_AUDIOLIST (128)</strong>
|
||||
Enumerate active mixer streams (owner PID, name, format,
|
||||
volume, mute/pause state).
|
||||
int montauk::audio_list(montauk::abi::AudioStreamInfo* buf, int maxCount);
|
||||
|
||||
<strong>SYS_AUDIOWAIT (129)</strong>
|
||||
Return the current mixer state serial. With timeoutMs > 0,
|
||||
blocks until the serial differs from prevSerial or the timeout
|
||||
elapses; with timeoutMs == 0 it returns immediately.
|
||||
uint64_t montauk::audio_wait(uint64_t prevSerial, uint64_t timeoutMs);
|
||||
|
||||
<strong>BLUETOOTH</strong>
|
||||
<strong>SYS_BTSCAN (84)</strong>
|
||||
Scan for discoverable Bluetooth devices for up to timeoutMs.
|
||||
int montauk::bt_scan(montauk::abi::BtScanResult* buf, int maxCount,
|
||||
uint32_t timeoutMs);
|
||||
|
||||
<strong>SYS_BTCONNECT (85)</strong>
|
||||
Connect (and pair/bond if needed) to a device by BD_ADDR.
|
||||
int montauk::bt_connect(const uint8_t* bdAddr);
|
||||
|
||||
<strong>SYS_BTDISCONNECT (86)</strong>
|
||||
Disconnect from a device by BD_ADDR.
|
||||
int montauk::bt_disconnect(const uint8_t* bdAddr);
|
||||
|
||||
<strong>SYS_BTLIST (87)</strong>
|
||||
List currently connected devices.
|
||||
int montauk::bt_list(montauk::abi::BtDevInfo* buf, int maxCount);
|
||||
|
||||
<strong>SYS_BTINFO (88)</strong>
|
||||
Get local adapter info (BD_ADDR, name, init/scanning state).
|
||||
int montauk::bt_info(montauk::abi::BtAdapterInfo* buf);
|
||||
|
||||
<strong>SYS_BTSETADDR (137)</strong>
|
||||
Change the adapter's BD_ADDR (6-byte buffer, byte 0 is the
|
||||
least-significant octet). Volatile -- apply after the last
|
||||
controller reset and persist separately to bluetooth.toml.
|
||||
int montauk::bt_set_addr(const uint8_t* bdAddr);
|
||||
|
||||
<strong>SYS_BTBONDS (138)</strong>
|
||||
List bonded (paired) devices.
|
||||
int montauk::bt_bonds(montauk::abi::BtBondInfo* buf, int maxCount);
|
||||
|
||||
<strong>SYS_BTFORGET (139)</strong>
|
||||
Forget a paired device; it must re-pair next time.
|
||||
int montauk::bt_forget(const uint8_t* bdAddr);
|
||||
|
||||
<strong>SOFTWARE-DEFINED RADIO</strong>
|
||||
Receive-only SDR API. Receivers are enumerated by index in
|
||||
[0, SYS_SDR_COUNT); SYS_SDR_OPEN returns a handle used by the
|
||||
rest of the calls. Samples are delivered as interleaved 8-bit
|
||||
unsigned I/Q (CU8, SDR_FORMAT_CU8) from the device's ring
|
||||
buffer. Backed by an RTL-SDR (RTL2832U + R820T2) driver.
|
||||
|
||||
<strong>SYS_SDR_COUNT (140)</strong>
|
||||
Number of available SDR receivers.
|
||||
int montauk::sdr_count();
|
||||
|
||||
<strong>SYS_SDR_INFO (141)</strong>
|
||||
Get static/dynamic info for one receiver by index (name, tuner,
|
||||
frequency/sample-rate ranges, gain steps, present/streaming
|
||||
flags).
|
||||
int montauk::sdr_info(int index, montauk::abi::SdrDeviceInfo* out);
|
||||
|
||||
<strong>SYS_SDR_OPEN (142)</strong>
|
||||
Open a receiver by index. Returns a handle.
|
||||
int montauk::sdr_open(int index);
|
||||
|
||||
<strong>SYS_SDR_CLOSE (143)</strong>
|
||||
Close a receiver handle.
|
||||
int montauk::sdr_close(int handle);
|
||||
|
||||
<strong>SYS_SDR_START (144)</strong>
|
||||
Begin streaming samples.
|
||||
int montauk::sdr_start(int handle);
|
||||
|
||||
<strong>SYS_SDR_STOP (145)</strong>
|
||||
Stop streaming samples.
|
||||
int montauk::sdr_stop(int handle);
|
||||
|
||||
<strong>SYS_SDR_READ (146)</strong>
|
||||
Non-blocking read of queued I/Q samples. Returns bytes copied.
|
||||
int montauk::sdr_read(int handle, void* buf, uint32_t len);
|
||||
|
||||
<strong>SYS_SDR_SETPARAM (147)</strong>
|
||||
Set a tunable parameter (see SDR_PARAM_* below).
|
||||
int montauk::sdr_set_param(int handle, int param, uint64_t value);
|
||||
|
||||
<strong>SYS_SDR_GETPARAM (148)</strong>
|
||||
Get a tunable parameter's current value.
|
||||
int64_t montauk::sdr_get_param(int handle, int param);
|
||||
|
||||
Parameters (montauk::abi::SDR_PARAM_*): FREQ (center frequency,
|
||||
Hz), SAMPLE_RATE (Hz), GAIN_MODE (0=auto/AGC, 1=manual), GAIN
|
||||
(tenths of dB), FREQ_CORR (ppm), AGC (demod digital AGC, 0/1),
|
||||
DIRECT_SAMP (0=off, 1=I, 2=Q). Convenience wrappers exist for
|
||||
each: sdr_set_freq/sdr_get_freq, sdr_set_sample_rate/
|
||||
sdr_get_sample_rate, sdr_set_gain_mode, sdr_set_gain,
|
||||
sdr_set_freq_correction, sdr_set_agc.
|
||||
|
||||
<strong>CLIPBOARD</strong>
|
||||
<strong>SYS_CLIPBOARD_SET_TEXT (119)</strong>
|
||||
Set the system clipboard's text contents (max
|
||||
CLIPBOARD_MAX_TEXT_BYTES, 256 KiB).
|
||||
int montauk::clipboard_set_text(const char* data, uint32_t len);
|
||||
|
||||
<strong>SYS_CLIPBOARD_GET_INFO (120)</strong>
|
||||
Get the clipboard's current size and serial number (for
|
||||
change detection).
|
||||
int montauk::clipboard_get_info(montauk::abi::ClipboardInfo* out);
|
||||
|
||||
<strong>SYS_CLIPBOARD_GET_TEXT (121)</strong>
|
||||
Read the clipboard's text contents.
|
||||
int montauk::clipboard_get_text(char* buf, uint32_t bufLen,
|
||||
uint32_t* outLen, uint64_t* outSerial = nullptr);
|
||||
|
||||
<strong>SYS_CLIPBOARD_CLEAR (122)</strong>
|
||||
Clear the clipboard.
|
||||
int montauk::clipboard_clear();
|
||||
|
||||
<strong>GENERIC IPC</strong>
|
||||
Handle-based IPC primitives underlying streams, mailboxes,
|
||||
waitsets, and shared-memory surfaces (see kernel/src/Ipc/Ipc.hpp).
|
||||
All are accessed via numeric handles with rights-based security
|
||||
and can be waited on with SYS_WAIT_HANDLE or a waitset.
|
||||
|
||||
<strong>SYS_DUPHANDLE (98)</strong>
|
||||
Duplicate a handle (e.g. to hand a copy to a child process).
|
||||
int montauk::dup_handle(int handle);
|
||||
|
||||
<strong>SYS_WAIT_HANDLE (99)</strong>
|
||||
Block until a handle's signals intersect wantedSignals, or
|
||||
timeoutMs elapses. See IPC_SIGNAL_* (READABLE, WRITABLE,
|
||||
PEER_CLOSED, EXITED, READY).
|
||||
uint32_t montauk::wait_handle(int handle, uint32_t wantedSignals,
|
||||
uint64_t timeoutMs = ~0ULL);
|
||||
|
||||
<strong>SYS_STREAM_CREATE (100)</strong>
|
||||
Create a byte-pipe stream, returning a read handle and a write
|
||||
handle.
|
||||
int montauk::stream_create(int* outReadHandle, int* outWriteHandle,
|
||||
uint32_t capacity = 0);
|
||||
|
||||
<strong>SYS_STREAM_READ (101)</strong>
|
||||
Read bytes from a stream handle.
|
||||
int montauk::stream_read(int handle, void* buf, int maxLen);
|
||||
|
||||
<strong>SYS_STREAM_WRITE (102)</strong>
|
||||
Write bytes to a stream handle.
|
||||
int montauk::stream_write(int handle, const void* data, int len);
|
||||
|
||||
<strong>SYS_MAILBOX_CREATE (103)</strong>
|
||||
Create a message-queue mailbox, returning a send handle and a
|
||||
receive handle.
|
||||
int montauk::mailbox_create(int* outSendHandle, int* outRecvHandle);
|
||||
|
||||
<strong>SYS_MAILBOX_SEND (104)</strong>
|
||||
Send a typed message, optionally attaching a handle to
|
||||
transfer to the receiver.
|
||||
int montauk::mailbox_send(int handle, uint32_t msgType, const void* data,
|
||||
uint16_t len, int attachHandle = -1);
|
||||
|
||||
<strong>SYS_MAILBOX_RECV (105)</strong>
|
||||
Receive a message.
|
||||
int montauk::mailbox_recv(int handle, uint32_t* outMsgType, void* data,
|
||||
uint16_t* inOutLen, int* outAttachHandle = nullptr);
|
||||
|
||||
<strong>SYS_WAITSET_CREATE (106)</strong>
|
||||
Create a waitset for multiplexing waits across many handles.
|
||||
int montauk::waitset_create();
|
||||
|
||||
<strong>SYS_WAITSET_ADD (107)</strong>
|
||||
Add a handle and its signal mask to a waitset.
|
||||
int montauk::waitset_add(int waitsetHandle, int targetHandle,
|
||||
uint32_t signals);
|
||||
|
||||
<strong>SYS_WAITSET_REMOVE (108)</strong>
|
||||
Remove an entry from a waitset by index.
|
||||
int montauk::waitset_remove(int waitsetHandle, int index);
|
||||
|
||||
<strong>SYS_WAITSET_WAIT (109)</strong>
|
||||
Block until any member handle's watched signals fire, or
|
||||
timeoutMs elapses.
|
||||
int montauk::waitset_wait(int waitsetHandle, montauk::abi::IpcWaitResult* outReady,
|
||||
uint64_t timeoutMs = ~0ULL);
|
||||
|
||||
<strong>SYS_PROC_OPEN (110)</strong>
|
||||
Open a handle to another process by PID (for waiting on its
|
||||
exit via IPC_SIGNAL_EXITED, etc.).
|
||||
int montauk::proc_open(int pid);
|
||||
|
||||
<strong>SYS_SURFACE_CREATE (111)</strong>
|
||||
Create a shared pixel-buffer surface of byteSize bytes.
|
||||
int montauk::surface_create(uint64_t byteSize);
|
||||
|
||||
<strong>SYS_SURFACE_MAP (112)</strong>
|
||||
Map a surface into the caller's address space.
|
||||
void* montauk::surface_map(int handle);
|
||||
|
||||
<strong>SYS_SURFACE_RESIZE (113)</strong>
|
||||
Resize a surface.
|
||||
int montauk::surface_resize(int handle, uint64_t newSize);
|
||||
|
||||
<strong>SHARED LIBRARIES</strong>
|
||||
<strong>SYS_LOAD_LIB (114)</strong>
|
||||
Load a shared library ELF (.lib) into the caller's address
|
||||
space.
|
||||
int montauk::load_lib(const char* path);
|
||||
|
||||
<strong>SYS_UNLOAD_LIB (115)</strong>
|
||||
Unload a previously loaded library.
|
||||
int montauk::unload_lib(int handle);
|
||||
|
||||
<strong>SYS_DLSYM (116)</strong>
|
||||
Resolve a symbol offset within a loaded library to a callable
|
||||
address.
|
||||
void* montauk::dlsym(int handle, uint64_t symbolOffset);
|
||||
|
||||
<strong>SYS_GETLIBBASE (117)</strong>
|
||||
Get the base virtual address a loaded library was mapped at.
|
||||
uint64_t montauk::get_libbase(int handle);
|
||||
|
||||
<strong>CRASH REPORTING</strong>
|
||||
<strong>SYS_CRASH_REPORT (118)</strong>
|
||||
Retrieve the kernel-filled crash report for the last faulting
|
||||
process (exception vector/name, faulting address, register
|
||||
state, page-fault error bits). Used by the crashpad app.
|
||||
int montauk::crash_report(montauk::abi::CrashReportInfo* out);
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
spawn(2), file(2), framebuffer(2), malloc(3), intro(1)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,355 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: tls-errors(5) - BearSSL TLS and X.509 error codes">
|
||||
<title>tls-errors(5) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>tls-errors(5)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
tls-errors - BearSSL TLS and X.509 error codes
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
MontaukOS uses BearSSL for TLS 1.2 connections. When a TLS
|
||||
operation fails, an integer error code is reported. This page
|
||||
lists all possible error codes.
|
||||
|
||||
<strong>SSL/TLS ENGINE ERRORS</strong>
|
||||
|
||||
<strong>0 BR_ERR_OK</strong>
|
||||
No error.
|
||||
|
||||
<strong>1 BR_ERR_BAD_PARAM</strong>
|
||||
Caller-provided parameter is incorrect.
|
||||
|
||||
<strong>2 BR_ERR_BAD_STATE</strong>
|
||||
Operation cannot be applied in the current engine state.
|
||||
|
||||
<strong>3 BR_ERR_UNSUPPORTED_VERSION</strong>
|
||||
Incoming protocol or record version is unsupported.
|
||||
|
||||
<strong>4 BR_ERR_BAD_VERSION</strong>
|
||||
Incoming record version does not match the expected version.
|
||||
|
||||
<strong>5 BR_ERR_BAD_LENGTH</strong>
|
||||
Incoming record length is invalid.
|
||||
|
||||
<strong>6 BR_ERR_TOO_LARGE</strong>
|
||||
Incoming record is too large, or buffer is too small for the
|
||||
handshake message to send.
|
||||
|
||||
<strong>7 BR_ERR_BAD_MAC</strong>
|
||||
Decryption found invalid padding, or the record MAC is
|
||||
not correct.
|
||||
|
||||
<strong>8 BR_ERR_NO_RANDOM</strong>
|
||||
No initial entropy was provided and none could be obtained
|
||||
from the OS.
|
||||
|
||||
<strong>9 BR_ERR_UNKNOWN_TYPE</strong>
|
||||
Incoming record type is unknown.
|
||||
|
||||
<strong>10 BR_ERR_UNEXPECTED</strong>
|
||||
Incoming record or message has wrong type for the current
|
||||
engine state.
|
||||
|
||||
<strong>12 BR_ERR_BAD_CCS</strong>
|
||||
ChangeCipherSpec message from the peer has invalid contents.
|
||||
|
||||
<strong>13 BR_ERR_BAD_ALERT</strong>
|
||||
Alert message from the peer has invalid contents (odd length).
|
||||
|
||||
<strong>14 BR_ERR_BAD_HANDSHAKE</strong>
|
||||
Incoming handshake message decoding failed.
|
||||
|
||||
<strong>15 BR_ERR_OVERSIZED_ID</strong>
|
||||
ServerHello contains a session ID larger than 32 bytes.
|
||||
|
||||
<strong>16 BR_ERR_BAD_CIPHER_SUITE</strong>
|
||||
Server wants to use a cipher suite that we did not advertise,
|
||||
or we tried to advertise a cipher suite that we do not support.
|
||||
|
||||
<strong>17 BR_ERR_BAD_COMPRESSION</strong>
|
||||
Server wants to use a compression method that we did not
|
||||
advertise.
|
||||
|
||||
<strong>18 BR_ERR_BAD_FRAGLEN</strong>
|
||||
Server's max fragment length does not match client's.
|
||||
|
||||
<strong>19 BR_ERR_BAD_SECRENEG</strong>
|
||||
Secure renegotiation failed.
|
||||
|
||||
<strong>20 BR_ERR_EXTRA_EXTENSION</strong>
|
||||
Server sent an extension type that we did not announce, or
|
||||
used the same extension type more than once in ServerHello.
|
||||
|
||||
<strong>21 BR_ERR_BAD_SNI</strong>
|
||||
Invalid Server Name Indication contents (when used by the
|
||||
server, this extension shall be empty).
|
||||
|
||||
<strong>22 BR_ERR_BAD_HELLO_DONE</strong>
|
||||
Invalid ServerHelloDone from the server (length is not 0).
|
||||
|
||||
<strong>23 BR_ERR_LIMIT_EXCEEDED</strong>
|
||||
Internal limit exceeded (e.g. server's public key is too
|
||||
large).
|
||||
|
||||
<strong>24 BR_ERR_BAD_FINISHED</strong>
|
||||
Finished message from peer does not match the expected value.
|
||||
|
||||
<strong>25 BR_ERR_RESUME_MISMATCH</strong>
|
||||
Session resumption attempted with a different version or
|
||||
cipher suite.
|
||||
|
||||
<strong>26 BR_ERR_INVALID_ALGORITHM</strong>
|
||||
Unsupported or invalid algorithm (ECDHE curve, signature
|
||||
algorithm, hash function).
|
||||
|
||||
<strong>27 BR_ERR_BAD_SIGNATURE</strong>
|
||||
Invalid signature on ServerKeyExchange or CertificateVerify.
|
||||
|
||||
<strong>28 BR_ERR_WRONG_KEY_USAGE</strong>
|
||||
Peer's public key does not have the proper type or is not
|
||||
allowed for the requested operation.
|
||||
|
||||
<strong>29 BR_ERR_NO_CLIENT_AUTH</strong>
|
||||
Client did not send a certificate upon request, or the client
|
||||
certificate could not be validated.
|
||||
|
||||
<strong>31 BR_ERR_IO</strong>
|
||||
I/O error or premature close on the underlying transport.
|
||||
|
||||
<strong>X.509 CERTIFICATE ERRORS</strong>
|
||||
|
||||
<strong>32 BR_ERR_X509_OK</strong>
|
||||
X.509 validation was successful (not an error).
|
||||
|
||||
<strong>33 BR_ERR_X509_INVALID_VALUE</strong>
|
||||
Invalid value in an ASN.1 structure.
|
||||
|
||||
<strong>34 BR_ERR_X509_TRUNCATED</strong>
|
||||
Truncated certificate.
|
||||
|
||||
<strong>35 BR_ERR_X509_EMPTY_CHAIN</strong>
|
||||
Empty certificate chain (no certificate at all).
|
||||
|
||||
<strong>36 BR_ERR_X509_INNER_TRUNC</strong>
|
||||
Inner element extends beyond outer element size.
|
||||
|
||||
<strong>37 BR_ERR_X509_BAD_TAG_CLASS</strong>
|
||||
Unsupported tag class (application or private).
|
||||
|
||||
<strong>38 BR_ERR_X509_BAD_TAG_VALUE</strong>
|
||||
Unsupported tag value.
|
||||
|
||||
<strong>39 BR_ERR_X509_INDEFINITE_LENGTH</strong>
|
||||
Indefinite length encoding found.
|
||||
|
||||
<strong>40 BR_ERR_X509_EXTRA_ELEMENT</strong>
|
||||
Extraneous element in certificate.
|
||||
|
||||
<strong>41 BR_ERR_X509_UNEXPECTED</strong>
|
||||
Unexpected element in certificate.
|
||||
|
||||
<strong>42 BR_ERR_X509_NOT_CONSTRUCTED</strong>
|
||||
Expected constructed element, but found primitive.
|
||||
|
||||
<strong>43 BR_ERR_X509_NOT_PRIMITIVE</strong>
|
||||
Expected primitive element, but found constructed.
|
||||
|
||||
<strong>44 BR_ERR_X509_PARTIAL_BYTE</strong>
|
||||
BIT STRING length is not a multiple of 8.
|
||||
|
||||
<strong>45 BR_ERR_X509_BAD_BOOLEAN</strong>
|
||||
BOOLEAN value has invalid length.
|
||||
|
||||
<strong>46 BR_ERR_X509_OVERFLOW</strong>
|
||||
Value is off-limits (overflow).
|
||||
|
||||
<strong>47 BR_ERR_X509_BAD_DN</strong>
|
||||
Invalid distinguished name.
|
||||
|
||||
<strong>48 BR_ERR_X509_BAD_TIME</strong>
|
||||
Invalid date/time representation in certificate.
|
||||
|
||||
<strong>49 BR_ERR_X509_UNSUPPORTED</strong>
|
||||
Certificate contains unsupported features that cannot be
|
||||
ignored.
|
||||
|
||||
<strong>50 BR_ERR_X509_LIMIT_EXCEEDED</strong>
|
||||
Key or signature size exceeds internal limits.
|
||||
|
||||
<strong>51 BR_ERR_X509_WRONG_KEY_TYPE</strong>
|
||||
Key type does not match that which was expected.
|
||||
|
||||
<strong>52 BR_ERR_X509_BAD_SIGNATURE</strong>
|
||||
Signature is invalid.
|
||||
|
||||
<strong>53 BR_ERR_X509_TIME_UNKNOWN</strong>
|
||||
Validation time is unknown (no time was set).
|
||||
|
||||
<strong>54 BR_ERR_X509_EXPIRED</strong>
|
||||
Certificate is expired or not yet valid.
|
||||
|
||||
<strong>55 BR_ERR_X509_DN_MISMATCH</strong>
|
||||
Issuer/subject DN mismatch in the chain.
|
||||
|
||||
<strong>56 BR_ERR_X509_BAD_SERVER_NAME</strong>
|
||||
Expected server name was not found in the chain.
|
||||
|
||||
<strong>57 BR_ERR_X509_CRITICAL_EXTENSION</strong>
|
||||
Unknown critical extension in certificate.
|
||||
|
||||
<strong>58 BR_ERR_X509_NOT_CA</strong>
|
||||
Not a CA, or path length constraint violation.
|
||||
|
||||
<strong>59 BR_ERR_X509_FORBIDDEN_KEY_USAGE</strong>
|
||||
Key Usage extension prohibits the intended usage.
|
||||
|
||||
<strong>60 BR_ERR_X509_WEAK_PUBLIC_KEY</strong>
|
||||
Public key found in certificate is too small.
|
||||
|
||||
<strong>62 BR_ERR_X509_NOT_TRUSTED</strong>
|
||||
Chain could not be linked to a trust anchor.
|
||||
|
||||
<strong>FATAL ALERTS</strong>
|
||||
When a fatal alert is received from the peer, the error code
|
||||
is 256 + the TLS alert value. When a fatal alert is sent to
|
||||
the peer, the error code is 512 + the TLS alert value.
|
||||
|
||||
Common alert values:
|
||||
0 close_notify
|
||||
10 unexpected_message
|
||||
20 bad_record_mac
|
||||
40 handshake_failure
|
||||
42 bad_certificate
|
||||
43 unsupported_certificate
|
||||
44 certificate_revoked
|
||||
45 certificate_expired
|
||||
46 certificate_unknown
|
||||
47 illegal_parameter
|
||||
48 unknown_ca
|
||||
50 decode_error
|
||||
51 decrypt_error
|
||||
70 protocol_version
|
||||
71 insufficient_security
|
||||
80 internal_error
|
||||
86 unrecognized_name
|
||||
112 no_application_protocol
|
||||
|
||||
For example, error 296 means a handshake_failure alert was
|
||||
received (256 + 40 = 296).
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
fetch(1), syscalls(2)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,182 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS manual page: wiki(1) - Wikipedia article viewer for MontaukOS">
|
||||
<title>wiki(1) - MontaukOS Manual</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">Man Pages</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>wiki(1)</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<pre><code><strong>NAME</strong>
|
||||
wiki - Wikipedia article viewer for MontaukOS
|
||||
|
||||
<strong>SYNOPSIS</strong>
|
||||
wiki <title>
|
||||
wiki -f <title>
|
||||
wiki -s <query>
|
||||
|
||||
<strong>DESCRIPTION</strong>
|
||||
wiki fetches and displays Wikipedia articles in the terminal.
|
||||
It connects to en.wikipedia.org over HTTPS (TLS 1.2) and
|
||||
uses the Wikipedia REST and Action APIs to retrieve article
|
||||
content as plain text.
|
||||
|
||||
Articles are displayed in a fullscreen interactive pager with
|
||||
color-coded headings and word-wrapped text. Multi-word titles
|
||||
are accepted as separate arguments and joined automatically.
|
||||
|
||||
<strong>OPTIONS</strong>
|
||||
<strong>-f</strong>
|
||||
Full article mode. Display the complete article text instead
|
||||
of just the summary. Section headings are color-coded.
|
||||
|
||||
<strong>-s</strong>
|
||||
Search mode. Search Wikipedia for articles matching the
|
||||
query and display a numbered list of up to 10 results.
|
||||
Press a number key to view that article's summary.
|
||||
|
||||
<strong>EXAMPLES</strong>
|
||||
wiki Linux
|
||||
Show a summary of the Linux article.
|
||||
|
||||
wiki -f C programming language
|
||||
Show the full text of the C programming language article.
|
||||
|
||||
wiki -s operating system
|
||||
Search for articles related to "operating system".
|
||||
|
||||
<strong>TLS SUPPORT</strong>
|
||||
Connections use BearSSL for TLS 1.2. Server certificates
|
||||
are validated against the system CA bundle at
|
||||
0:/os/certs/ca-certificates.crt.
|
||||
|
||||
<strong>KEYBOARD</strong>
|
||||
|
||||
<strong>Article pager</strong>
|
||||
j / Down Scroll down one line
|
||||
k / Up Scroll up one line
|
||||
Space / PgDn Scroll down one page
|
||||
b / PgUp Scroll up one page
|
||||
g / Home Jump to top
|
||||
G / End Jump to bottom
|
||||
q Quit pager
|
||||
|
||||
<strong>Search results</strong>
|
||||
1-9, 0 View article (0 = result 10)
|
||||
q Quit search
|
||||
|
||||
<strong>General</strong>
|
||||
Ctrl+Q Abort during network request
|
||||
|
||||
<strong>SEE ALSO</strong>
|
||||
fetch(1), ping(1), nslookup(1), shell(1)</code></pre>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB |
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Bootloader contract">
|
||||
<title>Bootloader contract - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="bootloader.html" class="current">Bootloader contract</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Bootloader contract</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Compositor">
|
||||
<title>Compositor - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="compositor.html" class="current">Compositor</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Compositor</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,157 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - MontaukOS Desktop">
|
||||
<title>MontaukOS Desktop - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="desktop.html" class="current">MontaukOS Desktop</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>MontaukOS Desktop</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<h2>Pages</h2>
|
||||
<ul class="doc-list">
|
||||
<li>
|
||||
<a href="compositor.html">Compositor</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="panel.html">Panel</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="files.html">Files app</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,200 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS Files app (File Manager)">
|
||||
<title>Files app - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
pre { background: #f0f0f0; padding: 0.5em; overflow-x: auto; }
|
||||
code { background: #f0f0f0; padding: 0 0.15em; }
|
||||
pre code { padding: 0; }
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #999;
|
||||
padding: 0.35em 0.5em;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
th { background: #f0f0f0; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.figure {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.figure img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
.figure p {
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
margin: 0.25em 0 0;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="files.html" class="current">Files app</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>File Manager</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h2>Overview</h2>
|
||||
<p>
|
||||
The Files app provides an interface that allows users of MontaukOS to:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Navigate and manage files on available volumes</li>
|
||||
<li>View and access user libraries</li>
|
||||
<li>View and access installed applications</li>
|
||||
<li>View and access installed system configuration applets</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
<div class="figure">
|
||||
<img src="assets/files.png" alt="Files app displaying Computer view with user libraries, ramdisk volume, Settings, and Apps folder.">
|
||||
<p>Files app displaying Computer view with user libraries, ramdisk volume, Settings, and Apps folder.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<h2>Technical notes</h2>
|
||||
<ul>
|
||||
<li>The Files app is one of two windowed applications (the other being the Desktop Settings applet) compiled directly into the desktop's binary (<i>0:/os/desktop.elf</i>) rather than being a separate application. Crashes of the Files app may therefore cause the entire desktop process to crash, kicking the user back to the login screen (<i>0:/os/login.elf</i>).</li>
|
||||
<br>
|
||||
<li>Most of the Files app does not use the MTK toolkit, relying on custom GUI views to render complex file views; however, Properties and delete confirmation windows within the Files app do rely on the MTK toolkit.</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
<hr>
|
||||
|
||||
<h2>Files virtual folders</h2>
|
||||
|
||||
<h3>Apps folder</h3>
|
||||
<div class="figure">
|
||||
<img src="assets/files_apps_view.png" alt="Apps folder in the Files app displaying installed applications on a MontaukOS development build.">
|
||||
<p>Apps folder in the Files app displaying installed applications on a MontaukOS development build.</p>
|
||||
</div>
|
||||
|
||||
<h3>Settings folder</h3>
|
||||
<div class="stub">
|
||||
<p><b>This section is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
<hr>
|
||||
<p class="center">Copyright © 2026 Montauk Operating System Project. All rights reserved.<br><br>Page last revised 26 May 2026.</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Hardware abstraction">
|
||||
<title>Hardware abstraction - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="hal.html" class="current">Hardware abstraction</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Hardware abstraction</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,190 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS Operating System Development Manual">
|
||||
<title>Operating System Development Manual - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html" class="current">OS Development Manual</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Operating System Development Manual</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<h2>Kernel architecture</h2>
|
||||
<ul class="doc-list">
|
||||
<li>
|
||||
<a href="bootloader.html">Bootloader contract</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="smp.html">SMP</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="vfs.html">Virtual File System (VFS)</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="syscalls.html">System calls</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="ipc.html">IPC</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="hal.html">Hardware abstraction</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="networking.html">Networking</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="power.html">Power management</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="winserver.html">Window Server</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Userspace architecture</h2>
|
||||
<ul class="doc-list">
|
||||
<li>
|
||||
<a href="init.html">Init system</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="shell.html">MontaukOS Shell</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="desktop.html">MontaukOS Desktop</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="multiuser.html">Multi-user system</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Init system">
|
||||
<title>Init system - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="init.html" class="current">Init system</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Init system</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - IPC">
|
||||
<title>IPC - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="ipc.html" class="current">IPC</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>IPC</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Multi-user system">
|
||||
<title>Multi-user system - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="multiuser.html" class="current">Multi-user system</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Multi-user system</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Networking">
|
||||
<title>Networking - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="networking.html" class="current">Networking</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Networking</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Panel">
|
||||
<title>Panel - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="panel.html" class="current">Panel</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Panel</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - Power management">
|
||||
<title>Power management - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="power.html" class="current">Power management</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>Power management</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - MontaukOS Shell">
|
||||
<title>MontaukOS Shell - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="shell.html" class="current">MontaukOS Shell</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>MontaukOS Shell</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,144 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="MontaukOS - SMP">
|
||||
<title>SMP - MontaukOS</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 1em;
|
||||
line-height: 1.5;
|
||||
display: flex;
|
||||
gap: 2em;
|
||||
}
|
||||
p { margin: 0 0 0.5em; }
|
||||
h2 { margin: 0.5em 0; }
|
||||
h3 { margin: 0.75em 0 0.35em; }
|
||||
.sidebar {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.sidebar li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.sidebar a {
|
||||
color: #0066CC;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.sidebar a:hover {
|
||||
color: #004499;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.sidebar .current {
|
||||
color: #004499;
|
||||
}
|
||||
.sidebar hr {
|
||||
border: none;
|
||||
border-top: 1px solid #999;
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
a { color: #0000EE; }
|
||||
a:visited { color: #0066CC; }
|
||||
hr { border-style: solid; border-width: 1px 0 0 0; border-color: #999; }
|
||||
.center { text-align: center; }
|
||||
.box {
|
||||
border: 1px solid #999;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
.stub {
|
||||
border: 1px solid #999;
|
||||
background: #f0f0f0;
|
||||
padding: 0.75em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.stub p { margin: 0; }
|
||||
.doc-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.doc-list li {
|
||||
margin: 0.75em 0;
|
||||
}
|
||||
.doc-list a {
|
||||
font-weight: 600;
|
||||
}
|
||||
.doc-list p {
|
||||
margin: 0.25em 0 0 1.5em;
|
||||
color: #555;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
@media (max-width: 700px) {
|
||||
body {
|
||||
flex-direction: column;
|
||||
gap: 1em;
|
||||
padding: 1em 0.75em;
|
||||
}
|
||||
.sidebar {
|
||||
width: auto;
|
||||
}
|
||||
.sidebar ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1em;
|
||||
}
|
||||
.sidebar hr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="sidebar">
|
||||
<ul>
|
||||
<li><a href="../../index.html">Home</a></li>
|
||||
<li><a href="../../downloads.html">Downloads</a></li>
|
||||
<li><a href="../index.html">Documentation</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS/issues">Issue tracker</a></li>
|
||||
<li><a href="https://git.montaukos.org/daniel/MontaukOS">Git</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<ul>
|
||||
<li><a href="index.html">OS Development Manual</a></li>
|
||||
<li><a href="smp.html" class="current">SMP</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="center">
|
||||
<h1>SMP</h1>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="stub">
|
||||
<p><b>This page is a stub.</b> It has been created as a placeholder while the
|
||||
MontaukOS documentation is reorganised, and does not have any content yet.</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="center">
|
||||
<a href="../index.html">Back to Documentation Index</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user