Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 65 additions & 8 deletions arch/x86_64/syscall/Syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
#include "Gdt.h"
#include "Idt.h"
#include "Ipc.h"
#include "KernelHeap.h"
#include "MemOps.h"
#include "PS2.h"
#include "Panic.h"
#include "Pic.h"
#include "Process.h"
#include "VFS.h"
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
extern void SyscallEntry(void);
Expand All @@ -17,12 +21,12 @@ uint64_t Syscall(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t ar
}
switch (syscall_num) {
case SYS_EXIT:
// Terminate current process

if (current) {
KillProcess(current->pid); // arg1 can be exit code
KillProcess(current->pid);
RequestSchedule();
}
// Should not return from TerminateProcess
while(1) { __asm__ __volatile__("hlt"); }

return 0;

case SYS_WRITE:
Expand All @@ -35,7 +39,7 @@ uint64_t Syscall(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t ar
if (unlikely(arg3 > MAX_SYSCALL_BUFFER_SIZE)) {
return -1; // Buffer too large
}

// Copy user buffer to a kernel-controlled buffer
char kernel_buffer[MAX_SYSCALL_BUFFER_SIZE + 1]; // +1 for null terminator
FastMemcpy(kernel_buffer, (const void*)arg2, arg3);
Expand All @@ -47,8 +51,11 @@ uint64_t Syscall(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t ar
return -1;

case SYS_READ:
// Not implemented yet
return 0;
// unix approach i think
while (!HasInput()) {
Yield(); // yield like a good citizen
}
return GetChar();

case SYS_GETPID:
return current ? current->pid : -1;
Expand All @@ -61,7 +68,57 @@ uint64_t Syscall(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t ar
case SYS_IPC_RECV:
// arg1 = message_buffer
return IpcReceiveMessage((IpcMessage*)arg1);


case SYS_OPEN:
return 0;

case SYS_CLOSE:
return 0;

case SYS_FREAD:
char path_fread[MAX_SYSCALL_BUFFER_SIZE + 1]; // +1 for null terminator
FastMemcpy(path_fread, (const void*)arg1, arg2);
path_fread[arg2] = '\0'; // Ensure null termination
uint8_t* fbuff = KernelMemoryAlloc(4096);
if (!fbuff) return -1;
int bytes_read = VfsReadFile(path_fread, fbuff, 4095);
KernelFree(fbuff);
return bytes_read;

case SYS_FWRITE:
return 0;

case SYS_MKDIR:
(void)arg3;
char mkdir_path[MAX_SYSCALL_BUFFER_SIZE + 1]; // +1 for null terminator
FastMemcpy(mkdir_path, (const void*)arg1, arg2);
mkdir_path[arg2] = '\0'; // Ensure null termination
VfsCreateDir(mkdir_path);
return 0;

case SYS_STAT:
(void)arg3;
char ls_path[MAX_SYSCALL_BUFFER_SIZE + 1]; // +1 for null terminator
FastMemcpy(ls_path, (const void*)arg1, arg2);
ls_path[arg2] = '\0'; // Ensure null termination
VfsListDir(ls_path);
return 0;

case SYS_RM:
(void)arg3;
char rm_path[MAX_SYSCALL_BUFFER_SIZE + 1]; // +1 for null terminator
FastMemcpy(rm_path, (const void*)arg1, arg2);
rm_path[arg2] = '\0'; // Ensure null termination
VfsDelete(rm_path);
return 0;

case SYS_SET_FREQ:
(void)arg2;
(void)arg3;
if (arg1 <= 0 || arg1 > 65535) return -1;
PitSetFrequency((uint16_t)arg1);
return 0;

default:
return -1;
}
Expand Down
20 changes: 14 additions & 6 deletions arch/x86_64/syscall/Syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@
#include "stdint.h"

// System call numbers
#define SYS_EXIT 1
#define SYS_WRITE 2
#define SYS_READ 3
#define SYS_GETPID 4
#define SYS_EXIT 1
#define SYS_WRITE 2
#define SYS_READ 3
#define SYS_GETPID 4
#define SYS_IPC_SEND 5
#define SYS_IPC_RECV 6
#define SYSCALL_INTERRUPT_VECTOR 0x80
#define SYS_OPEN 7
#define SYS_CLOSE 8
#define SYS_FREAD 9
#define SYS_FWRITE 10
#define SYS_MKDIR 11
#define SYS_STAT 12
#define SYS_RM 13
#define SYS_SET_FREQ 14
#define SYSCALL_INTERRUPT_VECTOR 0x80
#define IDT_INTERRUPT_GATE_KERNEL 0x8E
#define IDT_TRAP_GATE_USER 0xEE // Present, DPL 3, 64-bit Trap Gate
#define SYSCALL_SEGMENT_SELECTOR 0x08
#define MAX_SYSCALL_BUFFER_SIZE 4096
#define MAX_SYSCALL_BUFFER_SIZE 4096
// System call handler
void SyscallInit(void);
uint64_t Syscall(uint64_t syscall_num, uint64_t arg1, uint64_t arg2, uint64_t arg3);
Expand Down
25 changes: 16 additions & 9 deletions arch/x86_64/syscall/SyscallEntry.asm
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,24 @@ SyscallEntry:
push r13
push r14
push r15

; Stop SMAP temporarily
stac
Comment on lines +24 to +25
Copy link

@coderabbitai coderabbitai bot Aug 12, 2025

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify SMAP instructions are conditionally executed.

The stac and clac instructions will cause an invalid opcode exception if SMAP is not supported by the CPU. The kernel should check for SMAP support before executing these instructions.

Also applies to: 33-34


🏁 Script executed:

#!/bin/bash
# Check if SMAP support is verified before using stac/clac instructions
rg -A 10 "SMAP|stac|clac" --type c --type asm | head -50

# Check if there's conditional execution of SMAP instructions
ast-grep --pattern 'if ($_ & $_) {
  $$$
  stac
  $$$
}'

Length of output: 3294


To verify whether SMAP support is properly guarded around the stac/clac in the syscall entry, let’s locate and inspect the assembly snippet directly:

#!/bin/bash
# 1. Find the SyscallEntry.asm path
entry_file=$(fd SyscallEntry.asm)

echo "Located syscall entry at: $entry_file"
echo

# 2. Show context around stac/clac
grep -n -C4 "stac" "$entry_file" || echo "No stac found"
echo
grep -n -C4 "clac" "$entry_file" || echo "No clac found"
echo

# 3. Search for any CPUID-based SMAP checks in the same directory
grep -R -n "cpuid" "$(dirname "$entry_file")" || echo "No CPUID checks found"

#!/bin/bash
set -e

# 1. Locate the SyscallEntry.asm file
entry_file=$(fd SyscallEntry.asm)

echo "Located SyscallEntry.asm at: $entry_file"
echo

# 2. Show context around stac
echo "---- stac context ----"
grep -n -C3 "stac" "$entry_file" || echo "No 'stac' found"

# 3. Show context around clac
echo
echo "---- clac context ----"
grep -n -C3 "clac" "$entry_file" || echo "No 'clac' found"

# 4. Check for any SMAP identifiers in the same directory
echo
echo "---- SMAP references ----"
grep -R -n "SMAP" "$(dirname "$entry_file")" || echo "No SMAP references found"

# 5. Look for CPUID-based feature checks nearby
echo
echo "---- CPUID or feature-check code ----"
grep -R -n "cpuid" "$(dirname "$entry_file")" || echo "No CPUID checks found"

🏁 Script executed:

#!/bin/bash
set -e

# Locate the SyscallEntry.asm file
entry_file=$(find . -type f -name SyscallEntry.asm | head -n1)
if [[ -z "$entry_file" ]]; then
  echo "SyscallEntry.asm not found"
  exit 1
fi
echo "Found SyscallEntry.asm at: $entry_file"
echo

# Show context around 'stac'
echo "---- stac context ----"
grep -n -C5 "stac" "$entry_file" || echo "'stac' not found"

# Show context around 'clac'
echo
echo "---- clac context ----"
grep -n -C5 "clac" "$entry_file" || echo "'clac' not found"

# Search for CPUID or SMAP feature checks in the same directory
echo
echo "---- SMAP references in directory ----"
grep -R -n "SMAP" "$(dirname "$entry_file")" || echo "No SMAP references found"

echo
echo "---- CPUID or feature-check code ----"
grep -R -n "cpuid" "$(dirname "$entry_file")" || echo "No CPUID checks found"

Length of output: 1642


Guard SMAP instructions with a CPU‐feature check

We currently execute stac (line 25) and clac (line 34) unconditionally in
arch/x86_64/syscall/SyscallEntry.asm. On CPUs without SMAP, these will #UD.

Please add a CPUID-leaf-7 EBX[21] test (or use the kernel’s global SMAP flag) before each SMAP instruction:

• Wrap lines 24–25 (stac) and 33–34 (clac) in a runtime feature check
• Cache the result at boot (e.g. in a per-CPU or global variable) to avoid repeated CPUID calls

For example:

--- a/arch/x86_64/syscall/SyscallEntry.asm
+++ b/arch/x86_64/syscall/SyscallEntry.asm
@@ -23,6 +23,13 @@ SyscallEntry:
     push r15
     ; Stop SMAP temporarily
+
+    ;— check SMAP support via CPUID leaf 7, EBX[21] —#
+    xor     ecx, ecx
+    cpuid                     ; eax=7, ecx=0 → EBX[21]=SMAP
+    test    ebx, 1<<21
+    jz      .Lskip_stac
     stac
+.Lskip_stac:
 
     ; System call convention: rax=syscall_num…
@@ -30,6 +37,13 @@ SyscallEntry:
     call    Syscall
     ; Reset SMAP
+
+    ;— re-enable SMAP if supported —#
+    xor     ecx, ecx
+    cpuid
+    test    ebx, 1<<21
+    jz      .Lskip_clac
     clac
+.Lskip_clac:
 
     ; Return value in rax is already set
     pop     r15

This ensures stac/clac only execute on CPUs that actually support SMAP.

📝 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
; Stop SMAP temporarily
stac
push r15
; Stop SMAP temporarily
;— check SMAP support via CPUID leaf 7, EBX[21] —#
xor ecx, ecx
cpuid ; eax=7, ecx=0 → EBX[21]=SMAP
test ebx, 1<<21
jz .Lskip_stac
stac
.Lskip_stac:
; System call convention: rax=syscall_num…
call Syscall
; Reset SMAP
;— re-enable SMAP if supported —#
xor ecx, ecx
cpuid
test ebx, 1<<21
jz .Lskip_clac
clac
.Lskip_clac:
; Return value in rax is already set
pop r15
🤖 Prompt for AI Agents
In arch/x86_64/syscall/SyscallEntry.asm around lines 24–25 and 33–34, the
STAC/CLAC instructions are executed unconditionally which will #UD on CPUs
without SMAP; add a runtime guard so those instructions only execute when SMAP
is supported: introduce and use a cached boot-time feature flag (per-CPU or
global) set once at boot by testing CPUID leaf 7 EBX[21] (or reuse the kernel’s
existing SMAP global flag), then wrap the STAC at lines 24–25 and the CLAC at
lines 33–34 with conditional checks that jump over the instruction when the
cached flag is clear to avoid repeated CPUID calls.

Copy link
Owner Author

Choose a reason for hiding this comment

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

what, check static void SetupMemoryProtection(void) ?

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

@assembler-0, let me check for that function to see if there's already existing SMAP detection logic.


🏁 Script executed:

#!/bin/bash
# Search for SetupMemoryProtection function
echo "=== Searching for SetupMemoryProtection function ==="
ast-grep --pattern 'void SetupMemoryProtection($_) {
  $$$
}'

echo -e "\n=== Alternative search with rg ==="
rg -A 20 "SetupMemoryProtection" --type c --type asm

echo -e "\n=== Search for SMAP-related code ==="
rg -B 5 -A 10 "SMAP|smap" --type c --type asm

echo -e "\n=== Search for existing feature flags ==="
rg -B 3 -A 3 "feature.*flag|FLAG.*SMAP|SMAP.*flag" --type c --type asm

Length of output: 6200


@assembler-0, you're absolutely right! I found the SetupMemoryProtection() function in kernel/core/Kernel.c. It already performs the CPUID leaf 7 check for SMAP support (bit 20 in EBX) and enables CR4.SMAP if supported.

However, the issue remains that while SMAP is being detected and conditionally enabled at boot time, the syscall entry assembly still executes stac and clac unconditionally. Even though SetupMemoryProtection() won't enable SMAP on unsupported CPUs, the assembly instructions will still cause #UD.

The cleanest solution would be to:

  1. Export a global SMAP flag from the existing SetupMemoryProtection() function
  2. Check that flag in the assembly code instead of doing redundant CPUID calls

For example, in kernel/core/Kernel.c:

bool smap_supported = false;  // Global flag

static void SetupMemoryProtection(void) {
    // ... existing CPUID code ...
    
    // Enable SMAP if supported (bit 20 in EBX from CPUID leaf 7)
    if (ebx & (1 << 20)) {
        cr4 |= (1ULL << 21);  // CR4.SMAP
        PrintKernel("[SYSTEM] SMAP enabled\n");
        smap_supported = true;  // Set the global flag
        protection_enabled = true;
    }
    // ...
}

Then in SyscallEntry.asm, check this flag instead of doing CPUID each time. This leverages your existing detection logic without duplication.


🧠 Learnings used
Learnt from: assembler-0
PR: assembler-0/VoidFrame#62
File: kernel/core/Panic.c:69-79
Timestamp: 2025-08-10T06:22:28.788Z
Learning: In VoidFrame kernel, panic functions must call "asm volatile("cli")" as the very first instruction to disable interrupts immediately. Without this, if interrupts are enabled (after "sti"), an interrupt can fire during panic handling causing the panic to fail, hang, or behave unpredictably. This must be done in ALL panic entry points: Panic(), PanicWithCode(), PanicWithContext(), PanicFromInterrupt(), and at the start of KernelPanicHandler().

; System call convention: rax=syscall_num, rdi=arg1, rsi=arg2, rdx=arg3
mov rdi, rax ; syscall number
mov rsi, rbx ; arg1
mov rdx, rcx ; arg2
mov rcx, r8 ; arg3

mov r11, rdi ; Save arg1
mov r12, rsi ; Save arg2
mov r13, rdx ; Save arg3

; Now setup parameters for Syscall function
mov rdi, rax ; syscall number (1st param)
mov rsi, r11 ; arg1 (2nd param)
mov rdx, r12 ; arg2 (3rd param)
mov rcx, r13 ; arg3 (4th param)

call Syscall

; Reset SMAP
clac
; Return value in rax is already set

; Restore registers
pop r15
pop r14
Expand All @@ -48,5 +55,5 @@ SyscallEntry:
pop rcx
pop rbx
pop rax ; Pop original rax, but leave the syscall return value in rax

iretq
1 change: 1 addition & 0 deletions drivers/Pic.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ void PitSetFrequency(uint16_t hz) {
// Restore previous interrupt state
restore_irq_flags(flags);
}

void PicInstall() {
uint8_t a1, a2;

Expand Down
5 changes: 2 additions & 3 deletions drivers/ethernet/RTL8139.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#include "RTL8139.h"


#include "Console.h"
#include "Io.h"
#include "KernelHeap.h" // For allocating memory
Expand Down Expand Up @@ -118,4 +116,5 @@ void Rtl8139_SendPacket(void* data, uint32_t len) {

const Rtl8139Device* GetRtl8139Device() {
return &rtl_device;
}
}

6 changes: 4 additions & 2 deletions kernel/core/Kernel.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// VoidFrame Kernel Entry File
#include "Kernel.h"
#include "Console.h"
#include "FAT12.h"
Expand All @@ -16,14 +17,15 @@
#include "Panic.h"
#include "Pic.h"
#include "Process.h"
#include "ethernet/RTL8139.h"
#include "Serial.h"
#include "Shell.h"
#include "StackGuard.h"
#include "Syscall.h"
#include "UserMode.h"
#include "VFS.h"
#include "VMem.h"
#include "VesaBIOSExtension.h"
#include "ethernet/RTL8139.h"
#include "stdbool.h"
#include "stdint.h"

Expand Down Expand Up @@ -496,7 +498,6 @@ static void ValidateMemoryLayout(void) {
PrintKernelError("[ERROR] Virtual address space overlaps with kernel space\n");
}


PrintKernelSuccess("[SYSTEM] Memory layout validated\n");
}

Expand Down Expand Up @@ -529,6 +530,7 @@ static void PrintBootstrapSummary(void) {
PrintKernel(" Bootstrap complete\n");
}


void KernelMainHigherHalf(void) {
PrintKernelSuccess("[SYSTEM] Successfully jumped to higher half. Virtual memory is active.\n");

Expand Down
65 changes: 65 additions & 0 deletions kernel/elf/ELFloader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "ELFloader.h"
#include "MemOps.h"
#include "VFS.h"
#include "Process.h"
#include "KernelHeap.h"

void* CreateProcessFromElf(const char* filename) {
// 1. Read ELF file from VFS
uint8_t* elf_data = (uint8_t*)KernelMemoryAlloc(65536); // Max 64KB for simple ELFs
if (!VfsReadFile(filename, (char*)elf_data, 65536)) {
KernelFree(elf_data);
return NULL;
}

// 2. Validate ELF header
ElfHeader* header = (ElfHeader*)elf_data;
if (*(uint32_t*)header->e_ident != ELF_MAGIC ||
header->e_machine != EM_X86_64) {
KernelFree(elf_data);
return NULL;
}

// 3. Find PT_LOAD segments and allocate memory
ProgramHeader* ph = (ProgramHeader*)(elf_data + header->e_phoff);
void* process_memory = NULL;
uint64_t entry_point = header->e_entry;

for (int i = 0; i < header->e_phnum; i++) {
if (ph[i].p_type == PT_LOAD) {
// Allocate memory for this segment
void* segment_mem = KernelMemoryAlloc(ph[i].p_memsz);

// Copy file data to memory
FastMemcpy(segment_mem, elf_data + ph[i].p_offset, ph[i].p_filesz);

// Zero out BSS section (if memsz > filesz)
if (ph[i].p_memsz > ph[i].p_filesz) {
memset((uint8_t*)segment_mem + ph[i].p_filesz, 0,
ph[i].p_memsz - ph[i].p_filesz);
}

// For simplicity, use first LOAD segment as process memory
if (!process_memory) {
process_memory = segment_mem;
}
}
}

// 4. Create kernel process
if (process_memory) {
// Create process with ELF entry point
void* process = (void*)CreateProcess((void*)entry_point);
KernelFree(elf_data);
return process;
}

KernelFree(elf_data);
return NULL;
}

// Convenience function
int LoadElfFromFile(const char* filename) {
const void * process = CreateProcessFromElf(filename);
return process ? 0 : -1;
}
41 changes: 41 additions & 0 deletions kernel/elf/ELFloader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#ifndef VOIDFRAME_ELFLOADER_H
#define VOIDFRAME_ELFLOADER_H

#pragma once
#include "stdint.h"

typedef struct {
uint8_t e_ident[16]; // Magic number and class info
uint16_t e_type; // Object file type
uint16_t e_machine; // Architecture
uint32_t e_version; // Object file version
uint64_t e_entry; // Entry point virtual address
uint64_t e_phoff; // Program header table offset
uint64_t e_shoff; // Section header table offset
uint32_t e_flags; // Processor-specific flags
uint16_t e_ehsize; // ELF header size
uint16_t e_phentsize; // Program header table entry size
uint16_t e_phnum; // Program header table entry count
} ElfHeader;

// Program Header
typedef struct {
uint32_t p_type; // Segment type (PT_LOAD, etc.)
uint32_t p_flags; // Segment flags
uint64_t p_offset; // Segment file offset
uint64_t p_vaddr; // Segment virtual address
uint64_t p_paddr; // Segment physical address
uint64_t p_filesz; // Segment size in file
uint64_t p_memsz; // Segment size in memory
uint64_t p_align; // Segment alignment
} ProgramHeader;

#define ELF_MAGIC 0x464C457F // "\x7FELF"
#define PT_LOAD 1
#define EM_X86_64 62

// Main functions
int LoadElfFromFile(const char* filename);
void* CreateProcessFromElf(const char* filename);

#endif // VOIDFRAME_ELFLOADER_H
2 changes: 1 addition & 1 deletion kernel/etc/Editor.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#ifndef EDITOR_H
#define EDITOR_H

void EditorOpen(const char* file);
void EditorOpen(const char *file);

#endif
Loading