Initial commit: SUB-OS 64-Bit Production Monolithic Operating System with LazyBox userland suite

This commit is contained in:
subhobhai943 committed 2026-08-16 21:23:52 +05:30
commit cd9c786d14
88 files changed
+7132

No files matched your search

+37
View File
@@ -0,0 +1,37 @@
[BITS 64]
extern kernel_main
extern __bss_start
extern __bss_end
extern stack_top
global kernel_entry
section .text
kernel_entry:
; 1. Set up high-performance dedicated 64 KB kernel stack
mov rsp, stack_top
; 2. Preserve bootloader arguments safely in callee-saved registers R12 & R13
mov r12, rdi ; memory_map pointer
mov r13, rsi ; memory_map count
; 3. Zero out the entire BSS section (.bss)
mov rdi, __bss_start
mov rcx, __bss_end
sub rcx, rdi
xor al, al
cld
rep stosb
; 4. Restore bootloader arguments into RDI and RSI per AMD64 System V ABI
mov rdi, r12
mov rsi, r13
; 5. Call kernel_main(memory_map, memory_map_count)
call kernel_main
.hang:
cli
hlt
jmp .hang
+65
View File
@@ -0,0 +1,65 @@
#include <arch/x86_64/cpuid.h>
#include <lib/string.h>
static cpu_info_t global_cpu_info;
static bool cpu_detected = false;
static inline void cpuid(uint32_t code, uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d) {
__asm__ volatile("cpuid"
: "=a"(*a), "=b"(*b), "=c"(*c), "=d"(*d)
: "a"(code));
}
void cpuid_detect(cpu_info_t* info) {
uint32_t eax, ebx, ecx, edx;
memset(info, 0, sizeof(cpu_info_t));
// 1. Get Vendor String
cpuid(0, &eax, &ebx, &ecx, &edx);
((uint32_t*)info->vendor)[0] = ebx;
((uint32_t*)info->vendor)[1] = edx;
((uint32_t*)info->vendor)[2] = ecx;
info->vendor[12] = '\0';
// 2. Get Features & Model Info
cpuid(1, &eax, &ebx, &ecx, &edx);
info->stepping = eax & 0x0F;
info->model_id = (eax >> 4) & 0x0F;
info->family = (eax >> 8) & 0x0F;
info->has_fpu = (edx & (1 << 0)) != 0;
info->has_tsc = (edx & (1 << 4)) != 0;
info->has_msr = (edx & (1 << 5)) != 0;
info->has_pae = (edx & (1 << 6)) != 0;
info->has_apic = (edx & (1 << 9)) != 0;
info->has_sse = (edx & (1 << 25)) != 0;
info->has_sse2 = (edx & (1 << 26)) != 0;
info->has_sse3 = (ecx & (1 << 0)) != 0;
info->has_sse4_1= (ecx & (1 << 19)) != 0;
info->has_sse4_2= (ecx & (1 << 20)) != 0;
info->has_avx = (ecx & (1 << 28)) != 0;
info->has_rdrand= (ecx & (1 << 30)) != 0;
// 3. Get Brand String (Extended Functions)
cpuid(0x80000000, &eax, &ebx, &ecx, &edx);
if (eax >= 0x80000004) {
uint32_t* brand = (uint32_t*)info->model;
cpuid(0x80000002, &brand[0], &brand[1], &brand[2], &brand[3]);
cpuid(0x80000003, &brand[4], &brand[5], &brand[6], &brand[7]);
cpuid(0x80000004, &brand[8], &brand[9], &brand[10], &brand[11]);
info->model[48] = '\0';
} else {
strcpy(info->model, "x86_64 Compatible Processor");
}
global_cpu_info = *info;
cpu_detected = true;
}
const cpu_info_t* cpuid_get_info(void) {
if (!cpu_detected) {
cpuid_detect(&global_cpu_info);
}
return &global_cpu_info;
}
+65
View File
@@ -0,0 +1,65 @@
#include <arch/x86_64/gdt.h>
#include <lib/string.h>
extern void gdt_flush(uint64_t gdt_ptr);
extern void tss_flush(void);
static gdt_entry_t gdt_entries[7];
static gdt_ptr_t gdt_ptr;
static tss_entry_t tss_entry;
static void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) {
gdt_entries[num].base_low = (base & 0xFFFF);
gdt_entries[num].base_middle = (base >> 16) & 0xFF;
gdt_entries[num].base_high = (base >> 24) & 0xFF;
gdt_entries[num].limit_low = (limit & 0xFFFF);
gdt_entries[num].granularity = (limit >> 16) & 0x0F;
gdt_entries[num].granularity |= gran & 0xF0;
gdt_entries[num].access = access;
}
static void write_tss(int num, uint64_t base, uint32_t limit) {
gdt_set_gate(num, (uint32_t)base, limit, 0x89, 0x00);
// TSS descriptor takes 16 bytes in 64-bit mode (2 entries)
uint32_t* high_part = (uint32_t*)&gdt_entries[num + 1];
high_part[0] = (uint32_t)(base >> 32);
high_part[1] = 0;
}
void gdt_init(void) {
gdt_ptr.limit = sizeof(gdt_entries) - 1;
gdt_ptr.base = (uint64_t)&gdt_entries;
// 0x00: Null Descriptor
gdt_set_gate(0, 0, 0, 0, 0);
// 0x08: Kernel 64-bit Code Segment (Access=0x9A, Gran=0x20 for Long Mode)
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0x20);
// 0x10: Kernel 64-bit Data Segment (Access=0x92, Gran=0x00)
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0x00);
// 0x18: User 64-bit Code Segment (Access=0xFA, Gran=0x20)
gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0x20);
// 0x20: User 64-bit Data Segment (Access=0xF2, Gran=0x00)
gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0x00);
// 0x28: TSS (Task State Segment) - takes slots 5 & 6
extern char stack_top[];
memset(&tss_entry, 0, sizeof(tss_entry_t));
tss_entry.rsp0 = (uint64_t)stack_top;
tss_entry.ist1 = (uint64_t)stack_top;
tss_entry.iomap_base = sizeof(tss_entry_t);
write_tss(5, (uint64_t)&tss_entry, sizeof(tss_entry_t) - 1);
gdt_flush((uint64_t)&gdt_ptr);
tss_flush();
}
void gdt_set_kernel_stack(uint64_t stack) {
tss_entry.rsp0 = stack;
}
+29
View File
@@ -0,0 +1,29 @@
[BITS 64]
global gdt_flush
global tss_flush
section .text
gdt_flush:
lgdt [rdi]
; Reload data segments
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
; Reload CS via far return
push 0x08
lea rax, [rel .reload_cs]
push rax
retfq
.reload_cs:
ret
tss_flush:
mov ax, 0x28
ltr ax
ret
+34
View File
@@ -0,0 +1,34 @@
#include <arch/x86_64/idt.h>
#include <lib/string.h>
extern void idt_flush(uint64_t idt_ptr);
static idt_entry_t idt_entries[IDT_ENTRIES];
static idt_ptr_t idt_ptr;
void idt_set_gate(uint8_t vector, void* isr, uint8_t flags) {
uint64_t addr = (uint64_t)isr;
idt_entries[vector].isr_low = (uint16_t)(addr & 0xFFFF);
idt_entries[vector].kernel_cs = 0x08;
idt_entries[vector].ist = 0;
idt_entries[vector].attributes = flags;
idt_entries[vector].isr_mid = (uint16_t)((addr >> 16) & 0xFFFF);
idt_entries[vector].isr_high = (uint32_t)((addr >> 32) & 0xFFFFFFFF);
idt_entries[vector].reserved = 0;
}
void idt_enable_gate(uint8_t vector) {
idt_entries[vector].attributes |= 0x80;
}
void idt_disable_gate(uint8_t vector) {
idt_entries[vector].attributes &= ~0x80;
}
void idt_init(void) {
idt_ptr.limit = sizeof(idt_entries) - 1;
idt_ptr.base = (uint64_t)&idt_entries;
memset(&idt_entries, 0, sizeof(idt_entries));
idt_flush((uint64_t)&idt_ptr);
}
+8
View File
@@ -0,0 +1,8 @@
[BITS 64]
global idt_flush
section .text
idt_flush:
lidt [rdi]
ret
+87
View File
@@ -0,0 +1,87 @@
#include <arch/x86_64/isr.h>
#include <arch/x86_64/idt.h>
#include <arch/x86_64/pic.h>
#include <kernel/printk.h>
#include <kernel/panic.h>
#include <lib/string.h>
extern void* isr_stub_table[48];
static isr_handler_t interrupt_handlers[256];
static const char* exception_messages[32] = {
"Division By Zero",
"Debug",
"Non-Maskable Interrupt",
"Breakpoint",
"Into Detected Overflow",
"Out of Bounds",
"Invalid Opcode",
"No Coprocessor",
"Double Fault",
"Coprocessor Segment Overrun",
"Bad TSS",
"Segment Not Present",
"Stack Fault",
"General Protection Fault",
"Page Fault",
"Unknown Interrupt",
"Coprocessor Fault",
"Alignment Check",
"Machine Check",
"SIMD Floating-Point Exception",
"Virtualization Exception",
"Control Protection Exception",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Hypervisor Injection Exception",
"VMM Communication Exception",
"Security Exception",
"Reserved"
};
void isr_register_handler(uint8_t int_no, isr_handler_t handler) {
interrupt_handlers[int_no] = handler;
}
void isr_unregister_handler(uint8_t int_no) {
interrupt_handlers[int_no] = NULL;
}
void isr_handler_common(registers_t* regs) {
if (regs->int_no < 32) {
// CPU Exception Fault
printk(KERN_EMERG "\n[EXCEPTION] Vector %llu: %s (Error code: 0x%llx)\n",
regs->int_no, exception_messages[regs->int_no], regs->error_code);
panic_with_regs(exception_messages[regs->int_no], regs);
}
if (interrupt_handlers[regs->int_no] != NULL) {
interrupt_handlers[regs->int_no](regs);
}
// Acknowledge Hardware PIC Interrupts (IRQs 32-47)
if (regs->int_no >= 32 && regs->int_no < 48) {
pic_send_eoi((uint8_t)(regs->int_no - 32));
}
}
extern void* isr_stub_default;
void isr_init(void) {
memset(interrupt_handlers, 0, sizeof(interrupt_handlers));
// 1. Initialize all 256 gates to isr_stub_default
for (int i = 0; i < 256; i++) {
idt_set_gate(i, isr_stub_default, 0x8E);
}
// 2. Install dedicated CPU exception gates (0-31) and hardware IRQ gates (32-47)
for (uint8_t i = 0; i < 48; i++) {
idt_set_gate(i, isr_stub_table[i], 0x8E);
}
}
+133
View File
@@ -0,0 +1,133 @@
[BITS 64]
extern isr_handler_common
global isr_stub_table
global isr_stub_default
%macro ISR_NOERRCODE 1
isr_stub_%1:
push qword 0 ; Dummy error code
push qword %1 ; Interrupt vector number
jmp isr_common_stub
%endmacro
%macro ISR_ERRCODE 1
isr_stub_%1:
push qword %1 ; Interrupt vector number (error code already on stack)
jmp isr_common_stub
%endmacro
section .text
; CPU Exceptions 0-31
ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRCODE 8
ISR_NOERRCODE 9
ISR_ERRCODE 10
ISR_ERRCODE 11
ISR_ERRCODE 12
ISR_ERRCODE 13
ISR_ERRCODE 14
ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_ERRCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_ERRCODE 30
ISR_NOERRCODE 31
; Hardware IRQs 32-47
ISR_NOERRCODE 32
ISR_NOERRCODE 33
ISR_NOERRCODE 34
ISR_NOERRCODE 35
ISR_NOERRCODE 36
ISR_NOERRCODE 37
ISR_NOERRCODE 38
ISR_NOERRCODE 39
ISR_NOERRCODE 40
ISR_NOERRCODE 41
ISR_NOERRCODE 42
ISR_NOERRCODE 43
ISR_NOERRCODE 44
ISR_NOERRCODE 45
ISR_NOERRCODE 46
ISR_NOERRCODE 47
isr_stub_default:
push qword 0
push qword 255
jmp isr_common_stub
isr_common_stub:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
push rbp
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov rdi, rsp
cld
call isr_handler_common
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rbp
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
add rsp, 16
iretq
section .data
align 8
isr_stub_table:
dq isr_stub_0, isr_stub_1, isr_stub_2, isr_stub_3
dq isr_stub_4, isr_stub_5, isr_stub_6, isr_stub_7
dq isr_stub_8, isr_stub_9, isr_stub_10, isr_stub_11
dq isr_stub_12, isr_stub_13, isr_stub_14, isr_stub_15
dq isr_stub_16, isr_stub_17, isr_stub_18, isr_stub_19
dq isr_stub_20, isr_stub_21, isr_stub_22, isr_stub_23
dq isr_stub_24, isr_stub_25, isr_stub_26, isr_stub_27
dq isr_stub_28, isr_stub_29, isr_stub_30, isr_stub_31
dq isr_stub_32, isr_stub_33, isr_stub_34, isr_stub_35
dq isr_stub_36, isr_stub_37, isr_stub_38, isr_stub_39
dq isr_stub_40, isr_stub_41, isr_stub_42, isr_stub_43
dq isr_stub_44, isr_stub_45, isr_stub_46, isr_stub_47
+78
View File
@@ -0,0 +1,78 @@
#include <arch/x86_64/pic.h>
#include <arch/x86_64/io.h>
#define PIC1_COMMAND 0x20
#define PIC1_DATA 0x21
#define PIC2_COMMAND 0xA0
#define PIC2_DATA 0xA1
#define PIC_EOI 0x20
#define ICW1_INIT 0x10
#define ICW1_ICW4 0x01
#define ICW4_8086 0x01
void pic_send_eoi(uint8_t irq) {
if (irq >= 8) {
outb(PIC2_COMMAND, PIC_EOI);
}
outb(PIC1_COMMAND, PIC_EOI);
}
void pic_init(void) {
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4);
io_wait();
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
io_wait();
// Remap Master PIC to 0x20 (32), Slave PIC to 0x28 (40)
outb(PIC1_DATA, 0x20);
io_wait();
outb(PIC2_DATA, 0x28);
io_wait();
outb(PIC1_DATA, 4); // Tell Master that Slave is at IRQ2
io_wait();
outb(PIC2_DATA, 2); // Tell Slave its cascade identity
io_wait();
outb(PIC1_DATA, ICW4_8086);
io_wait();
outb(PIC2_DATA, ICW4_8086);
io_wait();
// Mask all interrupts initially except Cascade (IRQ2)
outb(PIC1_DATA, 0xFB);
outb(PIC2_DATA, 0xFF);
}
void pic_set_mask(uint8_t irq) {
uint16_t port;
if (irq < 8) {
port = PIC1_DATA;
} else {
port = PIC2_DATA;
irq -= 8;
}
uint8_t value = inb(port) | (1 << irq);
outb(port, value);
}
void pic_clear_mask(uint8_t irq) {
uint16_t port;
if (irq < 8) {
port = PIC1_DATA;
} else {
port = PIC2_DATA;
irq -= 8;
uint8_t master_val = inb(PIC1_DATA) & ~(1 << 2);
outb(PIC1_DATA, master_val);
}
uint8_t value = inb(port) & ~(1 << irq);
outb(port, value);
}
void pic_disable(void) {
outb(PIC1_DATA, 0xFF);
outb(PIC2_DATA, 0xFF);
}
+49
View File
@@ -0,0 +1,49 @@
#include <arch/x86_64/pit.h>
#include <arch/x86_64/isr.h>
#include <arch/x86_64/pic.h>
#include <arch/x86_64/io.h>
#include <kernel/sched.h>
#define PIT_COMMAND_PORT 0x43
#define PIT_CHANNEL0_PORT 0x40
#define PIT_BASE_FREQUENCY 1193182
static volatile uint64_t pit_ticks = 0;
static uint32_t pit_freq = 100;
static void pit_handler(registers_t* regs) {
(void)regs;
pit_ticks++;
// Notify preemptive scheduler
sched_tick();
}
void pit_init(uint32_t frequency) {
pit_freq = frequency;
uint32_t divisor = PIT_BASE_FREQUENCY / frequency;
// Channel 0, lobyte/hibyte, Rate generator (Mode 2)
outb(PIT_COMMAND_PORT, 0x36);
outb(PIT_CHANNEL0_PORT, (uint8_t)(divisor & 0xFF));
outb(PIT_CHANNEL0_PORT, (uint8_t)((divisor >> 8) & 0xFF));
// Register IRQ0 handler
isr_register_handler(32, pit_handler);
pic_clear_mask(0);
}
uint64_t pit_get_ticks(void) {
return pit_ticks;
}
uint64_t pit_get_uptime_ms(void) {
return (pit_ticks * 1000) / pit_freq;
}
void pit_sleep(uint32_t milliseconds) {
uint64_t target = pit_ticks + (milliseconds * pit_freq) / 1000;
while (pit_ticks < target) {
hlt();
}
}
+92
View File
@@ -0,0 +1,92 @@
#include <arch/x86_64/paging.h>
#include <mm/pmm.h>
#include <lib/string.h>
static pml4e_t* kernel_pml4 = (pml4e_t*)0x1000;
void paging_flush_tlb_single(uint64_t virt) {
__asm__ volatile("invlpg (%0)" : : "r"(virt) : "memory");
}
void paging_init(void) {
// PML4 is already loaded at 0x1000 by Stage 2 bootloader with 4GB mapped
kernel_pml4 = (pml4e_t*)0x1000;
}
void paging_map_page(uint64_t virt, uint64_t phys, uint64_t flags) {
size_t pml4_idx = (virt >> 39) & 0x1FF;
size_t pdpt_idx = (virt >> 30) & 0x1FF;
size_t pd_idx = (virt >> 21) & 0x1FF;
size_t pt_idx = (virt >> 12) & 0x1FF;
if (!(kernel_pml4[pml4_idx] & PAGE_PRESENT)) {
void* new_pdpt = pmm_alloc_page();
if (!new_pdpt) return;
memset(new_pdpt, 0, PAGE_SIZE);
kernel_pml4[pml4_idx] = (uint64_t)new_pdpt | PAGE_PRESENT | PAGE_WRITABLE | (flags & PAGE_USER);
}
pdpte_t* pdpt = (pdpte_t*)(kernel_pml4[pml4_idx] & ~0xFFFULL);
if (!(pdpt[pdpt_idx] & PAGE_PRESENT)) {
void* new_pd = pmm_alloc_page();
if (!new_pd) return;
memset(new_pd, 0, PAGE_SIZE);
pdpt[pdpt_idx] = (uint64_t)new_pd | PAGE_PRESENT | PAGE_WRITABLE | (flags & PAGE_USER);
}
pde_t* pd = (pde_t*)(pdpt[pdpt_idx] & ~0xFFFULL);
if (!(pd[pd_idx] & PAGE_PRESENT)) {
void* new_pt = pmm_alloc_page();
if (!new_pt) return;
memset(new_pt, 0, PAGE_SIZE);
pd[pd_idx] = (uint64_t)new_pt | PAGE_PRESENT | PAGE_WRITABLE | (flags & PAGE_USER);
}
pte_t* pt = (pte_t*)(pd[pd_idx] & ~0xFFFULL);
pt[pt_idx] = (phys & ~0xFFFULL) | flags | PAGE_PRESENT;
paging_flush_tlb_single(virt);
}
void paging_unmap_page(uint64_t virt) {
size_t pml4_idx = (virt >> 39) & 0x1FF;
size_t pdpt_idx = (virt >> 30) & 0x1FF;
size_t pd_idx = (virt >> 21) & 0x1FF;
size_t pt_idx = (virt >> 12) & 0x1FF;
if (!(kernel_pml4[pml4_idx] & PAGE_PRESENT)) return;
pdpte_t* pdpt = (pdpte_t*)(kernel_pml4[pml4_idx] & ~0xFFFULL);
if (!(pdpt[pdpt_idx] & PAGE_PRESENT)) return;
pde_t* pd = (pde_t*)(pdpt[pdpt_idx] & ~0xFFFULL);
if (!(pd[pd_idx] & PAGE_PRESENT)) return;
pte_t* pt = (pte_t*)(pd[pd_idx] & ~0xFFFULL);
pt[pt_idx] = 0;
paging_flush_tlb_single(virt);
}
uint64_t paging_get_phys(uint64_t virt) {
size_t pml4_idx = (virt >> 39) & 0x1FF;
size_t pdpt_idx = (virt >> 30) & 0x1FF;
size_t pd_idx = (virt >> 21) & 0x1FF;
size_t pt_idx = (virt >> 12) & 0x1FF;
if (!(kernel_pml4[pml4_idx] & PAGE_PRESENT)) return 0;
pdpte_t* pdpt = (pdpte_t*)(kernel_pml4[pml4_idx] & ~0xFFFULL);
if (!(pdpt[pdpt_idx] & PAGE_PRESENT)) return 0;
pde_t* pd = (pde_t*)(pdpt[pdpt_idx] & ~0xFFFULL);
// Check for 2MB huge page
if (pd[pd_idx] & PAGE_HUGE) {
return (pd[pd_idx] & ~0x1FFFFFULL) | (virt & 0x1FFFFF);
}
if (!(pd[pd_idx] & PAGE_PRESENT)) return 0;
pte_t* pt = (pte_t*)(pd[pd_idx] & ~0xFFFULL);
if (!(pt[pt_idx] & PAGE_PRESENT)) return 0;
return (pt[pt_idx] & ~0xFFFULL) | (virt & 0xFFF);
}