Skip to content

Just some additional Kernel designs. - #6

Open
Aditya-B-007 wants to merge 3 commits into
mainfrom
Kernel-Patch-work
Open

Just some additional Kernel designs.#6
Aditya-B-007 wants to merge 3 commits into
mainfrom
Kernel-Patch-work

Conversation

@Aditya-B-007

@Aditya-B-007 Aditya-B-007 commented Sep 26, 2025

Copy link
Copy Markdown
Owner

These are the baby steps for the task scheduler

Summary by CodeRabbit

  • New Features
    • Added basic cooperative multitasking and a background counter task; shell now starts as a task.
    • Added a simple system-call handler with a visible confirmation message on use.
  • Refactor
    • Overhauled physical memory manager to a page/buddy allocator for improved stability and free-memory tracking.
    • Switched network receive buffer to a larger block allocation for reliability.
  • Style
    • Reduced boot-time console verbosity; streamlined startup messages.

@coderabbitai

coderabbitai Bot commented Sep 26, 2025

Copy link
Copy Markdown

Walkthrough

The PR replaces the PMM with a buddy allocator and changes its public API to page-based functions, adds a cooperative tasking subsystem and syscall entry (interrupt 128), adjusts kernel init to spawn tasks and reduce verbose logs, updates NIC RX buffer allocation to a block-based pmm allocation, and modifies interrupt handling and paging metadata.

Changes

Cohort / File(s) Summary of Changes
PMM Buddy Allocator
pmm.c, pmm.h
Replaced previous allocator with a buddy allocator: added free_lists, orders, page_frame_t, pmm_alloc_page/pmm_free_page, pmm_get_free_memory; removed size-based pmm_alloc/pmm_free APIs and adjusted init to seed maximal aligned blocks.
Tasking Subsystem
task.c, task.h
Added cooperative multitasking: TCB/task_state types, tasking_install(), create_task(), switch_task(registers_t*), ready queue, basic round-robin scheduler and kernel stack setup.
Kernel init & tasks
kernel.c
Added includes for task.h and syscall.h; call to syscalls_install(), tasking_install(); create_task("shell", shell_init) and create_task("counter", counter_task); added counter_task() function; reduced verbose init prints and added final startup messages; still prints numeric free memory.
Syscall support
syscall.c, syscall.h
New syscall handler syscall_handler(registers_t* r) that handles r->eax==0 (prints message); syscalls_install() declaration added.
IDT / Interrupts
idt.c, interrupt.asm
Added syscall interrupt vector (isr128) and IDT gate for 128; added global isr128 symbol; removed STI in IRQ path, adjusting interrupt enable behavior.
NIC RX allocation
NIC.C
Changed RX buffer allocation from single pmm_alloc(8192+16) to block allocation via pmm_alloc_blocks(4) (16KB region) and minor comment tweak.
Paging metadata
paging.h, paging.c
Added paging layout constants and externs for page structures; refactored pt_entry_t bitfields (user_supervisor, pwt, pcd, reserved); small comment edit in paging.c.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant PIT as Timer IRQ
  participant IDT as ISR Dispatcher
  participant Scheduler as switch_task
  participant RunQ as ready_queue
  participant CPU as CPU/Context

  PIT->>IDT: IRQ0 (timer)
  IDT->>Scheduler: switch_task(registers_t* r)
  rect rgb(235,245,255)
    note right of Scheduler: Save current regs\nDequeue next task\nLoad next regs
    Scheduler->>RunQ: Enqueue/Dequeue operations
  end
  Scheduler-->>IDT: return (next task regs)
  IDT-->>CPU: iret to next task
Loading
sequenceDiagram
  autonumber
  participant User as User code
  participant IDT as ISR (int 128)
  participant Syscall as syscall_handler

  User->>IDT: int 128 (syscall)
  IDT->>Syscall: syscall_handler(registers_t* r)
  alt eax == 0
    Syscall-->>User: prints "SYSTEM CALL RECEIVED!, SIMPLE OS WORKING"
  else
    Syscall-->>User: no-op / unhandled
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Kernel-update 3 #4 — Also modifies PMM implementation and allocator interfaces; likely overlaps in pmm API and init semantics.

Poem

I nibble bytes beneath the moon,
Buddies split and bob in tune,
Tasks hop in line, the timer drums,
Syscall peeps—and kernel hums.
NIC nibbles packets, logs grow still—hip-hop, we build! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The pull request title "Just some additional Kernel designs" is vague and non-descriptive. While the changeset does involve kernel-level modifications, the primary change is the introduction of a cooperative multitasking subsystem including task scheduling, system call handling, and a complete replacement of the physical memory manager with a buddy allocator. The title uses generic phrasing ("some additional designs") that fails to convey the main technical change—implementing a task scheduler—which is explicitly stated in the PR description as "baby steps for the task scheduler." A reader scanning this title would not understand that this PR introduces fundamental multitasking capabilities to the kernel. Consider revising the title to specifically highlight the main technical change, such as "Add cooperative task scheduler and buddy allocator to kernel" or "Implement initial multitasking support with task scheduler." This would make the title clear and specific, allowing teammates to immediately understand the primary purpose of the changeset when reviewing commit history.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Kernel-Patch-work

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.

❤️ Share
🧪 Early access (Sonnet 4.5): enabled

We are currently testing the Sonnet 4.5 model, which is expected to improve code review quality. However, this model may lead to increased noise levels in the review comments. Please disable the early access features if the noise level causes any inconvenience.

Note:

  • Public repositories are always opted into early access features.
  • You can enable or disable early access features from the CodeRabbit UI or by updating the CodeRabbit configuration file.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
NIC.C (3)

68-76: API mismatch: pmm_alloc_blocks is undeclared/undefined

This will not compile with the current pmm.h/c. Either switch to the page API or add pmm_alloc_blocks to PMM. See pmm.h/pmm.c comments and proposed implementation.


