add missing fault handlers instead of throwing everything into the GP bin, add more ways to crash in native shell

This commit is contained in:
maksw authored and Maksymilian Wilczynski committed 2026-05-26 14:46:52 +02:00
1 parent e1248f4afe
commit 03bf767ac2
9 files changed
+387 -50

No files matched your search

Binary file not shown.
+54 -30
View File
@@ -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;
}
}
}
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;
}
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;
}
}
@@ -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);
}
}
}
+65 -14
View File
@@ -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,22 +441,23 @@ __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",
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);
default:
KePanic("PANIC: Exception #%llu | RIP: %016llx\n",
(unsigned long long)frame->Vector,
} 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);
}
}
+47 -3
View File
@@ -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] = {
+1
View File
@@ -32,3 +32,4 @@ extern void KeSyscallEntry(void);
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
+19
View File
@@ -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();
+161
View File
@@ -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
+38 -1
View File
@@ -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; }
+2 -2
View File
@@ -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.