major restructuring, prepare for smp
This commit is contained in:
1 parent
b3569a2b90
commit
6bff37746d
229 files changed
+1617
-1489
No files matched your search
@@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
// boot_info.h - shared between bootx64.efi and kernel.exe
|
||||
// The bootloader fills this structure and passes a pointer to KernelMain.
|
||||
#include <stdint.h>
|
||||
#include <memory_map.h>
|
||||
|
||||
#define BOOT_INFO_MAGIC 0x424F4F54494E464FULL /* 'BOOTINFO' */
|
||||
#define MAX_BOOT_MODULES 32
|
||||
|
||||
typedef struct BOOT_MODULE {
|
||||
char Name[32]; /* e.g. "hal.dll", "ide.sys" */
|
||||
uintptr_t VirtBase; /* Virtual address (mapped) */
|
||||
uintptr_t PhysBase; /* Raw physical address (file) */
|
||||
size_t ImageSize; /* File size / Image size */
|
||||
} BOOT_MODULE;
|
||||
|
||||
typedef struct BOOT_INFO {
|
||||
uint64_t Magic;
|
||||
|
||||
/* Physical memory. */
|
||||
MEMORY_MAP MemoryMap;
|
||||
|
||||
/* GOP framebuffer. */
|
||||
uintptr_t FramebufferBase;
|
||||
uint32_t FramebufferWidth;
|
||||
uint32_t FramebufferHeight;
|
||||
uint32_t FramebufferStride; /* pixels per row */
|
||||
|
||||
/* Loaded modules. */
|
||||
BOOT_MODULE Modules[MAX_BOOT_MODULES];
|
||||
uint32_t ModuleCount;
|
||||
uint32_t _pad1;
|
||||
|
||||
char CommandLine[64];
|
||||
|
||||
/* ACPI RSDP physical address (for APIC, HPET). */
|
||||
uintptr_t RsdpPhysical;
|
||||
|
||||
/* Direct physical window base - always 0xFFFF880000000000. */
|
||||
uintptr_t PhysicalMapBase;
|
||||
|
||||
/* From boot.ini */
|
||||
uint32_t Debug;
|
||||
} BOOT_INFO;
|
||||
@@ -0,0 +1,74 @@
|
||||
// hal_apic.c - Local APIC + I/O APIC
|
||||
#include <kernel/hal/hal_apic.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern HAL_API gApi;
|
||||
|
||||
// ---- Local APIC ----
|
||||
static volatile uint32_t* gLapic = (volatile uint32_t *)0;
|
||||
#define LAPIC_REG(off) (*(volatile uint32_t *)((uint8_t *)gLapic + (off)))
|
||||
#define LAPIC_EOI 0x0B0
|
||||
#define LAPIC_SPURIOUS 0x0F0
|
||||
#define LAPIC_ENABLE 0x100 // bit in spurious vector register
|
||||
|
||||
// ---- I/O APIC ----
|
||||
static volatile uint32_t* gIoapic = (volatile uint32_t *)0;
|
||||
#define IOAPIC_IDX 0x00
|
||||
#define IOAPIC_DATA 0x10
|
||||
|
||||
static uint32_t IoapicRead(uint8_t reg) {
|
||||
*(volatile uint32_t *)((uint8_t *)gIoapic + IOAPIC_IDX) = reg;
|
||||
return *(volatile uint32_t *)((uint8_t *)gIoapic + IOAPIC_DATA);
|
||||
}
|
||||
static void IoapicWrite(uint8_t reg, uint32_t val) {
|
||||
*(volatile uint32_t *)((uint8_t *)gIoapic + IOAPIC_IDX) = reg;
|
||||
*(volatile uint32_t *)((uint8_t *)gIoapic + IOAPIC_DATA) = val;
|
||||
}
|
||||
|
||||
// IRQ n -> redirection table at register 0x10 + 2*n (low), 0x11 + 2*n (high).
|
||||
#define IOREDTBL_LO(n) (0x10 + 2*(n))
|
||||
#define IOREDTBL_HI(n) (0x11 + 2*(n))
|
||||
// Interrupt vector base: IRQ 0 -> vector 0x20, IRQ 1 -> 0x21, ...
|
||||
#define IRQ_VECTOR_BASE 0x20
|
||||
|
||||
void HalApicRouteIrq(uint8_t irq) {
|
||||
// Full initial setup of the redirection entry.
|
||||
// Sets the vector and clears the mask bit in one write.
|
||||
uint32_t vector = IRQ_VECTOR_BASE + irq; // e.g. IRQ 1 -> vector 0x21
|
||||
IoapicWrite(IOREDTBL_LO(irq), vector); // unmasked (bit 16 = 0), edge, fixed
|
||||
IoapicWrite(IOREDTBL_HI(irq), 0); // deliver to LAPIC id 0
|
||||
}
|
||||
|
||||
void HalApicMaskIrq(uint8_t irq) {
|
||||
uint32_t lo = IoapicRead(IOREDTBL_LO(irq));
|
||||
lo |= (1 << 16);
|
||||
IoapicWrite(IOREDTBL_LO(irq), lo);
|
||||
}
|
||||
|
||||
void HalApicUnmaskIrq(uint8_t irq) {
|
||||
uint32_t lo = IoapicRead(IOREDTBL_LO(irq));
|
||||
lo &= ~(1 << 16); // clear mask bit
|
||||
lo = (lo & ~0xFF) | (IRQ_VECTOR_BASE + irq);
|
||||
IoapicWrite(IOREDTBL_LO(irq), lo);
|
||||
IoapicWrite(IOREDTBL_HI(irq), 0); // LAPIC id 0
|
||||
}
|
||||
|
||||
void HalApicSendEoi(void) {
|
||||
LAPIC_REG(LAPIC_EOI) = 0;
|
||||
}
|
||||
|
||||
void HalApicInitialize(uintptr_t lapicPhys, uintptr_t ioapicPhys) {
|
||||
gLapic = (volatile uint32_t *)(gApi.PhysToVirt(lapicPhys));
|
||||
gIoapic = (volatile uint32_t *)(gApi.PhysToVirt(ioapicPhys));
|
||||
|
||||
// Enable Local APIC, set spurious vector to 0xFF.
|
||||
LAPIC_REG(LAPIC_SPURIOUS) = LAPIC_ENABLE | 0xFF;
|
||||
|
||||
// Mask all IOAPIC lines initially.
|
||||
uint32_t maxRedir = (IoapicRead(1) >> 16) & 0xFF;
|
||||
for (uint8_t i = 0; i <= maxRedir; i++) {
|
||||
IoapicWrite(IOREDTBL_LO(i), (1 << 16) | (IRQ_VECTOR_BASE + i));
|
||||
IoapicWrite(IOREDTBL_HI(i), 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// hal_disk.c intentionally left empty.
|
||||
// Disk registration and dispatch live in kernel/io_device.c.
|
||||
@@ -0,0 +1,204 @@
|
||||
// hal_keyboard.c - PS/2 keyboard, interrupt-driven, with live state tracking
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
|
||||
// -- Port addresses ------------------------------------------------------------
|
||||
#define PS2_DATA 0x60u
|
||||
#define PS2_STAT 0x64u
|
||||
#define PS2_CMD 0x64u
|
||||
|
||||
// -- Ring buffer (for ASCII/typing) -------------------------------------------
|
||||
#define KBD_BUF_SIZE 256u
|
||||
#define KBD_BUF_MASK (KBD_BUF_SIZE - 1u)
|
||||
|
||||
static uint8_t gBuf[KBD_BUF_SIZE];
|
||||
static volatile int gHead = 0;
|
||||
static volatile int gTail = 0;
|
||||
|
||||
// -- Deferred callback queue ---------------------------------------------------
|
||||
// The hard IRQ path updates the key state and stores completed make codes.
|
||||
// Optional client callbacks are delivered later from the kernel-queued DPC,
|
||||
// never directly from DIRQL/ISR context.
|
||||
#define KBD_DPC_BUF_SIZE 128u
|
||||
#define KBD_DPC_BUF_MASK (KBD_DPC_BUF_SIZE - 1u)
|
||||
|
||||
static uint8_t gDpcBuf[KBD_DPC_BUF_SIZE];
|
||||
static volatile int gDpcHead = 0;
|
||||
static volatile int gDpcTail = 0;
|
||||
|
||||
// -- Live Key State (for GetAsyncKeyState) ------------------------------------
|
||||
// These arrays track whether a key is currently held down.
|
||||
static volatile uint8_t gKeyState[128]; // Standard scan codes
|
||||
static volatile uint8_t gKeyStateExt[128]; // Extended (E0) scan codes
|
||||
|
||||
// -- Scan-code set 1 constants -------------------------------------------------
|
||||
#define SC1_BREAK_BIT 0x80u
|
||||
#define SC1_MAKE_MASK 0x7Fu
|
||||
#define SC1_EXT_PREFIX 0xE0u
|
||||
|
||||
// -- Modifier flags ------------------------------------------------------------
|
||||
#define MOD_LSHIFT (1u << 0)
|
||||
#define MOD_RSHIFT (1u << 1)
|
||||
#define MOD_LCTRL (1u << 2)
|
||||
#define MOD_RCTRL (1u << 3)
|
||||
#define MOD_LALT (1u << 4)
|
||||
#define MOD_RALT (1u << 5)
|
||||
#define MOD_CAPS (1u << 6)
|
||||
#define MOD_NUMLOCK (1u << 7)
|
||||
|
||||
#define MOD_SHIFT (MOD_LSHIFT | MOD_RSHIFT)
|
||||
#define MOD_CTRL (MOD_LCTRL | MOD_RCTRL)
|
||||
#define MOD_ALT (MOD_LALT | MOD_RALT)
|
||||
|
||||
static volatile uint32_t gModifiers = 0;
|
||||
static volatile bool gNextExtended = false;
|
||||
static void (* volatile gCallback)(uint8_t) = NULL;
|
||||
|
||||
// -- ASCII Translation Tables --------------------------------------------------
|
||||
static const char gUnshifted[128] = {
|
||||
0, 0, '1','2','3','4','5','6','7','8','9','0','-','=','\b','\t',
|
||||
'q','w','e','r','t','y','u','i','o','p','[',']','\n', 0, 'a','s',
|
||||
'd','f','g','h','j','k','l',';','\'','`', 0, '\\','z','x','c','v',
|
||||
'b','n','m',',','.','/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, '7','8','9','-', '4','5','6','+', '1','2','3','0','.'
|
||||
};
|
||||
|
||||
static const char gShifted[128] = {
|
||||
0, 0, '!','@','#','$','%','^','&','*','(',')','_','+','\b','\t',
|
||||
'Q','W','E','R','T','Y','U','I','O','P','{','}','\n', 0, 'A','S',
|
||||
'D','F','G','H','J','K','L',':','"','~', 0, '|','Z','X','C','V',
|
||||
'B','N','M','<','>','?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, '7','8','9','-', '4','5','6','+', '1','2','3','0','.'
|
||||
};
|
||||
|
||||
// -- Deferred callback delivery ------------------------------------------------
|
||||
static void KbdDpc(void) {
|
||||
for (;;) {
|
||||
if (gDpcHead == gDpcTail)
|
||||
break;
|
||||
|
||||
uint8_t make = gDpcBuf[gDpcHead];
|
||||
gDpcHead = (gDpcHead + 1) & (int)KBD_DPC_BUF_MASK;
|
||||
|
||||
void (*cb)(uint8_t) = (void (*)(uint8_t))gCallback;
|
||||
if (cb)
|
||||
cb(make);
|
||||
}
|
||||
}
|
||||
|
||||
// -- ISR: The Engine -----------------------------------------------------------
|
||||
void KbdIsr(void) {
|
||||
uint8_t sc = __inbyte(PS2_DATA);
|
||||
|
||||
if (sc == SC1_EXT_PREFIX) {
|
||||
gNextExtended = true;
|
||||
return;
|
||||
}
|
||||
|
||||
bool extended = gNextExtended;
|
||||
bool is_break = (sc & SC1_BREAK_BIT) != 0;
|
||||
uint8_t make = sc & SC1_MAKE_MASK;
|
||||
gNextExtended = false;
|
||||
|
||||
// Update the live state arrays
|
||||
if (extended) {
|
||||
gKeyStateExt[make] = is_break ? 0 : 1;
|
||||
} else {
|
||||
gKeyState[make] = is_break ? 0 : 1;
|
||||
}
|
||||
|
||||
// Update modifiers
|
||||
if (!extended) {
|
||||
switch (make) {
|
||||
case 0x2A: gModifiers = is_break ? (gModifiers & ~MOD_LSHIFT) : (gModifiers | MOD_LSHIFT); break;
|
||||
case 0x36: gModifiers = is_break ? (gModifiers & ~MOD_RSHIFT) : (gModifiers | MOD_RSHIFT); break;
|
||||
case 0x1D: gModifiers = is_break ? (gModifiers & ~MOD_LCTRL) : (gModifiers | MOD_LCTRL); break;
|
||||
case 0x38: gModifiers = is_break ? (gModifiers & ~MOD_LALT) : (gModifiers | MOD_LALT); break;
|
||||
case 0x3A: if (!is_break) gModifiers ^= MOD_CAPS; break;
|
||||
case 0x45: if (!is_break) gModifiers ^= MOD_NUMLOCK; break;
|
||||
}
|
||||
} else {
|
||||
switch (make) {
|
||||
case 0x1D: gModifiers = is_break ? (gModifiers & ~MOD_RCTRL) : (gModifiers | MOD_RCTRL); break;
|
||||
case 0x38: gModifiers = is_break ? (gModifiers & ~MOD_RALT) : (gModifiers | MOD_RALT); break;
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer management (only for 'make' codes)
|
||||
if (!is_break) {
|
||||
int next = (gTail + 1) & (int)KBD_BUF_MASK;
|
||||
if (next != gHead) {
|
||||
gBuf[gTail] = make;
|
||||
gTail = next;
|
||||
}
|
||||
|
||||
// Queue optional callback delivery to the DPC path. Keep a separate
|
||||
// callback queue so the public ReadScancode ring remains independent.
|
||||
if (gCallback) {
|
||||
int dpcNext = (gDpcTail + 1) & (int)KBD_DPC_BUF_MASK;
|
||||
if (dpcNext != gDpcHead) {
|
||||
gDpcBuf[gDpcTail] = make;
|
||||
gDpcTail = dpcNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- Public API ----------------------------------------------------------------
|
||||
|
||||
// Use this inside your GetAsyncKeyState implementation!
|
||||
bool KbdIsKeyDown(uint8_t scancode, bool extended) {
|
||||
if (extended) return gKeyStateExt[scancode & 0x7F] != 0;
|
||||
return gKeyState[scancode & 0x7F] != 0;
|
||||
}
|
||||
|
||||
static uint8_t KbdPoll(void) {
|
||||
return (gHead == gTail) ? 0 : gBuf[gHead];
|
||||
}
|
||||
|
||||
static uint8_t KbdReadScancode(void) {
|
||||
while (gHead == gTail) { _mm_pause(); }
|
||||
uint8_t sc = gBuf[gHead];
|
||||
gHead = (gHead + 1) & (int)KBD_BUF_MASK;
|
||||
return sc;
|
||||
}
|
||||
|
||||
static char KbdReadChar(void) {
|
||||
uint8_t make = KbdReadScancode();
|
||||
if (make >= 128) return 0;
|
||||
|
||||
uint32_t mods = gModifiers;
|
||||
bool shifted = (mods & MOD_SHIFT) != 0;
|
||||
|
||||
if (mods & MOD_CAPS) {
|
||||
char base = gUnshifted[make];
|
||||
if (base >= 'a' && base <= 'z') shifted = !shifted;
|
||||
}
|
||||
return shifted ? gShifted[make] : gUnshifted[make];
|
||||
}
|
||||
|
||||
static uint32_t KbdGetModifiers(void) {
|
||||
return gModifiers;
|
||||
}
|
||||
|
||||
static void KbdSetCallback(void (*cb)(uint8_t scancode)) {
|
||||
gCallback = cb;
|
||||
}
|
||||
|
||||
void HalKeyboardInitialize(HAL_KEYBOARD *out) {
|
||||
while (__inbyte(PS2_STAT) & 2);
|
||||
__outbyte(PS2_CMD, 0xA8);
|
||||
while (__inbyte(PS2_STAT) & 1) { __inbyte(PS2_DATA); }
|
||||
|
||||
out->Poll = KbdPoll;
|
||||
out->ReadScancode = KbdReadScancode;
|
||||
out->ReadChar = KbdReadChar;
|
||||
out->GetModifiers = KbdGetModifiers;
|
||||
out->SetCallback = KbdSetCallback;
|
||||
out->IsKeyDown = KbdIsKeyDown;
|
||||
out->Isr = KbdIsr;
|
||||
out->Dpc = KbdDpc;
|
||||
out->IrqNumber = 1;
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
// hal_main.c - HAL initialisation and HalGetApi
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/hal/hal_apic.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <intrin.h>
|
||||
#include <common/printf.h>
|
||||
|
||||
// Declared in other hal_*.c files:
|
||||
void HalScreenInitialize(uintptr_t fb, int w, int h, int stride, HAL_SCREEN* out);
|
||||
void HalKeyboardInitialize(HAL_KEYBOARD* out);
|
||||
void HalMouseInitialize(HAL_MOUSE* out, int screenW, int screenH);
|
||||
KSTATUS HalTimerInitialize(uintptr_t lapicPhys, uintptr_t hpetPhys, HAL_TIMER *out);
|
||||
void HalPciInitialize(HAL_PCI* out);
|
||||
extern void HalSetDebugEnabled(int v);
|
||||
|
||||
// Defined in mm_virt.c - HAL needs it for MMIO window access.
|
||||
static uint64_t gPhysBase = 0xFFFF880000000000ULL;
|
||||
|
||||
HAL_API gApi;
|
||||
static int gInitialised = 0;
|
||||
extern void (*gTimerCallback)(void);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ACPI table walker - minimal, just enough to find APIC and HPET addresses.
|
||||
// ---------------------------------------------------------------------------
|
||||
#define ACPI_SIG(a,b,c,d) ((uint32_t)(a)|((uint32_t)(b)<<8)|((uint32_t)(c)<<16)|((uint32_t)(d)<<24))
|
||||
|
||||
#pragma pack(push, 1)
|
||||
#pragma warning(disable : 4200)
|
||||
|
||||
// ACPI RSDP (Root System Description Pointer)
|
||||
typedef struct {
|
||||
char Signature[8];
|
||||
uint8_t Checksum;
|
||||
char OEMID[6];
|
||||
uint8_t Revision;
|
||||
uint32_t RsdtAddress;
|
||||
uint32_t Length;
|
||||
uint64_t XsdtAddress;
|
||||
uint8_t ExtendedChecksum;
|
||||
uint8_t Reserved[3];
|
||||
} RSDP;
|
||||
|
||||
// ACPI SDT Header
|
||||
typedef struct {
|
||||
char Signature[4];
|
||||
uint32_t Length;
|
||||
uint8_t Revision;
|
||||
uint8_t Checksum;
|
||||
char OEMID[6];
|
||||
char OEMTableID[8];
|
||||
uint32_t OEMRevision;
|
||||
uint32_t CreatorID;
|
||||
uint32_t CreatorRevision;
|
||||
} ACPISDTHeader;
|
||||
|
||||
// RSDT (Root System Description Table)
|
||||
typedef struct {
|
||||
ACPISDTHeader Header;
|
||||
uint32_t PointerToOtherSDT[];
|
||||
} RSDT;
|
||||
|
||||
// XSDT (Extended System Description Table)
|
||||
typedef struct {
|
||||
ACPISDTHeader Header;
|
||||
uint64_t PointerToOtherSDT[];
|
||||
} XSDT;
|
||||
|
||||
// MADT (Multiple APIC Description Table)
|
||||
typedef struct {
|
||||
ACPISDTHeader Header;
|
||||
uint32_t LocalAPICAddress;
|
||||
uint32_t Flags;
|
||||
uint8_t Entries[];
|
||||
} MADT;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
static uintptr_t gLapicPhys = 0xFEE00000;
|
||||
static uintptr_t gIoapicPhys = 0xFEC00000;
|
||||
static uintptr_t gHpetPhys = 0xFED00000;
|
||||
|
||||
// ACPI power-off state (filled in by ParseFadt / ScanDsdtForS5)
|
||||
static uint16_t gPm1aCntBlk = 0;
|
||||
static uint16_t gPm1bCntBlk = 0;
|
||||
static uint8_t gSlpTypA = 5; // S5 SLP_TYP for PM1a; 5 is correct for QEMU/most VMs
|
||||
static uint8_t gSlpTypB = 5; // S5 SLP_TYP for PM1b
|
||||
|
||||
int debugEnabled = 0;
|
||||
int _fltused; // fuckass
|
||||
static void hal_putchar(char c) {
|
||||
gApi.Screen.PrintChar(c, 0x00FF00, 0x000000);
|
||||
}
|
||||
void HalDbgPrint(const char* fmt, ...) {
|
||||
if (!debugEnabled) return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void *HalPhysToVirt(uint64_t phys) {
|
||||
return (void *)(phys + gPhysBase);
|
||||
}
|
||||
|
||||
// FADT (Fixed ACPI Description Table) - partial layout up to PM1b_CNT_BLK.
|
||||
// We only need offsets 40 (DSDT ptr), 64 (PM1a_CNT_BLK), 68 (PM1b_CNT_BLK).
|
||||
// The full table is ~276 bytes (ACPI 2.0+); we never access beyond offset 68.
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
ACPISDTHeader Header; // 0 (36 bytes)
|
||||
uint32_t FirmwareCtrl; // 36
|
||||
uint32_t Dsdt; // 40 - physical addr of DSDT
|
||||
uint8_t Reserved1; // 44
|
||||
uint8_t PreferredPmProfile; // 45
|
||||
uint16_t SciInt; // 46
|
||||
uint32_t SmiCmd; // 48
|
||||
uint8_t AcpiEnable; // 52
|
||||
uint8_t AcpiDisable; // 53
|
||||
uint8_t S4BiosReq; // 54
|
||||
uint8_t PstateCnt; // 55
|
||||
uint32_t Pm1aEvtBlk; // 56
|
||||
uint32_t Pm1bEvtBlk; // 60
|
||||
uint32_t Pm1aCntBlk; // 64 - I/O port for PM1a control register
|
||||
uint32_t Pm1bCntBlk; // 68 - I/O port for PM1b control register
|
||||
} FADT;
|
||||
#pragma pack(pop)
|
||||
|
||||
// Scan DSDT AML for the _S5_ Name object and extract SLP_TYP values.
|
||||
// The encoding looks like:
|
||||
// 0x08 '_' 'S' '5' '_' ; NameOp + "_S5_"
|
||||
// 0x12 <PkgLen> ; PackageOp + encoded length
|
||||
// <NumElements> ; usually 0x04
|
||||
// 0x0A <slp_typ_a> ; BytePrefix + value (or 0x00 for zero)
|
||||
// 0x0A <slp_typ_b>
|
||||
static void ScanDsdtForS5(const uint8_t* aml, uint32_t len) {
|
||||
for (uint32_t i = 0; i + 9 < len; i++) {
|
||||
// Match NameOp + "_S5_" + PackageOp
|
||||
if (aml[i] != 0x08) continue;
|
||||
if (aml[i+1] != 0x5F || aml[i+2] != 0x53 ||
|
||||
aml[i+3] != 0x35 || aml[i+4] != 0x5F) continue;
|
||||
if (aml[i+5] != 0x12) continue;
|
||||
|
||||
// PkgLength encoding: low 6 bits = byte count (0 → only this byte);
|
||||
// bits [7:6] = number of following bytes in the length field.
|
||||
uint8_t pkgByte = aml[i+6];
|
||||
uint32_t pkgExtra = pkgByte >> 6; // 0..3 extra bytes
|
||||
uint32_t j = i + 7 + pkgExtra; // skip PkgLen field
|
||||
if (j + 2 >= len) break;
|
||||
j++; // skip NumElements byte
|
||||
|
||||
// SLP_TYP_A
|
||||
if (aml[j] == 0x0A && j + 1 < len) {
|
||||
gSlpTypA = aml[j + 1]; j += 2;
|
||||
} else if (aml[j] == 0x00) {
|
||||
gSlpTypA = 0; j++;
|
||||
} else {
|
||||
break; // unexpected encoding - keep defaults
|
||||
}
|
||||
|
||||
// SLP_TYP_B
|
||||
if (j < len && aml[j] == 0x0A && j + 1 < len)
|
||||
gSlpTypB = aml[j + 1];
|
||||
else if (j < len && aml[j] == 0x00)
|
||||
gSlpTypB = 0;
|
||||
|
||||
HalDbgPrint("HAL: _S5_ SLP_TYP_A=0x%02x SLP_TYP_B=0x%02x\n",
|
||||
gSlpTypA, gSlpTypB);
|
||||
return;
|
||||
}
|
||||
HalDbgPrint("HAL: _S5_ not found in DSDT, using defaults (A=0x%02x B=0x%02x)\n",
|
||||
gSlpTypA, gSlpTypB);
|
||||
}
|
||||
|
||||
static int validate_checksum(void* table, uint32_t length) {
|
||||
uint8_t sum = 0;
|
||||
uint8_t* bytes = (uint8_t*)table;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
sum += bytes[i];
|
||||
}
|
||||
return sum == 0;
|
||||
}
|
||||
|
||||
static void ParseFadt(const FADT* fadt) {
|
||||
gPm1aCntBlk = (uint16_t)fadt->Pm1aCntBlk;
|
||||
gPm1bCntBlk = (uint16_t)fadt->Pm1bCntBlk;
|
||||
HalDbgPrint("HAL: FADT PM1a_CNT=0x%04x PM1b_CNT=0x%04x DSDT=0x%08x\n",
|
||||
gPm1aCntBlk, gPm1bCntBlk, fadt->Dsdt);
|
||||
|
||||
if (fadt->Dsdt) {
|
||||
const ACPISDTHeader* dsdt =
|
||||
(const ACPISDTHeader*)((uintptr_t)fadt->Dsdt + gPhysBase);
|
||||
if (memcmp(dsdt->Signature, "DSDT", 4) == 0 &&
|
||||
validate_checksum((void*)dsdt, dsdt->Length)) {
|
||||
const uint8_t* aml = (const uint8_t*)(dsdt + 1);
|
||||
ScanDsdtForS5(aml, dsdt->Length - (uint32_t)sizeof(ACPISDTHeader));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ParseMadt(MADT* madt) {
|
||||
gLapicPhys = madt->LocalAPICAddress;
|
||||
uint8_t* p = (uint8_t *)(madt + 1);
|
||||
uint8_t* end = (uint8_t *)madt + madt->Header.Length;
|
||||
while (p < end) {
|
||||
uint8_t type = p[0], len = p[1];
|
||||
if (type == 1) { // I/O APIC entry
|
||||
gIoapicPhys = *(uint32_t *)(p + 4);
|
||||
}
|
||||
p += len ? len : 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void WalkAcpi(uintptr_t rsdpPhys) {
|
||||
if (!rsdpPhys) return;
|
||||
|
||||
// Map RSDP into virtual memory
|
||||
RSDP* rsdp = (RSDP*)(rsdpPhys + gPhysBase);
|
||||
|
||||
if (memcmp(rsdp->Signature, "RSD PTR ", 8) != 0) {
|
||||
HalDbgPrint("HAL: Invalid RSDP signature!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ACPISDTHeader* sdtHeader = NULL;
|
||||
uint32_t count = 0;
|
||||
int isXsdt = 0;
|
||||
|
||||
// Safely find and validate the XSDT (ACPI 2.0+)
|
||||
if (rsdp->Revision >= 2 && rsdp->XsdtAddress != 0) {
|
||||
XSDT* xsdt = (XSDT*)(rsdp->XsdtAddress + gPhysBase);
|
||||
|
||||
if (validate_checksum(xsdt, xsdt->Header.Length) &&
|
||||
memcmp(xsdt->Header.Signature, "XSDT", 4) == 0) {
|
||||
|
||||
sdtHeader = &xsdt->Header;
|
||||
count = (xsdt->Header.Length - sizeof(ACPISDTHeader)) / 8;
|
||||
isXsdt = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to safely finding and validating the RSDT (ACPI 1.0)
|
||||
if (sdtHeader == NULL && rsdp->RsdtAddress != 0) {
|
||||
RSDT* rsdt = (RSDT*)((uintptr_t)rsdp->RsdtAddress + gPhysBase);
|
||||
|
||||
if (validate_checksum(rsdt, rsdt->Header.Length) &&
|
||||
memcmp(rsdt->Header.Signature, "RSDT", 4) == 0) {
|
||||
|
||||
sdtHeader = &rsdt->Header;
|
||||
count = (rsdt->Header.Length - sizeof(ACPISDTHeader)) / 4;
|
||||
isXsdt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (sdtHeader == NULL) {
|
||||
HalDbgPrint("HAL: No valid XSDT or RSDT found!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Walk the tables
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uintptr_t tablePhys = 0;
|
||||
|
||||
if (isXsdt) {
|
||||
tablePhys = ((XSDT*)sdtHeader)->PointerToOtherSDT[i];
|
||||
} else {
|
||||
tablePhys = ((RSDT*)sdtHeader)->PointerToOtherSDT[i];
|
||||
}
|
||||
|
||||
// Paranoid alignment check to reject firmware garbage pointers
|
||||
if (!tablePhys || (tablePhys & 3) != 0) continue;
|
||||
|
||||
ACPISDTHeader* header = (ACPISDTHeader*)(tablePhys + gPhysBase);
|
||||
|
||||
// Ensure the table isn't corrupt before parsing
|
||||
if (!validate_checksum(header, header->Length)) continue;
|
||||
|
||||
// Parse MADT
|
||||
if (memcmp(header->Signature, "APIC", 4) == 0) {
|
||||
ParseMadt((MADT*)header);
|
||||
}
|
||||
// Parse FADT (signature "FACP") for PM1 control ports and DSDT pointer
|
||||
else if (memcmp(header->Signature, "FACP", 4) == 0) {
|
||||
ParseFadt((const FADT*)header);
|
||||
}
|
||||
// Parse HPET
|
||||
else if (memcmp(header->Signature, "HPET", 4) == 0) {
|
||||
// HPET base address block is located at offset 44
|
||||
// (which is 8 bytes past the 36-byte ACPISDTHeader)
|
||||
gHpetPhys = *(uint64_t*)((uint8_t*)header + 44) & ~0xFFFULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HalShutdown - ACPI S5 soft power-off. Does not return.
|
||||
// ---------------------------------------------------------------------------
|
||||
static void HalShutdown(void) {
|
||||
// Silence! Stop the world!
|
||||
_disable();
|
||||
|
||||
// SLP_EN (bit 13) | SLP_TYPx (bits 12:10)
|
||||
uint16_t valA = (uint16_t)((gSlpTypA << 10) | (1 << 13));
|
||||
uint16_t valB = (uint16_t)((gSlpTypB << 10) | (1 << 13));
|
||||
|
||||
if (gPm1aCntBlk) __outword(gPm1aCntBlk, valA);
|
||||
if (gPm1bCntBlk) __outword(gPm1bCntBlk, valB);
|
||||
|
||||
// Die!
|
||||
while (true) __halt();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) {
|
||||
if (gInitialised) return STATUS_ALREADY_EXISTS;
|
||||
gPhysBase = initParams->PhysicalWindowBase;
|
||||
gApi.PhysToVirt = HalPhysToVirt;
|
||||
debugEnabled = initParams->Debug;
|
||||
|
||||
// Screen first so DbgPrint works immediately.
|
||||
HalScreenInitialize(initParams->FramebufferBase + initParams->PhysicalWindowBase,
|
||||
(int)initParams->FramebufferWidth,
|
||||
(int)initParams->FramebufferHeight,
|
||||
(int)initParams->FramebufferStride,
|
||||
&gApi.Screen);
|
||||
|
||||
printf_set_putchar(hal_putchar);
|
||||
|
||||
HalDbgPrint("HAL: screen %dx%d\n",
|
||||
initParams->FramebufferWidth, initParams->FramebufferHeight);
|
||||
|
||||
// Parse ACPI to find LAPIC / IOAPIC / HPET addresses.
|
||||
if (initParams->RsdpPhysical) WalkAcpi(initParams->RsdpPhysical);
|
||||
HalDbgPrint("HAL: LAPIC=%016llx IOAPIC=%016llx HPET=%016llx\n",
|
||||
gLapicPhys, gIoapicPhys, gHpetPhys);
|
||||
|
||||
HalApicInitialize(gLapicPhys, gIoapicPhys);
|
||||
HalKeyboardInitialize(&gApi.Keyboard);
|
||||
HalMouseInitialize(&gApi.Mouse,
|
||||
(int)initParams->FramebufferWidth,
|
||||
(int)initParams->FramebufferHeight);
|
||||
|
||||
KSTATUS ts = HalTimerInitialize(gLapicPhys, gHpetPhys, &gApi.Timer);
|
||||
if (!KSUCCESS(ts)) HalDbgPrint("HAL: HPET unavailable, using fallback\n");
|
||||
|
||||
HalPciInitialize(&gApi.Pci);
|
||||
gApi.Intc.RouteIrq = HalApicRouteIrq;
|
||||
gApi.Intc.MaskIrq = HalApicMaskIrq;
|
||||
gApi.Intc.UnmaskIrq = HalApicUnmaskIrq;
|
||||
gApi.Intc.SendEoi = HalApicSendEoi;
|
||||
|
||||
gApi.Power.Shutdown = HalShutdown;
|
||||
|
||||
gApi.Version = HAL_API_VERSION;
|
||||
gInitialised = 1;
|
||||
|
||||
// This shouldn't be here but i have no other way.
|
||||
// After i send 0xF4 and move the mouse the controller sends an interrupts that doesn't fire
|
||||
// and because the CPU missed the IRQ 12 edge transition (cli) later when we expect it to fire, it doesn't
|
||||
// leaving us with no mouse and no keyboard. The dreaded 8042 will hold the CLOCK line low forever.
|
||||
// To combat this we just go about our day executing the rest of the init and just before the end
|
||||
// of init we just iterate 256 times to grab whatever the controller left for us ("Drain and Pray").
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (__inbyte(0x64) & 0x01) {
|
||||
__inbyte(0x60); // Consume
|
||||
}
|
||||
gApi.Timer.Stall(1); // Micro-stalls
|
||||
}
|
||||
|
||||
HalDbgPrint("HAL: initialisation complete.\n");
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void HalRegisterScheduler(void (*pKeSchedulerTick)(void)) {
|
||||
gTimerCallback = pKeSchedulerTick;
|
||||
}
|
||||
|
||||
__declspec(dllexport) const HAL_API* HalGetApi(void) { return &gApi; }
|
||||
@@ -0,0 +1,306 @@
|
||||
// hal_mouse.c - PS/2 auxiliary mouse driver
|
||||
//
|
||||
// Sits on IRQ 12, sharing the 8042 controller with the keyboard (IRQ 1).
|
||||
// hal_keyboard.c has already issued command 0xA8 (enable aux port) by the
|
||||
// time HalMouseInitialize is called, so we only need to:
|
||||
// 1. Enable aux interrupts in the 8042 command byte (bit 1, clears bit 5).
|
||||
// 2. Send the standard mouse init sequence (reset → defaults → enable).
|
||||
// 3. Wire up the ISR and expose the HAL_MOUSE vtable.
|
||||
//
|
||||
// Packet format - standard 3-byte PS/2 mouse (no scroll wheel):
|
||||
// Byte 0: [YOV][XOV][YS][XS][1][M][R][L]
|
||||
// Byte 1: delta-X unsigned magnitude (sign in byte-0 bit 4)
|
||||
// Byte 2: delta-Y unsigned magnitude (sign in byte-0 bit 5); PS/2 Y+ = up
|
||||
//
|
||||
// The ISR accumulates the 3-byte packet, converts it to a screen-space delta
|
||||
// (Y-axis flipped), and queues the completed packet to the HAL's kernel-queued
|
||||
// bottom half. Cursor drawing, public event enqueue, and optional callbacks
|
||||
// run from that bottom half instead of hard IRQ context.
|
||||
//
|
||||
// Threading model
|
||||
// ---------------
|
||||
// Public events are produced by the mouse DPC and consumed by callers.
|
||||
// Raw completed packets are produced by the ISR and consumed by the same DPC.
|
||||
// On SMP, add acquire/release barriers around the ring head/tail pairs.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/hal/hal_debug.h>
|
||||
|
||||
// -- PS/2 port addresses -------------------------------------------------------
|
||||
#define PS2_DATA 0x60u
|
||||
#define PS2_STAT 0x64u
|
||||
#define PS2_CMD 0x64u
|
||||
|
||||
// -- Ring buffer ---------------------------------------------------------------
|
||||
#define MOUSE_BUF_SIZE 64u
|
||||
#define MOUSE_BUF_MASK (MOUSE_BUF_SIZE - 1u)
|
||||
|
||||
typedef struct {
|
||||
int dx, dy;
|
||||
uint8_t buttons; // bit0=left, bit1=right, bit2=middle
|
||||
} MOUSE_EVENT;
|
||||
|
||||
static MOUSE_EVENT gBuf[MOUSE_BUF_SIZE];
|
||||
static volatile int gHead = 0; // public consumer read pointer
|
||||
static volatile int gTail = 0; // public producer write pointer (updated by DPC)
|
||||
|
||||
// Completed hardware packets waiting for the DPC bottom half.
|
||||
static MOUSE_EVENT gDpcBuf[MOUSE_BUF_SIZE];
|
||||
static volatile int gDpcHead = 0;
|
||||
static volatile int gDpcTail = 0;
|
||||
|
||||
// -- Absolute cursor position & screen bounds ----------------------------------
|
||||
static volatile int gMouseX = 0;
|
||||
static volatile int gMouseY = 0;
|
||||
static volatile int gBoundsW = 1920;
|
||||
static volatile int gBoundsH = 1080;
|
||||
|
||||
// -- 3-byte packet assembly ----------------------------------------------------
|
||||
static volatile uint8_t gPacket[3];
|
||||
static volatile int gPacketIdx = 0; // 0, 1, or 2
|
||||
|
||||
// -- Optional callback (called from mouse DPC) ---------------------------------
|
||||
static void (* volatile gCallback)(int dx, int dy, uint8_t buttons) = NULL;
|
||||
|
||||
// -- Cursor helpers provided by hal_screen.c -----------------------------------
|
||||
// Declared here; defined in hal_screen.c.
|
||||
extern void HalScreenDrawCursor(int x, int y);
|
||||
extern void HalScreenEraseCursor(void);
|
||||
|
||||
// -- 8042 low-level helpers ----------------------------------------------------
|
||||
// Wait until the 8042's input buffer is empty (bit 1 of status = 0) so we
|
||||
// can safely write to PS2_DATA or PS2_CMD.
|
||||
static void Ps2WaitInput(void) {
|
||||
int timeout = 100000;
|
||||
while ((__inbyte(PS2_STAT) & 0x02u) && --timeout) { /* spin */ }
|
||||
}
|
||||
|
||||
// Wait until the 8042's output buffer has data (bit 0 of status = 1).
|
||||
static void Ps2WaitOutput(void) {
|
||||
int timeout = 5000000;
|
||||
while (!(__inbyte(PS2_STAT) & 0x01u) && --timeout) { /* spin */ }
|
||||
}
|
||||
|
||||
// Reads from PS2_DATA until the output buffer is empty
|
||||
static void Ps2FlushOutput(void) {
|
||||
int timeout = 100000;
|
||||
while ((__inbyte(PS2_STAT) & 0x01u) && --timeout) {
|
||||
__inbyte(PS2_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
// Route the next write to the auxiliary (mouse) device rather than the keyboard.
|
||||
static void MouseSendByte(uint8_t byte) {
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_CMD, 0xD4u); // "write to mouse" prefix
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_DATA, byte);
|
||||
}
|
||||
|
||||
// Read one byte from the PS/2 output buffer (shared by keyboard and mouse).
|
||||
static uint8_t MouseRecvByte(void) {
|
||||
Ps2WaitOutput();
|
||||
return __inbyte(PS2_DATA);
|
||||
}
|
||||
|
||||
// -- Deferred mouse bottom half -------------------------------------------------
|
||||
static void MouseDpc(void) {
|
||||
for (;;) {
|
||||
if (gDpcHead == gDpcTail)
|
||||
break;
|
||||
|
||||
MOUSE_EVENT ev = gDpcBuf[gDpcHead];
|
||||
gDpcHead = (gDpcHead + 1) & (int)MOUSE_BUF_MASK;
|
||||
|
||||
// Update absolute position and redraw the cursor outside DIRQL.
|
||||
HalScreenEraseCursor();
|
||||
|
||||
int nx = gMouseX + ev.dx;
|
||||
int ny = gMouseY + ev.dy;
|
||||
if (nx < 0) nx = 0;
|
||||
if (nx >= gBoundsW) nx = gBoundsW - 1;
|
||||
if (ny < 0) ny = 0;
|
||||
if (ny >= gBoundsH) ny = gBoundsH - 1;
|
||||
gMouseX = nx;
|
||||
gMouseY = ny;
|
||||
|
||||
HalScreenDrawCursor(nx, ny);
|
||||
|
||||
// Publish event to the normal consumer ring.
|
||||
int next = (gTail + 1) & (int)MOUSE_BUF_MASK;
|
||||
if (next != gHead) {
|
||||
gBuf[gTail] = ev;
|
||||
gTail = next;
|
||||
}
|
||||
|
||||
void (*cb)(int, int, uint8_t) = (void (*)(int, int, uint8_t))gCallback;
|
||||
if (cb)
|
||||
cb(ev.dx, ev.dy, ev.buttons);
|
||||
}
|
||||
}
|
||||
|
||||
// -- Interrupt service routine --------------------------------------------------
|
||||
// Called with interrupts disabled by KeIrqDispatch (IRQ 12).
|
||||
// The dispatcher sends the APIC EOI after this function returns.
|
||||
static void MouseIsr(void) {
|
||||
uint8_t data = __inbyte(PS2_DATA);
|
||||
|
||||
// Synchronisation guard: the first byte of every packet has bit 3 set.
|
||||
// If we lose sync, wait for the next valid first byte before assembling.
|
||||
if (gPacketIdx == 0) {
|
||||
if (!(data & 0x08u)) return; // not a valid first byte - discard
|
||||
}
|
||||
|
||||
gPacket[gPacketIdx++] = data;
|
||||
if (gPacketIdx < 3) return; // packet not yet complete
|
||||
gPacketIdx = 0;
|
||||
|
||||
// -- Decode flags ----------------------------------------------------------
|
||||
uint8_t flags = gPacket[0];
|
||||
|
||||
// Overflow bits: if either is set the movement data is meaningless.
|
||||
if (flags & 0xC0u) return;
|
||||
|
||||
uint8_t buttons = flags & 0x07u;
|
||||
|
||||
// Convert unsigned magnitudes + sign bits to signed 9-bit values.
|
||||
int dx = (int)gPacket[1] - ((flags & 0x10u) ? 256 : 0);
|
||||
int dy = (int)gPacket[2] - ((flags & 0x20u) ? 256 : 0);
|
||||
dy = -dy; // PS/2 Y+ = up; screen Y+ = down, so invert.
|
||||
|
||||
// -- Queue completed packet to the DPC bottom half -------------------------
|
||||
int next = (gDpcTail + 1) & (int)MOUSE_BUF_MASK;
|
||||
if (next != gDpcHead) {
|
||||
gDpcBuf[gDpcTail].dx = dx;
|
||||
gDpcBuf[gDpcTail].dy = dy;
|
||||
gDpcBuf[gDpcTail].buttons = buttons;
|
||||
gDpcTail = next;
|
||||
}
|
||||
}
|
||||
|
||||
// -- Consumer API --------------------------------------------------------------
|
||||
static bool MousePoll(void) {
|
||||
return gHead != gTail;
|
||||
}
|
||||
|
||||
static bool MouseRead(int* dx, int* dy, uint8_t* buttons) {
|
||||
if (gHead == gTail) return false;
|
||||
*dx = gBuf[gHead].dx;
|
||||
*dy = gBuf[gHead].dy;
|
||||
*buttons = gBuf[gHead].buttons;
|
||||
gHead = (gHead + 1) & (int)MOUSE_BUF_MASK;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void MouseGetPosition(int* x, int* y) {
|
||||
*x = gMouseX;
|
||||
*y = gMouseY;
|
||||
}
|
||||
|
||||
static void MouseSetPosition(int x, int y) {
|
||||
gMouseX = x;
|
||||
gMouseY = y;
|
||||
}
|
||||
|
||||
static void MouseSetBounds(int w, int h) {
|
||||
gBoundsW = w;
|
||||
gBoundsH = h;
|
||||
}
|
||||
|
||||
static void MouseSetCallback(void (*cb)(int dx, int dy, uint8_t buttons)) {
|
||||
gCallback = cb;
|
||||
}
|
||||
|
||||
// -- Initializer ---------------------------------------------------------------
|
||||
// Called from HalInitialize in hal_main.c after the screen is ready.
|
||||
// hal_keyboard.c has already sent 0xA8 (enable aux port) to port 0x64.
|
||||
void HalMouseInitialize(HAL_MOUSE* out, int screenW, int screenH) {
|
||||
gBoundsW = screenW;
|
||||
gBoundsH = screenH;
|
||||
gMouseX = screenW / 2;
|
||||
gMouseY = screenH / 2;
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 0: Deafen the controller and flush
|
||||
// -----------------------------------------------------------------
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_CMD, 0xA7u); // Disable Aux port (ignores mouse)
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_CMD, 0xADu); // Disable Main port (ignores keyboard)
|
||||
|
||||
// Now flush the buffer. Because the ports are disabled, we guarantee
|
||||
// no new data can sneak in behind our backs.
|
||||
Ps2FlushOutput();
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 1: Safely configure the 8042 Command Byte
|
||||
// -----------------------------------------------------------------
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_CMD, 0x20u); // read current command byte
|
||||
Ps2WaitOutput();
|
||||
uint8_t cmd = __inbyte(PS2_DATA);
|
||||
|
||||
cmd |= 0x02u; // enable aux interrupt
|
||||
cmd &= ~0x20u; // clear "disable aux clock"
|
||||
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_CMD, 0x60u); // write command byte
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_DATA, cmd);
|
||||
|
||||
// Re-enable both ports now that config is safely written
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_CMD, 0xA8u); // Enable Aux port
|
||||
Ps2WaitInput();
|
||||
__outbyte(PS2_CMD, 0xAEu); // Enable Main port
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 2: Reset mouse (0xFF).
|
||||
// -----------------------------------------------------------------
|
||||
MouseSendByte(0xFFu); // reset
|
||||
|
||||
// The BIOS or an earlier phase might have left a movement packet
|
||||
// partially in flight right as we re-enabled the port.
|
||||
// We loop and discard anything that isn't the 0xFA ACK.
|
||||
uint8_t ack;
|
||||
int retries = 10;
|
||||
do {
|
||||
ack = MouseRecvByte();
|
||||
} while (ack != 0xFAu && --retries > 0);
|
||||
|
||||
// Thanks to the increased Ps2WaitOutput timeout, we will actually
|
||||
// wait long enough for the physical hardware self-test to finish here.
|
||||
MouseRecvByte(); // 0xAA - BAT passed
|
||||
MouseRecvByte(); // 0x00 - device ID
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 3: Set default parameters (sampling rate 100, 4 counts/mm).
|
||||
// Mouse replies: ACK.
|
||||
// -----------------------------------------------------------------
|
||||
MouseSendByte(0xF6u); // set defaults
|
||||
MouseRecvByte(); // ACK
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Step 4: Enable data reporting.
|
||||
// Mouse will now send 3-byte packets on every movement/button event.
|
||||
// -----------------------------------------------------------------
|
||||
MouseSendByte(0xF4u); // enable data reporting
|
||||
MouseRecvByte(); // ACK
|
||||
|
||||
// Draw the initial cursor at the centre of the screen.
|
||||
HalScreenDrawCursor(gMouseX, gMouseY);
|
||||
|
||||
// Populate vtable.
|
||||
out->Poll = MousePoll;
|
||||
out->Read = MouseRead;
|
||||
out->SetCallback = MouseSetCallback;
|
||||
out->GetPosition = MouseGetPosition;
|
||||
out->SetPosition = MouseSetPosition;
|
||||
out->SetBounds = MouseSetBounds;
|
||||
out->Isr = MouseIsr;
|
||||
out->Dpc = MouseDpc;
|
||||
out->IrqNumber = 12;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// hal_pci.c - PCI configuration space via Mechanism 1 port I/O
|
||||
#include <stdint.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
|
||||
#define PCI_CONFIG_ADDR 0xCF8
|
||||
#define PCI_CONFIG_DATA 0xCFC
|
||||
|
||||
static uint32_t MakeAddr(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t reg) {
|
||||
return 0x80000000UL
|
||||
| ((uint32_t)bus << 16)
|
||||
| ((uint32_t)(dev & 0x1F) << 11)
|
||||
| ((uint32_t)(fn & 0x07) << 8)
|
||||
| (reg & 0xFC);
|
||||
}
|
||||
|
||||
static uint32_t PciRead32(uint8_t bus, uint8_t dev, uint8_t fn, uint8_t reg) {
|
||||
__outdword(PCI_CONFIG_ADDR, MakeAddr(bus, dev, fn, reg));
|
||||
return __indword(PCI_CONFIG_DATA);
|
||||
}
|
||||
|
||||
static void PciWrite32(uint8_t bus, uint8_t dev, uint8_t fn,
|
||||
uint8_t reg, uint32_t val) {
|
||||
__outdword(PCI_CONFIG_ADDR, MakeAddr(bus, dev, fn, reg));
|
||||
__outdword(PCI_CONFIG_DATA, val);
|
||||
}
|
||||
|
||||
static bool PciFindDevice(uint16_t vendor, uint16_t device,
|
||||
uint8_t* outBus, uint8_t* outDev, uint8_t* outFn) {
|
||||
for (int bus = 0; bus < 256; bus++)
|
||||
for (int dev = 0; dev < 32; dev++)
|
||||
for (int fn = 0; fn < 8; fn++) {
|
||||
uint32_t id = PciRead32((uint8_t)bus, (uint8_t)dev, (uint8_t)fn, 0);
|
||||
if ((id & 0xFFFF) == vendor && (id >> 16) == device) {
|
||||
*outBus = (uint8_t)bus; *outDev = (uint8_t)dev; *outFn = (uint8_t)fn;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool PciFindClass(uint8_t classCode, uint8_t subclass,
|
||||
uint8_t* outBus, uint8_t* outDev, uint8_t* outFn) {
|
||||
for (int bus = 0; bus < 256; bus++)
|
||||
for (int dev = 0; dev < 32; dev++)
|
||||
for (int fn = 0; fn < 8; fn++) {
|
||||
uint32_t id = PciRead32((uint8_t)bus,(uint8_t)dev,(uint8_t)fn,0);
|
||||
if ((id & 0xFFFF) == 0xFFFF) continue; // no device
|
||||
uint32_t cc = PciRead32((uint8_t)bus,(uint8_t)dev,(uint8_t)fn,8);
|
||||
if (((cc >> 24) & 0xFF) == classCode &&
|
||||
((cc >> 16) & 0xFF) == subclass) {
|
||||
*outBus = (uint8_t)bus; *outDev = (uint8_t)dev; *outFn = (uint8_t)fn;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint8_t PciGetIrqLine(uint8_t bus, uint8_t dev, uint8_t fn) {
|
||||
// PCI config register 0x3C: byte 0 = IRQ line, byte 1 = IRQ pin
|
||||
uint32_t val = PciRead32(bus, dev, fn, 0x3C);
|
||||
return (uint8_t)(val & 0xFF); // 0xFF = not connected
|
||||
}
|
||||
|
||||
void HalPciInitialize(HAL_PCI* out) {
|
||||
out->Read32 = PciRead32;
|
||||
out->Write32 = PciWrite32;
|
||||
out->FindDevice = PciFindDevice;
|
||||
out->FindClass = PciFindClass;
|
||||
out->GetIrqLine = PciGetIrqLine;
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
// hal_screen.c - GOP framebuffer screen driver with embedded 8x16 font
|
||||
//
|
||||
// The 8x16 bitmap font is a standard VGA/PC font. We embed just the
|
||||
// printable ASCII range (0x20–0x7E). Each glyph is 16 bytes; bit 7 of
|
||||
// each byte is the leftmost pixel.
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
|
||||
static uint32_t* gFb = (uint32_t *)0;
|
||||
static int gWidth = 0;
|
||||
static int gHeight = 0;
|
||||
static int gStride = 0; // pixels per row
|
||||
static int gCursorX = 0, gCursorY = 0;
|
||||
|
||||
#define FONT_W 8
|
||||
#define FONT_H 16
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 8x16 VGA font - printable ASCII only (glyph 0 = space = 0x20)
|
||||
// ---------------------------------------------------------------------------
|
||||
#include <kernel/hal/font8x16.h> // see font8x16.h below
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Internals
|
||||
// ---------------------------------------------------------------------------
|
||||
static inline void PutPixelRaw(int x, int y, uint32_t color) {
|
||||
if ((unsigned)x < (unsigned)gWidth && (unsigned)y < (unsigned)gHeight)
|
||||
gFb[y * gStride + x] = color;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HAL_SCREEN implementations
|
||||
// ---------------------------------------------------------------------------
|
||||
static void ScreenClear(uint32_t color) {
|
||||
for (int y = 0; y < gHeight; y++)
|
||||
for (int x = 0; x < gWidth; x++)
|
||||
gFb[y * gStride + x] = color;
|
||||
gCursorX = 0; gCursorY = 0;
|
||||
}
|
||||
|
||||
static void ScreenPutPixel(int x, int y, uint32_t color) {
|
||||
PutPixelRaw(x, y, color);
|
||||
}
|
||||
|
||||
static void ScreenBlit(int x, int y, int w, int h, const uint32_t* pixels) {
|
||||
// Calculate clipping bounds
|
||||
int startX = (x < 0) ? 0 : x;
|
||||
int startY = (y < 0) ? 0 : y;
|
||||
int endX = (x + w > gWidth) ? gWidth : (x + w);
|
||||
int endY = (y + h > gHeight) ? gHeight : (y + h);
|
||||
|
||||
int drawW = endX - startX;
|
||||
int drawH = endY - startY;
|
||||
|
||||
// If the image is entirely off-screen, do nothing
|
||||
if (drawW <= 0 || drawH <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust the source pointer if the image was clipped on the top or left
|
||||
int srcOffsetX = startX - x;
|
||||
int srcOffsetY = startY - y;
|
||||
|
||||
// Calculate bytes to copy per row
|
||||
size_t bytesPerRow = (size_t)drawW * sizeof(uint32_t);
|
||||
|
||||
// If the width we are drawing exactly matches the physical screen stride,
|
||||
// AND it matches the original source image width, memory is perfectly contiguous.
|
||||
if (drawW == gStride && drawW == w) {
|
||||
uint32_t* dst = gFb + (startY * gStride) + startX;
|
||||
const uint32_t* src = pixels + (srcOffsetY * w) + srcOffsetX;
|
||||
|
||||
memcpy(dst, src, bytesPerRow * drawH);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy row-by-row
|
||||
for (int row = 0; row < drawH; row++) {
|
||||
// Destination in the actual framebuffer (accounting for stride)
|
||||
uint32_t* dst = gFb + ((startY + row) * gStride) + startX;
|
||||
|
||||
// Source from the provided pixel buffer
|
||||
const uint32_t* src = pixels + ((srcOffsetY + row) * w) + srcOffsetX;
|
||||
|
||||
memcpy(dst, src, bytesPerRow);
|
||||
}
|
||||
}
|
||||
|
||||
static void ScreenGetSize(int* w, int* h) { *w = gWidth; *h = gHeight; }
|
||||
|
||||
static void ScreenScroll(int lines) {
|
||||
if (lines <= 0 || lines >= gHeight) { ScreenClear(0); return; }
|
||||
int rowBytes = gStride * sizeof(uint32_t);
|
||||
memmove(gFb, gFb + lines * gStride,
|
||||
(size_t)(gHeight - lines) * rowBytes);
|
||||
// Clear the revealed rows at the bottom.
|
||||
memset(gFb + (gHeight - lines) * gStride, 0,
|
||||
(size_t)lines * rowBytes);
|
||||
}
|
||||
|
||||
static void ScreenPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) {
|
||||
if ((unsigned char)c < 0x20 || (unsigned char)c > 0x7E) c = '?';
|
||||
const uint8_t* glyph = Font8x16 + ((unsigned char)c - 0x20) * 16;
|
||||
for (int row = 0; row < 16; row++) {
|
||||
uint8_t bits = glyph[row];
|
||||
for (int col = 0; col < 8; col++) {
|
||||
uint32_t color = (bits & (1 << col)) ? fg : bg; // i don't know why, i don't want to know why
|
||||
// i shouldn't have to wonder why but the font is backwards unless we do this terriblness
|
||||
PutPixelRaw(x + col, y + row, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ScreenPrintChar(char c, uint32_t fg, uint32_t bg) {
|
||||
int cols = gWidth / FONT_W;
|
||||
int rows = gHeight / FONT_H;
|
||||
|
||||
// Mirror to serial
|
||||
while ((__inbyte(0x3FD) & 0x20) == 0);
|
||||
if (c == '\n') __outbyte(0x3F8, '\r');
|
||||
__outbyte(0x3F8, c);
|
||||
|
||||
if (c == '\n') {
|
||||
gCursorX = 0;
|
||||
gCursorY++;
|
||||
} else if (c == '\r') {
|
||||
gCursorX = 0;
|
||||
} else if (c == '\b') {
|
||||
if (gCursorX > 0) {
|
||||
gCursorX--;
|
||||
} else if (gCursorY > 0) {
|
||||
gCursorY--;
|
||||
gCursorX = cols - 1;
|
||||
}
|
||||
} else {
|
||||
ScreenPutChar(c, gCursorX * FONT_W, gCursorY * FONT_H, fg, bg);
|
||||
if (++gCursorX >= cols) { gCursorX = 0; gCursorY++; }
|
||||
}
|
||||
if (gCursorY >= rows) {
|
||||
ScreenScroll(FONT_H);
|
||||
gCursorY = rows - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mouse cursor – 12×19 monochrome arrow sprite
|
||||
// 0 = transparent 1 = black outline 2 = white fill
|
||||
// The hotspot is at (0,0) (top-left pixel = tip of the arrow).
|
||||
// ---------------------------------------------------------------------------
|
||||
#define CURSOR_W 16
|
||||
#define CURSOR_H 25
|
||||
|
||||
// Each entry is 0xAARRGGBB stored as a uint32_t.
|
||||
// Pixels with brightness < 24 are fully transparent.
|
||||
static const unsigned int gCursorPixels[CURSOR_H][CURSOR_W] = {
|
||||
{ 0x7F575757, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFAFAFA, 0x855D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFEFEFE, 0xFFFFFFFF, 0x865E5E5E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFFFFFF, 0x8A626262, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x8D656565, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x8E666666, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x91696969, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x946C6C6C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x956D6D6D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x99717171, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x9B737373, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x9D757575, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0xA0787878, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFCFCFC, 0xFFF9F9F9, 0xFFFFFFFF, 0xA1797979, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xAE868686, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFFFFFF, 0xFDD5D5D5, 0xF4CCCCCC, 0xF4CCCCCC, 0xF4CCCCCC, 0xF3CBCBCB, 0xF1C9C9C9, 0xFAD2D2D2, 0x7E565656 },
|
||||
{ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFEFEFEF, 0xFFFCFCFC, 0xFFFFFFFF, 0x9A727272, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x7F575757, 0x8F676767, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE0E0E0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x90686868, 0x00000000, 0x00000000, 0xFFEBEBEB, 0xFFFFFFFF, 0xFFFFFFFF, 0x81595959, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFDFDFD, 0xFFFFFFFF, 0x8C646464, 0x00000000, 0x00000000, 0x00000000, 0xB78F8F8F, 0xFFFFFFFF, 0xFFFEFEFE, 0xECC4C4C4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0xFFFFFFFF, 0x90686868, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50282828, 0xFFF9F9F9, 0xFFFFFFFF, 0xFFFFFFFF, 0x623A3A3A, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0x865E5E5E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xD8B0B0B0, 0xFFFFFFFF, 0xFFFFFFFF, 0xCDA5A5A5, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6C444444, 0xFFFFFFFF, 0xFFFCFCFC, 0xFFF4F4F4, 0x4D252525, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF2CACACA, 0xFFFFFFFF, 0xFFFFFFFF, 0x99717171, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8E666666, 0xEFC7C7C7, 0x7D555555, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
};
|
||||
|
||||
// Saved framebuffer pixels beneath the current cursor position.
|
||||
static uint32_t gCursorSavedBg[CURSOR_W * CURSOR_H];
|
||||
static int gCursorDrawX = -1; // -1 = cursor not yet drawn
|
||||
static int gCursorDrawY = -1;
|
||||
|
||||
// Save the background rectangle [x,y,CURSOR_W,CURSOR_H] from the framebuffer.
|
||||
static void CursorSaveBg(int x, int y) {
|
||||
for (int row = 0; row < CURSOR_H; row++) {
|
||||
int fy = y + row;
|
||||
for (int col = 0; col < CURSOR_W; col++) {
|
||||
int fx = x + col;
|
||||
uint32_t px = 0;
|
||||
if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight)
|
||||
px = gFb[fy * gStride + fx];
|
||||
gCursorSavedBg[row * CURSOR_W + col] = px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the background previously saved by CursorSaveBg.
|
||||
static void CursorRestoreBg(int x, int y) {
|
||||
for (int row = 0; row < CURSOR_H; row++) {
|
||||
int fy = y + row;
|
||||
for (int col = 0; col < CURSOR_W; col++) {
|
||||
int fx = x + col;
|
||||
if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight)
|
||||
gFb[fy * gStride + fx] = gCursorSavedBg[row * CURSOR_W + col];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Alpha-blend a cursor pixel over a framebuffer pixel.
|
||||
// Framebuffer format: 0x00RRGGBB (high byte ignored / always 0).
|
||||
// Cursor pixel format: 0xAARRGGBB.
|
||||
static inline uint32_t BlendPixel(uint32_t dst, uint32_t src) {
|
||||
uint8_t a = (uint8_t)(src >> 24);
|
||||
if (a == 0) return dst;
|
||||
if (a == 255) return src & 0x00FFFFFFu;
|
||||
|
||||
uint8_t sr = (uint8_t)(src >> 16);
|
||||
uint8_t sg = (uint8_t)(src >> 8);
|
||||
uint8_t sb = (uint8_t)(src );
|
||||
uint8_t dr = (uint8_t)(dst >> 16);
|
||||
uint8_t dg = (uint8_t)(dst >> 8);
|
||||
uint8_t db = (uint8_t)(dst );
|
||||
|
||||
uint8_t na = 255 - a;
|
||||
uint8_t or_ = (uint8_t)((a * sr + na * dr) >> 8);
|
||||
uint8_t og = (uint8_t)((a * sg + na * dg) >> 8);
|
||||
uint8_t ob = (uint8_t)((a * sb + na * db) >> 8);
|
||||
return ((uint32_t)or_ << 16) | ((uint32_t)og << 8) | ob;
|
||||
}
|
||||
|
||||
// Draw the arrow sprite at (x,y), saving the background first.
|
||||
// Called from hal_mouse.c via extern declaration.
|
||||
void HalScreenDrawCursor(int x, int y) {
|
||||
CursorSaveBg(x, y);
|
||||
gCursorDrawX = x;
|
||||
gCursorDrawY = y;
|
||||
for (int row = 0; row < CURSOR_H; row++) {
|
||||
int fy = y + row;
|
||||
for (int col = 0; col < CURSOR_W; col++) {
|
||||
uint32_t src = gCursorPixels[row][col];
|
||||
if ((src >> 24) == 0) continue; // fully transparent, skip
|
||||
int fx = x + col;
|
||||
if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) {
|
||||
uint32_t* dst = &gFb[fy * gStride + fx];
|
||||
*dst = BlendPixel(*dst, src);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Erase the cursor by restoring the saved background.
|
||||
void HalScreenEraseCursor(void) {
|
||||
if (gCursorDrawX < 0) return; // never drawn
|
||||
CursorRestoreBg(gCursorDrawX, gCursorDrawY);
|
||||
gCursorDrawX = -1;
|
||||
gCursorDrawY = -1;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Populate the HAL_SCREEN vtable
|
||||
// ---------------------------------------------------------------------------
|
||||
void HalScreenInitialize(uintptr_t fb, int w, int h, int stride,
|
||||
HAL_SCREEN* out) {
|
||||
gFb = (uint32_t *)fb;
|
||||
gWidth = w;
|
||||
gHeight = h;
|
||||
gStride = stride;
|
||||
out->Clear = ScreenClear;
|
||||
out->PutPixel = ScreenPutPixel;
|
||||
out->Blit = ScreenBlit;
|
||||
out->GetSize = ScreenGetSize;
|
||||
out->Scroll = ScreenScroll;
|
||||
out->PutChar = ScreenPutChar;
|
||||
out->PrintChar = ScreenPrintChar;
|
||||
out->DrawCursor = HalScreenDrawCursor;
|
||||
out->EraseCursor = HalScreenEraseCursor;
|
||||
ScreenClear(0x000000);
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
// hal_timer.c - Local APIC timer
|
||||
#include <stdint.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/hal/hal_apic.h>
|
||||
#include <kernel/hal/hal_debug.h>
|
||||
|
||||
#define LAPIC_TIMER_LVT 0x320
|
||||
#define LAPIC_TIMER_INIT 0x380
|
||||
#define LAPIC_TIMER_CUR 0x390
|
||||
#define LAPIC_TIMER_DIV 0x3E0
|
||||
|
||||
#define TIMER_VECTOR 0x20 // vector 0x20 = IRQ 0 slot in IDT
|
||||
#define TIMER_PERIODIC (1 << 17)
|
||||
#define TIMER_MASKED (1 << 16)
|
||||
#define TIMER_DIV16 0x3
|
||||
|
||||
#define TIMER_PERIOD_MS 10
|
||||
#define PIT_HZ 1193182UL
|
||||
|
||||
extern HAL_API gApi;
|
||||
static volatile uint32_t* gLapic = NULL;
|
||||
static uint32_t gApicTicksPerUs = 0;
|
||||
static uint32_t gApicTicksPerQuantum = 0;
|
||||
static uint64_t gHpetTicksPerUs = 0;
|
||||
static uint64_t gHpetHz = 0;
|
||||
static volatile uint64_t* gHpet = NULL;
|
||||
#define LAPIC_REG(off) (*(volatile uint32_t *)((uint8_t *)gLapic + (off)))
|
||||
|
||||
static volatile uint64_t gTicks = 0;
|
||||
void (* volatile gTimerCallback)(void) = NULL;
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val) { __outbyte(port, val); }
|
||||
static inline uint8_t inb(uint16_t port) { return __inbyte(port); }
|
||||
|
||||
static void TimerIsr(void) {
|
||||
gTicks++;
|
||||
if (gTimerCallback) gTimerCallback();
|
||||
// EOI is sent by KeIrqDispatch after this returns
|
||||
}
|
||||
|
||||
static uint64_t QueryTicks(void) {
|
||||
// Prefer HPET when present: it is already a monotonically increasing main
|
||||
// counter and avoids races between the LAPIC periodic tick and the current
|
||||
// countdown register.
|
||||
if (gHpet && gHpetHz) {
|
||||
uint64_t counter = gHpet[30]; // offset 0xF0: main counter
|
||||
uint64_t seconds = counter / gHpetHz;
|
||||
uint64_t rem = counter % gHpetHz;
|
||||
return seconds * 1000000ull + (rem * 1000000ull) / gHpetHz;
|
||||
}
|
||||
|
||||
// Fallback: combine the coarse periodic tick with the current LAPIC
|
||||
// countdown. Read the coarse tick twice so an interrupt between gTicks and
|
||||
// LAPIC_TIMER_CUR cannot make time jump backwards by almost one quantum.
|
||||
uint64_t baseTicks1;
|
||||
uint64_t baseTicks2;
|
||||
uint32_t current;
|
||||
do {
|
||||
baseTicks1 = gTicks;
|
||||
current = LAPIC_REG(LAPIC_TIMER_CUR);
|
||||
baseTicks2 = gTicks;
|
||||
} while (baseTicks1 != baseTicks2);
|
||||
|
||||
uint32_t initial = gApicTicksPerQuantum;
|
||||
|
||||
uint32_t elapsedApicTicks = 0;
|
||||
if (current < initial) {
|
||||
elapsedApicTicks = initial - current;
|
||||
}
|
||||
|
||||
uint64_t subQuantumUs = 0;
|
||||
if (gApicTicksPerUs)
|
||||
subQuantumUs = elapsedApicTicks / gApicTicksPerUs;
|
||||
|
||||
return (baseTicks1 * TIMER_PERIOD_MS * 1000ull) + subQuantumUs;
|
||||
}
|
||||
|
||||
static void Stall(uint32_t us) {
|
||||
// Priority 1: Use HPET (Highest Precision)
|
||||
if (gHpet && gHpetTicksPerUs > 0) {
|
||||
uint64_t start = gHpet[30]; // Offset 0xF0 is the Main Counter
|
||||
uint64_t delta = (uint64_t)us * gHpetTicksPerUs;
|
||||
while ((gHpet[30] - start) < delta) {
|
||||
_mm_pause(); // Hint to CPU we are in a spin-loop
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Priority 2: Use APIC Timer (High Precision, but handles wrap-around)
|
||||
if (gApicTicksPerUs > 0) {
|
||||
uint32_t total_ticks = us * gApicTicksPerUs;
|
||||
uint32_t last_val = LAPIC_REG(LAPIC_TIMER_CUR);
|
||||
uint32_t elapsed = 0;
|
||||
|
||||
while (elapsed < total_ticks) {
|
||||
uint32_t current = LAPIC_REG(LAPIC_TIMER_CUR);
|
||||
if (current < last_val) {
|
||||
elapsed += (last_val - current);
|
||||
} else if (current > last_val) {
|
||||
// Handled the reset/wrap at the end of TIMER_PERIOD_MS
|
||||
uint32_t init_val = LAPIC_REG(LAPIC_TIMER_INIT);
|
||||
elapsed += (last_val + (init_val - current));
|
||||
}
|
||||
last_val = current;
|
||||
_mm_pause();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Priority 3: Fallback to Port 0x80
|
||||
for (uint32_t i = 0; i < us; i++) {
|
||||
__outbyte(0x80, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns HPET ticks/second, or 0 if HPET is unavailable.
|
||||
static uint64_t HpetInit(uintptr_t hpetPhys) {
|
||||
// HPET register block, accessed through physical window
|
||||
volatile uint64_t *hpet = (volatile uint64_t *)gApi.PhysToVirt(hpetPhys);
|
||||
|
||||
// General Capabilities register (offset 0):
|
||||
// Bits 63:32 = counter clock period in femtoseconds
|
||||
uint64_t caps = hpet[0];
|
||||
uint32_t periodFs = (uint32_t)(caps >> 32);
|
||||
if (!periodFs) return 0;
|
||||
|
||||
// Enable the HPET main counter (General Configuration, offset 0x10, bit 0)
|
||||
hpet[2] |= 1; // offset 0x10 / 8 = index 2
|
||||
|
||||
return (uint64_t)1e15 / periodFs; // ticks per second
|
||||
}
|
||||
|
||||
// Use PIT channel 2 one-shot to measure how many APIC ticks elapse in 10 ms, then return ticks/ms.
|
||||
static uint32_t CalibrateApicTimerViaPit(void) {
|
||||
// Divide by 16
|
||||
LAPIC_REG(LAPIC_TIMER_DIV) = TIMER_DIV16;
|
||||
// Mask, set vector - leave masked during calibration
|
||||
LAPIC_REG(LAPIC_TIMER_LVT) = TIMER_MASKED | TIMER_VECTOR;
|
||||
// Load max count
|
||||
LAPIC_REG(LAPIC_TIMER_INIT) = 0xFFFFFFFF;
|
||||
|
||||
// PIT channel 2: one-shot, ~10 ms
|
||||
uint16_t pit_count = (uint16_t)(PIT_HZ / 100); // 11931 ≈ 10 ms
|
||||
outb(0x61, (inb(0x61) & ~0x02) | 0x01); // gate on, speaker off
|
||||
outb(0x43, 0xB0); // ch2, lo/hi, mode 0, binary
|
||||
outb(0x42, (uint8_t)(pit_count & 0xFF));
|
||||
outb(0x42, (uint8_t)(pit_count >> 8));
|
||||
|
||||
// Wait for OUT2 (bit 5 of port 0x61) to go high
|
||||
while (!(inb(0x61) & 0x20)) _mm_pause();
|
||||
|
||||
uint32_t elapsed = 0xFFFFFFFF - LAPIC_REG(LAPIC_TIMER_CUR);
|
||||
return elapsed / 10; // ticks per ms
|
||||
}
|
||||
|
||||
// Use HPET main counter to measure how many APIC ticks elapse in 10 ms, then return ticks/ms.
|
||||
static uint32_t CalibrateApicTimerViaHpet(uintptr_t hpetPhys) {
|
||||
volatile uint64_t *hpet = (volatile uint64_t *)gApi.PhysToVirt(hpetPhys);
|
||||
uint64_t hpetHz = HpetInit(hpetPhys);
|
||||
if (!hpetHz) return 0;
|
||||
|
||||
// Ticks in 10 ms = hpetHz / 100
|
||||
uint64_t ticksFor10ms = hpetHz / 100;
|
||||
|
||||
LAPIC_REG(LAPIC_TIMER_DIV) = TIMER_DIV16;
|
||||
LAPIC_REG(LAPIC_TIMER_LVT) = TIMER_MASKED | TIMER_VECTOR;
|
||||
LAPIC_REG(LAPIC_TIMER_INIT) = 0xFFFFFFFF;
|
||||
|
||||
// HPET main counter is at offset 0xF0, index 0xF0/8 = 30
|
||||
uint64_t hpetStart = hpet[30];
|
||||
while ((hpet[30] - hpetStart) < ticksFor10ms) _mm_pause();
|
||||
|
||||
uint32_t elapsed = 0xFFFFFFFF - LAPIC_REG(LAPIC_TIMER_CUR);
|
||||
return elapsed / 10; // APIC ticks per ms
|
||||
}
|
||||
|
||||
KSTATUS HalTimerInitialize(uintptr_t lapicPhys, uintptr_t hpetPhys, HAL_TIMER *out) {
|
||||
gLapic = (volatile uint32_t *)gApi.PhysToVirt(lapicPhys);
|
||||
HalDbgPrint("HAL: lapicPhys=0x%llX, lapicVirt=0x%llX, hpetPhys=0x%llX\n", lapicPhys, (uintptr_t)gLapic, hpetPhys);
|
||||
|
||||
uint32_t ticksPerMs = 0;
|
||||
if (hpetPhys) {
|
||||
HalDbgPrint("HAL: Attempting to calibrate APIC timer via HPET...\n");
|
||||
ticksPerMs = CalibrateApicTimerViaHpet(hpetPhys);
|
||||
}
|
||||
if (!ticksPerMs) {
|
||||
HalDbgPrint("HAL: HPET unavailable or calibration failed. Falling back to PIT...\n");
|
||||
ticksPerMs = CalibrateApicTimerViaPit();
|
||||
}
|
||||
if (!ticksPerMs) {
|
||||
HalDbgPrint("HAL: APIC calibration failed. How?\n");
|
||||
return STATUS_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
gApicTicksPerUs = ticksPerMs / 1000;
|
||||
gApicTicksPerQuantum = ticksPerMs * TIMER_PERIOD_MS;
|
||||
|
||||
if (hpetPhys) {
|
||||
gHpet = (volatile uint64_t*)gApi.PhysToVirt(hpetPhys);
|
||||
uint64_t hpetHz = HpetInit(hpetPhys);
|
||||
gHpetHz = hpetHz;
|
||||
gHpetTicksPerUs = hpetHz / 1000000;
|
||||
}
|
||||
|
||||
HalDbgPrint("HAL: APIC timer: %u ticks/ms\n", ticksPerMs);
|
||||
|
||||
// Configure LAPIC timer in periodic mode
|
||||
LAPIC_REG(LAPIC_TIMER_DIV) = TIMER_DIV16;
|
||||
LAPIC_REG(LAPIC_TIMER_LVT) = TIMER_PERIODIC | TIMER_VECTOR;
|
||||
LAPIC_REG(LAPIC_TIMER_INIT) = gApicTicksPerQuantum;
|
||||
|
||||
out->QueryTicks = QueryTicks;
|
||||
out->Stall = Stall;
|
||||
out->Isr = TimerIsr;
|
||||
out->IrqNumber = 0;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
#pragma once
|
||||
// font8x16.h - embedded 8x16 VGA bitmap font, printable ASCII (0x20-0x7E)
|
||||
// 95 glyphs × 16 bytes = 1520 bytes total.
|
||||
// Each byte is one scanline; bit 7 = leftmost pixel.
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint8_t Font8x16[95 * 16] = {
|
||||
// 0x20 ' '
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x21 '!'
|
||||
0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x22 '"'
|
||||
0x00,0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x23 '#'
|
||||
0x00,0x36,0x36,0x7F,0x36,0x36,0x36,0x7F,0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x24 '$'
|
||||
0x18,0x18,0x3E,0x63,0x03,0x03,0x1E,0x30,0x30,0x63,0x3E,0x18,0x18,0x00,0x00,0x00,
|
||||
// 0x25 '%'
|
||||
0x00,0x00,0x63,0x33,0x18,0x0C,0x06,0x31,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x26 '&'
|
||||
0x00,0x1C,0x36,0x1C,0x0E,0x3B,0x33,0x33,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x27 '\''
|
||||
0x00,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x28 '('
|
||||
0x00,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x29 ')'
|
||||
0x00,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x2A '*'
|
||||
0x00,0x00,0x00,0x33,0x1E,0xFF,0x1E,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x2B '+'
|
||||
0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x2C ','
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x08,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x2D '-'
|
||||
0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x2E '.'
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x2F '/'
|
||||
0x00,0x60,0x30,0x18,0x0C,0x06,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x30 '0'
|
||||
0x00,0x3C,0x66,0x66,0x76,0x6E,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x31 '1'
|
||||
0x00,0x18,0x1C,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x32 '2'
|
||||
0x00,0x3C,0x66,0x60,0x30,0x18,0x0C,0x66,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x33 '3'
|
||||
0x00,0x3C,0x66,0x60,0x38,0x60,0x60,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x34 '4'
|
||||
0x00,0x30,0x38,0x3C,0x36,0x33,0x7F,0x30,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x35 '5'
|
||||
0x00,0x7E,0x06,0x06,0x3E,0x60,0x60,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x36 '6'
|
||||
0x00,0x38,0x0C,0x06,0x3E,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x37 '7'
|
||||
0x00,0x7E,0x60,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x38 '8'
|
||||
0x00,0x3C,0x66,0x66,0x3C,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x39 '9'
|
||||
0x00,0x3C,0x66,0x66,0x7C,0x60,0x30,0x18,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x3A ':'
|
||||
0x00,0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x3B ';'
|
||||
0x00,0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x3C '<'
|
||||
0x00,0x60,0x30,0x18,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x3D '='
|
||||
0x00,0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x3E '>'
|
||||
0x00,0x06,0x0C,0x18,0x30,0x18,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x3F '?'
|
||||
0x00,0x3C,0x66,0x60,0x30,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x40 '@'
|
||||
0x00,0x3E,0x63,0x7B,0x7B,0x7B,0x3B,0x03,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x41 'A'
|
||||
0x00,0x18,0x3C,0x66,0x66,0x7E,0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x42 'B'
|
||||
0x00,0x3E,0x66,0x66,0x3E,0x66,0x66,0x66,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x43 'C'
|
||||
0x00,0x3C,0x66,0x06,0x06,0x06,0x06,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x44 'D'
|
||||
0x00,0x1E,0x36,0x66,0x66,0x66,0x66,0x36,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x45 'E'
|
||||
0x00,0x7E,0x06,0x06,0x3E,0x06,0x06,0x06,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x46 'F'
|
||||
0x00,0x7E,0x06,0x06,0x3E,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x47 'G'
|
||||
0x00,0x3C,0x66,0x06,0x06,0x76,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x48 'H'
|
||||
0x00,0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x49 'I'
|
||||
0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x4A 'J'
|
||||
0x00,0x78,0x30,0x30,0x30,0x30,0x36,0x36,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x4B 'K'
|
||||
0x00,0x66,0x36,0x1E,0x0E,0x1E,0x36,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x4C 'L'
|
||||
0x00,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x4D 'M'
|
||||
0x00,0x63,0x77,0x7F,0x6B,0x63,0x63,0x63,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x4E 'N'
|
||||
0x00,0x63,0x67,0x6F,0x7B,0x73,0x63,0x63,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x4F 'O'
|
||||
0x00,0x3C,0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x50 'P'
|
||||
0x00,0x3E,0x66,0x66,0x3E,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x51 'Q'
|
||||
0x00,0x3C,0x66,0x66,0x66,0x66,0x6E,0x3C,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x52 'R'
|
||||
0x00,0x3E,0x66,0x66,0x3E,0x36,0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x53 'S'
|
||||
0x00,0x3C,0x66,0x06,0x1C,0x30,0x60,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x54 'T'
|
||||
0x00,0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x55 'U'
|
||||
0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x56 'V'
|
||||
0x00,0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x57 'W'
|
||||
0x00,0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x58 'X'
|
||||
0x00,0x66,0x66,0x3C,0x18,0x3C,0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x59 'Y'
|
||||
0x00,0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x5A 'Z'
|
||||
0x00,0x7E,0x60,0x30,0x18,0x0C,0x06,0x06,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x5B '['
|
||||
0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x5C '\'
|
||||
0x00,0x03,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x5D ']'
|
||||
0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x5E '^'
|
||||
0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x5F '_'
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x60 '`'
|
||||
0x0C,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x61-0x7A lowercase - condensed to save space, using same shapes as upper offset by 32
|
||||
// 0x61 'a'
|
||||
0x00,0x00,0x00,0x1C,0x30,0x3C,0x33,0x33,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x62 'b'
|
||||
0x00,0x06,0x06,0x1E,0x36,0x66,0x66,0x66,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x63 'c'
|
||||
0x00,0x00,0x00,0x3C,0x06,0x06,0x06,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x64 'd'
|
||||
0x00,0x60,0x60,0x7C,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x65 'e'
|
||||
0x00,0x00,0x00,0x3C,0x66,0x7E,0x06,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x66 'f'
|
||||
0x00,0x38,0x0C,0x0C,0x3E,0x0C,0x0C,0x0C,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x67 'g'
|
||||
0x00,0x00,0x7C,0x66,0x66,0x66,0x7C,0x60,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x68 'h'
|
||||
0x00,0x06,0x06,0x3E,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x69 'i'
|
||||
0x00,0x18,0x00,0x1C,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x6A 'j'
|
||||
0x00,0x30,0x00,0x38,0x30,0x30,0x30,0x36,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x6B 'k'
|
||||
0x00,0x06,0x06,0x66,0x36,0x1E,0x36,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x6C 'l'
|
||||
0x00,0x1C,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x6D 'm'
|
||||
0x00,0x00,0x00,0x37,0x7F,0x6B,0x6B,0x6B,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x6E 'n'
|
||||
0x00,0x00,0x00,0x1E,0x36,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x6F 'o'
|
||||
0x00,0x00,0x00,0x3C,0x66,0x66,0x66,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x70 'p'
|
||||
0x00,0x00,0x3E,0x66,0x66,0x3E,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x71 'q'
|
||||
0x00,0x00,0x7C,0x66,0x66,0x7C,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x72 'r'
|
||||
0x00,0x00,0x00,0x3E,0x06,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x73 's'
|
||||
0x00,0x00,0x00,0x3C,0x06,0x1C,0x30,0x66,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x74 't'
|
||||
0x00,0x0C,0x0C,0x3E,0x0C,0x0C,0x0C,0x0C,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x75 'u'
|
||||
0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x7C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x76 'v'
|
||||
0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x3C,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x77 'w'
|
||||
0x00,0x00,0x00,0x63,0x6B,0x6B,0x7F,0x77,0x63,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x78 'x'
|
||||
0x00,0x00,0x00,0x66,0x3C,0x18,0x3C,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x79 'y'
|
||||
0x00,0x00,0x66,0x66,0x66,0x7C,0x60,0x30,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x7A 'z'
|
||||
0x00,0x00,0x00,0x7E,0x30,0x18,0x0C,0x06,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x7B '{'
|
||||
0x00,0x30,0x18,0x18,0x0C,0x18,0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x7C '|'
|
||||
0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x7D '}'
|
||||
0x00,0x0C,0x18,0x18,0x30,0x18,0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
// 0x7E '~'
|
||||
0x00,0x6E,0x3B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
// hal_apic.h
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
void HalApicRouteIrq(uint8_t irq);
|
||||
void HalApicMaskIrq(uint8_t irq);
|
||||
void HalApicUnmaskIrq(uint8_t irq);
|
||||
void HalApicSendEoi(void);
|
||||
void HalApicInitialize(uintptr_t lapicPhys, uintptr_t ioapicPhys);
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
// hal_debug.h - HAL debug output function
|
||||
|
||||
void HalDbgPrint(const char* fmt, ...);
|
||||
@@ -4,22 +4,15 @@
|
||||
// Kernel and user code open files through IoOpenFile.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ke_status.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <sys_string.h>
|
||||
#include <ntlite/status.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <ntlite/string_view.h>
|
||||
#include <ntlite/dirent.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// KDIR_ENTRY - one directory entry returned by ReadDir / SYS_READ_DIR
|
||||
// Attribute flags match FAT32/Win32: ATTR_DIRECTORY=0x10, ATTR_ARCHIVE=0x20
|
||||
// ---------------------------------------------------------------------------
|
||||
#define KDIR_NAME_MAX 260
|
||||
|
||||
typedef struct KDIR_ENTRY {
|
||||
char Name[KDIR_NAME_MAX]; // null-terminated filename
|
||||
uint8_t Attr; // FAT attribute byte (0x10=dir, 0x20=file)
|
||||
uint32_t FileSize; // 0 for directories
|
||||
} KDIR_ENTRY;
|
||||
// Kernel/filesystem code uses the public directory-entry ABI directly.
|
||||
typedef SYS_DIR_ENTRY KDIR_ENTRY;
|
||||
#define KDIR_NAME_MAX SYS_DIR_NAME_MAX
|
||||
|
||||
// Native asynchronous filesystem completion. Filesystems that provide
|
||||
// ReadAsync/WriteAsync accept the operation, return STATUS_PENDING, and later
|
||||
@@ -99,7 +92,7 @@ typedef struct IO_FILESYSTEM {
|
||||
// Fills 'out' with name, attributes, and size.
|
||||
// Returns STATUS_NO_MORE_FILES when the directory is exhausted.
|
||||
KSTATUS (*ReadDir)(struct IO_FILESYSTEM* fs, void* dirCtx,
|
||||
struct KDIR_ENTRY* out);
|
||||
KDIR_ENTRY* out);
|
||||
|
||||
// Release resources allocated by OpenDir.
|
||||
void (*CloseDir)(struct IO_FILESYSTEM* fs, void* dirCtx);
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_status.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
struct THREAD;
|
||||
typedef struct THREAD THREAD;
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
// ke_cpu.h - per-CPU execution state
|
||||
//
|
||||
// SMP phase 1 still runs only the bootstrap processor, but the kernel now
|
||||
// represents CPU-local state explicitly. AP startup can fill additional KCPU
|
||||
// entries later without first untangling scheduler/IRQL/TSS globals.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ke_idt.h>
|
||||
|
||||
#define KE_MAX_CPUS 32u
|
||||
#define KE_INVALID_CPU 0xFFFFFFFFu
|
||||
|
||||
struct THREAD;
|
||||
|
||||
typedef struct KCPU {
|
||||
uint32_t CpuIndex;
|
||||
uint32_t LapicId;
|
||||
|
||||
uint8_t Present;
|
||||
uint8_t Online;
|
||||
uint8_t IsBsp;
|
||||
uint8_t Reserved0;
|
||||
|
||||
struct THREAD* CurrentThread;
|
||||
struct THREAD* IdleThread;
|
||||
struct THREAD* OutgoingThread;
|
||||
|
||||
KIRQL CurrentIrql;
|
||||
uint32_t InterruptDepth;
|
||||
volatile long ReschedulePending;
|
||||
|
||||
TSS64 Tss;
|
||||
void* KernelStack;
|
||||
uintptr_t KernelStackTop;
|
||||
|
||||
uint64_t SchedulerTicks;
|
||||
uint64_t ContextSwitches;
|
||||
} KCPU;
|
||||
|
||||
extern KCPU gCpus[KE_MAX_CPUS];
|
||||
extern uint32_t gCpuCount;
|
||||
|
||||
void KeInitializeBootstrapCpu(void);
|
||||
KCPU* KeGetCurrentCpu(void);
|
||||
KCPU* KeGetCpuByIndex(uint32_t index);
|
||||
uint32_t KeGetCurrentCpuIndex(void);
|
||||
TSS64* KeGetCurrentTss(void);
|
||||
File renamed without changes.
@@ -8,8 +8,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
|
||||
typedef struct KDPC KDPC;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
// ke_event.h - auto-reset and manual-reset events
|
||||
#include <stdint.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_status.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
typedef enum EVENT_TYPE { EventAutoReset, EventManualReset } EVENT_TYPE;
|
||||
|
||||
@@ -17,6 +17,9 @@ typedef struct TSS64 {
|
||||
} TSS64;
|
||||
#pragma pack(pop)
|
||||
|
||||
// Compatibility pointer used by ke_switch.asm.
|
||||
// It points at CPU0's TSS until the context-switch path grows a true
|
||||
// per-CPU base lookup.
|
||||
extern TSS64* gTss;
|
||||
|
||||
void KeInitializeGdt(void);
|
||||
@@ -13,9 +13,9 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_status.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
// Forward declarations - full types live in ke_event.h / ke_semaphore.h.
|
||||
// We only ever hold pointers in IRP and IRP_QUEUE, so a forward decl suffices.
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <hal_api.h>
|
||||
|
||||
void KeIrqInitialize(const HAL_API *hal);
|
||||
__declspec(dllexport) void KeIrqRegister(uint8_t irq, void (*handler)(void));
|
||||
void KeIrqDispatch(uint8_t irq); // called from ke_syscall.asm
|
||||
|
||||
// Low-level interrupt entry/exit hooks. These are called by the common IRQ
|
||||
// assembly stub around KeIrqDispatch. The exit hook is where deferred
|
||||
// interrupt-time scheduling now happens, so IRQ handlers never switch away
|
||||
// before KeIrqDispatch has returned and sent EOI.
|
||||
void KiEnterInterrupt(void);
|
||||
void KiExitInterrupt(void);
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
|
||||
void KeIrqInitialize(const HAL_API *hal);
|
||||
__declspec(dllexport) void KeIrqRegister(uint8_t irq, void (*handler)(void));
|
||||
void KeIrqDispatch(uint8_t irq); // called from ke_syscall.asm
|
||||
|
||||
// Low-level interrupt entry/exit hooks. These are called by the common IRQ
|
||||
// assembly stub around KeIrqDispatch. The exit hook is where deferred
|
||||
// interrupt-time scheduling now happens, so IRQ handlers never switch away
|
||||
// before KeIrqDispatch has returned and sent EOI.
|
||||
void KiEnterInterrupt(void);
|
||||
void KiExitInterrupt(void);
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
// ke_mutex.h - binary ownership mutex
|
||||
#include <ob_object.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_status.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
typedef struct MUTEX {
|
||||
OBJECT Header;
|
||||
@@ -2,13 +2,13 @@
|
||||
// ke_process.h - process model
|
||||
// A process owns a handle table and a list of threads.
|
||||
// There is one shared address space - processes do not own one.
|
||||
#include <ob_object.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_event.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys_string.h>
|
||||
#include <ntlite/string_view.h>
|
||||
|
||||
#define PROCESS_NAME_MAX 64
|
||||
#define HANDLE_TABLE_SIZE 256
|
||||
@@ -4,10 +4,10 @@
|
||||
// on both sides. The queue is only a synchronisation and ordering tool.
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_status.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
#define QUEUE_MESSAGE_SIZE 64 /* bytes, fixed */
|
||||
#define QUEUE_DEPTH 256
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
// ke_sched.h - Multi-Level Feedback Queue scheduler
|
||||
// Three run queues (Low / Normal / High). IRQ 0 drives KeSchedulerTick.
|
||||
#include <ke_thread.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
|
||||
#define SCHED_TICK_MS 10u
|
||||
#define SCHED_HIGH_QUANTUM_TICKS 2u
|
||||
@@ -50,8 +50,8 @@ __declspec(dllexport) void KeYield(void);
|
||||
|
||||
void KeGetSchedulerStats(KE_SCHED_STATS* out);
|
||||
|
||||
// Returns the thread currently running on this CPU (reads GS:[0]).
|
||||
// Returns the thread currently running on this CPU.
|
||||
THREAD* KeGetCurrentThread(void);
|
||||
|
||||
// Set the current-thread GS slot. Called once per CPU at boot.
|
||||
// Set the current thread for the current CPU. Used during CPU/scheduler bring-up.
|
||||
void KeSetCurrentThread(THREAD* thread);
|
||||
@@ -4,6 +4,6 @@
|
||||
// These tests intentionally live behind one call site so they can be walled
|
||||
// off or compiled out later without touching kmain.c again.
|
||||
|
||||
#include <hal_api.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
|
||||
void KiRunKernelSelfTests(const HAL_API* hal);
|
||||
@@ -1,20 +1,20 @@
|
||||
#pragma once
|
||||
// ke_semaphore.h - counting semaphore
|
||||
#include <stdint.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_status.h>
|
||||
|
||||
typedef struct SEMAPHORE {
|
||||
OBJECT Header;
|
||||
SPINLOCK Lock;
|
||||
int32_t Count;
|
||||
int32_t MaxCount;
|
||||
LIST_ENTRY WaitList;
|
||||
} SEMAPHORE;
|
||||
|
||||
__declspec(dllexport) KSTATUS KeSemaphoreCreate(int32_t initial, int32_t max, SEMAPHORE **out);
|
||||
__declspec(dllexport) KSTATUS KeWaitSemaphore(SEMAPHORE* semaphore);
|
||||
__declspec(dllexport) KSTATUS KeTrySemaphore(SEMAPHORE* semaphore);
|
||||
__declspec(dllexport) void KeSignalSemaphore(SEMAPHORE* semaphore);
|
||||
#pragma once
|
||||
// ke_semaphore.h - counting semaphore
|
||||
#include <stdint.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
typedef struct SEMAPHORE {
|
||||
OBJECT Header;
|
||||
SPINLOCK Lock;
|
||||
int32_t Count;
|
||||
int32_t MaxCount;
|
||||
LIST_ENTRY WaitList;
|
||||
} SEMAPHORE;
|
||||
|
||||
__declspec(dllexport) KSTATUS KeSemaphoreCreate(int32_t initial, int32_t max, SEMAPHORE **out);
|
||||
__declspec(dllexport) KSTATUS KeWaitSemaphore(SEMAPHORE* semaphore);
|
||||
__declspec(dllexport) KSTATUS KeTrySemaphore(SEMAPHORE* semaphore);
|
||||
__declspec(dllexport) void KeSignalSemaphore(SEMAPHORE* semaphore);
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <ke_status.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
#define KE_SETTINGS_PATH "system\\settings.ini"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// Never hold a spinlock across a wait/blocking operation.
|
||||
#include <stdint.h>
|
||||
#include <intrin.h>
|
||||
#include <ke_irql.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
|
||||
typedef struct SPINLOCK {
|
||||
volatile long Locked;
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <hal_api.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
|
||||
// Set the HAL API pointer for syscalls
|
||||
void KeSyscallInit(const HAL_API* hal);
|
||||
@@ -2,12 +2,12 @@
|
||||
// ke_thread.h - thread model
|
||||
// A THREAD is the unit of scheduling. Every thread belongs to exactly one process.
|
||||
#include <stdint.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_process.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_trap.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_trap.h>
|
||||
|
||||
#define KSTACK_SIZE (64 * 1024) /* 64 KB kernel stack */
|
||||
#define USTACK_SIZE (512 * 1024) /* 512 KB user stack */
|
||||
@@ -88,6 +88,7 @@ typedef struct THREAD {
|
||||
uint32_t QuantumRemaining;
|
||||
uint32_t CpuBurstTicks;
|
||||
uint32_t LastRunTicks;
|
||||
uint32_t RunningCpu;
|
||||
|
||||
// Set by wait primitives before blocking and completed by the object
|
||||
// that wakes the thread. Keep this field after the assembly-visible
|
||||
@@ -7,7 +7,7 @@
|
||||
// slots before returning to user mode.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <ke_status.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
#define KTRAP_FRAME_MAGIC 0x4650545241544B31ULL
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <sys_string.h>
|
||||
#include <ntlite/string_view.h>
|
||||
|
||||
#define SYS_PEB_MAGIC 0x30424550u /* "PEB0" */
|
||||
#define SYS_TEB_MAGIC 0x30424554u /* "TEB0" */
|
||||
@@ -2,7 +2,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <mm_virt.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Dynamic array (stretchy-buffer style)
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <ke_status.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
// Map a PE32+ image from 'buffer' into the kernel virtual address space.
|
||||
// On success, *baseOut receives the virtual base address.
|
||||
@@ -1,28 +1,28 @@
|
||||
#pragma once
|
||||
// mm_phys.h - physical memory manager (PMM)
|
||||
// Stack allocator over 4 KB frames, seeded from MEMORY_MAP.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <memory_map.h>
|
||||
|
||||
#define PAGE_SIZE 4096ULL
|
||||
#define PAGE_MASK (~(PAGE_SIZE - 1))
|
||||
|
||||
typedef uint64_t PHYSICAL_ADDRESS;
|
||||
|
||||
// Called once by KernelMain with the MEMORY_MAP from BOOT_INFO.
|
||||
bool MmInitializePhysical(const MEMORY_MAP* map,
|
||||
uint64_t physicalWindowBase);
|
||||
|
||||
// Allocate / free a single 4 KB frame.
|
||||
// MmAllocateFrame returns 0 on failure.
|
||||
PHYSICAL_ADDRESS MmAllocateFrame(void);
|
||||
void MmFreeFrame(PHYSICAL_ADDRESS frame);
|
||||
|
||||
// Allocate 'count' physically contiguous 4 KB frames.
|
||||
// Returns 0 on failure. Use sparingly (DMA, page tables).
|
||||
__declspec(dllexport) PHYSICAL_ADDRESS MmAllocateContiguous(size_t count);
|
||||
|
||||
size_t MmFreeFrameCount(void);
|
||||
size_t MmTotalFrameCount(void);
|
||||
#pragma once
|
||||
// mm_phys.h - physical memory manager (PMM)
|
||||
// Stack allocator over 4 KB frames, seeded from MEMORY_MAP.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <ntlite/memory_map.h>
|
||||
|
||||
#define PAGE_SIZE 4096ULL
|
||||
#define PAGE_MASK (~(PAGE_SIZE - 1))
|
||||
|
||||
typedef uint64_t PHYSICAL_ADDRESS;
|
||||
|
||||
// Called once by KernelMain with the MEMORY_MAP from BOOT_INFO.
|
||||
bool MmInitializePhysical(const MEMORY_MAP* map,
|
||||
uint64_t physicalWindowBase);
|
||||
|
||||
// Allocate / free a single 4 KB frame.
|
||||
// MmAllocateFrame returns 0 on failure.
|
||||
PHYSICAL_ADDRESS MmAllocateFrame(void);
|
||||
void MmFreeFrame(PHYSICAL_ADDRESS frame);
|
||||
|
||||
// Allocate 'count' physically contiguous 4 KB frames.
|
||||
// Returns 0 on failure. Use sparingly (DMA, page tables).
|
||||
__declspec(dllexport) PHYSICAL_ADDRESS MmAllocateContiguous(size_t count);
|
||||
|
||||
size_t MmFreeFrameCount(void);
|
||||
size_t MmTotalFrameCount(void);
|
||||
@@ -3,8 +3,8 @@
|
||||
// Manages the single shared PML4, kernel pool, and user heap.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <mm_phys.h>
|
||||
#include <ke_status.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Page flags (x86-64 PTE bits)
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ke_status.h>
|
||||
#include <ob_object.h>
|
||||
#include <net_abi.h>
|
||||
#include <ntlite/status.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <ntlite/ddi/net_abi.h>
|
||||
|
||||
struct NET_STACK_PROVIDER;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// The OBJECT header must be the first member of any derived struct.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ke_status.h>
|
||||
#include <ntlite/status.h>
|
||||
|
||||
typedef enum OBJECT_TYPE {
|
||||
ObjectTypeProcess = 1,
|
||||
File renamed without changes.
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
typedef void (*IniCallback)(const char* section, const char* key,
|
||||
const char* value, void* userdata);
|
||||
void IniParse(char* buf, IniCallback cb, void* userdata);
|
||||
@@ -1,8 +1,8 @@
|
||||
// io_device.c - kernel driver/device dispatch and disk registry
|
||||
#include <string.h>
|
||||
#include <io_driver.h>
|
||||
#include <ke_irp.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <kernel/ke_irp.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
|
||||
#define MAX_DISK_DEVICES 8
|
||||
|
||||
|
||||
+12
-12
@@ -1,17 +1,17 @@
|
||||
// io_file.c - VFS dispatch layer
|
||||
#include <string.h>
|
||||
#include <ke_list.h>
|
||||
#include <ke_process.h>
|
||||
#include <io_file.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_userenv.h>
|
||||
#include <ke_irql.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_apc.h>
|
||||
#include <ke_semaphore.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_userenv.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_apc.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
|
||||
#define MAX_DRIVES 8
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// ke_apc.c - kernel APC infrastructure
|
||||
#include <ke_apc.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_irql.h>
|
||||
#include <ke_trap.h>
|
||||
#include <mm_virt.h>
|
||||
#include <kernel/ke_apc.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ke_trap.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// ke_cpu.c - bootstrap/per-CPU state plumbing
|
||||
#include <string.h>
|
||||
#include <kernel/ke_cpu.h>
|
||||
|
||||
#define BOOTSTRAP_CPU_STACK_SIZE 0x4000u
|
||||
|
||||
KCPU gCpus[KE_MAX_CPUS];
|
||||
uint32_t gCpuCount = 0;
|
||||
|
||||
static uint8_t gBootstrapCpuStack[BOOTSTRAP_CPU_STACK_SIZE];
|
||||
|
||||
void KeInitializeBootstrapCpu(void) {
|
||||
memset(gCpus, 0, sizeof(gCpus));
|
||||
|
||||
KCPU* cpu = &gCpus[0];
|
||||
cpu->CpuIndex = 0;
|
||||
cpu->LapicId = 0;
|
||||
cpu->Present = 1;
|
||||
cpu->Online = 1;
|
||||
cpu->IsBsp = 1;
|
||||
cpu->CurrentIrql = PASSIVE_LEVEL;
|
||||
cpu->InterruptDepth = 0;
|
||||
cpu->ReschedulePending = 0;
|
||||
cpu->KernelStack = gBootstrapCpuStack;
|
||||
cpu->KernelStackTop = (uintptr_t)(gBootstrapCpuStack + sizeof(gBootstrapCpuStack));
|
||||
memset(&cpu->Tss, 0, sizeof(cpu->Tss));
|
||||
cpu->Tss.IoMapBase = sizeof(TSS64);
|
||||
cpu->Tss.RSP0 = cpu->KernelStackTop;
|
||||
|
||||
gCpuCount = 1;
|
||||
}
|
||||
|
||||
KCPU* KeGetCurrentCpu(void) {
|
||||
// SMP phase 1: only CPU0 is online. Later this becomes LAPIC-ID or
|
||||
// per-CPU-base based lookup once the bootloader parks APs for us.
|
||||
return &gCpus[0];
|
||||
}
|
||||
|
||||
KCPU* KeGetCpuByIndex(uint32_t index) {
|
||||
if (index >= KE_MAX_CPUS)
|
||||
return (KCPU*)0;
|
||||
return &gCpus[index];
|
||||
}
|
||||
|
||||
uint32_t KeGetCurrentCpuIndex(void) {
|
||||
return KeGetCurrentCpu()->CpuIndex;
|
||||
}
|
||||
|
||||
TSS64* KeGetCurrentTss(void) {
|
||||
return &KeGetCurrentCpu()->Tss;
|
||||
}
|
||||
@@ -2,14 +2,14 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <hal_api.h>
|
||||
#include <ke_debug.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_process.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <mm_virt.h>
|
||||
#include <printf.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <common/printf.h>
|
||||
int _fltused; // fuckass
|
||||
|
||||
int gDebugEnabled = 0;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// ke_dpc.c - deferred procedure calls
|
||||
#include <ke_dpc.h>
|
||||
#include <ke_irql.h>
|
||||
#include <ke_debug.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <intrin.h>
|
||||
|
||||
static LIST_ENTRY gDpcQueue;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// ke_event.c - synchronisation events
|
||||
#include <stdbool.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_irql.h>
|
||||
#include <ke_apc.h>
|
||||
#include <ob_object.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ke_apc.h>
|
||||
#include <kernel/ob_object.h>
|
||||
|
||||
static void KiEventDestroy(OBJECT* obj) { (void)obj; }
|
||||
|
||||
|
||||
+13
-11
@@ -10,7 +10,8 @@
|
||||
// 0x30 TSS high
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ke_idt.h>
|
||||
#include <kernel/ke_idt.h>
|
||||
#include <kernel/ke_cpu.h>
|
||||
|
||||
// External assembly procedures
|
||||
extern void KeFlushGdt(void* pGdtr, uint16_t codeSel, uint16_t dataSel, uint16_t tssSel);
|
||||
@@ -77,10 +78,7 @@ extern void KeFaultRSV31(void);
|
||||
// GDT
|
||||
// ---------------------------------------------------------------------------
|
||||
#define GDT_ENTRIES 8
|
||||
#define KERNEL_IST_STACK_SIZE 0x4000
|
||||
|
||||
static uint64_t gGdt[GDT_ENTRIES];
|
||||
static uint8_t gKernelStack[KERNEL_IST_STACK_SIZE];
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct { uint16_t Limit; uint64_t Base; } GDTR;
|
||||
@@ -112,12 +110,14 @@ static void KiGdtSetTss(int idx, uintptr_t base, uint32_t limit) {
|
||||
gGdt[idx+1] = (base >> 32);
|
||||
}
|
||||
|
||||
TSS64 gTssStruct;
|
||||
TSS64* gTss = &gTssStruct;
|
||||
TSS64* gTss = (TSS64*)0;
|
||||
|
||||
void KeInitializeGdt(void) {
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
TSS64* tss = &cpu->Tss;
|
||||
gTss = tss;
|
||||
|
||||
memset(gGdt, 0, sizeof(gGdt));
|
||||
memset(&gTssStruct, 0, sizeof(gTssStruct));
|
||||
|
||||
// 0x00: Null
|
||||
KiGdtSetEntry(0, 0, 0, 0, 0);
|
||||
@@ -130,11 +130,13 @@ void KeInitializeGdt(void) {
|
||||
// 0x20: Ring-3 Data
|
||||
KiGdtSetEntry(4, 0, 0xFFFFF, 0xF2, 0xC);
|
||||
|
||||
// 0x28: TSS (Occupies two slots: 0x28 and 0x30)
|
||||
gTssStruct.IoMapBase = sizeof(TSS64);
|
||||
gTssStruct.RSP0 = (uint64_t)(gKernelStack + KERNEL_IST_STACK_SIZE); // top of stack
|
||||
// 0x28: TSS (Occupies two slots: 0x28 and 0x30).
|
||||
// The TSS storage is per-CPU; phase 1 initializes CPU0 only.
|
||||
tss->IoMapBase = sizeof(TSS64);
|
||||
if (!tss->RSP0)
|
||||
tss->RSP0 = cpu->KernelStackTop;
|
||||
|
||||
KiGdtSetTss(5, (uintptr_t)&gTssStruct, sizeof(TSS64) - 1);
|
||||
KiGdtSetTss(5, (uintptr_t)tss, sizeof(TSS64) - 1);
|
||||
|
||||
GDTR gdtr = { sizeof(gGdt) - 1, (uint64_t)gGdt };
|
||||
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
// → ob_object.h (ObDereferenceObject)
|
||||
// → io_driver.h (DEVICE_OBJECT / IoCallDriver)
|
||||
|
||||
#include <ke_irp.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_semaphore.h>
|
||||
#include <ke_apc.h>
|
||||
#include <ob_object.h>
|
||||
#include <io_driver.h>
|
||||
#include <kernel/ke_irp.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_apc.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// IRP_QUEUE operations
|
||||
|
||||
+12
-11
@@ -1,10 +1,11 @@
|
||||
// ke_irq.c - IRQ dispatch, kernel-side
|
||||
#include <ke_irq.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_irql.h>
|
||||
#include <ke_dpc.h>
|
||||
#include <hal_api.h>
|
||||
#include <ke_debug.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_cpu.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
|
||||
#define MAX_IRQS 24
|
||||
#define MAX_IRQ_HANDLERS 8
|
||||
@@ -16,7 +17,6 @@ typedef struct IRQ_HANDLER_LIST {
|
||||
|
||||
static IRQ_HANDLER_LIST gIrqHandlers[MAX_IRQS];
|
||||
static const HAL_API *gHal;
|
||||
static volatile uint32_t gInterruptDepth = 0;
|
||||
|
||||
static KIRQL KiIrqToIrql(uint8_t irq) {
|
||||
KIRQL irql = (KIRQL)(DIRQL_BASE + irq);
|
||||
@@ -77,18 +77,19 @@ void KeIrqDispatch(uint8_t irq) {
|
||||
}
|
||||
|
||||
void KiEnterInterrupt(void) {
|
||||
gInterruptDepth++;
|
||||
KeGetCurrentCpu()->InterruptDepth++;
|
||||
}
|
||||
|
||||
void KiExitInterrupt(void) {
|
||||
if (gInterruptDepth)
|
||||
gInterruptDepth--;
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
if (cpu->InterruptDepth)
|
||||
cpu->InterruptDepth--;
|
||||
|
||||
// A reschedule is only legal after the interrupt unwound back to the
|
||||
// interrupted thread's original PASSIVE context. If a future path ever
|
||||
// permits nested interrupting of DISPATCH/DIRQL code, leave the request
|
||||
// pending instead of switching while a spinlock/high-IRQL region is active.
|
||||
if (gInterruptDepth == 0 && KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
if (cpu->InterruptDepth == 0 && KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
KiDrainDpcQueue();
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL)
|
||||
KiScheduleFromInterrupt();
|
||||
|
||||
+11
-11
@@ -1,10 +1,8 @@
|
||||
// ke_irql.c - software IRQL skeleton
|
||||
#include <ke_irql.h>
|
||||
#include <ke_debug.h>
|
||||
#include <ke_apc.h>
|
||||
|
||||
// Single-CPU bring-up state. When SMP arrives this becomes per-CPU state.
|
||||
static volatile KIRQL gCurrentIrql = PASSIVE_LEVEL;
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ke_cpu.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_apc.h>
|
||||
|
||||
__declspec(dllexport) const char* KeIrqlName(KIRQL irql) {
|
||||
switch (irql) {
|
||||
@@ -20,11 +18,12 @@ __declspec(dllexport) const char* KeIrqlName(KIRQL irql) {
|
||||
}
|
||||
|
||||
__declspec(dllexport) KIRQL KeGetCurrentIrql(void) {
|
||||
return gCurrentIrql;
|
||||
return KeGetCurrentCpu()->CurrentIrql;
|
||||
}
|
||||
|
||||
__declspec(dllexport) KIRQL KeRaiseIrql(KIRQL newIrql) {
|
||||
KIRQL oldIrql = gCurrentIrql;
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
KIRQL oldIrql = cpu->CurrentIrql;
|
||||
|
||||
if (newIrql > HIGH_LEVEL) {
|
||||
KePanic("KeRaiseIrql: invalid target IRQL %u\n", (unsigned)newIrql);
|
||||
@@ -36,12 +35,13 @@ __declspec(dllexport) KIRQL KeRaiseIrql(KIRQL newIrql) {
|
||||
KeIrqlName(newIrql), (unsigned)newIrql);
|
||||
}
|
||||
|
||||
gCurrentIrql = newIrql;
|
||||
cpu->CurrentIrql = newIrql;
|
||||
return oldIrql;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void KeLowerIrql(KIRQL oldIrql) {
|
||||
KIRQL currentIrql = gCurrentIrql;
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
KIRQL currentIrql = cpu->CurrentIrql;
|
||||
|
||||
if (oldIrql > HIGH_LEVEL) {
|
||||
KePanic("KeLowerIrql: invalid target IRQL %u\n", (unsigned)oldIrql);
|
||||
@@ -53,7 +53,7 @@ __declspec(dllexport) void KeLowerIrql(KIRQL oldIrql) {
|
||||
KeIrqlName(oldIrql), (unsigned)oldIrql);
|
||||
}
|
||||
|
||||
gCurrentIrql = oldIrql;
|
||||
cpu->CurrentIrql = oldIrql;
|
||||
|
||||
if (oldIrql == PASSIVE_LEVEL && currentIrql != PASSIVE_LEVEL)
|
||||
KiDeliverKernelApcs();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// ke_mutex.c - binary ownership mutex
|
||||
#include <stdbool.h>
|
||||
#include <ke_mutex.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_irql.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
#include <kernel/ke_mutex.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ob_object.h>
|
||||
|
||||
static void KiMutexDestroy(OBJECT* obj) { (void)obj; }
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// ke_process.c - process model
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ke_process.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_userenv.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_userenv.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ob_object.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Global process list
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// ke_queue.c - ring-buffer message queue
|
||||
#include <string.h>
|
||||
#include <ke_queue.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_irql.h>
|
||||
#include <ob_object.h>
|
||||
#include <kernel/ke_queue.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ob_object.h>
|
||||
|
||||
static void KiQueueDestroy(OBJECT* obj) { (void)obj; }
|
||||
|
||||
|
||||
+47
-36
@@ -1,23 +1,20 @@
|
||||
// ke_sched.c - MLFQ scheduler
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_process.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_irql.h>
|
||||
#include <ke_apc.h>
|
||||
#include <ke_list.h>
|
||||
#include <mm_virt.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_cpu.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ke_apc.h>
|
||||
#include <kernel/ke_list.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
|
||||
static LIST_ENTRY gRunQueue[PriorityCount];
|
||||
static SPINLOCK gSchedLock;
|
||||
|
||||
static THREAD* gCurrentThread;
|
||||
static THREAD* gIdleThread;
|
||||
static THREAD* gOutgoingThread;
|
||||
static uint32_t gAgingCounter = 0;
|
||||
static volatile long gReschedulePending = 0;
|
||||
static KE_SCHED_STATS gSchedStats;
|
||||
|
||||
static uint32_t KiBaseQuantumForPriority(THREAD_PRIORITY priority) {
|
||||
@@ -30,7 +27,7 @@ static uint32_t KiBaseQuantumForPriority(THREAD_PRIORITY priority) {
|
||||
}
|
||||
|
||||
static uint32_t KiComputeQuantumLocked(THREAD* thread) {
|
||||
if (!thread || thread == gIdleThread)
|
||||
if (!thread || thread == KeGetCurrentCpu()->IdleThread)
|
||||
return 1;
|
||||
|
||||
uint32_t quantum = KiBaseQuantumForPriority(thread->Priority);
|
||||
@@ -53,8 +50,8 @@ static void KiRefillQuantumLocked(THREAD* thread) {
|
||||
thread->QuantumRemaining = thread->QuantumTicks;
|
||||
}
|
||||
|
||||
THREAD* KeGetCurrentThread(void) { return gCurrentThread; }
|
||||
void KeSetCurrentThread(THREAD* t) { gCurrentThread = t; }
|
||||
THREAD* KeGetCurrentThread(void) { return KeGetCurrentCpu()->CurrentThread; }
|
||||
void KeSetCurrentThread(THREAD* t) { KeGetCurrentCpu()->CurrentThread = t; }
|
||||
|
||||
void KeGetSchedulerStats(KE_SCHED_STATS* out) {
|
||||
if (!out) return;
|
||||
@@ -68,6 +65,8 @@ static void KiIdleLoop(void) {
|
||||
}
|
||||
|
||||
void KeInitializeScheduler(void) {
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
|
||||
for (int i = 0; i < PriorityCount; i++) LIST_INIT(&gRunQueue[i]);
|
||||
gSchedLock = (SPINLOCK)SPINLOCK_INIT;
|
||||
|
||||
@@ -90,6 +89,7 @@ void KeInitializeScheduler(void) {
|
||||
idle->QuantumRemaining = 1;
|
||||
idle->CpuBurstTicks = 0;
|
||||
idle->LastRunTicks = 0;
|
||||
idle->RunningCpu = cpu->CpuIndex;
|
||||
idle->Owner = idleProc;
|
||||
idle->WaitObject = (OBJECT *)0;
|
||||
idle->WaitStatus = STATUS_SUCCESS;
|
||||
@@ -127,20 +127,23 @@ void KeInitializeScheduler(void) {
|
||||
idleProc->ThreadCount++;
|
||||
KeReleaseSpinlock(&idleProc->HandleLock, idleFlags);
|
||||
|
||||
gIdleThread = idle;
|
||||
gCurrentThread = idle;
|
||||
cpu->IdleThread = idle;
|
||||
cpu->CurrentThread = idle;
|
||||
cpu->OutgoingThread = (THREAD*)0;
|
||||
}
|
||||
|
||||
// Safely release the lock and clean up any dead threads natively post-switch
|
||||
void KeSchedulerPostSwitch(void) {
|
||||
THREAD* cur = KeGetCurrentThread();
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
THREAD* cur = cpu->CurrentThread;
|
||||
KeReleaseSpinlock(&gSchedLock, cur->SchedLockFlags);
|
||||
|
||||
KiDeliverKernelApcs();
|
||||
|
||||
if (gOutgoingThread && gOutgoingThread->State == ThreadDead) {
|
||||
THREAD* dead = gOutgoingThread;
|
||||
gOutgoingThread = (THREAD*)0;
|
||||
if (cpu->OutgoingThread && cpu->OutgoingThread->State == ThreadDead) {
|
||||
THREAD* dead = cpu->OutgoingThread;
|
||||
cpu->OutgoingThread = (THREAD*)0;
|
||||
dead->RunningCpu = KE_INVALID_CPU;
|
||||
ObDereferenceObject(&dead->Header);
|
||||
}
|
||||
}
|
||||
@@ -155,8 +158,9 @@ void KeReadyThread(THREAD* thread) {
|
||||
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
|
||||
ListInsertTail(&gRunQueue[p], &thread->SchedulerLink);
|
||||
|
||||
THREAD* cur = gCurrentThread;
|
||||
if (!cur || cur == gIdleThread) {
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
THREAD* cur = cpu->CurrentThread;
|
||||
if (!cur || cur == cpu->IdleThread) {
|
||||
KiRequestReschedule();
|
||||
} else if (cur->State == ThreadRunning && thread->Priority > cur->Priority) {
|
||||
gSchedStats.PriorityPreemptions++;
|
||||
@@ -189,22 +193,24 @@ static THREAD* KiPickNext(void) {
|
||||
return CONTAINING_RECORD(node, THREAD, SchedulerLink);
|
||||
}
|
||||
}
|
||||
return gIdleThread;
|
||||
return KeGetCurrentCpu()->IdleThread;
|
||||
}
|
||||
|
||||
void KiRequestReschedule(void) {
|
||||
gReschedulePending = 1;
|
||||
KeGetCurrentCpu()->ReschedulePending = 1;
|
||||
}
|
||||
|
||||
int KiIsReschedulePending(void) {
|
||||
return gReschedulePending != 0;
|
||||
return KeGetCurrentCpu()->ReschedulePending != 0;
|
||||
}
|
||||
|
||||
static void KiSchedule(void) {
|
||||
THREAD* cur = gCurrentThread;
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
THREAD* cur = cpu->CurrentThread;
|
||||
THREAD* idle = cpu->IdleThread;
|
||||
if (!cur) return;
|
||||
|
||||
gReschedulePending = 0;
|
||||
cpu->ReschedulePending = 0;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
|
||||
cur->SchedLockFlags = flags;
|
||||
@@ -213,7 +219,7 @@ static void KiSchedule(void) {
|
||||
// still runnable and nobody else is ready, keep executing it. Otherwise a
|
||||
// lone CPU-bound thread gets alternated with the idle thread on every timer
|
||||
// tick, effectively turning a periodic preemption check into a 50% throttle.
|
||||
if (cur != gIdleThread && cur->State == ThreadRunning && !KiAnyRunnableLocked()) {
|
||||
if (cur != idle && cur->State == ThreadRunning && !KiAnyRunnableLocked()) {
|
||||
if (cur->QuantumRemaining == 0)
|
||||
KiRefillQuantumLocked(cur);
|
||||
gSchedStats.KeepCurrentDecisions++;
|
||||
@@ -222,12 +228,12 @@ static void KiSchedule(void) {
|
||||
}
|
||||
|
||||
THREAD* next = KiPickNext();
|
||||
if (next == gIdleThread) gSchedStats.IdleSelections++;
|
||||
if (next == idle) gSchedStats.IdleSelections++;
|
||||
if (next == cur) {
|
||||
// Only re-queue if still running normally; a ThreadWaiting/ThreadDead
|
||||
// caller has already placed SchedulerLink elsewhere (or is discarding
|
||||
// the thread).
|
||||
if (cur != gIdleThread && cur->State == ThreadRunning)
|
||||
if (cur != idle && cur->State == ThreadRunning)
|
||||
ListInsertTail(&gRunQueue[cur->Priority], &cur->SchedulerLink);
|
||||
KeReleaseSpinlock(&gSchedLock, flags);
|
||||
return;
|
||||
@@ -235,19 +241,22 @@ static void KiSchedule(void) {
|
||||
|
||||
next->State = ThreadRunning;
|
||||
KiRefillQuantumLocked(next);
|
||||
gCurrentThread = next;
|
||||
cpu->CurrentThread = next;
|
||||
next->RunningCpu = cpu->CpuIndex;
|
||||
|
||||
// Only re-queue the outgoing thread if it is still in the running state.
|
||||
// If KeBlockThread set State = ThreadWaiting, SchedulerLink was already
|
||||
// inserted into the sync object's wait list - inserting it here too would
|
||||
// corrupt both lists simultaneously (same intrusive node in two lists).
|
||||
// If KeExitThread set State = ThreadDead, the thread must not be re-queued.
|
||||
if (cur != gIdleThread && cur->State == ThreadRunning) {
|
||||
if (cur != idle && cur->State == ThreadRunning) {
|
||||
cur->State = ThreadReady;
|
||||
ListInsertTail(&gRunQueue[cur->Priority], &cur->SchedulerLink);
|
||||
}
|
||||
|
||||
gOutgoingThread = cur;
|
||||
if (cur) cur->RunningCpu = KE_INVALID_CPU;
|
||||
cpu->OutgoingThread = cur;
|
||||
cpu->ContextSwitches++;
|
||||
gSchedStats.ContextSwitches++;
|
||||
KeSwitchContext(cur, next);
|
||||
KeSchedulerPostSwitch();
|
||||
@@ -267,7 +276,9 @@ void KiSchedulerNoteVoluntaryBlock(THREAD* thread) {
|
||||
}
|
||||
|
||||
void KeSchedulerTick(void) {
|
||||
THREAD* cur = gCurrentThread;
|
||||
KCPU* cpu = KeGetCurrentCpu();
|
||||
THREAD* cur = cpu->CurrentThread;
|
||||
THREAD* idle = cpu->IdleThread;
|
||||
if (!cur) return;
|
||||
|
||||
uintptr_t flags = KeAcquireSpinlock(&gSchedLock);
|
||||
@@ -277,7 +288,7 @@ void KeSchedulerTick(void) {
|
||||
|
||||
int requestReschedule = 0;
|
||||
|
||||
if (cur == gIdleThread) {
|
||||
if (cur == idle) {
|
||||
if (KiAnyRunnableLocked())
|
||||
requestReschedule = 1;
|
||||
} else {
|
||||
|
||||
+14
-14
@@ -3,20 +3,20 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <hal_api.h>
|
||||
#include <ke_apc.h>
|
||||
#include <ke_debug.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_irp.h>
|
||||
#include <ke_irql.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_status.h>
|
||||
#include <ke_thread.h>
|
||||
#include <io_file.h>
|
||||
#include <io_driver.h>
|
||||
#include <ke_trap.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/ke_apc.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_irp.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <ntlite/status.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <ntlite/ddi/io_driver.h>
|
||||
#include <kernel/ke_trap.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ob_object.h>
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Kernel APC self-test
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// ke_semaphore.c - counting semaphore
|
||||
#include <stdbool.h>
|
||||
#include <ke_semaphore.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_irql.h>
|
||||
#include <ob_object.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ob_object.h>
|
||||
|
||||
static void KiSemaphoreDestroy(OBJECT* obj) { (void)obj; }
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ke_settings.h>
|
||||
#include <io_file.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ini_parser.h>
|
||||
#include <kernel/ke_settings.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <common/ini_parser.h>
|
||||
|
||||
#define KE_SETTINGS_MAX_FILE_BYTES (64u * 1024u)
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
// ke_status.h - kernel status codes
|
||||
// All fallible kernel functions return KSTATUS.
|
||||
// Success is always zero; failures are positive named constants.
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint32_t KSTATUS;
|
||||
|
||||
// --- General & Control (0x0000) ---
|
||||
#define STATUS_SUCCESS ((KSTATUS)0x0000)
|
||||
#define STATUS_FAILURE ((KSTATUS)0x0001)
|
||||
#define STATUS_NOT_SUPPORTED ((KSTATUS)0x0002)
|
||||
#define STATUS_TIMEOUT ((KSTATUS)0x0003)
|
||||
#define STATUS_PENDING ((KSTATUS)0x0004)
|
||||
#define STATUS_ALERTED ((KSTATUS)0x0005)
|
||||
#define STATUS_KERNEL_APC ((KSTATUS)0x0006)
|
||||
#define STATUS_USER_APC ((KSTATUS)0x0007)
|
||||
|
||||
// --- Validation & Parameters (0x0010) ---
|
||||
#define STATUS_INVALID_PARAMETER ((KSTATUS)0x0010)
|
||||
#define STATUS_INVALID_HANDLE ((KSTATUS)0x0011)
|
||||
#define STATUS_BUFFER_TOO_SMALL ((KSTATUS)0x0012)
|
||||
#define STATUS_CHECKSUM_MISMATCH ((KSTATUS)0x0013)
|
||||
|
||||
// --- Resource & Access (0x0020) ---
|
||||
#define STATUS_NO_MEMORY ((KSTATUS)0x0020)
|
||||
#define STATUS_ACCESS_DENIED ((KSTATUS)0x0021)
|
||||
#define STATUS_NOT_FOUND ((KSTATUS)0x0022)
|
||||
#define STATUS_ALREADY_EXISTS ((KSTATUS)0x0023)
|
||||
#define STATUS_OBJECT_NAME_COLLISION ((KSTATUS)0x0024)
|
||||
|
||||
// --- State & Lifecycle (0x0030) ---
|
||||
#define STATUS_OBJECT_DEAD ((KSTATUS)0x0030)
|
||||
#define STATUS_QUEUE_FULL ((KSTATUS)0x0031)
|
||||
#define STATUS_QUEUE_EMPTY ((KSTATUS)0x0032)
|
||||
#define STATUS_CANCELLED ((KSTATUS)0x0033)
|
||||
|
||||
// --- Hardware & External (0x0040) ---
|
||||
#define STATUS_DEVICE_ERROR ((KSTATUS)0x0040)
|
||||
|
||||
#define STATUS_NO_MORE_FILES ((KSTATUS)0x0050)
|
||||
|
||||
// --- Execution level (0x0060) ---
|
||||
#define STATUS_INVALID_IRQL ((KSTATUS)0x0060)
|
||||
|
||||
#define KSUCCESS(s) ((s) == STATUS_SUCCESS)
|
||||
@@ -14,7 +14,7 @@ THREAD_Teb EQU 112
|
||||
THREAD_FpuContext EQU 168
|
||||
TSS_RSP0_OFFSET EQU 4
|
||||
|
||||
EXTERN gTss:QWORD ; address of the single TSS, exported from ke_idt.c
|
||||
EXTERN gTss:QWORD ; phase-1 pointer to current CPU TSS, exported from ke_idt.c
|
||||
|
||||
.CODE
|
||||
|
||||
|
||||
+17
-17
@@ -33,23 +33,23 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_process.h>
|
||||
#include <ke_mutex.h>
|
||||
#include <ke_semaphore.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_apc.h>
|
||||
#include <ke_queue.h>
|
||||
#include <ke_debug.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ldr_image.h>
|
||||
#include <syscall_nr.h>
|
||||
#include <hal_api.h>
|
||||
#include <io_file.h>
|
||||
#include <net_socket.h>
|
||||
#include <ke_trap.h>
|
||||
#include <syscall.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_mutex.h>
|
||||
#include <kernel/ke_semaphore.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_apc.h>
|
||||
#include <kernel/ke_queue.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ldr_image.h>
|
||||
#include <kernel/syscall_nr.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/net_socket.h>
|
||||
#include <kernel/ke_trap.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HAL pointer - set once at boot by KeSyscallSetHal()
|
||||
|
||||
+11
-8
@@ -2,14 +2,15 @@
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <intrin.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_irql.h>
|
||||
#include <ke_event.h>
|
||||
#include <ke_apc.h>
|
||||
#include <ke_userenv.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_cpu.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
#include <kernel/ke_event.h>
|
||||
#include <kernel/ke_apc.h>
|
||||
#include <kernel/ke_userenv.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ob_object.h>
|
||||
|
||||
#define FPU_FCW_DEFAULT 0x037Fu
|
||||
#define FPU_MXCSR_DEFAULT 0x1F80u
|
||||
@@ -91,6 +92,7 @@ __declspec(dllexport) KSTATUS KeCreateThread(PROCESS* owner, uintptr_t entry, ui
|
||||
thread->QuantumRemaining = 0;
|
||||
thread->CpuBurstTicks = 0;
|
||||
thread->LastRunTicks = 0;
|
||||
thread->RunningCpu = KE_INVALID_CPU;
|
||||
thread->WaitObject = (OBJECT *)0;
|
||||
thread->WaitStatus = STATUS_SUCCESS;
|
||||
thread->WaitListHead = (LIST_ENTRY*)0;
|
||||
@@ -249,6 +251,7 @@ __declspec(dllexport) KSTATUS KeCreateKernelThread(uintptr_t fn, uintptr_t arg,
|
||||
thread->QuantumRemaining = 0;
|
||||
thread->CpuBurstTicks = 0;
|
||||
thread->LastRunTicks = 0;
|
||||
thread->RunningCpu = KE_INVALID_CPU;
|
||||
thread->WaitObject = (OBJECT*)0;
|
||||
thread->WaitStatus = STATUS_SUCCESS;
|
||||
thread->WaitListHead = (LIST_ENTRY*)0;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// ke_trap.c - current-thread trap-frame tracking and validation
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ke_trap.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_debug.h>
|
||||
#include <ke_apc.h>
|
||||
#include <kernel/ke_trap.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_apc.h>
|
||||
|
||||
#define STATIC_ASSERT_OFFSET(type, field, expected) \
|
||||
_Static_assert(offsetof(type, field) == (expected), \
|
||||
|
||||
+22
-20
@@ -2,25 +2,26 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <boot_info.h>
|
||||
#include <mm_phys.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ob_object.h>
|
||||
#include <ke_process.h>
|
||||
#include <ke_thread.h>
|
||||
#include <ke_sched.h>
|
||||
#include <ke_idt.h>
|
||||
#include <ke_irq.h>
|
||||
#include <ke_dpc.h>
|
||||
#include <ke_syscall.h>
|
||||
#include <net_device.h>
|
||||
#include <ke_debug.h>
|
||||
#include <ke_util.h>
|
||||
#include <ldr_image.h>
|
||||
#include <hal_api.h>
|
||||
#include <io_file.h>
|
||||
#include <ke_selftest.h>
|
||||
#include <ini_parser.h>
|
||||
#include <ntlite/boot_info.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/ke_thread.h>
|
||||
#include <kernel/ke_sched.h>
|
||||
#include <kernel/ke_cpu.h>
|
||||
#include <kernel/ke_idt.h>
|
||||
#include <kernel/ke_irq.h>
|
||||
#include <kernel/ke_dpc.h>
|
||||
#include <kernel/ke_syscall.h>
|
||||
#include <ntlite/ddi/net_device.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_util.h>
|
||||
#include <kernel/ldr_image.h>
|
||||
#include <ntlite/ddi/hal_api.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/ke_selftest.h>
|
||||
#include <common/ini_parser.h>
|
||||
|
||||
typedef struct {
|
||||
BOOT_INFO* bootInfo;
|
||||
@@ -438,8 +439,9 @@ extern void KernelMain(BOOT_INFO* bootInfo) {
|
||||
MmInitializeVirtual(pml4, 4 * 1024 * 1024 /* 4 MB initial pool */);
|
||||
|
||||
//
|
||||
// CPU Architecture setup (GDT/IDT/Faults)
|
||||
// Bootstrap CPU and architecture setup (GDT/IDT/Faults)
|
||||
//
|
||||
KeInitializeBootstrapCpu();
|
||||
KeInitializeGdt();
|
||||
KeInitializeIdt();
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// ldr_image.c - PE32+ loader and export resolver
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ldr_image.h>
|
||||
#include <boot_info.h>
|
||||
#include <ke_process.h>
|
||||
#include <io_file.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ke_debug.h>
|
||||
#include <kernel/ldr_image.h>
|
||||
#include <ntlite/boot_info.h>
|
||||
#include <kernel/ke_process.h>
|
||||
#include <kernel/io_file.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
|
||||
int _stricmp(const char* a, const char* b);
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
// memory_map.h - shared between bootx64.efi and kernel.exe
|
||||
// Describes physical memory layout passed from bootloader to kernel.
|
||||
#include <stdint.h>
|
||||
|
||||
#define MEMORY_MAP_MAGIC 0x4D454D4D41500000ULL /* 'MEMMAP\0\0' */
|
||||
#define MEMORY_MAX_REGIONS 256
|
||||
|
||||
typedef enum MEMORY_TYPE {
|
||||
MemoryUsable = 0, /* free RAM, available to the PMM */
|
||||
MemoryReserved = 1, /* firmware-reserved, do not touch */
|
||||
MemoryMappedIO = 2, /* device MMIO regions */
|
||||
MemoryFramebuffer = 3, /* GOP framebuffer */
|
||||
MemoryBootloader = 4, /* bootloader code/data (reclaimable) */
|
||||
MemoryKernel = 5, /* kernel + HAL + drivers image pages */
|
||||
} MEMORY_TYPE;
|
||||
|
||||
typedef struct MEMORY_REGION {
|
||||
uint64_t Base;
|
||||
uint64_t Length; /* in bytes, always page-aligned */
|
||||
MEMORY_TYPE Type;
|
||||
uint32_t _pad;
|
||||
} MEMORY_REGION;
|
||||
|
||||
typedef struct MEMORY_MAP {
|
||||
uint64_t Magic;
|
||||
uint32_t RegionCount;
|
||||
uint32_t _pad;
|
||||
MEMORY_REGION Regions[MEMORY_MAX_REGIONS];
|
||||
} MEMORY_MAP;
|
||||
@@ -6,8 +6,8 @@
|
||||
// bootloader at PhysicalWindowBase (always 0xFFFF880000000000).
|
||||
//
|
||||
// Lock: a single spinlock guards Push/Pop. O(1) for both operations.
|
||||
#include <mm_phys.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <kernel/mm_phys.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Physical-to-virtual translation via the direct map window.
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
// Backed on demand from the PMM. No per-process heaps.
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <mm_virt.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ke_irql.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
#include <kernel/ke_irql.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers - physical window access
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
// net_abi.h - native networking ABI shared by the kernel socket facade
|
||||
// and network stack provider drivers.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define SYS_AF_INET 2u
|
||||
#define SYS_SOCK_STREAM 1u
|
||||
#define SYS_SOCK_DGRAM 2u
|
||||
#define SYS_SOCK_RAW 3u
|
||||
#define SYS_IPPROTO_ICMP 1u
|
||||
#define SYS_IPPROTO_TCP 6u
|
||||
#define SYS_IPPROTO_UDP 17u
|
||||
|
||||
#define SYS_SHUTDOWN_READ 0u
|
||||
#define SYS_SHUTDOWN_WRITE 1u
|
||||
#define SYS_SHUTDOWN_BOTH 2u
|
||||
|
||||
#define SYS_SOCKET_ADDRESS_IPV4_SIZE 16u
|
||||
#define SYS_RESOLVE_HOST_NAME_MAX 255u
|
||||
|
||||
#define SYS_SOCKET_OPT_RECV_TIMEOUT_MS 1u
|
||||
#define SYS_SOCKET_OPT_SEND_TIMEOUT_MS 2u
|
||||
#define SYS_SOCKET_OPT_NONBLOCKING 3u
|
||||
|
||||
#define SYS_POLL_READ 0x0001u
|
||||
#define SYS_POLL_WRITE 0x0002u
|
||||
#define SYS_POLL_ERROR 0x0004u
|
||||
#define SYS_POLL_HANGUP 0x0008u
|
||||
#define SYS_POLL_INVALID 0x0010u
|
||||
#define SYS_POLL_TIMEOUT_INFINITE 0xFFFFFFFFu
|
||||
|
||||
typedef struct SYS_SOCKET_ADDRESS_IPV4_K {
|
||||
uint16_t Family; // SYS_AF_INET
|
||||
uint16_t Port; // host byte order
|
||||
uint8_t Address[4]; // dotted-quad bytes, e.g. {10,0,2,2}
|
||||
uint8_t Reserved[8];
|
||||
} SYS_SOCKET_ADDRESS_IPV4_K;
|
||||
|
||||
typedef struct SYS_POLLFD_K {
|
||||
uint32_t Handle;
|
||||
uint32_t Events;
|
||||
uint32_t ReturnedEvents;
|
||||
} SYS_POLLFD_K;
|
||||
@@ -3,10 +3,10 @@
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <net_device.h>
|
||||
#include <net_stack.h>
|
||||
#include <ke_debug.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ntlite/ddi/net_device.h>
|
||||
#include <ntlite/ddi/net_stack.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
|
||||
#define NET_MAX_DEVICES 8u
|
||||
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
// net_device.h - kernel network-device registry.
|
||||
//
|
||||
// NIC drivers register NET_DEVICE objects here and feed complete Ethernet
|
||||
// frames into NetReceiveFrame(). Protocol ownership belongs to whichever
|
||||
// network stack driver registered through net_stack.h.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ke_status.h>
|
||||
|
||||
#define NET_DEVICE_NAME_MAX 16u
|
||||
#define NET_HWADDR_MAX 16u
|
||||
#define NET_MTU_ETHERNET 1500u
|
||||
#define NET_ETHERNET_ADDRESS_LENGTH 6u
|
||||
#define NET_ETHERNET_HEADER_LENGTH 14u
|
||||
#define NET_ETHERNET_MIN_FRAME 60u
|
||||
#define NET_ETHERNET_MAX_FRAME (NET_ETHERNET_HEADER_LENGTH + NET_MTU_ETHERNET)
|
||||
|
||||
struct NET_DEVICE;
|
||||
typedef struct NET_DEVICE NET_DEVICE;
|
||||
|
||||
typedef KSTATUS (*NET_SEND_FRAME)(NET_DEVICE* device,
|
||||
const void* frame,
|
||||
size_t length);
|
||||
|
||||
typedef struct NET_DEVICE_STATS {
|
||||
uint64_t TxPackets;
|
||||
uint64_t TxBytes;
|
||||
uint64_t TxErrors;
|
||||
uint64_t RxPackets;
|
||||
uint64_t RxBytes;
|
||||
uint64_t RxDrops;
|
||||
} NET_DEVICE_STATS;
|
||||
|
||||
struct NET_DEVICE {
|
||||
char Name[NET_DEVICE_NAME_MAX];
|
||||
uint32_t Index;
|
||||
uint32_t Mtu;
|
||||
uint8_t HardwareAddressLength;
|
||||
uint8_t HardwareAddress[NET_HWADDR_MAX];
|
||||
void* DriverContext;
|
||||
NET_SEND_FRAME SendFrame;
|
||||
NET_DEVICE_STATS Stats;
|
||||
};
|
||||
|
||||
__declspec(dllexport) KSTATUS NetRegisterDevice(NET_DEVICE* device);
|
||||
__declspec(dllexport) uint32_t NetGetDeviceCount(void);
|
||||
__declspec(dllexport) NET_DEVICE* NetGetDevice(uint32_t index);
|
||||
__declspec(dllexport) KSTATUS NetGetDeviceStats(uint32_t index,
|
||||
NET_DEVICE_STATS* stats);
|
||||
__declspec(dllexport) KSTATUS NetSendFrame(uint32_t index,
|
||||
const void* frame,
|
||||
size_t length);
|
||||
__declspec(dllexport) void NetReceiveFrame(NET_DEVICE* device,
|
||||
const void* frame,
|
||||
size_t length);
|
||||
@@ -4,9 +4,9 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <net_socket.h>
|
||||
#include <net_stack.h>
|
||||
#include <ob_object.h>
|
||||
#include <kernel/net_socket.h>
|
||||
#include <ntlite/ddi/net_stack.h>
|
||||
#include <kernel/ob_object.h>
|
||||
|
||||
#define NET_SOCKET_IO_MAX_BYTES (1024u * 1024u)
|
||||
#define NET_SOCKET_UDP_MAX_BYTES 65507u
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <net_stack.h>
|
||||
#include <net_device.h>
|
||||
#include <ke_debug.h>
|
||||
#include <ke_spinlock.h>
|
||||
#include <ntlite/ddi/net_stack.h>
|
||||
#include <ntlite/ddi/net_device.h>
|
||||
#include <kernel/ke_debug.h>
|
||||
#include <kernel/ke_spinlock.h>
|
||||
|
||||
static const NET_STACK_PROVIDER* gNetStackProvider = 0;
|
||||
static SPINLOCK gNetStackLock = SPINLOCK_INIT;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
#pragma once
|
||||
// net_stack.h - provider interface between kernel socket facade and network stack drivers.
|
||||
//
|
||||
// The kernel owns process handles, user-memory copying, NET_DEVICE registration,
|
||||
// and SOCKET_OBJECT lifetime. A stack driver owns protocol implementation.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ke_status.h>
|
||||
#include <net_device.h>
|
||||
#include <net_abi.h>
|
||||
|
||||
#define NET_STACK_PROVIDER_VERSION 1u
|
||||
|
||||
typedef struct NET_STACK_PROVIDER NET_STACK_PROVIDER;
|
||||
|
||||
struct NET_STACK_PROVIDER {
|
||||
uint32_t Version;
|
||||
const char* Name;
|
||||
|
||||
KSTATUS (*AttachDevice)(NET_DEVICE* device);
|
||||
KSTATUS (*ReceiveFrame)(NET_DEVICE* device, const void* frame, size_t length);
|
||||
|
||||
KSTATUS (*SocketCreate)(uint32_t domain, uint32_t type, uint32_t protocol, void** outSocket);
|
||||
void (*SocketClose)(void* socket);
|
||||
|
||||
KSTATUS (*Bind)(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize);
|
||||
KSTATUS (*Listen)(void* socket, uint32_t backlog);
|
||||
KSTATUS (*Accept)(void* socket, void** acceptedOut, SYS_SOCKET_ADDRESS_IPV4_K* remoteOut);
|
||||
KSTATUS (*Connect)(void* socket, const SYS_SOCKET_ADDRESS_IPV4_K* address, size_t addressSize);
|
||||
|
||||
KSTATUS (*Send)(void* socket, const void* buffer, size_t length, uint32_t flags, size_t* sentOut);
|
||||
KSTATUS (*Receive)(void* socket, void* buffer, size_t length, uint32_t flags, size_t* receivedOut);
|
||||
KSTATUS (*SendTo)(void* socket, const void* buffer, size_t length, uint32_t flags,
|
||||
const SYS_SOCKET_ADDRESS_IPV4_K* remote, size_t remoteSize, size_t* sentOut);
|
||||
KSTATUS (*ReceiveFrom)(void* socket, void* buffer, size_t length, uint32_t flags,
|
||||
SYS_SOCKET_ADDRESS_IPV4_K* remoteOut, size_t* receivedOut);
|
||||
|
||||
KSTATUS (*Shutdown)(void* socket, uint32_t how);
|
||||
KSTATUS (*SetOption)(void* socket, uint32_t option, const void* value, size_t valueSize);
|
||||
KSTATUS (*GetOption)(void* socket, uint32_t option, void* value, size_t valueSize, size_t* actualSizeOut);
|
||||
KSTATUS (*Poll)(void* socket, uint32_t events, uint32_t* returnedEventsOut);
|
||||
|
||||
KSTATUS (*ResolveHostIpv4)(const char* name, SYS_SOCKET_ADDRESS_IPV4_K* addressOut);
|
||||
};
|
||||
|
||||
__declspec(dllexport) KSTATUS NetRegisterStackProvider(const NET_STACK_PROVIDER* provider);
|
||||
__declspec(dllexport) const NET_STACK_PROVIDER* NetGetStackProvider(void);
|
||||
@@ -1,8 +1,8 @@
|
||||
// ob_object.c - executive object manager
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ob_object.h>
|
||||
#include <mm_virt.h>
|
||||
#include <kernel/ob_object.h>
|
||||
#include <kernel/mm_virt.h>
|
||||
|
||||
__declspec(dllexport) OBJECT* ObAllocateObject(OBJECT_TYPE type, size_t size) {
|
||||
if (size < sizeof(OBJECT)) size = sizeof(OBJECT);
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// \author (c) Marco Paland (info@paland.com)
|
||||
// 2014-2019, PALANDesign Hannover, Germany
|
||||
//
|
||||
// \license The MIT License (MIT)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
|
||||
// embedded systems with a very limited resources.
|
||||
// Use this instead of bloated standard/newlib printf.
|
||||
// These routines are thread safe and reentrant.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _PRINTF_H_
|
||||
#define _PRINTF_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// maksw: yeah that hack i mentioned earlier
|
||||
void printf_set_putchar(void (*fn)(char));
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Output a character to a custom device like UART, used by the printf() function
|
||||
* This function is declared here only. You have to write your custom implementation somewhere
|
||||
* \param character Character to output
|
||||
*/
|
||||
//void _putchar(char character);
|
||||
|
||||
|
||||
/**
|
||||
* Tiny printf implementation
|
||||
* You have to implement _putchar if you use printf()
|
||||
* To avoid conflicts with the regular printf() API it is overridden by macro defines
|
||||
* and internal underscore-appended functions like printf_() are used
|
||||
* \param format A string that specifies the format of the output
|
||||
* \return The number of characters that are written into the array, not counting the terminating null character
|
||||
*/
|
||||
#define printf printf_
|
||||
int printf_(const char* format, ...);
|
||||
|
||||
|
||||
/**
|
||||
* Tiny sprintf implementation
|
||||
* Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
|
||||
* \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
|
||||
* \param format A string that specifies the format of the output
|
||||
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
|
||||
*/
|
||||
#define sprintf sprintf_
|
||||
int sprintf_(char* buffer, const char* format, ...);
|
||||
|
||||
|
||||
/**
|
||||
* Tiny snprintf/vsnprintf implementation
|
||||
* \param buffer A pointer to the buffer where to store the formatted string
|
||||
* \param count The maximum number of characters to store in the buffer, including a terminating null character
|
||||
* \param format A string that specifies the format of the output
|
||||
* \param va A value identifying a variable arguments list
|
||||
* \return The number of characters that COULD have been written into the buffer, not counting the terminating
|
||||
* null character. A value equal or larger than count indicates truncation. Only when the returned value
|
||||
* is non-negative and less than count, the string has been completely written.
|
||||
*/
|
||||
#define snprintf snprintf_
|
||||
#define vsnprintf vsnprintf_
|
||||
int snprintf_(char* buffer, size_t count, const char* format, ...);
|
||||
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
|
||||
|
||||
|
||||
/**
|
||||
* Tiny vprintf implementation
|
||||
* \param format A string that specifies the format of the output
|
||||
* \param va A value identifying a variable arguments list
|
||||
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
|
||||
*/
|
||||
#define vprintf vprintf_
|
||||
int vprintf_(const char* format, va_list va);
|
||||
|
||||
|
||||
/**
|
||||
* printf with output function
|
||||
* You may use this as dynamic alternative to printf() with its fixed _putchar() output
|
||||
* \param out An output function which takes one character and an argument pointer
|
||||
* \param arg An argument pointer for user data passed to output function
|
||||
* \param format A string that specifies the format of the output
|
||||
* \return The number of characters that are sent to the output function, not counting the terminating null character
|
||||
*/
|
||||
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // _PRINTF_H_
|
||||
Reference in new issue
Block a user