40-60: Constrain NIC DMA buffers to <4 MB or expand identity mapping
Either restrict PMM to return frames below 4 MB for rx_buffer allocations in NIC.C, or identity-map additional RAM in paging_install (paging.c §34–44).


68-76: Fix RTL8139 receive buffer allocation and RCR configuration

  • Allocate 5 blocks (20 KB) instead of 4 to include the required 16-byte slack:
    rx_buffer = (uint8_t*)pmm_alloc_blocks(5);
  • Set RCR RBLEN bits [12:11]=01 for 16 KB + 16 bytes:
    outl(rtl8139_io_base + REG_RCR, 0x0F | (1u << 11));
🧹 Nitpick comments (1)
pmm.c (1)

73-107: Add basic input validation in free; optional

Consider asserting page alignment and non-NULL inputs in pmm_free_page to catch misuse early during development.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3659a55 and 8a2995c.

📒 Files selected for processing (6)
  • NIC.C (2 hunks)
  • kernel.c (0 hunks)
  • pmm.c (1 hunks)
  • pmm.h (1 hunks)
  • task.c (1 hunks)
  • task.h (1 hunks)
💤 Files with no reviewable changes (1)
  • kernel.c
🧰 Additional context used
🧬 Code graph analysis (3)
pmm.h (1)
pmm.c (3)
  • pmm_init (17-47)
  • pmm_alloc_page (49-71)
  • pmm_free_page (73-107)
