From 03bf767ac2fe5d952defad2a30c3ad5f89019fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maksymilian=20Wilczy=C5=84ski?= Date: Tue, 26 May 2026 12:23:22 +0200 Subject: [PATCH] add missing fault handlers instead of throwing everything into the GP bin, add more ways to crash in native shell --- OVMF/OVMF_VARS_4M_d.fd | Bin 540672 -> 540672 bytes code/boot/main.c | 84 ++++++++++------ code/kernel/ke_debug.c | 87 +++++++++++++---- code/kernel/ke_idt.c | 50 +++++++++- code/kernel/ke_idt.h | 69 ++++++------- code/kernel/ke_selftest.c | 19 ++++ code/kernel/ke_syscall.asm | 161 +++++++++++++++++++++++++++++++ code/shell_native/shell_native.c | 39 +++++++- readme.md | 4 +- 9 files changed, 425 insertions(+), 88 deletions(-) diff --git a/OVMF/OVMF_VARS_4M_d.fd b/OVMF/OVMF_VARS_4M_d.fd index 3cb17543e9b0db35ed810e2453a506e9d08da5f5..2e3b119c3b3b081e5e6acefe2d59ed513fbfd8ab 100644 GIT binary patch delta 73 zcmZo@P;3CA7RDB)EzEIRll_96wlC0PzQncpUG6Sc#?H+XgSrJ6yMUCZ!1OpRW+%q( b?F+S-cW-VJU}+OzZ4+Q?6JTE^z+nIY?VA}R delta 53 zcmZo@P;3CA7RDB)EzEIRlhag{wl`=of8yFMmd^ZY#r6d`%u5X01X$VxSla~H+635_ I32+zy0RP7kZ2$lO diff --git a/code/boot/main.c b/code/boot/main.c index caa946a..d7db83e 100644 --- a/code/boot/main.c +++ b/code/boot/main.c @@ -745,36 +745,60 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable EFI_MEMORY_DESCRIPTOR* efiMap = AllocatePool(mapSize); gBS->GetMemoryMap(&mapSize, efiMap, &mapKey, &descSz, &descVer); { - UINT64 idHoles[4]; - UINT64 winHoles[4]; - UINTN holeCount = 0; - if (fbBytes) { - idHoles[holeCount * 2 + 0] = fbBase; - idHoles[holeCount * 2 + 1] = fbBase + fbBytes; - winHoles[holeCount * 2 + 0] = PHYS_MAP_BASE + fbBase; - winHoles[holeCount * 2 + 1] = PHYS_MAP_BASE + fbBase + fbBytes; - holeCount++; - } - idHoles[holeCount * 2 + 0] = 0xFEC00000ULL; - idHoles[holeCount * 2 + 1] = 0xFEE01000ULL; - winHoles[holeCount * 2 + 0] = PHYS_MAP_BASE + 0xFEC00000ULL; - winHoles[holeCount * 2 + 1] = PHYS_MAP_BASE + 0xFEE01000ULL; - holeCount++; + UINT64 idHoles[8]; + UINT64 winHoles[8]; + UINTN idHoleCount = 0; + UINTN winHoleCount = 0; - if (holeCount == 2) { - if (idHoles[2] < idHoles[0]) { - UINT64 t0 = idHoles[0], t1 = idHoles[1]; - idHoles[0] = idHoles[2]; - idHoles[1] = idHoles[3]; - idHoles[2] = t0; - idHoles[3] = t1; + // Leave virtual page zero genuinely unmapped. Mapping it supervisor/NX + // still hides null pointer bugs from the page-fault path and makes + // diagnostics worse. The physical-window alias for frame zero remains + // available at PHYS_MAP_BASE if low physical memory is ever needed. + idHoles[idHoleCount * 2 + 0] = 0; + idHoles[idHoleCount * 2 + 1] = PAGE_SIZE; + idHoleCount++; + + if (fbBytes) { + idHoles[idHoleCount * 2 + 0] = fbBase; + idHoles[idHoleCount * 2 + 1] = fbBase + fbBytes; + idHoleCount++; + + winHoles[winHoleCount * 2 + 0] = PHYS_MAP_BASE + fbBase; + winHoles[winHoleCount * 2 + 1] = PHYS_MAP_BASE + fbBase + fbBytes; + winHoleCount++; + } + + idHoles[idHoleCount * 2 + 0] = 0xFEC00000ULL; + idHoles[idHoleCount * 2 + 1] = 0xFEE01000ULL; + idHoleCount++; + + winHoles[winHoleCount * 2 + 0] = PHYS_MAP_BASE + 0xFEC00000ULL; + winHoles[winHoleCount * 2 + 1] = PHYS_MAP_BASE + 0xFEE01000ULL; + winHoleCount++; + + for (UINTN pass = 0; pass < idHoleCount; pass++) { + for (UINTN i = 1; i < idHoleCount; i++) { + if (idHoles[i * 2] < idHoles[(i - 1) * 2]) { + UINT64 s0 = idHoles[(i - 1) * 2 + 0]; + UINT64 e0 = idHoles[(i - 1) * 2 + 1]; + idHoles[(i - 1) * 2 + 0] = idHoles[i * 2 + 0]; + idHoles[(i - 1) * 2 + 1] = idHoles[i * 2 + 1]; + idHoles[i * 2 + 0] = s0; + idHoles[i * 2 + 1] = e0; + } } - if (winHoles[2] < winHoles[0]) { - UINT64 t0 = winHoles[0], t1 = winHoles[1]; - winHoles[0] = winHoles[2]; - winHoles[1] = winHoles[3]; - winHoles[2] = t0; - winHoles[3] = t1; + } + + for (UINTN pass = 0; pass < winHoleCount; pass++) { + for (UINTN i = 1; i < winHoleCount; i++) { + if (winHoles[i * 2] < winHoles[(i - 1) * 2]) { + UINT64 s0 = winHoles[(i - 1) * 2 + 0]; + UINT64 e0 = winHoles[(i - 1) * 2 + 1]; + winHoles[(i - 1) * 2 + 0] = winHoles[i * 2 + 0]; + winHoles[(i - 1) * 2 + 1] = winHoles[i * 2 + 1]; + winHoles[i * 2 + 0] = s0; + winHoles[i * 2 + 1] = e0; + } } } @@ -791,8 +815,8 @@ EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable // memory because of buggy UEFI size reporting. end += 2 * PAGE_SIZE; - MapRangeSkipping(start, start, end - start, 0x03, idHoles, holeCount); - MapRangeSkipping(PHYS_MAP_BASE + start, start, end - start, 0x03, winHoles, holeCount); + MapRangeSkipping(start, start, end - start, 0x03, idHoles, idHoleCount); + MapRangeSkipping(PHYS_MAP_BASE + start, start, end - start, 0x03, winHoles, winHoleCount); } } } diff --git a/code/kernel/ke_debug.c b/code/kernel/ke_debug.c index 935f030..e536120 100644 --- a/code/kernel/ke_debug.c +++ b/code/kernel/ke_debug.c @@ -76,16 +76,66 @@ __declspec(dllexport) void KePrintConsole(const char* buf, size_t len) { // can continue running everything else. // --------------------------------------------------------------------------- -// Return the short exception name for a vector number. +// Return the short exception name for a CPU exception vector. +// Keep this table complete for vectors 0..31 so faults never collapse into +// a vague "general protection" bucket just because the IDT entry existed +// first or the crash printer lacked a name. static const char* KiFaultVectorName(int vector) { switch (vector) { case 0: return "#DE Divide Error"; + case 1: return "#DB Debug"; + case 2: return "NMI Interrupt"; + case 3: return "#BP Breakpoint"; + case 4: return "#OF Overflow"; + case 5: return "#BR Bound Range Exceeded"; + case 6: return "#UD Invalid Opcode"; + case 7: return "#NM Device Not Available"; + case 8: return "#DF Double Fault"; + case 9: return "Coprocessor Segment Overrun"; + case 10: return "#TS Invalid TSS"; + case 11: return "#NP Segment Not Present"; + case 12: return "#SS Stack-Segment Fault"; case 13: return "#GP General Protection"; case 14: return "#PF Page Fault"; + case 15: return "Reserved Exception 15"; + case 16: return "#MF x87 Floating-Point Exception"; + case 17: return "#AC Alignment Check"; + case 18: return "#MC Machine Check"; + case 19: return "#XM SIMD Floating-Point Exception"; + case 20: return "#VE Virtualization Exception"; + case 21: return "#CP Control Protection Exception"; + case 22: return "Reserved Exception 22"; + case 23: return "Reserved Exception 23"; + case 24: return "Reserved Exception 24"; + case 25: return "Reserved Exception 25"; + case 26: return "Reserved Exception 26"; + case 27: return "Reserved Exception 27"; + case 28: return "#HV Hypervisor Injection Exception"; + case 29: return "#VC VMM Communication Exception"; + case 30: return "#SX Security Exception"; + case 31: return "Reserved Exception 31"; default: return "Unknown Exception"; } } +static int KiFaultHasErrorCode(int vector) { + switch (vector) { + case 8: // #DF + case 10: // #TS + case 11: // #NP + case 12: // #SS + case 13: // #GP + case 14: // #PF + case 17: // #AC + case 21: // #CP + case 29: // #VC + case 30: // #SX + return 1; + default: + return 0; + } +} + // Walk the process module list to find which image owns an address. // Returns the module name string, or "unknown" if the address is outside all // known image ranges. Base/size are optional and are useful for offset dumps. @@ -391,23 +441,24 @@ __declspec(dllexport) _Noreturn void KeFaultDispatchFrame(const KFAULT_FRAME* fr } if ((frame->Cs & 3) == 0) { - switch ((int)frame->Vector) { - case 0: - KePanic("PANIC: #DE Divide Error | RIP: %016llx\n", - (unsigned long long)frame->Rip); - case 13: - KePanic("PANIC: #GP General Protection | RIP: %016llx | Code: %04llx\n", - (unsigned long long)frame->Rip, - (unsigned long long)frame->ErrorCode); - case 14: - KePanic("PANIC: #PF Page Fault | RIP: %016llx | Code: %04llx | CR2: %016llx\n", - (unsigned long long)frame->Rip, - (unsigned long long)frame->ErrorCode, - (unsigned long long)frame->Cr2); - default: - KePanic("PANIC: Exception #%llu | RIP: %016llx\n", - (unsigned long long)frame->Vector, - (unsigned long long)frame->Rip); + int vector = (int)frame->Vector; + const char* name = KiFaultVectorName(vector); + + if (vector == 14) { + KePanic("PANIC: %s | RIP: %016llx | Code: %04llx | CR2: %016llx\n", + name, + (unsigned long long)frame->Rip, + (unsigned long long)frame->ErrorCode, + (unsigned long long)frame->Cr2); + } else if (KiFaultHasErrorCode(vector)) { + KePanic("PANIC: %s | RIP: %016llx | Code: %04llx\n", + name, + (unsigned long long)frame->Rip, + (unsigned long long)frame->ErrorCode); + } else { + KePanic("PANIC: %s | RIP: %016llx\n", + name, + (unsigned long long)frame->Rip); } } diff --git a/code/kernel/ke_idt.c b/code/kernel/ke_idt.c index 9211f07..7182d69 100644 --- a/code/kernel/ke_idt.c +++ b/code/kernel/ke_idt.c @@ -41,6 +41,36 @@ extern void KeIrqStub21(void); extern void KeIrqStub22(void); extern void KeIrqStub23(void); +extern void KeFaultDB(void); +extern void KeFaultNMI(void); +extern void KeFaultBP(void); +extern void KeFaultOF(void); +extern void KeFaultBR(void); +extern void KeFaultUD(void); +extern void KeFaultNM(void); +extern void KeFaultDF(void); +extern void KeFaultCSO(void); +extern void KeFaultTS(void); +extern void KeFaultNP(void); +extern void KeFaultSS(void); +extern void KeFaultRSV15(void); +extern void KeFaultMF(void); +extern void KeFaultAC(void); +extern void KeFaultMC(void); +extern void KeFaultXM(void); +extern void KeFaultVE(void); +extern void KeFaultCP(void); +extern void KeFaultRSV22(void); +extern void KeFaultRSV23(void); +extern void KeFaultRSV24(void); +extern void KeFaultRSV25(void); +extern void KeFaultRSV26(void); +extern void KeFaultRSV27(void); +extern void KeFaultHV(void); +extern void KeFaultVC(void); +extern void KeFaultSX(void); +extern void KeFaultRSV31(void); + #define NUM_IRQS 24 // --------------------------------------------------------------------------- @@ -150,9 +180,23 @@ static void KiIdtSetGate(int vec, uintptr_t handler, void KeInitializeIdt(void) { memset(gIdt, 0, sizeof(gIdt)); - KiIdtSetGate(0, (uintptr_t)KeFaultDE, 0x8E, 0); - KiIdtSetGate(13, (uintptr_t)KeFaultGP, 0x8E, 0); - KiIdtSetGate(14, (uintptr_t)KeFaultPF, 0x8E, 0); + static void* faultStubs[32] = { + (void*)KeFaultDE, (void*)KeFaultDB, (void*)KeFaultNMI, + (void*)KeFaultBP, (void*)KeFaultOF, (void*)KeFaultBR, + (void*)KeFaultUD, (void*)KeFaultNM, (void*)KeFaultDF, + (void*)KeFaultCSO, (void*)KeFaultTS, (void*)KeFaultNP, + (void*)KeFaultSS, (void*)KeFaultGP, (void*)KeFaultPF, + (void*)KeFaultRSV15, (void*)KeFaultMF, (void*)KeFaultAC, + (void*)KeFaultMC, (void*)KeFaultXM, (void*)KeFaultVE, + (void*)KeFaultCP, (void*)KeFaultRSV22, (void*)KeFaultRSV23, + (void*)KeFaultRSV24, (void*)KeFaultRSV25, (void*)KeFaultRSV26, + (void*)KeFaultRSV27, (void*)KeFaultHV, (void*)KeFaultVC, + (void*)KeFaultSX, (void*)KeFaultRSV31, + }; + + for (int i = 0; i < 32; i++) + KiIdtSetGate(i, (uintptr_t)faultStubs[i], 0x8E, 0); + KiIdtSetGate(0x40, (uintptr_t)KeSyscallEntry, 0xEE, 0); static void *irqStubs[NUM_IRQS] = { diff --git a/code/kernel/ke_idt.h b/code/kernel/ke_idt.h index cd495b4..16af8e9 100644 --- a/code/kernel/ke_idt.h +++ b/code/kernel/ke_idt.h @@ -1,34 +1,35 @@ -#pragma once -// ke_idt.h - IDT, GDT, and TSS setup -#include - -// 64-bit TSS - minimal; we only care about RSP0. -#pragma pack(push, 1) -typedef struct TSS64 { - uint32_t Reserved0; - uint64_t RSP0; // loaded into RSP on ring-0 entry - uint64_t RSP1; - uint64_t RSP2; - uint64_t Reserved1; - uint64_t IST[7]; - uint64_t Reserved2; - uint16_t Reserved3; - uint16_t IoMapBase; -} TSS64; -#pragma pack(pop) - -extern TSS64* gTss; - -void KeInitializeGdt(void); -void KeInitializeIdt(void); - -// Called from hal_timer.c after HPET/APIC timer fires. -void KeTimerInterrupt(void); - -// Syscall entry - implemented in ke_syscall.asm. -extern void KeSyscallEntry(void); - -// Fault stubs installed by KeInitializeIdt. -extern void KeFaultDE(void); // #DE divide error -extern void KeFaultGP(void); // #GP general protection -extern void KeFaultPF(void); // #PF page fault +#pragma once +// ke_idt.h - IDT, GDT, and TSS setup +#include + +// 64-bit TSS - minimal; we only care about RSP0. +#pragma pack(push, 1) +typedef struct TSS64 { + uint32_t Reserved0; + uint64_t RSP0; // loaded into RSP on ring-0 entry + uint64_t RSP1; + uint64_t RSP2; + uint64_t Reserved1; + uint64_t IST[7]; + uint64_t Reserved2; + uint16_t Reserved3; + uint16_t IoMapBase; +} TSS64; +#pragma pack(pop) + +extern TSS64* gTss; + +void KeInitializeGdt(void); +void KeInitializeIdt(void); + +// Called from hal_timer.c after HPET/APIC timer fires. +void KeTimerInterrupt(void); + +// Syscall entry - implemented in ke_syscall.asm. +extern void KeSyscallEntry(void); + +// Fault stubs installed by KeInitializeIdt. +extern void KeFaultDE(void); // #DE divide error +extern void KeFaultGP(void); // #GP general protection +extern void KeFaultPF(void); // #PF page fault +extern void KeFaultUD(void); // #UD invalid opcode diff --git a/code/kernel/ke_selftest.c b/code/kernel/ke_selftest.c index 6a0b392..2ab414d 100644 --- a/code/kernel/ke_selftest.c +++ b/code/kernel/ke_selftest.c @@ -477,6 +477,24 @@ static void KiRunIrpKernelApcSelfTest(const HAL_API* hal) { + +// ------------------------------------------------------------------------- +// Virtual memory mapping invariants +// ------------------------------------------------------------------------- +static void KiRunMemoryMappingSelfTest(void) { + PHYSICAL_ADDRESS phys = 0; + uint64_t flags = 0; + KSTATUS s = MmQueryPage(0, &phys, &flags); + if (s != STATUS_NOT_FOUND) { + KePanic("MM selftest: virtual page zero is mapped phys=%016llx flags=%016llx status=%u\n", + (unsigned long long)phys, + (unsigned long long)flags, + (unsigned)s); + } + + DbgPrint("MM selftest: page zero unmapped\n"); +} + // ------------------------------------------------------------------------- // String-view ABI self-test // ------------------------------------------------------------------------- @@ -732,6 +750,7 @@ static void KiRunTrapFrameSelfTest(void) { void KiRunKernelSelfTests(const HAL_API* hal) { DbgPrint("Kernel selftests: begin\n"); KiRunKernelApcSelfTest(); + KiRunMemoryMappingSelfTest(); KiRunStringViewSelfTest(); KiRunIrpKernelApcSelfTest(hal); KiRunAsyncFileIoSelfTest(); diff --git a/code/kernel/ke_syscall.asm b/code/kernel/ke_syscall.asm index 74f5e54..03f3f0c 100644 --- a/code/kernel/ke_syscall.asm +++ b/code/kernel/ke_syscall.asm @@ -377,6 +377,167 @@ pf_call: call KeFaultDispatchFrame KeFaultPF ENDP + +; --------------------------------------------------------- +; Generic CPU exception stubs. +; Each vector gets its own IDT entry and vector number so faults like #UD +; do not fall through an absent descriptor and reappear as #GP. +; --------------------------------------------------------- +FAULT_NOERR MACRO StubName, VecNum + LOCAL noerr_kernel_frame, noerr_call +StubName PROC + push rax + push r11 + mov r11, rsp + sub rsp, KFAULT_SIZE + mov rax, rsp + mov [rax + KF_R10], r10 + + mov r10, KFAULT_MAGIC + mov [rax + KF_Magic], r10 + mov qword ptr [rax + KF_Vector], VecNum + mov qword ptr [rax + KF_ErrorCode], 0 + mov qword ptr [rax + KF_Cr2], 0 + + mov [rax + KF_Rcx], rcx + mov [rax + KF_Rdx], rdx + mov [rax + KF_Rbx], rbx + mov [rax + KF_Rbp], rbp + mov [rax + KF_Rsi], rsi + mov [rax + KF_Rdi], rdi + mov [rax + KF_R8], r8 + mov [rax + KF_R9], r9 + mov r10, [r11] + mov [rax + KF_R11], r10 + mov r10, [r11+8] + mov [rax + KF_Rax], r10 + mov [rax + KF_R12], r12 + mov [rax + KF_R13], r13 + mov [rax + KF_R14], r14 + mov [rax + KF_R15], r15 + + lea rdx, [r11+16] + mov r10, [rdx] + mov [rax + KF_Rip], r10 + mov r10, [rdx+8] + mov [rax + KF_Cs], r10 + mov r10, [rdx+16] + mov [rax + KF_Rflags], r10 + + mov r10, [rax + KF_Cs] + test r10, 3 + jz noerr_kernel_frame + mov r10, [rdx+24] + mov [rax + KF_Rsp], r10 + mov r10, [rdx+32] + mov [rax + KF_Ss], r10 + jmp noerr_call + +noerr_kernel_frame: + mov qword ptr [rax + KF_Rsp], 0 + mov qword ptr [rax + KF_Ss], 0 + +noerr_call: + and rsp, -16 + sub rsp, FAULT_CALL_SHADOW + mov rcx, rax + call KeFaultDispatchFrame +StubName ENDP +ENDM + +FAULT_ERR MACRO StubName, VecNum + LOCAL err_kernel_frame, err_call +StubName PROC + push rax + push r11 + mov r11, rsp + sub rsp, KFAULT_SIZE + mov rax, rsp + mov [rax + KF_R10], r10 + + mov r10, KFAULT_MAGIC + mov [rax + KF_Magic], r10 + mov qword ptr [rax + KF_Vector], VecNum + mov qword ptr [rax + KF_Cr2], 0 + + mov [rax + KF_Rcx], rcx + mov [rax + KF_Rdx], rdx + mov [rax + KF_Rbx], rbx + mov [rax + KF_Rbp], rbp + mov [rax + KF_Rsi], rsi + mov [rax + KF_Rdi], rdi + mov [rax + KF_R8], r8 + mov [rax + KF_R9], r9 + mov r10, [r11] + mov [rax + KF_R11], r10 + mov r10, [r11+8] + mov [rax + KF_Rax], r10 + mov [rax + KF_R12], r12 + mov [rax + KF_R13], r13 + mov [rax + KF_R14], r14 + mov [rax + KF_R15], r15 + + lea rdx, [r11+16] + mov r10, [rdx] + mov [rax + KF_ErrorCode], r10 + mov r10, [rdx+8] + mov [rax + KF_Rip], r10 + mov r10, [rdx+16] + mov [rax + KF_Cs], r10 + mov r10, [rdx+24] + mov [rax + KF_Rflags], r10 + + mov r10, [rax + KF_Cs] + test r10, 3 + jz err_kernel_frame + mov r10, [rdx+32] + mov [rax + KF_Rsp], r10 + mov r10, [rdx+40] + mov [rax + KF_Ss], r10 + jmp err_call + +err_kernel_frame: + mov qword ptr [rax + KF_Rsp], 0 + mov qword ptr [rax + KF_Ss], 0 + +err_call: + and rsp, -16 + sub rsp, FAULT_CALL_SHADOW + mov rcx, rax + call KeFaultDispatchFrame +StubName ENDP +ENDM + +FAULT_NOERR KeFaultDB, 1 +FAULT_NOERR KeFaultNMI, 2 +FAULT_NOERR KeFaultBP, 3 +FAULT_NOERR KeFaultOF, 4 +FAULT_NOERR KeFaultBR, 5 +FAULT_NOERR KeFaultUD, 6 +FAULT_NOERR KeFaultNM, 7 +FAULT_ERR KeFaultDF, 8 +FAULT_NOERR KeFaultCSO, 9 +FAULT_ERR KeFaultTS, 10 +FAULT_ERR KeFaultNP, 11 +FAULT_ERR KeFaultSS, 12 +FAULT_NOERR KeFaultRSV15, 15 +FAULT_NOERR KeFaultMF, 16 +FAULT_ERR KeFaultAC, 17 +FAULT_NOERR KeFaultMC, 18 +FAULT_NOERR KeFaultXM, 19 +FAULT_NOERR KeFaultVE, 20 +FAULT_ERR KeFaultCP, 21 +FAULT_NOERR KeFaultRSV22, 22 +FAULT_NOERR KeFaultRSV23, 23 +FAULT_NOERR KeFaultRSV24, 24 +FAULT_NOERR KeFaultRSV25, 25 +FAULT_NOERR KeFaultRSV26, 26 +FAULT_NOERR KeFaultRSV27, 27 +FAULT_NOERR KeFaultHV, 28 +FAULT_ERR KeFaultVC, 29 +FAULT_ERR KeFaultSX, 30 +FAULT_NOERR KeFaultRSV31, 31 + EXTERN KeIrqDispatch:PROC EXTERN KiEnterInterrupt:PROC EXTERN KiExitInterrupt:PROC diff --git a/code/shell_native/shell_native.c b/code/shell_native/shell_native.c index 8866472..e1dc451 100644 --- a/code/shell_native/shell_native.c +++ b/code/shell_native/shell_native.c @@ -209,7 +209,44 @@ static int runCommand(const char* cmd) { if (strcmp(cmd, "exit") == 0) return 1; if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; } if (strcmp(cmd, "clear") == 0) { SysScreenClear(0); return 0; } - if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; } + if (strncmp(cmd, "crash", 5) == 0) { + // Check the sub-option trailing "crash " (offset 6) + if (strcmp(cmd + 6, "void") == 0) { + // Jumping into the void + void (*invalid_func)(void) = NULL; + invalid_func(); + return 0; + } + else if (strcmp(cmd + 6, "noncanonical") == 0) { + // Non-canonical pointer dereference + volatile int *ptr = (volatile int*)0x4242424242424242ULL; + *ptr = 1337; + return 0; + } + else if (strcmp(cmd + 6, "ud2") == 0) { + __ud2(); + return 0; + } + else if (strcmp(cmd + 6, "nx") == 0) { + // NX / DEP Violation + unsigned char code[] = { 0xC3 }; + void (*execute_stack)(void) = (void (*)(void))code; + execute_stack(); + return 0; + } + else if (strcmp(cmd + 6, "divzero") == 0) { + // Division by zero + volatile int zero = 0; + volatile int result = 1 / zero; + (void)result; + return 0; + } + else { + // Default fallback if just "crash" or an unknown sub-option is passed + *(volatile int*)0 = 42; + return 0; + } + } if (strcmp(cmd, "mem") == 0) { cmd_mem(); return 0; } if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; } if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; } diff --git a/readme.md b/readme.md index 20042bf..3a088c2 100644 --- a/readme.md +++ b/readme.md @@ -10,7 +10,7 @@ NT-Lite is a 64-bit operating system kernel written from scratch. It has a UEFI It boots into a shell, launches user processes, survives application crashes, and can run things it probably should not be able to run yet. -The codebase is still small enough to understand, but large enough to hurt your feelings. The kernel is just 7k lines of code and the entire project is about 15k lines. +The codebase is still small enough to understand, but large enough to hurt your feelings. The kernel is just 10k lines of code and the entire project is about 20k lines. ## features @@ -295,7 +295,7 @@ Q: How often does it crash? A: Like Windows 11. Q: Can it recover from a crash? -A: Yes. Unless the kernel faulted. +A: Works on my machine. Q: Is this finished? A: No. And it never will be.