-
Notifications
You must be signed in to change notification settings - Fork 1
Development #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Development #46
Conversation
WalkthroughThis update introduces a kernel heap memory allocator with supporting headers and integrates it into the kernel initialization sequence. It also refactors locking in memory, virtual memory, and process management to use interrupt-safe spinlocks, and adds low-level interrupt flag manipulation utilities. Minor changes include adjusting PIT frequency and updating interrupt and standard type handling. Changes
Sequence Diagram(s)sequenceDiagram
participant KernelMain
participant VMem
participant KernelHeap
KernelMain->>VMem: VMemInit()
KernelMain->>KernelHeap: KernelHeapInit()
KernelMain->>...: Continue kernel startup
sequenceDiagram
participant Allocator
participant Spinlock
participant VMem
Allocator->>Spinlock: SpinLockIrqSave(&heap_lock)
Allocator->>VMem: VMemAlloc(size)
VMem-->>Allocator: pointer
Allocator->>Spinlock: SpinUnlockIrqRestore(&heap_lock, flags)
Allocator-->>Caller: user pointer
sequenceDiagram
participant Process
participant Spinlock
Process->>Spinlock: SpinLockIrqSave(&scheduler_lock)
Process->>Process: Critical section (create/terminate/schedule)
Process->>Spinlock: SpinUnlockIrqRestore(&scheduler_lock, flags)
Possibly related PRs
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🔭 Outside diff range comments (2)
kernel/memory/VMem.c (1)
380-383
: Inconsistent spinlock usage in VMemGetStats.This function still uses the basic
SpinLock
/SpinUnlock
pattern while other functions have been updated to use interrupt-safe variants. For consistency and proper interrupt safety, this should also be updated.- SpinLock(&vmem_lock); + irq_flags_t flags = SpinLockIrqSave(&vmem_lock); if (used_pages) *used_pages = kernel_space.used_pages; if (total_mapped) *total_mapped = kernel_space.total_mapped; - SpinUnlock(&vmem_lock); + SpinUnlockIrqRestore(&vmem_lock, flags);kernel/process/Process.c (1)
115-115
: Fix typo in error message.- PANIC("Termination queu1e overflow"); + PANIC("Termination queue overflow");
🧹 Nitpick comments (3)
arch/x86_64/interrupts/Interrupts.c (1)
109-109
: Remove duplicate comment.The comment "// The C-level interrupt handler" is duplicated and appears to be leftover from editing.
-// The C-level interrupt handler
kernel/memory/KernelHeap.h (1)
1-13
: Good header structure with naming suggestions.The header is well-structured with proper include guards and standard includes. However, consider these improvements:
- Naming consistency:
KernelMemoryAlloc
vsKernelFree
- considerKernelAlloc
for consistency- Documentation: The purpose of
KernelCallLocation
andKernelRealLocation
is unclear from their namesConsider adding brief documentation comments:
+// Initialize the kernel heap void KernelHeapInit(); +// Allocate memory block of specified size void* KernelMemoryAlloc(size_t size); +// Purpose unclear - consider renaming or documenting void* KernelCallLocation(size_t num, size_t size); +// Purpose unclear - consider renaming or documenting void* KernelRealLocation(void* ptr, size_t size); +// Free previously allocated memory block void KernelFree(void* ptr);kernel/memory/KernelHeap.c (1)
57-57
: Consider renaming functions for clarity.The names
KernelCallLocation
andKernelRealLocation
are confusing. Consider using more standard names:
KernelCallLocation
→KernelCalloc
(allocates zeroed memory like calloc)KernelRealLocation
→KernelRealloc
(reallocates memory like realloc)Also applies to: 66-66
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
arch/x86_64/interrupts/Interrupts.c
(3 hunks)drivers/Pic.h
(1 hunks)include/Io.h
(1 hunks)include/stddef.h
(1 hunks)kernel/atomic/Spinlock.h
(2 hunks)kernel/core/Kernel.c
(2 hunks)kernel/memory/KernelHeap.c
(1 hunks)kernel/memory/KernelHeap.h
(1 hunks)kernel/memory/Memory.c
(1 hunks)kernel/memory/VMem.c
(7 hunks)kernel/process/Process.c
(13 hunks)meson.build
(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
arch/x86_64/interrupts/Interrupts.c (1)
Learnt from: assembler-0
PR: assembler-0/VoidFrame#2
File: Kernel/Syscall.c:44-44
Timestamp: 2025-07-08T00:36:50.297Z
Learning: In IdtSetGate function calls, the parameters are: (interrupt_vector, handler_address, selector, flags). The interrupt vector (like 0x80) is the first argument, and the selector (like 0x08 for kernel code segment) is the third argument.
include/Io.h (1)
Learnt from: assembler-0
PR: assembler-0/VoidFrame#2
File: Kernel/Syscall.c:44-44
Timestamp: 2025-07-08T00:36:50.297Z
Learning: In IdtSetGate function calls, the parameters are: (interrupt_vector, handler_address, selector, flags). The interrupt vector (like 0x80) is the first argument, and the selector (like 0x08 for kernel code segment) is the third argument.
🧬 Code Graph Analysis (5)
kernel/core/Kernel.c (2)
kernel/memory/KernelHeap.h (1)
KernelHeapInit
(7-7)kernel/memory/KernelHeap.c (1)
KernelHeapInit
(19-23)
kernel/memory/VMem.c (1)
kernel/atomic/Spinlock.h (4)
SpinLockIrqSave
(19-26)SpinLockIrqSave
(19-19)SpinUnlockIrqRestore
(28-31)SpinUnlockIrqRestore
(28-28)
kernel/memory/Memory.c (5)
kernel/core/Kernel.c (1)
PrintKernelSuccess
(158-162)kernel/core/Kernel.h (1)
PrintKernelSuccess
(15-15)kernel/memory/Memory.h (2)
AllocPage
(10-10)FreePage
(11-11)kernel/atomic/Spinlock.h (4)
SpinLockIrqSave
(19-26)SpinLockIrqSave
(19-19)SpinUnlockIrqRestore
(28-31)SpinUnlockIrqRestore
(28-28)kernel/core/Panic.h (1)
Panic
(11-11)
kernel/atomic/Spinlock.h (1)
include/Io.h (6)
save_irq_flags
(18-22)save_irq_flags
(18-18)cli
(28-30)cli
(28-28)restore_irq_flags
(24-26)restore_irq_flags
(24-24)
kernel/memory/KernelHeap.c (4)
kernel/memory/KernelHeap.h (5)
KernelHeapInit
(7-7)KernelMemoryAlloc
(8-8)KernelCallLocation
(9-9)KernelRealLocation
(10-10)KernelFree
(11-11)kernel/core/Kernel.c (4)
PrintKernelSuccess
(158-162)PrintKernelError
(164-168)PrintKernelInt
(203-227)PrintKernel
(143-155)kernel/atomic/Spinlock.h (4)
SpinLockIrqSave
(19-26)SpinLockIrqSave
(19-19)SpinUnlockIrqRestore
(28-31)SpinUnlockIrqRestore
(28-28)kernel/memory/VMem.c (2)
VMemAlloc
(174-222)VMemFree
(224-273)
🔇 Additional comments (24)
drivers/Pic.h (1)
4-4
: Verify scheduler timing and tick-based constants after PIT frequency changeThe PIT rate was reduced from 1000 Hz to 200 Hz (1 ms → 5 ms per tick). This 5× coarser granularity affects all tick-based logic—please review and adjust as needed:
• kernel/process/Process.h
– QUANTUM_BASE (10 ticks → now 50 ms quantum)
– BOOST_INTERVAL (100 ticks → now 500 ms boost interval)
• kernel/process/Process.c
– GetCurrentProcess() security scan interval (every 100 ticks → 500 ms)
– FastSchedule() tick_counter increments and quantum_remaining logic
• arch/x86_64/interrupts/Interrupts.c
– tick_count and FastSchedule called on IRQ0 (timer interrupt)
• drivers/Pic.h & drivers/Pic.c
– PIT_FREQUENCY_HZ divisor use (1193180 / 200 vs. 1193180 / 1000)Ensure scheduler responsiveness, timeouts, and any timing-sensitive operations still meet design requirements.
include/stddef.h (1)
1-13
: LGTM! Standard definitions header is well-implemented.The header correctly provides kernel-appropriate standard definitions:
size_t
asuint64_t
is suitable for 64-bit kernel architectureNULL
definition with proper guard prevents redefinition issues- Header guard follows standard conventions
The intentional
#undef size_t
before redefinition ensures consistent kernel-wide type usage.include/Io.h (1)
16-35
: LGTM! Interrupt flag management functions are correctly implemented.The x86_64 assembly implementation is accurate:
save_irq_flags()
usespushfq/popq
to save the 64-bit flags registerrestore_irq_flags()
usespushq/popfq
to restore flagscli()
andsti()
provide standard interrupt controlvolatile
keyword prevents unwanted compiler optimizationsstatic inline
ensures optimal performanceThese functions provide the essential primitives for interrupt-safe spinlock implementations.
meson.build (1)
69-69
: LGTM! Build system integration is correct.The kernel heap source file is properly added to the build configuration alongside other memory management components.
kernel/core/Kernel.c (2)
17-17
: LGTM! Header inclusion is properly placed.The
KernelHeap.h
header is correctly included with other kernel memory management headers.
394-394
: LGTM! Kernel heap initialization is correctly positioned.The
KernelHeapInit()
call is properly placed afterVMemInit()
in the kernel startup sequence. This ensures the virtual memory subsystem is initialized before the heap allocator, which is the correct dependency order.Based on the current implementation, the function performs minimal initialization and primarily serves as a initialization checkpoint.
arch/x86_64/interrupts/Interrupts.c (1)
64-64
: Good addition of clarifying comment.The comment explaining the EOI (End Of Interrupt) operation improves code readability.
kernel/memory/VMem.c (3)
119-159
: Excellent improvement to interrupt-safe locking.The systematic replacement of basic spinlocks with interrupt-safe variants in
VMemMap
properly prevents race conditions and deadlocks that could occur if interrupts fired during critical sections. The pattern of saving flags on lock acquisition and restoring them on release is correctly implemented.
179-217
: Proper interrupt-safe locking implementation in VMemAlloc.The function correctly uses interrupt-safe spinlocks and properly handles the locking pattern around critical sections. The approach of releasing the lock before calling
VMemMap
(which has its own locking) prevents deadlocks.
240-268
: Good interrupt-safe locking in VMemFree.The function properly implements interrupt-safe spinlocks with correct flag saving/restoring. The lock is appropriately released before calling
FreePage
to avoid potential deadlocks.kernel/atomic/Spinlock.h (2)
5-5
: Appropriate dependency addition.Adding
#include "Io.h"
is necessary for the interrupt flag management functions used in the new spinlock variants.
18-31
: Excellent implementation of interrupt-safe spinlocks.The implementation correctly follows the standard pattern:
- Save current interrupt flags
- Disable interrupts
- Acquire the spinlock
- Return saved flags for later restoration
- Release lock and restore interrupt flags
This prevents race conditions and deadlocks that could occur if interrupts fired during critical sections. The use of
__builtin_ia32_pause()
for busy-waiting is appropriate for x86_64 architecture.kernel/memory/Memory.c (3)
143-143
: Good addition of initialization success message.The success message provides valuable feedback about physical memory manager initialization, consistent with logging practices used elsewhere in the kernel.
149-161
: Proper interrupt-safe locking in AllocPage.The function correctly implements interrupt-safe spinlocks, saving flags on entry and restoring them on all exit paths. The pattern ensures atomicity during page allocation and prevents race conditions.
165-184
: Correct interrupt-safe locking in FreePage.The function properly uses interrupt-safe spinlocks with consistent flag saving/restoring on all exit paths, including error conditions. This ensures proper synchronization during page deallocation.
kernel/process/Process.c (6)
9-10
: LGTM!The addition of these headers is appropriate for the new interrupt-safe spinlock functionality.
35-35
: LGTM!The scheduler lock is correctly declared as
volatile int
for use with atomic spinlock operations.
161-249
: Spinlock usage is correct and comprehensive.The interrupt-safe spinlock properly protects all critical sections in the termination process, with correct release on all exit paths.
498-587
: Proper scheduler synchronization implemented.The interrupt-safe spinlock correctly protects the entire scheduling operation, ensuring atomicity during context switches.
678-768
: Process creation properly synchronized.The spinlock correctly protects the entire process creation operation, including slot allocation and initialization.
776-825
: Cleanup operations properly synchronized.The spinlock ensures atomic cleanup of terminated processes, preventing race conditions during resource deallocation.
kernel/memory/KernelHeap.c (3)
1-23
: Basic heap structure looks good.The simple page-based allocator with header metadata is appropriate for initial implementation.
25-55
: Memory allocation implementation is correct.The function properly handles zero-size requests, adds header overhead, and provides appropriate error logging.
98-115
: Memory free implementation is correct.The function properly handles NULL pointers and correctly calculates the total size to free including the header.
if (regs->interrupt_number == 32 || regs->interrupt_number == 33) { // Force ignore keyboard interrupt | ||
tick_count++; | ||
// FastDisplayTicks(tick_count); | ||
FastSchedule(regs); | ||
outb(0x20, 0x20); | ||
outb(0x20, 0x20); // EOI to master PIC | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider proper keyboard interrupt handling instead of treating it as a timer interrupt.
Treating keyboard interrupt (33) the same as timer interrupt (32) by incrementing tick count and calling the scheduler seems like a workaround. This could lead to:
- Incorrect tick counting and timing
- Unnecessary scheduler invocations on keyboard input
- Missing proper keyboard input processing
Consider implementing proper keyboard interrupt handling:
- if (regs->interrupt_number == 32 || regs->interrupt_number == 33) { // Force ignore keyboard interrupt
+ if (regs->interrupt_number == 32) { // Timer interrupt
tick_count++;
// FastDisplayTicks(tick_count);
FastSchedule(regs);
outb(0x20, 0x20); // EOI to master PIC
return;
+ }
+
+ if (regs->interrupt_number == 33) { // Keyboard interrupt
+ // Handle keyboard input properly
+ HandleKeyboardInterrupt(regs);
+ outb(0x20, 0x20); // EOI to master PIC
+ return;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if (regs->interrupt_number == 32 || regs->interrupt_number == 33) { // Force ignore keyboard interrupt | |
tick_count++; | |
// FastDisplayTicks(tick_count); | |
FastSchedule(regs); | |
outb(0x20, 0x20); | |
outb(0x20, 0x20); // EOI to master PIC | |
return; | |
} | |
if (regs->interrupt_number == 32) { // Timer interrupt | |
tick_count++; | |
// FastDisplayTicks(tick_count); | |
FastSchedule(regs); | |
outb(0x20, 0x20); // EOI to master PIC | |
return; | |
} | |
if (regs->interrupt_number == 33) { // Keyboard interrupt | |
// Handle keyboard input properly | |
HandleKeyboardInterrupt(regs); | |
outb(0x20, 0x20); // EOI to master PIC | |
return; | |
} |
🤖 Prompt for AI Agents
In arch/x86_64/interrupts/Interrupts.c around lines 60 to 66, the keyboard
interrupt (interrupt number 33) is currently handled the same way as the timer
interrupt (32), which is incorrect. To fix this, separate the handling logic for
interrupt 33 by removing the tick_count increment and scheduler call for it, and
instead implement proper keyboard input processing code to handle keyboard
events. Ensure the timer interrupt continues to increment tick_count and call
the scheduler as needed, while the keyboard interrupt processes input and sends
the appropriate EOI to the PIC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah i would love to but the current setup is not suitable, i think tomorrow ill bring back the minimal shell and kbd interrupt, should i?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@assembler-0 That makes perfect sense! Yes, I'd recommend bringing back the minimal shell and keyboard interrupt handling tomorrow. It's a good foundation to have proper interrupt handling in place.
For now, the current workaround is acceptable during development - it's clearly marked with a comment indicating it's temporary. When you do implement the proper keyboard interrupt handling tomorrow, you'll be able to:
- Separate the timer and keyboard interrupt logic properly
- Have a clean foundation for building more keyboard functionality
- Remove the tick count increment from keyboard events
The minimal shell will also give you a nice way to test the keyboard interrupt handling is working correctly. Good prioritization! 👍
🧠 Learnings used
Learnt from: assembler-0
PR: assembler-0/VoidFrame#2
File: Kernel/Syscall.c:44-44
Timestamp: 2025-07-08T00:36:50.297Z
Learning: In IdtSetGate function calls, the parameters are: (interrupt_vector, handler_address, selector, flags). The interrupt vector (like 0x80) is the first argument, and the selector (like 0x08 for kernel code segment) is the third argument.
void* KernelCallLocation(size_t num, size_t size) { | ||
size_t total_size = num * size; | ||
void* ptr = KernelMemoryAlloc(total_size); | ||
if (ptr) { | ||
FastMemset(ptr, 0, total_size); | ||
} | ||
return ptr; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add integer overflow check for allocation size.
The multiplication num * size
could overflow, leading to incorrect allocation.
void* KernelCallLocation(size_t num, size_t size) {
+ // Check for multiplication overflow
+ if (num != 0 && size > SIZE_MAX / num) {
+ return NULL;
+ }
size_t total_size = num * size;
void* ptr = KernelMemoryAlloc(total_size);
if (ptr) {
FastMemset(ptr, 0, total_size);
}
return ptr;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
void* KernelCallLocation(size_t num, size_t size) { | |
size_t total_size = num * size; | |
void* ptr = KernelMemoryAlloc(total_size); | |
if (ptr) { | |
FastMemset(ptr, 0, total_size); | |
} | |
return ptr; | |
} | |
void* KernelCallLocation(size_t num, size_t size) { | |
// Check for multiplication overflow | |
if (num != 0 && size > SIZE_MAX / num) { | |
return NULL; | |
} | |
size_t total_size = num * size; | |
void* ptr = KernelMemoryAlloc(total_size); | |
if (ptr) { | |
FastMemset(ptr, 0, total_size); | |
} | |
return ptr; | |
} |
🤖 Prompt for AI Agents
In kernel/memory/KernelHeap.c around lines 57 to 64, add a check to detect
integer overflow when calculating total_size as num multiplied by size. Before
calling KernelMemoryAlloc, verify that the multiplication does not overflow by
ensuring that total_size divided by size equals num. If overflow is detected,
handle it appropriately by returning NULL or an error to prevent incorrect
allocation size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah but idk why when i try to call any function, it kernell panics (page fault), hmm interesting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
@assembler-0 That's a concerning issue! Page faults when calling functions suggest a systemic problem with memory management or stack handling. Let me help you debug this.
Could you provide more details about:
- When exactly does it panic? - During kernel initialization, first function call, or after some operations?
- What's the specific function you're trying to call? - Is it
KernelCallLocation
,KernelMemoryAlloc
, or others? - Any pattern? - Does it happen immediately or after certain operations?
- Stack trace or register state? - If you have debug output showing where the page fault occurs
This will help pinpoint whether it's a stack corruption, memory mapping issue, or interrupt handling problem.
🏁 Script executed:
#!/bin/bash
# Let's examine the interrupt and stack handling code to identify potential issues
echo "=== Examining interrupt handling code ==="
rg -A 5 -B 5 "page fault|Page fault|PAGE_FAULT" --type c
echo -e "\n=== Checking stack pointer and calling conventions ==="
rg -A 10 -B 5 "rsp|stack.*pointer|call.*function" --type c
echo -e "\n=== Looking for function call patterns and potential issues ==="
rg -A 3 -B 3 "KernelCallLocation|KernelMemoryAlloc" --type c
echo -e "\n=== Examining virtual memory and page mapping ==="
rg -A 5 -B 5 "VMemAlloc|VMemFree|page.*map" --type c
echo -e "\n=== Checking spinlock usage and interrupt handling ==="
rg -A 3 -B 3 "SpinLockIrqSave|SpinUnlockIrqRestore|cli\(\)|sti\(\)" --type c
Length of output: 41464
Based on the search results, I can see several potential causes for your kernel panic issue. The most concerning finding is that no page fault handler code was found in the search results, which is likely the root cause.
Most likely cause: Your system is missing a page fault handler (interrupt #14). When any function call triggers a page fault (which can happen during normal memory allocation, stack operations, or accessing unmapped memory), the kernel has no handler to deal with it, causing a panic.
Immediate questions to help debug:
- What's the exact error message/panic screen? - Does it show interrupt number 14 or any specific error codes?
- When does it happen? - During kernel initialization, first heap allocation, or when calling specific functions?
- Are you calling the new heap functions? - Like
KernelMemoryAlloc
orKernelCallLocation
?
Likely fixes needed:
- Add a page fault handler to your interrupt table (interrupt Development #14)
- Verify interrupt table setup - Make sure all critical interrupts are properly registered
- Check stack alignment - The process creation code shows stack setup, but stack corruption could cause function call failures
Let me know what the script finds and we can pinpoint the exact issue!
🏁 Script executed:
#!/bin/bash
# Let's specifically look for page fault handling and interrupt table setup
echo "=== Searching for page fault handler and interrupt 14 (page fault) ==="
rg -A 10 -B 5 "14|PAGE_FAULT|page_fault_handler|exception.*14" --type c
echo -e "\n=== Examining interrupt table setup ==="
rg -A 10 -B 5 "IdtInstall|IDT|interrupt.*table|exception.*handler" --type c
echo -e "\n=== Checking if interrupt 14 is properly set up ==="
rg -A 5 -B 5 "SetIdtGate.*14|interrupt.*14" --type c
echo -e "\n=== Looking for fault/exception handling infrastructure ==="
rg -A 5 -B 5 "fault.*handler|exception.*handler|InterruptHandler|isr" --type c
echo -e "\n=== Examining the interrupt dispatcher ==="
rg -A 15 -B 5 "interrupt_handler|InterruptDispatcher|irq_handler" --type c
Length of output: 69484
Perfect! Now I can see the exact issue. The problem is NOT missing interrupt registration, but rather missing interrupt handling logic.
🎯 Found the issue! Your interrupt 14 (page fault) IS properly registered in the IDT, but the InterruptHandler
function only handles interrupts 32 and 33 (timer and keyboard).
The problem: When a page fault occurs (interrupt 14), it goes to InterruptHandler
but there's no specific case for it, so the function just returns without handling the fault, leaving the system in a corrupted state.
Current problematic code:
void InterruptHandler(struct Registers* regs) {
ASSERT(regs != NULL);
if (regs->interrupt_number == 32 || regs->interrupt_number == 33) { // Only timer/keyboard
tick_count++;
FastSchedule(regs);
outb(0x20, 0x20); // Send EOI to master PIC
}
// ❌ NO HANDLING for interrupt 14 (page fault) or other exceptions!
}
Quick fix: Add page fault handling to your InterruptHandler
function:
void InterruptHandler(struct Registers* regs) {
ASSERT(regs != NULL);
if (regs->interrupt_number == 32 || regs->interrupt_number == 33) {
tick_count++;
FastSchedule(regs);
outb(0x20, 0x20);
}
else if (regs->interrupt_number == 14) { // Page fault
// Handle page fault
PrintKernelError("Page fault detected!\n");
PrintKernelError("Error code: ");
PrintKernelInt(regs->error_code);
PrintKernelError("\nFaulting address: ");
// You can get the faulting address from CR2 register
uint64_t fault_addr;
asm volatile("mov %%cr2, %0" : "=r"(fault_addr));
PrintKernelInt(fault_addr);
PrintKernelError("\nRIP: ");
PrintKernelInt(regs->rip);
PrintKernelError("\n");
// For now, panic to see what's happening
PANIC("Page fault - see details above");
}
}
This will help you debug exactly what's causing the page fault and where it's happening!
🏁 Script executed:
#!/bin/bash
# Let's look at the current InterruptHandler logic more closely
echo "=== Current InterruptHandler implementation ==="
rg -A 15 -B 5 "InterruptHandler.*struct.*Registers" --type c
echo -e "\n=== Let's see what happens for unhandled interrupts ==="
rg -A 10 -B 5 "interrupt_number.*32|interrupt_number.*33" --type c
echo -e "\n=== Let's check if there are any assembly interrupt files ==="
find . -name "*.asm" -o -name "*.s" | head -10
echo -e "\n=== Check for any page fault specific handling ==="
rg -i "page.*fault|fault.*page" --type c
Length of output: 3444
Perfect! Now I can see the real issue. Your page fault handler IS working correctly! The problem is that you're getting actual page faults when calling functions, which means there's a memory mapping or stack issue.
Let me help you debug this:
The Good News: Your page fault handler is working perfectly! It's correctly detecting and reporting page faults.
The Bad News: You're getting real page faults when calling functions, which means:
- Stack corruption - The stack pointer might be invalid or corrupted
- Code section not mapped - The function code might not be properly mapped in virtual memory
- Memory mapping issues - Virtual memory setup might be incomplete
- Function pointer corruption - The function addresses might be invalid
Questions to help debug:
-
What does the page fault output show?
- What's the faulting address (CR2 value)?
- What's the error code?
- What's the RIP (instruction pointer)?
-
When exactly does it happen?
- During kernel initialization?
- When calling the new heap functions?
- When calling ANY function?
-
Are you calling functions from interrupt context?
- This could cause stack issues
Immediate debugging steps:
- Add the missing
PrintKernelHex
function (if it doesn't exist) - Check if the issue happens during specific function calls
- Verify memory mappings are correct
Can you share the exact page fault output you're seeing? That will tell us exactly what's going wrong and where.
🏁 Script executed:
#!/bin/bash
# Let's look at the complete InterruptHandler to see all the diagnostic info
echo "=== Complete InterruptHandler implementation ==="
rg -A 25 -B 5 "PrintKernelError.*PAGE FAULT" --type c
# Let's also check what PrintKernelHex is and if it exists
echo -e "\n=== Check if PrintKernelHex exists ==="
rg -A 5 -B 5 "PrintKernelHex" --type c
# Let's check memory initialization order
echo -e "\n=== Check kernel initialization sequence ==="
rg -A 10 -B 5 "KernelMain|main.*function" --type c
# Let's look at the stack setup in process creation
echo -e "\n=== Check stack setup ==="
rg -A 5 -B 5 "STACK_SIZE|stack.*alloc" --type c
Length of output: 17797
Excellent! Now I can see the complete picture. You have a very comprehensive page fault handler that provides detailed debugging information. The handler shows:
- Faulting address (CR2 register)
- Error code with detailed breakdown
- Whether it's page not present or protection violation
- Read vs Write operation
- User vs Supervisor mode
The key question is: What exactly do you see on screen when the kernel panics?
You should be seeing something like:
PAGE FAULT
Address: 0x[some_address]
Error Code: 0x[error_code]
Reason: Page Not Present (or Protection Violation)
Operation: Read (or Write)
Mode: Supervisor (or User)
If you're NOT seeing this detailed output, then the page fault is happening before the kernel is fully initialized or during a context where printing doesn't work.
If you ARE seeing this output, then we can debug the exact issue by analyzing:
- What's the faulting address? - This tells us what memory location caused the fault
- What's the error code? - This tells us the exact type of fault
- What's the instruction pointer (RIP)? - This tells us which instruction caused the fault
Can you please share:
- The exact text you see on screen when the kernel panics
- When does the panic happen? (during boot, after specific operations, etc.)
- Are you trying to call any specific functions when it happens?
This will help us pinpoint the exact cause and fix it quickly!
🏁 Script executed:
#!/bin/bash
# Let's check if there are any obvious issues with the heap functions
echo "=== Let's see if there are any function calls that might be causing issues ==="
rg -A 3 -B 3 "KernelMemoryAlloc|KernelCallLocation|KernelHeapInit" --type c
echo -e "\n=== Check if there are any obvious stack or function pointer issues ==="
rg -A 5 -B 5 "entry_point|function.*pointer" --type c
echo -e "\n=== Check for any obvious memory corruption patterns ==="
rg -A 5 -B 5 "corruption|corrupt|invalid.*pointer" --type c
Length of output: 7633
Now I can see the heap functions are being called during kernel initialization. The setup looks correct, but I need to understand exactly what's happening when you get the page fault.
Critical Question: What exactly do you see on screen when the kernel panics?
You should see the detailed page fault output from your handler. If you're not seeing it, that's a clue about when/where the fault is occurring.
Meanwhile, please tell me:
-
What specific function are you trying to call when it crashes? (e.g.,
KernelMemoryAlloc
,CreateSecureProcess
, or something else?) -
What exact text appears on screen when the panic happens? Even if it's just partial, that's helpful.
-
Does the kernel complete initialization before the panic, or does it happen during boot?
-
Are you adding any new function calls to test the heap, or is this happening with existing code?
Most likely scenarios:
- Stack overflow - The 4KB process stack might be too small
- Function called from wrong context - Calling heap functions from interrupt context
- Virtual memory issue - Functions not properly mapped after heap init
- Spinlock deadlock - Getting stuck in spinlock during function calls
The detailed page fault info from your handler will tell us exactly what's happening!
🏁 Script executed:
#!/bin/bash
# Let's check what might be called after heap initialization
echo "=== Check what happens after KernelHeapInit in the boot sequence ==="
rg -A 15 -B 5 "KernelHeapInit" --type c
echo -e "\n=== Check if there are any obvious function calls that might be failing ==="
rg -A 5 -B 5 "CreateSecureProcess|SecureKernelIntegritySubsystem" --type c
echo -e "\n=== Check for any assembly function calls that might be problematic ==="
rg -A 5 -B 5 "EnablePagingAndJump|KernelMainHigherHalf" --type c
echo -e "\n=== Let's see if there are any obvious issues with the initialization order ==="
rg -A 20 "KernelMainHigherHalf.*{" --type c
Length of output: 13743
Perfect! Now I can see the exact sequence that's causing the issue. Let me fix that regex and get the complete picture:
🎯 I think I found the issue! Looking at your initialization sequence:
KernelHeapInit()
- Called in lower half memory ✅- Virtual memory setup - Creates new page tables ✅
EnablePagingAndJump()
- Jumps to higher half virtual memory ✅KernelMainHigherHalf()
- Now running in virtual memory ✅CreateSecureProcess()
- This calls heap functions! ❌
The Problem: The heap functions (like KernelMemoryAlloc
) are being called after the virtual memory jump, but the heap's static variables (like kheap_lock
) might not be properly mapped in the new virtual memory space!
The kheap_lock
static variable in KernelHeap.c
was accessible in lower half memory during KernelHeapInit()
, but after the virtual memory jump, it might not be properly mapped.
Quick test to confirm:
- What exact page fault address do you see? (The CR2 value)
- Does it happen immediately when calling
CreateSecureProcess()
? - Try adding a simple test: Add a call to
KernelMemoryAlloc(64)
right afterKernelHeapInit()
(before the virtual memory jump) to see if it works there.
Likely fix: The issue is that the heap's static variables need to be properly mapped in the virtual memory space. The VMemMapKernel()
function should map the .bss
section which contains static variables like kheap_lock
.
Can you share the exact page fault output you see? That will confirm this theory!
🏁 Script executed:
#!/bin/bash
# Fix the regex and check the initialization sequence
echo "=== Check KernelMainHigherHalf initialization sequence ==="
rg -A 20 "KernelMainHigherHalf.*)" --type c
echo -e "\n=== Check what CoreInit does ==="
rg -A 15 -B 5 "CoreInit" --type c
echo -e "\n=== Check the virtual memory mapping details ==="
rg -A 10 -B 5 "VMemMapKernel|\.text.*mapped|\.data.*mapped|\.bss.*mapped" --type c
Length of output: 8901
🎯 FOUND THE ISSUE!
Looking at your initialization sequence, I can see the problem:
- Lower half (
KernelMain
):KernelHeapInit()
works fine ✅ - Virtual memory jump:
EnablePagingAndJump()
switches to higher half ✅ - Higher half (
KernelMainHigherHalf
):CoreInit()
runs ✅ - Missing step:
VMemMapKernel()
is never called! ❌
The Problem: The VMemMapKernel()
function is supposed to map the kernel sections (.text
, .data
, .bss
) in the virtual memory space, but it's never called! Your static variable kheap_lock
is in the .bss
section and isn't properly mapped in the higher half.
The Fix: Add VMemMapKernel()
call after CoreInit()
in KernelMainHigherHalf()
:
void KernelMainHigherHalf(void) {
CoreInit();
// ADD THIS LINE:
VMemMapKernel(0, 0); // Parameters are unused according to the code
PrintKernelSuccess("[SYSTEM] Successfully jumped to higher half. Virtual memory is active.\n");
PrintKernel("[INFO] Creating security manager process...\n");
uint64_t security_pid = CreateSecureProcess(SecureKernelIntegritySubsystem, PROC_PRIV_SYSTEM);
// ... rest of the function
}
This will properly map the kernel sections including the .bss
section where kheap_lock
lives.
Quick verification: Look at the VMemMapKernel()
function - it maps:
.text
section (read-only).rodata
section (read-only).data
section (read-write).bss
section (read-write) ← This is where yourkheap_lock
static variable is
Without this mapping, when CreateSecureProcess()
tries to call heap functions, accessing kheap_lock
causes a page fault because it's not mapped in the virtual memory space!
Try adding that one line and the issue should be resolved!
🧬 Code Graph Analysis Results
Relevant snippets from kernel/memory/KernelHeap.h
(lines 7-11)
KernelHeapInit()
KernelMemoryAlloc(size_t size)
KernelCallLocation(size_t num, size_t size)
KernelRealLocation(void* ptr, size_t size)
KernelFree(void* ptr)
These are the function declarations corresponding to the implementations in KernelHeap.c
.
Relevant snippets from kernel/atomic/Spinlock.h
(lines 19-31)
static inline irq_flags_t SpinLockIrqSave(volatile int* lock) {
irq_flags_t flags = save_irq_flags();
cli();
while (__sync_lock_test_and_set(lock, 1)) {
while (*lock) __builtin_ia32_pause();
}
return flags;
}
static inline void SpinUnlockIrqRestore(volatile int* lock, irq_flags_t flags) {
__sync_lock_release(lock);
restore_irq_flags(flags);
}
These functions implement spinlock acquisition and release with interrupt flag saving/restoring, used in KernelHeap.c
to protect heap operations.
Relevant snippets from kernel/memory/VMem.c
(lines 174-222)
void* VMemAlloc(uint64_t size) {
if (size == 0) return NULL;
size = PAGE_ALIGN_UP(size);
irq_flags_t flags = SpinLockIrqSave(&vmem_lock);
uint64_t vaddr = kernel_space.next_vaddr;
// Reserve the virtual address space
kernel_space.next_vaddr += size;
SpinUnlockIrqRestore(&vmem_lock, flags);
// Now map pages without holding the lock
uint64_t allocated_size = 0;
for (uint64_t offset = 0; offset < size; offset += PAGE_SIZE) {
void* paddr = AllocPage();
if (!paddr) {
// Cleanup on failure
if (allocated_size > 0) {
VMemFree((void*)vaddr, allocated_size);
}
return NULL;
}
int result = VMemMap(vaddr + offset, (uint64_t)paddr, PAGE_WRITABLE);
if (result != VMEM_SUCCESS) {
// Free the physical page and cleanup
FreePage(paddr);
if (allocated_size > 0) {
VMemFree((void*)vaddr, allocated_size);
}
return NULL;
}
allocated_size += PAGE_SIZE;
}
// Update tracking
flags = SpinLockIrqSave(&vmem_lock);
kernel_space.used_pages += size / PAGE_SIZE;
kernel_space.total_mapped += size;
SpinUnlockIrqRestore(&vmem_lock, flags);
// Zero the allocated memory
FastMemset((void*)vaddr, 0, size);
return (void*)vaddr;
}
This function allocates virtual memory pages, maps physical pages, and returns a pointer to the allocated virtual address. It is called by KernelMemoryAlloc
in KernelHeap.c
.
Relevant snippets from kernel/memory/VMem.c
(lines 224-273)
void VMemFree(void* vaddr, uint64_t size) {
if (!vaddr || size == 0) return;
size = PAGE_ALIGN_UP(size);
uint64_t start_vaddr = PAGE_ALIGN_DOWN((uint64_t)vaddr);
uint64_t num_pages = size / PAGE_SIZE;
for (uint64_t i = 0; i < num_pages; i++) {
uint64_t current_vaddr = start_vaddr + (i * PAGE_SIZE);
// Get physical address first (this has its own locking internally)
uint64_t paddr = VMemGetPhysAddr(current_vaddr);
if (paddr == 0) continue; // Not mapped
// Now acquire lock for modification
irq_flags_t flags = SpinLockIrqSave(&vmem_lock);
// Navigate to the Page Table Entry (PTE)
uint64_t pdp_phys = VMemGetPageTablePhys((uint64_t)kernel_space.pml4, current_vaddr, 0, 0);
if (!pdp_phys) { SpinUnlockIrqRestore(&vmem_lock, flags); continue; }
uint64_t pd_phys = VMemGetPageTablePhys(pdp_phys, current_vaddr, 1, 0);
if (!pd_phys) { SpinUnlockIrqRestore(&vmem_lock, flags); continue; }
uint64_t pt_phys = VMemGetPageTablePhys(pd_phys, current_vaddr, 2, 0);
if (!pt_phys) { SpinUnlockIrqRestore(&vmem_lock, flags); continue; }
// Get virtual address of the page table to modify it
uint64_t* pt_virt = (uint64_t*)PHYS_TO_VIRT(pt_phys);
int pt_index = (current_vaddr >> PT_SHIFT) & PT_INDEX_MASK;
// Check if it's actually mapped before proceeding
if (pt_virt[pt_index] & PAGE_PRESENT) {
// Clear the entry (unmap)
pt_virt[pt_index] = 0;
// Flush the TLB for this specific address
VMemFlushTLBSingle(current_vaddr);
// Update stats
kernel_space.used_pages--;
kernel_space.total_mapped -= PAGE_SIZE;
}
SpinUnlockIrqRestore(&vmem_lock, flags);
// Free physical page outside the lock
FreePage((void*)paddr);
}
}
This function frees virtual memory pages by unmapping them and releasing physical pages. It is called by KernelFree
in KernelHeap.c
.
Relevant snippets from kernel/core/Kernel.c
(lines 143-155, 158-168, 203-227)
void PrintKernel(const char* str) {
if (!str) return;
SpinLock(&lock);
// Cache the original color
const uint8_t original_color = console.color;
for (const char* p = str; *p; p++) {
ConsolePutchar(*p);
}
console.color = original_color;
SpinUnlock(&lock);
}
void PrintKernelSuccess(const char* str) {
ConsoleSetColor(VGA_COLOR_SUCCESS);
PrintKernel(str);
ConsoleSetColor(VGA_COLOR_DEFAULT);
}
void PrintKernelError(const char* str) {
ConsoleSetColor(VGA_COLOR_ERROR);
PrintKernel(str);
ConsoleSetColor(VGA_COLOR_DEFAULT);
}
void PrintKernelInt(int64_t num) {
char buffer[21]; // Max digits for 64-bit signed integer + sign + null
if (num == 0) {
PrintKernel("0");
return;
}
bool negative = num < 0;
if (negative) num = -num;
int pos = 20;
buffer[pos--] = '\0';
while (num > 0 && pos >= 0) {
buffer[pos--] = '0' + (num % 10);
num /= 10;
}
if (negative && pos >= 0) {
buffer[pos--] = '-';
}
PrintKernel(&buffer[pos + 1]);
}
These functions handle kernel printing with color and locking, used for debug/error messages in KernelHeap.c
.
These snippets provide context on:
- How kernel heap memory is allocated and freed (
KernelMemoryAlloc
,KernelFree
). - How virtual memory pages are allocated and mapped (
VMemAlloc
) and freed/unmapped (VMemFree
). - The spinlock mechanism protecting heap operations.
- Kernel printing functions used for error reporting.
This context is relevant to understanding why calling functions might cause a kernel panic (page fault) if memory allocation or mapping fails or is corrupted.
void* KernelRealLocation(void* ptr, size_t size) { | ||
if (ptr == NULL) { | ||
return KernelMemoryAlloc(size); | ||
} | ||
if (size == 0) { | ||
KernelFree(ptr); | ||
return NULL; | ||
} | ||
|
||
irq_flags_t flags = SpinLockIrqSave(&kheap_lock); | ||
|
||
// Get the original header and size | ||
HeapBlockHeader* old_header = (HeapBlockHeader*)((uint8_t*)ptr - sizeof(HeapBlockHeader)); | ||
size_t old_size = old_header->size; | ||
|
||
// Allocate new memory | ||
void* new_ptr = KernelMemoryAlloc(size); | ||
if (new_ptr) { | ||
// Copy data from old to new, up to the minimum of old_size and new_size | ||
FastMemcpy(new_ptr, ptr, (old_size < size) ? old_size : size); | ||
// Free the old memory block | ||
KernelFree(ptr); | ||
} else { | ||
PrintKernelError("[ERROR] KernelRealLocation: Failed to reallocate "); | ||
PrintKernelInt(size); | ||
PrintKernel(" bytes.\n"); | ||
} | ||
|
||
SpinUnlockIrqRestore(&kheap_lock, flags); | ||
return new_ptr; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Deadlock in reallocation function.
The function holds kheap_lock
while calling KernelMemoryAlloc
and KernelFree
, which also acquire the same lock, causing a deadlock.
void* KernelRealLocation(void* ptr, size_t size) {
if (ptr == NULL) {
return KernelMemoryAlloc(size);
}
if (size == 0) {
KernelFree(ptr);
return NULL;
}
irq_flags_t flags = SpinLockIrqSave(&kheap_lock);
// Get the original header and size
HeapBlockHeader* old_header = (HeapBlockHeader*)((uint8_t*)ptr - sizeof(HeapBlockHeader));
size_t old_size = old_header->size;
+ SpinUnlockIrqRestore(&kheap_lock, flags);
+
// Allocate new memory
void* new_ptr = KernelMemoryAlloc(size);
if (new_ptr) {
// Copy data from old to new, up to the minimum of old_size and new_size
FastMemcpy(new_ptr, ptr, (old_size < size) ? old_size : size);
// Free the old memory block
KernelFree(ptr);
} else {
PrintKernelError("[ERROR] KernelRealLocation: Failed to reallocate ");
PrintKernelInt(size);
PrintKernel(" bytes.\n");
}
- SpinUnlockIrqRestore(&kheap_lock, flags);
return new_ptr;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
void* KernelRealLocation(void* ptr, size_t size) { | |
if (ptr == NULL) { | |
return KernelMemoryAlloc(size); | |
} | |
if (size == 0) { | |
KernelFree(ptr); | |
return NULL; | |
} | |
irq_flags_t flags = SpinLockIrqSave(&kheap_lock); | |
// Get the original header and size | |
HeapBlockHeader* old_header = (HeapBlockHeader*)((uint8_t*)ptr - sizeof(HeapBlockHeader)); | |
size_t old_size = old_header->size; | |
// Allocate new memory | |
void* new_ptr = KernelMemoryAlloc(size); | |
if (new_ptr) { | |
// Copy data from old to new, up to the minimum of old_size and new_size | |
FastMemcpy(new_ptr, ptr, (old_size < size) ? old_size : size); | |
// Free the old memory block | |
KernelFree(ptr); | |
} else { | |
PrintKernelError("[ERROR] KernelRealLocation: Failed to reallocate "); | |
PrintKernelInt(size); | |
PrintKernel(" bytes.\n"); | |
} | |
SpinUnlockIrqRestore(&kheap_lock, flags); | |
return new_ptr; | |
} | |
void* KernelRealLocation(void* ptr, size_t size) { | |
if (ptr == NULL) { | |
return KernelMemoryAlloc(size); | |
} | |
if (size == 0) { | |
KernelFree(ptr); | |
return NULL; | |
} | |
irq_flags_t flags = SpinLockIrqSave(&kheap_lock); | |
// Get the original header and size | |
HeapBlockHeader* old_header = (HeapBlockHeader*)((uint8_t*)ptr - sizeof(HeapBlockHeader)); | |
size_t old_size = old_header->size; | |
// Release lock before calling alloc/free to avoid deadlock | |
SpinUnlockIrqRestore(&kheap_lock, flags); | |
// Allocate new memory | |
void* new_ptr = KernelMemoryAlloc(size); | |
if (new_ptr) { | |
// Copy data from old to new, up to the minimum of old_size and new_size | |
FastMemcpy(new_ptr, ptr, (old_size < size) ? old_size : size); | |
// Free the old memory block | |
KernelFree(ptr); | |
} else { | |
PrintKernelError("[ERROR] KernelRealLocation: Failed to reallocate "); | |
PrintKernelInt(size); | |
PrintKernel(" bytes.\n"); | |
} | |
return new_ptr; | |
} |
🤖 Prompt for AI Agents
In kernel/memory/KernelHeap.c around lines 66 to 96, the function
KernelRealLocation holds the kheap_lock while calling KernelMemoryAlloc and
KernelFree, which themselves acquire the same lock, causing a deadlock. To fix
this, restructure the function to release the lock before calling
KernelMemoryAlloc and KernelFree, and only acquire the lock when accessing or
modifying shared data structures that require synchronization. This avoids
nested locking and prevents deadlocks.
Summary by CodeRabbit
New Features
Improvements
Other Changes