task.h (1)
idt.h (1)
  • struct registers { (19-24)
pmm.c (1)
paging.c (1)
  • paging_install (9-62)
🔇 Additional comments (1)
task.h (1)

15-19: Typedef registers_t exists; no action required. The typedef is declared in idt.h (typedef struct registers { … } registers_t;), so the code compiles as is.

Likely an incorrect or invalid review comment.

Comment thread pmm.c
Comment on lines +4 to +6
#define PAGE_SIZE 4096
#define MAX_ORDER 10 // Max block size: 4KB * 2^10 = 4MB
#define KERNEL_RESERVED_END 0x200000 // Reserve the first 2MB for kernel/modules/metadata

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not hardcode kernel reservation without validating actual end-of-kernel

KERNEL_RESERVED_END is fixed at 2MB. If the kernel grows beyond this, you’ll hand out memory still occupied by kernel/modules. Consider taking the actual end address from the linker symbol or bootloader and seeding from that.

🤖 Prompt for AI Agents
In pmm.c around lines 4 to 6, KERNEL_RESERVED_END is hardcoded to 0x200000 which
can overlap with a larger kernel; replace the constant with a runtime-determined
end-of-kernel value obtained from the linker or bootloader (e.g. an extern
symbol like _end or kernel_end provided by the linker, or a bootloader-provided
memory map field), validate and PAGE_SIZE-align that address, ensure it is
capped/validated against available physical memory and the allocator base, and
use that aligned runtime value when seeding/reserving pages (add a fallback and
an explicit error/log if the symbol is missing or the computed reservation is
out of range).

Comment thread pmm.c
Comment on lines +17 to +47
void pmm_init(uint32_t memory_end) {
uint32_t total_pages = memory_end / PAGE_SIZE;

// Validate memory_top alignment
if (memory_top & 15) {
memory_top = (memory_top + 15) & ~15;
// Initialize all free lists to be empty
for (int i = 0; i <= MAX_ORDER; i++) {
free_lists[i] = NULL;
}

// Find free allocation slot
for (int i = 0; i < MAX_ALLOCATIONS; i++) {
if (!allocations[i].used) {
allocations[i].address = memory_top;
allocations[i].size = size;
allocations[i].used = 1;
allocation_count++;

void* ptr = (void*)memory_top;
memory_top += size;

// Clear allocated memory for security
uint8_t* clear_ptr = (uint8_t*)ptr;
for (size_t j = 0; j < size; j++) {
clear_ptr[j] = 0;
// Correctly initialize by creating maximal aligned blocks
uint32_t start_page = KERNEL_RESERVED_END / PAGE_SIZE;
uint32_t current_page = start_page;

while (current_page < total_pages) {
// Find the largest possible block that fits and is aligned
int order = MAX_ORDER;
while (order >= 0) {
uint32_t block_size = 1 << order;
if ((current_page % block_size == 0) && (current_page + block_size <= total_pages)) {
break;
}

return ptr;
order--;
}
}

return NULL;
}
// Add this maximal block to the correct free list
buddy_node_t* block = (buddy_node_t*)addr_from_page_index(current_page);
block->next = free_lists[order];
free_lists[order] = block;

void pmm_free(void* ptr) {
if (ptr == NULL) {
return;
current_page += (1 << order);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Potentially returning unmapped frames while only 4MB is identity-mapped

pmm_init seeds all memory up to memory_end, but paging.c only identity-maps the first 4MB. Any allocation >4MB used as a pointer will fault (TCBs, stacks, rx buffer).

Options:

  • Restrict PMM to [KERNEL_RESERVED_END, 4MB) until paging maps more RAM.
  • Or expand identity mapping to cover memory_end.

I can propose a paging_install update to identity-map N MB based on a detected limit if you prefer that route.

🤖 Prompt for AI Agents
In pmm.c around lines 17-47, pmm_init currently seeds free lists up to
memory_end which can include physical frames beyond the 4MB identity-mapped
region and thus produce pointers into unmapped memory; fix by constraining PMM
seeding to the identity-mapped range or by expanding identity mapping: either
(A) compute a safe_limit = min(memory_end, IDENTITY_MAPPED_BYTES) (e.g.,
4*1024*1024) and use safe_limit/PAGE_SIZE as total_pages so only mapped frames
are inserted, or (B) modify paging_install to identity-map up to memory_end (or
a detected usable RAM limit) before pmm_init runs so all seeded frames are
accessible; implement one of these two options and ensure any callers that
expect physical->virtual pointer use only frames within the mapped range.

Comment thread pmm.c
Comment on lines +49 to +71
void* pmm_alloc_page(void) {
for (int order = 0; order <= MAX_ORDER; order++) {
if (free_lists[order] != NULL) {
// Found a block, take it from the list
buddy_node_t* block = free_lists[order];
free_lists[order] = block->next;

// Split the block if it's larger than a single page (order 0)
int current_order = order;
while (current_order > 0) {
current_order--;
// The buddy is the second half of the split block
buddy_node_t* buddy = (buddy_node_t*)((uint32_t)block + (PAGE_SIZE * (1 << current_order)));

// Add the new buddy to the lower-order free list
buddy->next = free_lists[current_order];
free_lists[current_order] = buddy;
}
return block;
}
}
return NULL; // Out of memory
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Provide a contiguous multi-page allocation API to match NIC usage

Implement pmm_alloc_blocks(count) (contiguous pages) by generalizing the split logic to a requested order. This also enables 8K/16K/32K allocations cleanly.

Apply this diff to add pmm_alloc_blocks after pmm_alloc_page:

     return NULL; // Out of memory
 }
 
+// Allocate 'count' contiguous pages (count >= 1). Returns base or NULL.
+void* pmm_alloc_blocks(uint32_t count) {
+    if (count == 0) return NULL;
+    // Find smallest order with capacity >= count
+    int req_order = 0;
+    uint32_t sz = 1;
+    while (sz < count && req_order < MAX_ORDER) { sz <<= 1; req_order++; }
+    if (req_order > MAX_ORDER) return NULL;
+
+    // Find a block at order >= req_order
+    int order = req_order;
+    while (order <= MAX_ORDER && free_lists[order] == NULL) order++;
+    if (order > MAX_ORDER) return NULL;
+
+    // Take a block from 'order' and split down to req_order
+    buddy_node_t* block = free_lists[order];
+    free_lists[order] = block->next;
+    while (order > req_order) {
+        order--;
+        buddy_node_t* buddy = (buddy_node_t*)((uint32_t)block + (PAGE_SIZE * (1u << order)));
+        buddy->next = free_lists[order];
+        free_lists[order] = buddy;
+    }
+    return block;
+}

If you add pmm_free_blocks later, pass the same count to allow proper coalescing, or store order metadata per allocation.

📝 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.

Suggested change
void* pmm_alloc_page(void) {
for (int order = 0; order <= MAX_ORDER; order++) {
if (free_lists[order] != NULL) {
// Found a block, take it from the list
buddy_node_t* block = free_lists[order];
free_lists[order] = block->next;
// Split the block if it's larger than a single page (order 0)
int current_order = order;
while (current_order > 0) {
current_order--;
// The buddy is the second half of the split block
buddy_node_t* buddy = (buddy_node_t*)((uint32_t)block + (PAGE_SIZE * (1 << current_order)));
// Add the new buddy to the lower-order free list
buddy->next = free_lists[current_order];
free_lists[current_order] = buddy;
}
return block;
}
}
return NULL; // Out of memory
}
void* pmm_alloc_page(void) {
for (int order = 0; order <= MAX_ORDER; order++) {
if (free_lists[order] != NULL) {
// Found a block, take it from the list
buddy_node_t* block = free_lists[order];
free_lists[order] = block->next;
// Split the block if it's larger than a single page (order 0)
int current_order = order;
while (current_order > 0) {
current_order--;
// The buddy is the second half of the split block
buddy_node_t* buddy = (buddy_node_t*)((uint32_t)block + (PAGE_SIZE * (1 << current_order)));
// Add the new buddy to the lower-order free list
buddy->next = free_lists[current_order];
free_lists[current_order] = buddy;
}
return block;
}
}
return NULL; // Out of memory
}
// Allocate 'count' contiguous pages (count >= 1). Returns base or NULL.
void* pmm_alloc_blocks(uint32_t count) {
if (count == 0) return NULL;
// Find smallest order with capacity >= count
int req_order = 0;
uint32_t sz = 1;
while (sz < count && req_order < MAX_ORDER) {
sz <<= 1;
req_order++;
}
if (req_order > MAX_ORDER) return NULL;
// Find a block at order >= req_order
int order = req_order;
while (order <= MAX_ORDER && free_lists[order] == NULL) {
order++;
}
if (order > MAX_ORDER) return NULL;
// Take a block from 'order' and split down to req_order
buddy_node_t* block = free_lists[order];
free_lists[order] = block->next;
while (order > req_order) {
order--;
buddy_node_t* buddy = (buddy_node_t*)((uint32_t)block + (PAGE_SIZE * (1u << order)));
buddy->next = free_lists[order];
free_lists[order] = buddy;
}
return block;
}

Comment thread pmm.h Outdated
Comment on lines +7 to +9
void pmm_init(uint32_t memory_end);
void* pmm_alloc_page(void);
void pmm_free_page(void* p);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Expose missing PMM APIs and PAGE_SIZE used by other modules

  • NIC.C calls pmm_alloc_blocks(4) but pmm.h does not declare it; compilation will fail.
  • task.c uses PAGE_SIZE but it’s not defined in any included header; compilation will fail.

Add the missing declarations and make PAGE_SIZE available to consumers.

Apply this diff:

 #include <stdint.h>
 #include <stddef.h>

-void pmm_init(uint32_t memory_end);
-void* pmm_alloc_page(void);
-void pmm_free_page(void* p);
+#define PAGE_SIZE 4096
+
+void pmm_init(uint32_t memory_end);
+void* pmm_alloc_page(void);
+void pmm_free_page(void* p);
+// Allocate contiguous physical pages (count >= 1). Returns page-aligned base or NULL.
+void* pmm_alloc_blocks(uint32_t count);
+void pmm_free_blocks(void* p, uint32_t count);
📝 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.

Suggested change
void pmm_init(uint32_t memory_end);
void* pmm_alloc_page(void);
void pmm_free_page(void* p);
#include <stdint.h>
#include <stddef.h>
#define PAGE_SIZE 4096
void pmm_init(uint32_t memory_end);
void* pmm_alloc_page(void);
void pmm_free_page(void* p);
// Allocate contiguous physical pages (count >= 1). Returns page-aligned base or NULL.
void* pmm_alloc_blocks(uint32_t count);
void pmm_free_blocks(void* p, uint32_t count);
🤖 Prompt for AI Agents
In pmm.h around lines 7 to 9, add missing public API declarations and the
PAGE_SIZE macro: declare a function prototype for pmm_alloc_blocks that takes a
uint32_t count and returns void* (so NIC.C can call pmm_alloc_blocks(4)),
declare a prototype for pmm_free_blocks that takes a void* pointer and a
uint32_t count, and ensure PAGE_SIZE is defined (e.g., via a macro or constant)
so task.c can include it; keep parameter types consistent with existing
pmm_alloc_page/pmm_free_page prototypes and include any needed standard integer
headers if not already present.

Comment thread task.c
Comment on lines +9 to +21
void tasking_install(void) {
vga_print_string("Initializing multitasking... ");
current_task = (task_t*)pmm_alloc_page();
memset(current_task, 0, sizeof(task_t));
current_task->id = next_pid++;
current_task->state = TASK_RUNNING;
current_task->kernel_stack = NULL;
current_task->next = NULL;
ready_queue = current_task;
irq_install_handler(0, switch_task);

vga_print_string("[OK]\n");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Initialize and verify tasking install allocations

Add NULL checks and consider allocating the bootstrap task TCB/stack from known low memory to avoid mapping issues until paging maps more than 4MB.

🤖 Prompt for AI Agents
In task.c around lines 9 to 21, the bootstrap task TCB/stack allocation and
setup lack NULL checks and may be placed above low memory causing page mapping
issues; check the result of pmm_alloc_page() and any other allocations, print a
clear error and halt if allocation fails, allocate the bootstrap task control
block and its kernel stack from a known low physical memory region (below 4MB)
or use a dedicated low-memory allocator, initialize kernel_stack to the
allocated stack top, ensure memset only after a successful allocation, set
ready_queue and install the IRQ handler only after all allocations succeed, and
avoid leaving partially initialized task state on failure.

Comment thread task.c
Comment on lines +11 to +18
current_task = (task_t*)pmm_alloc_page();
memset(current_task, 0, sizeof(task_t));
current_task->id = next_pid++;
current_task->state = TASK_RUNNING;
current_task->kernel_stack = NULL;
current_task->next = NULL;
ready_queue = current_task;
irq_install_handler(0, switch_task);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not allocate scheduler state on potentially unmapped memory; check alloc results

pmm_alloc_page can return frames above the first 4MB, but paging.c only identity maps 4MB. current_task may live in unmapped memory and cause page faults. Also, no NULL checks.

  • Ensure allocations come from mapped/low memory or expand identity mapping before using the memory as pointers.
  • Add NULL checks and fail-safe.
🤖 Prompt for AI Agents
In task.c around lines 11-18, the code allocates current_task with
pmm_alloc_page and immediately uses it but pmm_alloc_page may return frames
above the 4MB identity-mapped region and may return NULL; update the logic to
(1) call a low-memory allocation API or ensure the returned physical frame is
identity-mapped into the kernel virtual space before dereferencing, (2) check
the allocation result for NULL and handle failure (log and abort/return error or
retry), and (3) if choosing to map the frame, create an identity mapping for
that page (or expand the identity range) before calling memset or accessing
fields, then set up the task fields and queue as before.

Comment thread task.c
Comment on lines +30 to +35
new_task->kernel_stack = pmm_alloc_page() + PAGE_SIZE;

// Set up the initial register state
new_task->regs.eip = (uint32_t)entry_point;
new_task->regs.esp = (uint32_t)new_task->kernel_stack;
new_task->regs.eflags = 0x202; // Enable interrupts

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix void arithmetic; initialize a runnable kernel context correctly*

  • void* + PAGE_SIZE is non‑standard; cast to uint8_t* first.
  • Only setting eip/esp/eflags is insufficient; cs/ss remain zeroed by memset and esp won’t be switched by iret/popad (popa ignores ESP). Tasks will share the same kernel stack and corrupt each other.

Apply this diff for the cast and minimal guards (stack switching still needs the ISR stub fix below):

-    new_task->kernel_stack = pmm_alloc_page() + PAGE_SIZE;
+    void* ks_page = pmm_alloc_page();
+    if (!ks_page) { return; }
+    new_task->kernel_stack = (uint8_t*)ks_page + PAGE_SIZE;
+
+    // Ensure code/data segments are sane (kernel segments). Adjust to your GDT.
+    new_task->regs.cs = 0x08;
+    new_task->regs.ss = 0x10;

Follow-up required: adjust the ISR/interrupt stub to actually load ESP from the next task’s kernel_stack before returning from interrupt; otherwise tasks still share the current stack. This cannot be fixed purely in C via registers_t because popa ignores ESP and iret won’t pop ESP in ring0. You need a small asm trampoline to save current ESP into current_task and load next_task->kernel_stack into ESP before restoring regs and iret.

I can draft the ISR stub changes if you share the common interrupt/IRQ entry assembly.

🤖 Prompt for AI Agents
In task.c around lines 30 to 35, the code does pointer arithmetic on void* and
only initializes eip/esp/eflags leaving cs/ss zero (and risks sharing kernel
stacks); cast the allocated page to a byte pointer (e.g. uint8_t*) before adding
PAGE_SIZE to compute the top of the stack, set new_task->kernel_stack to that
computed pointer, and initialize the saved register context's segment selectors
(regs.cs and regs.ss) to the kernel code/data selectors (the ring0 selectors
your kernel uses) so the task has correct segment state; also ensure each task
gets its own kernel stack by allocating per-task pages (do not reuse a global
stack) and add a minimal guard or assertion that kernel_stack is
unique/non-null; separately, follow up by updating the ISR/interrupt assembly
stub to save current ESP into current_task and load next_task->kernel_stack into
ESP before restoring registers and iret (popa does not restore ESP), otherwise
tasks will still share the same stack.

Comment thread task.c
Comment on lines +51 to +63
if (!current_task->next) {
return;
}

// Move to the next task in the round-robin queue
task_t* prev_task = current_task;
current_task = current_task->next;

// If we've reached the end of the list, loop back to the start
if (!current_task) {
current_task = ready_queue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Round-robin bug: last task never advances; remove early return

If current_task is at the tail (next == NULL), switch_task returns and never wraps to the head; scheduling stalls on the last task.

Apply this diff:

-    // If there's no other task, just return
-    if (!current_task->next) {
-        return;
-    }
-
-    // Move to the next task in the round-robin queue
-    task_t* prev_task = current_task;
-    current_task = current_task->next;
-    
-    // If we've reached the end of the list, loop back to the start
-    if (!current_task) {
-        current_task = ready_queue;
-    }
+    // Move to next task or wrap to the head
+    task_t* prev_task = current_task;
+    current_task = current_task->next ? current_task->next : ready_queue;

Optional: set prev_task->state = TASK_READY.

📝 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.

Suggested change
if (!current_task->next) {
return;
}
// Move to the next task in the round-robin queue
task_t* prev_task = current_task;
current_task = current_task->next;
// If we've reached the end of the list, loop back to the start
if (!current_task) {
current_task = ready_queue;
}
// Move to next task or wrap to the head
task_t* prev_task = current_task;
current_task = current_task->next ? current_task->next : ready_queue;
🤖 Prompt for AI Agents
In task.c around lines 51 to 63 the code returns early when current_task->next
is NULL which prevents wrapping to the head and stalls scheduling; remove the
early "return", instead record prev_task = current_task, advance current_task =
current_task->next and if current_task is NULL set current_task = ready_queue so
the round-robin wraps; optionally set prev_task->state = TASK_READY before
advancing.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
kernel.c (1)

36-46: Remove duplicate shell initialization.

The shell is initialized twice:

  1. Line 36: Creates a "shell" task with entry point shell_init
  2. Line 46: Calls shell_init() directly in kernel context

This dual initialization means the shell will run concurrently in two contexts:

  • As a scheduled task (cooperative multitasking)
  • In the kernel main thread

This creates a race condition for keyboard input and VGA output, as both instances will compete for the same resources without synchronization.

Remove the direct call if task-based execution is intended:

     tasking_install();
     create_task("shell",shell_init);
     create_task("counter",counter_task);
     uint32_t free_mem = pmm_get_free_memory();
     vga_print_dec(free_mem / 1024);
     vga_print_string(" KB\n");
     
     vga_print_string("\nKernel initialization complete!\n");
     vga_print_string("Security features: Buffer overflow protection, bounds checking\n");
     vga_print_string("Starting shell...\n\n");
     
-    shell_init();

     // Main kernel loop - wait for interrupts
     while(1) {
         asm volatile("hlt");
     }

Alternatively, if the direct call is intentional (for initial setup before task switching), add a comment explaining this design decision.

🧹 Nitpick comments (3)
syscall.h (1)

1-8: Consider declaring syscall_handler in this header.

The syscall_handler function defined in syscall.c is likely called from the interrupt handling path (via isr128). Without a declaration in a header, the linker must resolve it by symbol name alone, which can lead to type-safety issues and makes the API less clear.

Add the handler declaration after line 6:

 void syscalls_install(void);
+void syscall_handler(registers_t *r);
pmm.h (1)

5-11: Clarify the purpose and usage of page_frame_t.

page_frame_t is defined in the public header but appears unused by the current buddy allocator implementation. The allocator uses buddy_node_t internally (pmm.c line 8) and maintains page_frame_database as an unused static variable (pmm.c line 16).

Either document the intended future use of this structure or remove it until needed.

pmm.c (1)

16-17: Remove unused page_frame_database and total_pages.

The static variables page_frame_database and total_pages (line 17) are declared but never used. Line 19 shadows the global total_pages with a local variable.

Apply this diff:

 static inline uint32_t get_buddy_index(uint32_t index, uint32_t order) { return index ^ (1 << order); }
-static page_frame_t* page_frame_database;
-static uint32_t total_pages;
+
 void pmm_init(uint32_t memory_end) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a2995c and b96f461.

📒 Files selected for processing (9)
  • idt.c (2 hunks)
  • interrupt.asm (2 hunks)
  • kernel.c (1 hunks)
  • paging.c (1 hunks)
  • paging.h (1 hunks)
  • pmm.c (1 hunks)
  • pmm.h (1 hunks)
  • syscall.c (1 hunks)
  • syscall.h (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • paging.c
🧰 Additional context used
🧬 Code graph analysis (3)
syscall.c (1)
vga.c (1)
  • vga_print_string (70-74)
paging.h (1)
paging.c (1)
  • paging_install (9-62)
kernel.c (3)
vga.c (2)
  • vga_putentryat (35-38)
  • vga_print_string (70-74)
task.c (2)
  • tasking_install (9-21)
  • create_task (22-41)
shell.c (1)
  • shell_init (140-146)
🔇 Additional comments (16)
idt.c (2)

3-3: LGTM!

The include is appropriate for referencing the syscall installation function.


212-212: LGTM! Correct user-mode syscall gate setup.

The IDT gate 128 is correctly configured with flags 0xEE (DPL=3, present, 32-bit interrupt gate), enabling user-mode processes to invoke system calls via int 0x80 equivalent.

interrupt.asm (2)

51-51: LGTM! Syscall vector symbol exported.

The isr128 symbol is correctly exported for the syscall IDT entry.


152-173: STI removal from IRQ path is correct.

The sti instruction was removed from irq_common_stub (line 149 was in isr_common_stub, not irq_common_stub). This is the proper approach for IRQ handling:

  1. The iret instruction at line 173 automatically restores EFLAGS from the stack, including the interrupt flag. If interrupts were enabled before the IRQ, they will be re-enabled upon return.
  2. This allows proper nested interrupt handling without forcing interrupts on.
  3. Note that isr_common_stub (line 149) still contains sti, which is appropriate for exception handlers that need different behavior.

The change correctly differentiates IRQ handling from exception handling.

kernel.c (3)

12-13: LGTM! Correct includes for new subsystems.

The includes for task and syscall headers are appropriate for the new functionality.


28-28: LGTM! Syscall installation added to kernel init.

The call to syscalls_install() is correctly positioned in the initialization sequence.

However, verify that syscalls_install() is implemented. The function is declared in syscall.h but the implementation is not visible in syscall.c.


35-37: LGTM! Task subsystem initialization.

The calls to tasking_install() and task creation are correctly placed after the necessary subsystems (GDT, IDT, paging, PMM, interrupts) are initialized.

paging.h (3)

12-22: LGTM! pt_entry_t structure changes align with x86 paging specification.

The structure modifications correctly reflect standard x86 page table entry fields:

  • user_supervisor (bit 2) for U/S flag
  • pwt (bit 3) for write-through caching policy
  • pcd (bit 4) for cache disable
  • reserved bits properly sized

The corresponding implementation in paging.c (lines 16-28) correctly initializes these fields.


26-27: New reserved region helpers support buddy allocator integration.

The addition of paging_get_reserved_start/end provides a clean interface for the PMM to query paging metadata boundaries, which is essential for proper memory region exclusion during initialization.


7-8:

PAGING_STRUCTURES_START macro correctly references extern array.

The macro PAGING_STRUCTURES_START uses &page_directory where page_directory is declared extern in the header. Verification confirms that both page_directory[1024] and fpage_table[1024] are properly defined in paging.c (lines 6-7) without the extern keyword and with appropriate 4096-byte alignment attributes. This follows correct C conventions: extern declarations in the header with a single definition in the implementation file.

pmm.c (5)

74-108: LGTM! Buddy coalescing logic is correct.

The pmm_free_page implementation properly:

  1. Calculates the buddy index using XOR
  2. Searches the appropriate free list for the buddy
  3. Removes the buddy if found and merges to the next order
  4. Stops coalescing when buddy is not free or MAX_ORDER is reached
  5. Inserts the final block into the correct free list

The pointer manipulation and list traversal are correct.


110-124: LGTM! Free memory calculation correctly traverses all orders.

The function properly:

  1. Iterates through all orders from 0 to MAX_ORDER
  2. Calculates the correct block size for each order (PAGE_SIZE * 2^order)
  3. Traverses the linked list and accumulates total free memory

4-6: Hardcoded KERNEL_RESERVED_END may overlap with actual kernel size.

KERNEL_RESERVED_END is fixed at 2MB. If the kernel, modules, or paging structures exceed this size, the allocator will hand out pages still in use by the kernel, causing memory corruption.

Replace the hardcoded constant with a runtime-determined value:

+// External linker symbol marking actual end of kernel
+extern uint32_t _kernel_end;
+
 #define PAGE_SIZE 4096
 #define MAX_ORDER 10 // Max block size: 4KB * 2^10 = 4MB
-#define KERNEL_RESERVED_END 0x200000 // Reserve the first 2MB for kernel/modules/metadata

Then in pmm_init:

 void pmm_init(uint32_t memory_end) {
     uint32_t total_pages = memory_end / PAGE_SIZE;
+    
+    // Get actual kernel end from linker, align up to page boundary
+    uint32_t kernel_end = ((uint32_t)&_kernel_end + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+    
+    // Also reserve paging structures
+    uint32_t reserved_end = kernel_end;
+    // Add paging structures if needed: reserved_end += PAGING_STRUCTURES_SIZE;
 
     // Initialize all free lists to be empty
     for (int i = 0; i <= MAX_ORDER; i++) {
         free_lists[i] = NULL;
     }
 
     // Correctly initialize by creating maximal aligned blocks
-    uint32_t start_page = KERNEL_RESERVED_END / PAGE_SIZE;
+    uint32_t start_page = reserved_end / PAGE_SIZE;
     uint32_t current_page = start_page;

Ensure your linker script exports _kernel_end at the end of the kernel image.


18-48: Critical: PMM seeds pages beyond the 4MB identity-mapped region.

pmm_init adds all pages from KERNEL_RESERVED_END to memory_end into free lists. However, paging.c only identity-maps the first 4MB (paging.c lines 28-39). Any allocation beyond 4MB will return an unmapped physical address, causing page faults when dereferenced (e.g., TCBs, task stacks, NIC rx_buffer).

Solution A (simpler): Constrain PMM to the identity-mapped region:

 void pmm_init(uint32_t memory_end) {
-    uint32_t total_pages = memory_end / PAGE_SIZE;
+    // Only manage memory within the identity-mapped region (first 4MB)
+    #define IDENTITY_MAPPED_SIZE (4 * 1024 * 1024)  // 4MB
+    uint32_t safe_limit = (memory_end < IDENTITY_MAPPED_SIZE) ? memory_end : IDENTITY_MAPPED_SIZE;
+    uint32_t total_pages = safe_limit / PAGE_SIZE;

Solution B (better long-term): Expand identity mapping in paging_install to cover detected RAM, then call pmm_init with that limit. This requires allocating/mapping additional page tables before the PMM is available.

I recommend Solution A for now, and defer dynamic paging expansion to a follow-up task.

Would you like me to propose a paging_install update for Solution B?


50-72: Missing pmm_alloc_blocks API required by NIC.C.

pmm_alloc_page correctly implements single-page allocation with buddy splitting. However, NIC.C calls pmm_alloc_blocks(4) to allocate a contiguous 16KB rx_buffer, but this function is not implemented.

Add the multi-page allocation function after pmm_alloc_page:

     return NULL; // Out of memory
 }
 
+// Allocate 'count' contiguous pages (count >= 1). Returns page-aligned base or NULL.
+void* pmm_alloc_blocks(uint32_t count) {
+    if (count == 0) return NULL;
+    
+    // Find smallest order that can hold 'count' pages
+    int req_order = 0;
+    uint32_t block_pages = 1;
+    while (block_pages < count && req_order < MAX_ORDER) {
+        block_pages <<= 1;
+        req_order++;
+    }
+    if (req_order > MAX_ORDER) return NULL;  // Request too large
+    
+    // Find a block at order >= req_order
+    int order = req_order;
+    while (order <= MAX_ORDER && free_lists[order] == NULL) {
+        order++;
+    }
+    if (order > MAX_ORDER) return NULL;  // Out of memory
+    
+    // Take block from list and split down to req_order
+    buddy_node_t* block = free_lists[order];
+    free_lists[order] = block->next;
+    
+    while (order > req_order) {
+        order--;
+        buddy_node_t* buddy = (buddy_node_t*)((uint32_t)block + (PAGE_SIZE * (1u << order)));
+        buddy->next = free_lists[order];
+        free_lists[order] = buddy;
+    }
+    
+    return block;
+}
+
+// Free 'count' contiguous pages starting at 'p'
+void pmm_free_blocks(void* p, uint32_t count) {
+    if (p == NULL || count == 0) return;
+    
+    // For now, free each page individually
+    // TODO: Optimize to free as a single block of the appropriate order
+    for (uint32_t i = 0; i < count; i++) {
+        pmm_free_page((void*)((uint32_t)p + i * PAGE_SIZE));
+    }
+}

Also declare these in pmm.h (already covered in the pmm.h review comment).

pmm.h (1)

1-11: Add include guards, standard includes, and missing public API declarations.

This header is missing:

  • Include guards to prevent multiple inclusion
  • #include <stdint.h> for uint8_t in page_frame_t
  • Public function declarations: pmm_init, pmm_alloc_page, pmm_free_page, pmm_alloc_blocks, pmm_free_blocks, pmm_get_free_memory
  • PAGE_SIZE constant (used by task.c and required by consumers)

Without these declarations, compilation will fail when other modules call PMM functions.

Apply this diff:

+#ifndef PMM_H
+#define PMM_H
+
 #include <stdint.h>
+
+#define PAGE_SIZE 4096
 
 #define PAGE_STATE_FREE      0x01
 #define PAGE_STATE_USED      0x02
 #define PAGE_STATE_RESERVED  0x04
 #define PAGE_STATE_KERNEL    0x08
+
 typedef struct page_frame {
     uint8_t state;
     uint8_t order;
     struct page_frame* next;
     struct page_frame* prev;
-
 } page_frame_t;
+
+// PMM initialization
+void pmm_init(uint32_t memory_end);
+
+// Single page allocation/deallocation
+void* pmm_alloc_page(void);
+void pmm_free_page(void* p);
+
+// Multi-page allocation/deallocation (contiguous blocks)
+void* pmm_alloc_blocks(uint32_t count);
+void pmm_free_blocks(void* p, uint32_t count);
+
+// Memory statistics
+uint32_t pmm_get_free_memory(void);
+
+#endif

Comment thread interrupt.asm
isr_no_error 30
isr_no_error 31

isr_no_error 128

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Syscall routing to wrong handler.

The isr_no_error 128 macro routes isr128 through isr_common_stub to isr_handler, which handles CPU exceptions (interrupt numbers < 32). System calls on vector 128 will not be dispatched to syscall_handler as intended.

You need to create a separate entry path for syscalls:

Add after line 127:

+syscall_common_stub:
+    pusha
+    mov ax, ds
+    push eax
+
+    mov ax, 0x10
+    mov ds, ax
+    mov es, ax
+    mov fs, ax
+    mov gs, ax
+
+    call syscall_handler
+
+    pop eax
+    mov ds, ax
+    mov es, ax
+    mov fs, ax
+    mov gs, ax
+
+    popa
+    add esp, 8
+    sti
+    iret

Then change line 110 to use a custom macro:

-isr_no_error 128
+isr128:
+    cli
+    push byte 0
+    push byte 128
+    jmp syscall_common_stub

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In interrupt.asm around line 110 (and after line 127), the syscall vector 128 is
currently using isr_no_error which routes through isr_common_stub and ultimately
dispatches to isr_handler for CPU exceptions; instead add a dedicated syscall
entry path after line 127 that mirrors the no-error/with-error macros but
targets a syscall_common_stub which pushes the same minimal context and then
jumps to syscall_handler, and then change line 110 to use this new macro (e.g.,
replace isr_no_error 128 with isr_syscall 128) so vector 128 is dispatched to
syscall_handler rather than isr_handler.

Comment thread kernel.c
Comment on lines +14 to +19
void counter_task(){
int i=0;
while(1){
vga_putentryat('A' + (i++%26),0x0F,79,0);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add synchronization for VGA access.

The counter_task writes to VGA memory at a fixed position (79,0) without synchronization. This creates a race condition with other tasks and kernel code that may write to the VGA buffer concurrently, potentially causing visual corruption or tearing.

Consider adding a spinlock or disabling interrupts around VGA writes, or use a VGA subsystem with built-in synchronization.

🤖 Prompt for AI Agents
In kernel.c around lines 14 to 19, counter_task writes directly to VGA at (79,0)
without synchronization; wrap the VGA write in a synchronization primitive
(preferably a kernel spinlock with IRQ-save) to prevent races with other
writers: declare or use an existing vga_lock, call spin_lock_irqsave(&vga_lock,
flags) before vga_putentryat and spin_unlock_irqrestore(&vga_lock, flags) after,
or if a lock is not available at this layer, disable interrupts around the write
(cli()/sti()) as a last resort; ensure the lock/irq state is properly
saved/restored to avoid deadlocks and interrupt-loss.

⚠️ Potential issue | 🟠 Major

Fix integer overflow and negative modulo in counter.

The counter_task function has an integer overflow issue: when i exceeds INT_MAX, it wraps to negative, causing (i++ % 26) to produce negative values, resulting in incorrect characters (potentially non-printable or control characters).

Apply this diff to fix the overflow:

 void counter_task(){
-    int i=0;
+    uint32_t i=0;
     while(1){
-        vga_putentryat('A' + (i++%26),0x0F,79,0);
+        vga_putentryat('A' + ((i++)%26),0x0F,79,0);
     }
 }
📝 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.

Suggested change
void counter_task(){
int i=0;
while(1){
vga_putentryat('A' + (i++%26),0x0F,79,0);
}
}
void counter_task(){
uint32_t i=0;
while(1){
vga_putentryat('A' + ((i++)%26),0x0F,79,0);
}
}
🤖 Prompt for AI Agents
In kernel.c around lines 14-19, counter_task uses a signed int i that can
overflow and become negative, making (i++ % 26) yield negative results; change i
to an unsigned type or keep it bounded by computing index = (i % 26) where i is
unsigned (or update i with i = (i + 1) % 26) before using it, and use that
non-negative index to compute the character passed to vga_putentryat so the
value never goes negative or overflows.

Comment thread syscall.c
Comment on lines +3 to +7
void syscall_handler(registers_t *r){
if (r->eax==0){
vga_print_string("SYSTEM CALL RECEIVED!, SIMPLE OS WORKING\n");
}
} No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Missing syscalls_install() implementation and null pointer check.

This file declares syscall_handler but does not implement syscalls_install(), which is called in kernel.c (line 28). The missing implementation will cause a linker error.

Additionally, syscall_handler lacks a null pointer check on r, which could lead to undefined behavior if called with a NULL pointer.

Add the syscalls_install() implementation and null check:

 #include "syscall.h"
 #include "vga.h"
+#include "idt.h"
+
+extern void syscall_dispatcher(void);
+
+void syscalls_install(void) {
+    // Register the syscall handler with the interrupt system
+    // Note: IDT gate 128 already points to isr128 in idt.c
+    // isr128 will call isr_handler, which needs to route to syscall_handler
+    // This requires modifying isr_handler or creating a separate path
+}
+
 void syscall_handler(registers_t *r){
+    if (!r) {
+        return;
+    }
     if (r->eax==0){
         vga_print_string("SYSTEM CALL RECEIVED!, SIMPLE OS WORKING\n");
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In syscall.c around lines 3 to 7, implement the missing syscalls_install()
function (it is referenced from kernel.c line 28 and causes a linker error) and
add a defensive null-pointer check in syscall_handler: check if the incoming
registers_t *r is NULL and return early if so, then proceed with existing
handling; implement syscalls_install() to register syscall_handler with the
interrupt/gate table (or wrapper used by this kernel) and perform any necessary
ISR/IRQ setup so the symbol is defined and the syscall handler is properly
wired.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify syscall routing from isr128 to syscall_handler.

The current interrupt architecture routes isr128 through isr_common_stub to isr_handler (in idt.c), which handles exceptions, not system calls. You need to either:

  1. Modify isr_handler to check for int_no == 128 and dispatch to syscall_handler, or
  2. Create a separate syscall_common_stub in interrupt.asm that calls syscall_handler directly, and point isr128 to it instead.

The current implementation will not correctly route syscalls to this handler.

Run the following script to verify the interrupt routing:


🏁 Script executed:

#!/bin/bash
# Check how isr_handler routes interrupt 128
rg -nP -C5 'void\s+isr_handler\s*\(' --type=c

# Check if there's a syscall-specific stub
ast-grep --pattern $'syscall_common_stub'

Length of output: 935


🏁 Script executed:

#!/bin/bash
# Get the full isr_handler implementation
rg -nP -A30 'void\s+isr_handler\s*\(' --type=c idt.c

# Check how isr128 is defined in assembly
rg -nP -C10 'isr128' 

# Check if syscall_handler is called anywhere
rg -nP '\bsyscall_handler\s*\(' --type=c

Length of output: 2954


🏁 Script executed:

#!/bin/bash
# Get the complete isr_handler function to see what happens for int_no >= 32
cat idt.c | sed -n '57,150p'

# Check the isr_common_stub implementation to confirm it calls isr_handler
rg -nP -A20 'isr_common_stub:' interrupt.asm

# Double-check if syscall_handler is referenced anywhere else
rg -n 'syscall_handler' --type=c --type=h

Length of output: 3038


Verify syscall routing from isr128 to syscall_handler.

The current interrupt architecture routes isr128 through isr_common_stub to isr_handler (idt.c:57), which only handles exceptions (int_no < 32) and then returns for all other interrupts. When interrupt 128 fires, isr_handler will simply return without dispatching to syscall_handler.

Verified routing path:

  • isr128 (interrupt.asm:51) → uses isr_no_error macro → jumps to isr_common_stub (interrupt.asm:128)
  • isr_common_stub calls isr_handler (interrupt.asm:139)
  • isr_handler only processes if (r->int_no < 32) (idt.c:69), then returns

syscall_handler in syscall.c is orphaned—no code path invokes it.

Fix by either:

  1. Adding if (r->int_no == 128) { syscall_handler(r); return; } in isr_handler before line 69, or
  2. Creating a separate syscall_common_stub in interrupt.asm that calls syscall_handler directly, and pointing isr128 to it instead of using the isr_no_error macro.
🤖 Prompt for AI Agents
In syscall.c around lines 3-7 the syscall_handler is never invoked because
isr128 is routed through isr_common_stub → isr_handler which only handles int_no
< 32; update the interrupt dispatch so syscalls reach syscall_handler by
modifying isr_handler (idt.c) to check for r->int_no == 128 before the existing
exception branch and call syscall_handler(r) then return, or alternatively
implement a dedicated syscall_common_stub in interrupt.asm and point isr128 to
it so it calls syscall_handler directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant