diff --git a/build_programs.sh b/build_programs.sh index 7389f79..de9c6d2 100755 --- a/build_programs.sh +++ b/build_programs.sh @@ -5,13 +5,25 @@ mkdir -p programs/lib mkdir -p programs/bin cp rocklibc/rlibc.a programs/lib - + cd programs cd rocksh - make clean bear -- make cp rocksh.elf ../bin +cd .. -cd ../.. \ No newline at end of file +cd vga_text_term +make clean +bear -- make +cp vga_text_term.elf ../bin +cd .. + +cd test +make clean +bear -- make +cp test.elf ../bin +cd .. + +cd .. \ No newline at end of file diff --git a/compile_commands.json b/compile_commands.json index ada386e..f26fed2 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -84,6 +84,23 @@ "file": "/home/slinky/source/RockOS/kernel/drivers/cpio/cpio.c", "output": "/home/slinky/source/RockOS/obj/kernel/drivers/cpio/cpio.c.o" }, + { + "arguments": [ + "/home/slinky/opt/cross/bin/i686-elf-gcc", + "-c", + "-ffreestanding", + "-O2", + "-Wall", + "-Wextra", + "-Ikernel", + "-o", + "obj/kernel/drivers/pty/pty.c.o", + "kernel/drivers/pty/pty.c" + ], + "directory": "/home/slinky/source/RockOS", + "file": "/home/slinky/source/RockOS/kernel/drivers/pty/pty.c", + "output": "/home/slinky/source/RockOS/obj/kernel/drivers/pty/pty.c.o" + }, { "arguments": [ "/home/slinky/opt/cross/bin/i686-elf-gcc", @@ -305,6 +322,23 @@ "file": "/home/slinky/source/RockOS/kernel/lib/print.c", "output": "/home/slinky/source/RockOS/obj/kernel/lib/print.c.o" }, + { + "arguments": [ + "/home/slinky/opt/cross/bin/i686-elf-gcc", + "-c", + "-ffreestanding", + "-O2", + "-Wall", + "-Wextra", + "-Ikernel", + "-o", + "obj/kernel/lib/ringbuf.c.o", + "kernel/lib/ringbuf.c" + ], + "directory": "/home/slinky/source/RockOS", + "file": "/home/slinky/source/RockOS/kernel/lib/ringbuf.c", + "output": "/home/slinky/source/RockOS/obj/kernel/lib/ringbuf.c.o" + }, { "arguments": [ "/home/slinky/opt/cross/bin/i686-elf-gcc", diff --git a/kernel/drivers/pty/pty.c b/kernel/drivers/pty/pty.c new file mode 100644 index 0000000..a1b3a2c --- /dev/null +++ b/kernel/drivers/pty/pty.c @@ -0,0 +1,62 @@ +#include "lib/ringbuf.h" +#include + +uint32_t pty_master_read(struct vfs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer) { + pty_t* pty = (pty_t*)node->data; + uint32_t bytes_read = 0; + while (bytes_read < size) { + char c; + if (ring_buf_read(&pty->slave_rb, &c) == 0) { + buffer[bytes_read++] = c; + } else { + break; + } + } + return bytes_read; +} + +uint32_t pty_master_write(struct vfs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer) { + pty_t* pty = (pty_t*)node->data; + uint32_t bytes_written = 0; + + while (bytes_written < size) { + if (ring_buf_write(&pty->master_rb, buffer[bytes_written]) == 0) { + bytes_written++; + ring_buf_write(&pty->slave_rb, buffer[bytes_written-1]); + } else { + break; + } + } + return bytes_written; +} + +uint32_t pty_slave_read(struct vfs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer) { + pty_t* pty = (pty_t*)node->data; + uint32_t bytes_read = 0; + + while (bytes_read < size) { + char c; + if (ring_buf_read(&pty->master_rb, &c) == 0) { + buffer[bytes_read++] = c; + } else { + break; + } + } + + return bytes_read; +} + +uint32_t pty_slave_write(struct vfs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer) { + pty_t* pty = (pty_t*)node->data; + uint32_t bytes_written = 0; + + while (bytes_written < size) { + if (ring_buf_write(&pty->slave_rb, buffer[bytes_written]) == 0) { + bytes_written++; + } else { + break; + } + } + + return bytes_written; +} \ No newline at end of file diff --git a/kernel/drivers/pty/pty.h b/kernel/drivers/pty/pty.h new file mode 100644 index 0000000..0127163 --- /dev/null +++ b/kernel/drivers/pty/pty.h @@ -0,0 +1,27 @@ +#ifndef KPTY_H +#define KPTY_H + +#include +#include +#include + +typedef struct { + +} wait_queue_t; + +typedef struct { + ring_buf_t master_rb; + ring_buf_t slave_rb; + wait_queue_t master_wq; + wait_queue_t slave_wq; + uint32_t flags; + uint32_t pid; +} pty_t; + +uint32_t pty_master_read(struct vfs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer); +uint32_t pty_master_write(struct vfs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer); + +uint32_t pty_slave_read(struct vfs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer); +uint32_t pty_slave_write(struct vfs_node* node, uint32_t offset, uint32_t size, uint8_t* buffer); + +#endif \ No newline at end of file diff --git a/kernel/kmain.c b/kernel/kmain.c index 1aa8749..6dc5a9d 100644 --- a/kernel/kmain.c +++ b/kernel/kmain.c @@ -129,7 +129,9 @@ void load_root_program(const char* path) { *(--u_esp) = 0; // argv = NULL *(--u_esp) = 0; // argc = 0 - create_task(e_hdr->e_entry, (uint32_t)cr3, 1, (uint32_t)u_esp); + process_t* process = kalloc(sizeof(process_t)); + init_task(process, e_hdr->e_entry, (uint32_t)cr3, 1, (uint32_t)u_esp); + add_task(process); enable_interrupts(); load_pd((void*)kernel_cr3); @@ -173,6 +175,6 @@ void kmain(uint32_t magic, multiboot_info* mbi) { init_scheduler(); enable_interrupts(); - kprintf("loading rocksh...\n"); - load_root_program("/rocksh.elf"); + kprintf("loading test program\n"); + load_root_program("/test.elf"); } diff --git a/kernel/lib/ringbuf.c b/kernel/lib/ringbuf.c new file mode 100644 index 0000000..e537ccc --- /dev/null +++ b/kernel/lib/ringbuf.c @@ -0,0 +1,34 @@ +#include + +void ring_buf_init(ring_buf_t* rb) { + rb->head = 0; + rb->tail = 0; +} + +int ring_buf_is_empty(ring_buf_t* rb) { + return rb->head == rb->tail; +} + +int ring_buf_is_full(ring_buf_t* rb) { + return ((rb->head + 1) % PTY_BUFFER_SIZE) == rb->tail; +} + +int ring_buf_write(ring_buf_t *buf, char c) { + if (ring_buf_is_full(buf)) { + return -1; + } + + buf->data[buf->head] = c; + buf->head = (buf->head + 1) % PTY_BUFFER_SIZE; + return 0; +} + +int ring_buf_read(ring_buf_t *buf, char *c) { + if (ring_buf_is_empty(buf)) { + return -1; + } + + *c = buf->data[buf->tail]; + buf->tail = (buf->tail + 1) % PTY_BUFFER_SIZE; + return 0; +} \ No newline at end of file diff --git a/kernel/lib/ringbuf.h b/kernel/lib/ringbuf.h new file mode 100644 index 0000000..5c23511 --- /dev/null +++ b/kernel/lib/ringbuf.h @@ -0,0 +1,20 @@ +#ifndef KRINGBUF_H +#define KRINGBUF_H + +#include + +#define PTY_BUFFER_SIZE 1024 + +typedef struct { + char data[PTY_BUFFER_SIZE]; + size_t head; + size_t tail; +} ring_buf_t; + +void ring_buf_init(ring_buf_t* rb); +int ring_buf_is_empty(ring_buf_t* rb); +int ring_buf_is_full(ring_buf_t* rb); +int ring_buf_write(ring_buf_t *buf, char c); +int ring_buf_read(ring_buf_t *buf, char *c); + +#endif \ No newline at end of file diff --git a/kernel/memory/mm.c b/kernel/memory/mm.c index 1b10b30..1814e83 100644 --- a/kernel/memory/mm.c +++ b/kernel/memory/mm.c @@ -1,8 +1,8 @@ #include "memory/mm.h" -#include "lib/print.h" #include "multiboot.h" #include +#include extern uint8_t __kernel_start; extern uint8_t __kernel_end; @@ -17,6 +17,8 @@ extern uint32_t page_directory[1024]; #define PAGE_DIRECTORY 0xFFFFF000 #define TEMP_MAPPING_VADDR 0xDFFF0000 +#define TEMP_COPY_SRC 0xDFFE0000 // Safely 1 page down +#define TEMP_COPY_DST 0xDFFD0000 #define USR_PAGE 0x07 #define SUP_PAGE 0x03 @@ -190,4 +192,77 @@ void* create_task_pd() { uint32_t* kernel_pt = (uint32_t*)(PAGE_TABLES + (pde * PAGE_SIZE)); kernel_pt[pte] = 0; return (void*)pd_address; +} + +void* clone_task_pd() { + uint32_t child_pd_phys = (uint32_t)p_alloc_frame(); + if (child_pd_phys == 0) return NULL; + + map_page(TEMP_MAPPING_VADDR, child_pd_phys, PAGE_PRESENT | PAGE_WRITABLE); + uint32_t* child_pd = (uint32_t*)TEMP_MAPPING_VADDR; + uint32_t* parent_pd = (uint32_t*)PAGE_DIRECTORY; + + for (int i = 768; i < 1023; i++) { + child_pd[i] = parent_pd[i]; + } + + child_pd[1023] = child_pd_phys | PAGE_PRESENT | PAGE_WRITABLE; + + for (int i = 0; i < 768; i++) { + child_pd[i] = 0; + } + + for (int i = 0; i < 768; i++) { + uint32_t pde = parent_pd[i]; + if (!(pde & PAGE_PRESENT)) continue; + uint32_t child_pt_phys = (uint32_t)p_alloc_frame(); + + child_pd[i] = child_pt_phys | (pde & 0xFFF); + + map_page(TEMP_COPY_SRC, pde & ~(PAGE_SIZE - 1), PAGE_PRESENT | PAGE_WRITABLE); + map_page(TEMP_COPY_DST, child_pt_phys, PAGE_PRESENT | PAGE_WRITABLE); + flush_tlb(); + + uint32_t* src_pt = (uint32_t*)TEMP_COPY_SRC; + uint32_t* dst_pt = (uint32_t*)TEMP_COPY_DST; + + for (int j = 0; j < 1024; j++) { + uint32_t pte = src_pt[j]; + if (!(pte & PAGE_PRESENT)) { + dst_pt[j] = 0; + continue; + } + + uint32_t child_frame_phys = (uint32_t)p_alloc_frame(); + dst_pt[j] = child_frame_phys | (pte & 0xFFF); + + uint32_t parent_data_phys = pte & ~(PAGE_SIZE - 1); + + map_page(TEMP_COPY_SRC, parent_data_phys, PAGE_PRESENT | PAGE_WRITABLE); + map_page(TEMP_COPY_DST, child_frame_phys, PAGE_PRESENT | PAGE_WRITABLE); + flush_tlb(); + + memcpy((void*)TEMP_COPY_DST, (void*)TEMP_COPY_SRC, PAGE_SIZE); + + map_page(TEMP_COPY_SRC, pde & ~(PAGE_SIZE - 1), PAGE_PRESENT | PAGE_WRITABLE); + map_page(TEMP_COPY_DST, child_pt_phys, PAGE_PRESENT | PAGE_WRITABLE); + flush_tlb(); + } + } + + uint32_t pde_main = TEMP_MAPPING_VADDR >> 22; + uint32_t* pt_main = (uint32_t*)(PAGE_TABLES + (pde_main * PAGE_SIZE)); + pt_main[(TEMP_MAPPING_VADDR >> 12) & 0x3FF] = 0; + + uint32_t pde_src = TEMP_COPY_SRC >> 22; + uint32_t* pt_src = (uint32_t*)(PAGE_TABLES + (pde_src * PAGE_SIZE)); + pt_src[(TEMP_COPY_SRC >> 12) & 0x3FF] = 0; + + uint32_t pde_dst = TEMP_COPY_DST >> 22; + uint32_t* pt_dst = (uint32_t*)(PAGE_TABLES + (pde_dst * PAGE_SIZE)); + pt_dst[(TEMP_COPY_DST >> 12) & 0x3FF] = 0; + + flush_tlb(); + + return (void*)child_pd_phys; } \ No newline at end of file diff --git a/kernel/memory/mm.h b/kernel/memory/mm.h index 7d73dfc..0248aa7 100644 --- a/kernel/memory/mm.h +++ b/kernel/memory/mm.h @@ -21,6 +21,7 @@ void* p_alloc_frame(); uint32_t total_free_memory(); void* create_task_pd(); +void* clone_task_pd(); void map_page(uint32_t vaddr, uint32_t paddr, uint32_t flags); diff --git a/kernel/process.h b/kernel/process.h index 95e58e5..153d380 100644 --- a/kernel/process.h +++ b/kernel/process.h @@ -3,6 +3,10 @@ #include +#include + +#define MAX_PROCESS_FDS 32 + typedef enum { STATE_READY = 1, STATE_SLEEPING = 2, @@ -16,7 +20,8 @@ typedef enum { PROCESS_FLAG_USER = 1 << 1 } process_flags_t; -typedef struct { +struct process_t; +typedef struct process_t { uint32_t pid; uint32_t esp; uint32_t kstack_top; @@ -25,6 +30,8 @@ typedef struct { uint32_t sleep; uint32_t heap_end; uint32_t flags; + file_t* fd_table[MAX_PROCESS_FDS]; + struct process_t* sched_next; } __attribute__((packed)) process_t; #endif \ No newline at end of file diff --git a/kernel/scheduler.c b/kernel/scheduler.c index 2cd4765..700bc0c 100644 --- a/kernel/scheduler.c +++ b/kernel/scheduler.c @@ -11,12 +11,11 @@ #include "scheduler.h" #include "gdt.h" -static process_t* process_table; - -static int current_process = 0; -static int total_processes = 0; +static process_t* process_table = NULL; +static process_t* current_process = NULL; +static process_t* idle_task = NULL; -#define IDLE_TASK_PID 0 +static int total_processes = 0; static void lock_scheduler() { disable_interrupts(); @@ -26,9 +25,8 @@ static void unlock_scheduler() { enable_interrupts(); } -// called from isr and yield, interrupts are already disabled and re enabled in the isr/yield func uint32_t switch_context(uint32_t current_esp) { - process_table[current_process].esp = current_esp; + current_process->esp = current_esp; for (int i = 0; i < total_processes; i++) { if (process_table[i].state == STATE_SLEEPING) { @@ -42,12 +40,12 @@ uint32_t switch_context(uint32_t current_esp) { } } - int next_process = current_process; + process_t* next_process = current_process; int found_ready = 0; while (1) { - next_process = (next_process + 1) % total_processes; - if (next_process != IDLE_TASK_PID && process_table[next_process].state == STATE_READY) { + next_process = (process_t*)next_process->sched_next; + if (next_process != idle_task && next_process->state == STATE_READY) { found_ready = 1; break; } @@ -58,53 +56,52 @@ uint32_t switch_context(uint32_t current_esp) { } if (!found_ready) { - if (process_table[current_process].state == STATE_READY) { + if (current_process->state == STATE_READY) { next_process = current_process; } else { - next_process = IDLE_TASK_PID; + next_process = idle_task; } } current_process = next_process; uint32_t current_cr3 = fetch_cr3(); - if (current_cr3 != process_table[current_process].cr3) { - load_pd((void*)process_table[current_process].cr3); + if (current_cr3 != current_process->cr3) { + load_pd((void*)current_process->cr3); } - if (process_table[current_process].flags & PROCESS_FLAG_USER) { - tss.esp0 = process_table[current_process].kstack_top; + if (current_process->flags & PROCESS_FLAG_USER) { + tss.esp0 = current_process->kstack_top; } - return process_table[current_process].esp; + return current_process->esp; } void init_scheduler() { - process_table = kalloc(16 * sizeof(process_t)); - memset((void*)process_table, 0, 16 * sizeof(process_t)); - total_processes = 0; + process_t* root = kalloc(sizeof(process_t)); + root->pid = 0; + root->esp = 0; + root->cr3 = fetch_cr3(); + root->state = STATE_READY; + root->sleep = 0; + root->flags = PROCESS_FLAG_KERNEL; + root->sched_next = NULL; - process_t* process = &process_table[total_processes]; - process->pid = total_processes; - process->esp = 0; - process->cr3 = fetch_cr3(); - process->state = STATE_READY; - process->sleep = 0; - process->flags = PROCESS_FLAG_KERNEL; - total_processes++; + current_process = root; + idle_task = root; + process_table = root; - current_process = 0; + total_processes = 1; } -int create_task(uint32_t entry_point, uint32_t cr3, int user, uint32_t u_esp) { - if (total_processes >= 16) { - return 0; - } - - // default to a new task, but if we find a dead one, re use it - process_t* process = &process_table[total_processes]; - uint32_t pid = total_processes; - process->pid = pid; +void init_task ( + process_t* process, + uint32_t entry_point, + uint32_t cr3, + int user, + uint32_t u_esp +) { + process->pid = get_next_pid(); process->cr3 = cr3; process->state = STATE_READY; process->sleep = 0; @@ -145,32 +142,61 @@ int create_task(uint32_t entry_point, uint32_t cr3, int user, uint32_t u_esp) { } process->esp = (uint32_t)esp; - return 1; +} + +int get_next_pid() { + lock_scheduler(); + int pid = total_processes; + total_processes++; + unlock_scheduler(); + return pid; +} + +void add_task(process_t* task) { + lock_scheduler(); + + if (!task) { + unlock_scheduler(); + return; + } + + if (process_table->sched_next == NULL) { + process_table->sched_next = task; + task->sched_next = process_table; + unlock_scheduler(); + return; + } + + process_t* curr = process_table; + while (curr->sched_next != process_table) { + curr = curr->sched_next; + } + + curr->sched_next = task; + task->sched_next = process_table; + + unlock_scheduler(); } process_t* current_task() { lock_scheduler(); - process_t* rtn = &process_table[current_process]; + process_t* rtn = current_process; unlock_scheduler(); return rtn; } -process_t* current_task_unsafe() { - return &process_table[current_process]; -} - void sleep(uint32_t ms) { if (ms == 0) return; lock_scheduler(); - process_table[current_process].sleep = ms; - process_table[current_process].state = STATE_SLEEPING; + current_process->sleep = ms; + current_process->state = STATE_SLEEPING; yield(); unlock_scheduler(); } void exit() { lock_scheduler(); - process_table[current_process].state = STATE_DEAD; + current_process->state = STATE_DEAD; yield(); // yield forever unlock_scheduler(); } diff --git a/kernel/scheduler.h b/kernel/scheduler.h index e771226..e9b3ffc 100644 --- a/kernel/scheduler.h +++ b/kernel/scheduler.h @@ -5,10 +5,13 @@ #include void init_scheduler(); +void init_task(process_t* process, uint32_t entry_point, uint32_t cr3, int user, uint32_t u_esp); -int create_task(uint32_t entry_point, uint32_t cr3, int user, uint32_t u_esp); process_t* current_task(); +int get_next_pid(); +void add_task(process_t* task); + void sleep(uint32_t ms); void yield(); void exit(); diff --git a/kernel/syscall.c b/kernel/syscall.c index b50d52f..7292b7a 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -1,52 +1,57 @@ #include #include +#include +#include "common.h" +#include "lib/memory.h" +#include "lib/ringbuf.h" #include "memory/mm.h" #include "process.h" #include "scheduler.h" -#include "kbd.h" +#include "vfs.h" #include #include +#include +typedef struct registers { + uint32_t gs, fs, es, ds; // Pushed manually + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha + uint32_t int_no, err_code; // Pushed manually + uint32_t eip, cs, eflags, useresp, ss; // Pushed automatically by CPU +} registers_t; static int32_t sys_exit(int status) { process_t* task = current_task(); task->state = STATE_DEAD; - kprintf("Process %d exited with status %d\n", task->pid, status); + kprintf("exiting task %d with status %d\n", task->pid, status); exit(); return 0; } static int32_t sys_write(int fd, const void* buf, size_t count) { - if (fd == 1 || fd == 2) { // stdout or stderr - const char* cbuf = (const char*)buf; - for (size_t i = 0; i < count; i++) { - vga_putchar(cbuf[i]); - } - return count; - } - return -1; + if (fd < 0 || fd >= MAX_PROCESS_FDS) return -1; + + file_t* f = current_task()->fd_table[fd]; + if (!f || !f->node) return -1; + + uint32_t rd = f->node->write(f->node, f->offset, count, (uint8_t*)buf); + f->offset += rd; + + return rd; } static int32_t sys_read(int fd, void* buf, size_t count) { - if (fd == 0) { - char* cbuf = (char*)buf; - size_t bytes_read = 0; + if (fd < 0 || fd >= MAX_PROCESS_FDS) return -1; - while (bytes_read < count) { - kbd_wait(); - - if (kbd_ready()) { - char c = kbd_read(); - cbuf[bytes_read++] = c; - if (c == '\n') break; - } - } - return bytes_read; - } - return -1; + file_t* f = current_task()->fd_table[fd]; + if (!f || !f->node) return -1; + + uint32_t rd = f->node->read(f->node, f->offset, count, (uint8_t*)buf); + f->offset += rd; + + return rd; } static int32_t sys_open(const char* filename, int flags) { @@ -84,12 +89,78 @@ static int32_t sys_brk(uint32_t new_break) { return current->heap_end; } -typedef struct registers { - uint32_t gs, fs, es, ds; // Pushed manually - uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha - uint32_t int_no, err_code; // Pushed manually - uint32_t eip, cs, eflags, useresp, ss; // Pushed automatically by CPU -} registers_t; +static int32_t sys_pty(int* slave_fd) { + pty_t* pty = (pty_t*)kalloc(sizeof(pty_t)); + ring_buf_init(&pty->master_rb); + ring_buf_init(&pty->slave_rb); + + vfs_node_t* master =(vfs_node_t*)kalloc(sizeof(vfs_node_t)); + master->flags = VFS_CHARDEVICE; + master->size = 0; + master->read = pty_master_read; + master->write = pty_master_write; + master->finddir = NULL; + master->data = pty; + + vfs_node_t* slave =(vfs_node_t*)kalloc(sizeof(vfs_node_t)); + slave->flags = VFS_CHARDEVICE; + slave->size = 0; + slave->read = pty_slave_read; + slave->write = pty_slave_write; + slave->finddir = NULL; + slave->data = pty; + + file_t* fmaster = (file_t*)kalloc(sizeof(file_t)); + fmaster->node = master; + fmaster->offset = 0; + fmaster->flags = 0; + + file_t* fslave = (file_t*)kalloc(sizeof(file_t)); + fslave->node = slave; + fslave->offset = 0; + fslave->flags = 0; + + *slave_fd = alloc_fd(fslave); + return alloc_fd(fmaster); +} + +static int32_t sys_fork(registers_t* parent_regs) { + process_t* parent = current_task(); + + process_t* child = kalloc(sizeof(process_t)); + if (!child) { + return -1; + } + + child->pid = get_next_pid(); + child->state = STATE_READY; + child->flags = parent->flags; + child->heap_end = parent->heap_end; + child->sleep = 0; + + for (int i = 0; i < MAX_PROCESS_FDS; i++) { + child->fd_table[i] = parent->fd_table[i]; + } + + disable_interrupts(); + + uint32_t kstack_size = 4096; + void* kstack_alloc = kalloc(kstack_size); + child->kstack_top = (uint32_t)kstack_alloc + kstack_size; + + child->cr3 = (uint32_t)clone_task_pd(); + + registers_t* child_regs = (registers_t*)(child->kstack_top - sizeof(registers_t)); + memcpy(child_regs, parent_regs, sizeof(registers_t)); + child_regs->eax = 0; + child->esp = (uint32_t)child_regs; + + enable_interrupts(); + + add_task(child); + return child->pid; +} + int32_t syscall(registers_t* regs) { switch(regs->eax) { case 1: return sys_exit(regs->ebx); @@ -98,6 +169,8 @@ int32_t syscall(registers_t* regs) { case 5: return sys_open((const char*)regs->ebx, regs->ecx); case 6: return sys_close(regs->ebx); case 12: return sys_brk(regs->ebx); + case 20: return sys_pty((int*)regs->ebx); + case 21: return sys_fork(regs); default: return -1; } diff --git a/kernel/vfs.c b/kernel/vfs.c index 704f1cd..9884707 100644 --- a/kernel/vfs.c +++ b/kernel/vfs.c @@ -1,9 +1,8 @@ -#include "vfs.h" +#include +#include +#include -#include "lib/memory.h" - -#define MAX_PROCESS_FDS 32 -file_t fd_table[MAX_PROCESS_FDS]; +#include vfs_node_t* root = NULL; @@ -46,23 +45,22 @@ static vfs_node_t* lookup(const char* path) { int open(const char* _path, int flags) { vfs_node_t* file = lookup(_path); if (!file) return -1; - for (int i = 0; i < MAX_PROCESS_FDS; i++) { - if (fd_table[i].node == NULL) { - fd_table[i].node = file; - fd_table[i].offset = 0; - fd_table[i].flags = flags; - return i; - } - } - return -1; + + file_t* f = (file_t*)kalloc(sizeof(file_t)); + f->node = file; + f->offset = 0; + f->flags = flags; + + return alloc_fd(f); } int read(int fd, void* buf, size_t sz) { - if (fd < 0 || fd >= MAX_PROCESS_FDS || !fd_table[fd].node) { + process_t* curr_p = current_task(); + if (fd < 0 || fd >= MAX_PROCESS_FDS || !curr_p->fd_table[fd]->node) { return -1; } - file_t* file = &fd_table[fd]; + file_t* file = curr_p->fd_table[fd]; if (!file->node->read) return -1; @@ -73,21 +71,37 @@ int read(int fd, void* buf, size_t sz) { } int fstat(int fd, file_t* dst) { - if (fd < 0 || fd >= MAX_PROCESS_FDS || !fd_table[fd].node) { + process_t* curr_p = current_task(); + if (fd < 0 || fd >= MAX_PROCESS_FDS || !curr_p->fd_table[fd]->node) { return 0; } - file_t* src = &fd_table[fd]; + file_t* src = curr_p->fd_table[fd]; memcpy(src, dst, sizeof(file_t)); return 1; } int fseek(int fd, size_t offset) { - if (fd < 0 || fd >= MAX_PROCESS_FDS || !fd_table[fd].node) { + process_t* curr_p = current_task(); + if (fd < 0 || fd >= MAX_PROCESS_FDS || !curr_p->fd_table[fd]->node) { return 0; } - file_t* file = &fd_table[fd]; + file_t* file = curr_p->fd_table[fd]; file->offset = offset; return 1; -} \ No newline at end of file +} + +int alloc_fd(file_t* f) { + process_t* curr_p = current_task(); + if (!curr_p) return -1; + + for (int i = 0; i < MAX_PROCESS_FDS; i++) { + if (curr_p->fd_table[i] == NULL) { + curr_p->fd_table[i] = f; + return i; + } + } + + return -1; +} \ No newline at end of file diff --git a/kernel/vfs.h b/kernel/vfs.h index 4530658..f9f91fc 100644 --- a/kernel/vfs.h +++ b/kernel/vfs.h @@ -44,4 +44,6 @@ int read(int fd, void* buf, size_t sz); int fstat(int fd, file_t* _f); int fseek(int fd, size_t offset); +int alloc_fd(file_t* f); + #endif \ No newline at end of file diff --git a/programs/lib/rlibc.a b/programs/lib/rlibc.a index 6844338..f41ac65 100644 Binary files a/programs/lib/rlibc.a and b/programs/lib/rlibc.a differ diff --git a/programs/test/compile_commands.json b/programs/test/compile_commands.json new file mode 100644 index 0000000..63209b9 --- /dev/null +++ b/programs/test/compile_commands.json @@ -0,0 +1,36 @@ +[ + { + "arguments": [ + "/home/slinky/opt/cross/bin/i686-elf-gcc", + "-ffreestanding", + "-O2", + "-Wall", + "-Wextra", + "-I../../rocklibc/include", + "-c", + "-o", + "crt0.o", + "crt0.S" + ], + "directory": "/home/slinky/source/RockOS/programs/test", + "file": "/home/slinky/source/RockOS/programs/test/crt0.S", + "output": "/home/slinky/source/RockOS/programs/test/crt0.o" + }, + { + "arguments": [ + "/home/slinky/opt/cross/bin/i686-elf-gcc", + "-ffreestanding", + "-O2", + "-Wall", + "-Wextra", + "-I../../rocklibc/include", + "-c", + "-o", + "main.o", + "main.c" + ], + "directory": "/home/slinky/source/RockOS/programs/test", + "file": "/home/slinky/source/RockOS/programs/test/main.c", + "output": "/home/slinky/source/RockOS/programs/test/main.o" + } +] diff --git a/programs/test/crt0.S b/programs/test/crt0.S new file mode 100644 index 0000000..d97f158 --- /dev/null +++ b/programs/test/crt0.S @@ -0,0 +1,21 @@ +.global _start +.extern exit +.section .text +_start: + xor %ebp, %ebp + + mov (%esp), %eax + lea 4(%esp), %ebx + lea 8(%esp,%eax,4), %ecx + + and $-16, %esp + + push %ecx + push %ebx + push %eax + + call main + + push %eax + call exit + diff --git a/programs/test/linker.ld b/programs/test/linker.ld new file mode 100644 index 0000000..03d6e3c --- /dev/null +++ b/programs/test/linker.ld @@ -0,0 +1,31 @@ +ENTRY(_start) + +SECTIONS +{ + . = 0x40000000; + + .text ALIGN(4K) : + { + /* Force the crt0.o entry code to be placed FIRST in memory */ + KEEP(*crt0.o(.text)) + *(.text .text.*) + } + + .rodata ALIGN(4K) : + { + *(.rodata .rodata.*) + } + + .data ALIGN(4K) : + { + *(.data .data.*) + } + + .bss ALIGN(4K) : + { + _bss_start = .; + *(.bss .bss.*) + *(COMMON) + _bss_end = .; + } +} \ No newline at end of file diff --git a/programs/test/main.c b/programs/test/main.c new file mode 100644 index 0000000..c786f2b --- /dev/null +++ b/programs/test/main.c @@ -0,0 +1,12 @@ +#include + +int main(int argc, char *argv[]) { + (void)argc; + (void)argv; + + if (!fork()) { + exit(1); + } + + return 0; +} \ No newline at end of file diff --git a/programs/test/makefile b/programs/test/makefile new file mode 100644 index 0000000..cca7c29 --- /dev/null +++ b/programs/test/makefile @@ -0,0 +1,23 @@ +CC = i686-elf-gcc +LD = i686-elf-ld + +CFLAGS = -ffreestanding -O2 -Wall -Wextra -I../../rocklibc/include +LDFLAGS = -m elf_i386 -nostdlib + +TARGET = test.elf + +all: $(TARGET) + +crt0.o: crt0.S + $(CC) $(CFLAGS) -c crt0.S -o crt0.o + +main.o: main.c + $(CC) $(CFLAGS) -c main.c -o main.o + +$(TARGET): crt0.o main.o ../lib/rlibc.a + $(LD) $(LDFLAGS) -T linker.ld crt0.o main.o ../lib/rlibc.a -o $(TARGET) + +clean: + rm -f *.o $(TARGET) + +.PHONY: all clean test \ No newline at end of file diff --git a/programs/vga_text_term/compile_commands.json b/programs/vga_text_term/compile_commands.json new file mode 100644 index 0000000..6386dfb --- /dev/null +++ b/programs/vga_text_term/compile_commands.json @@ -0,0 +1,36 @@ +[ + { + "arguments": [ + "/home/slinky/opt/cross/bin/i686-elf-gcc", + "-ffreestanding", + "-O2", + "-Wall", + "-Wextra", + "-I../../rocklibc/include", + "-c", + "-o", + "crt0.o", + "crt0.S" + ], + "directory": "/home/slinky/source/RockOS/programs/vga_text_term", + "file": "/home/slinky/source/RockOS/programs/vga_text_term/crt0.S", + "output": "/home/slinky/source/RockOS/programs/vga_text_term/crt0.o" + }, + { + "arguments": [ + "/home/slinky/opt/cross/bin/i686-elf-gcc", + "-ffreestanding", + "-O2", + "-Wall", + "-Wextra", + "-I../../rocklibc/include", + "-c", + "-o", + "main.o", + "main.c" + ], + "directory": "/home/slinky/source/RockOS/programs/vga_text_term", + "file": "/home/slinky/source/RockOS/programs/vga_text_term/main.c", + "output": "/home/slinky/source/RockOS/programs/vga_text_term/main.o" + } +] diff --git a/programs/vga_text_term/crt0.S b/programs/vga_text_term/crt0.S new file mode 100644 index 0000000..d97f158 --- /dev/null +++ b/programs/vga_text_term/crt0.S @@ -0,0 +1,21 @@ +.global _start +.extern exit +.section .text +_start: + xor %ebp, %ebp + + mov (%esp), %eax + lea 4(%esp), %ebx + lea 8(%esp,%eax,4), %ecx + + and $-16, %esp + + push %ecx + push %ebx + push %eax + + call main + + push %eax + call exit + diff --git a/programs/vga_text_term/linker.ld b/programs/vga_text_term/linker.ld new file mode 100644 index 0000000..03d6e3c --- /dev/null +++ b/programs/vga_text_term/linker.ld @@ -0,0 +1,31 @@ +ENTRY(_start) + +SECTIONS +{ + . = 0x40000000; + + .text ALIGN(4K) : + { + /* Force the crt0.o entry code to be placed FIRST in memory */ + KEEP(*crt0.o(.text)) + *(.text .text.*) + } + + .rodata ALIGN(4K) : + { + *(.rodata .rodata.*) + } + + .data ALIGN(4K) : + { + *(.data .data.*) + } + + .bss ALIGN(4K) : + { + _bss_start = .; + *(.bss .bss.*) + *(COMMON) + _bss_end = .; + } +} \ No newline at end of file diff --git a/programs/vga_text_term/main.c b/programs/vga_text_term/main.c new file mode 100644 index 0000000..5ef21ec --- /dev/null +++ b/programs/vga_text_term/main.c @@ -0,0 +1,5 @@ +#include + +int main(int argc, char *argv[]) { + return 0; +} \ No newline at end of file diff --git a/programs/vga_text_term/makefile b/programs/vga_text_term/makefile new file mode 100644 index 0000000..3b8f645 --- /dev/null +++ b/programs/vga_text_term/makefile @@ -0,0 +1,23 @@ +CC = i686-elf-gcc +LD = i686-elf-ld + +CFLAGS = -ffreestanding -O2 -Wall -Wextra -I../../rocklibc/include +LDFLAGS = -m elf_i386 -nostdlib + +TARGET = vga_text_term.elf + +all: $(TARGET) + +crt0.o: crt0.S + $(CC) $(CFLAGS) -c crt0.S -o crt0.o + +main.o: main.c + $(CC) $(CFLAGS) -c main.c -o main.o + +$(TARGET): crt0.o main.o ../lib/rlibc.a + $(LD) $(LDFLAGS) -T linker.ld crt0.o main.o ../lib/rlibc.a -o $(TARGET) + +clean: + rm -f *.o $(TARGET) + +.PHONY: all clean test \ No newline at end of file diff --git a/programs/vga_text_term/syscall.c b/programs/vga_text_term/syscall.c new file mode 100644 index 0000000..b50d52f --- /dev/null +++ b/programs/vga_text_term/syscall.c @@ -0,0 +1,104 @@ +#include +#include + +#include "memory/mm.h" +#include "process.h" +#include "scheduler.h" +#include "kbd.h" + +#include + +#include + + +static int32_t sys_exit(int status) { + process_t* task = current_task(); + task->state = STATE_DEAD; + kprintf("Process %d exited with status %d\n", task->pid, status); + exit(); + return 0; +} + +static int32_t sys_write(int fd, const void* buf, size_t count) { + if (fd == 1 || fd == 2) { // stdout or stderr + const char* cbuf = (const char*)buf; + for (size_t i = 0; i < count; i++) { + vga_putchar(cbuf[i]); + } + return count; + } + return -1; +} + +static int32_t sys_read(int fd, void* buf, size_t count) { + if (fd == 0) { + char* cbuf = (char*)buf; + size_t bytes_read = 0; + + while (bytes_read < count) { + kbd_wait(); + + if (kbd_ready()) { + char c = kbd_read(); + cbuf[bytes_read++] = c; + if (c == '\n') break; + } + } + return bytes_read; + } + return -1; +} + +static int32_t sys_open(const char* filename, int flags) { + return -1; +} + +static int32_t sys_close(int fd) { + return -1; +} + +static int32_t sys_brk(uint32_t new_break) { + process_t* current = current_task(); + + if (new_break == 0) { + return current->heap_end; + } + + if (new_break < current->heap_end) { + current->heap_end = new_break; + return current->heap_end; + } + + uint32_t page_start = (current->heap_end + 4095) & ~4095; + uint32_t page_end = (new_break + 4095) & ~4095; + + for (uint32_t addr = page_start; addr < page_end; addr += 4096) { + // 1. Allocate a physical frame using your PMM (Physical Memory Manager) + void* phys = p_alloc_frame(); + + // 2. Map it into the current page directory using your VMM (Virtual Memory Manager) + map_page(addr, (uint32_t)phys, PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER); + } + + current->heap_end = new_break; + return current->heap_end; +} + +typedef struct registers { + uint32_t gs, fs, es, ds; // Pushed manually + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha + uint32_t int_no, err_code; // Pushed manually + uint32_t eip, cs, eflags, useresp, ss; // Pushed automatically by CPU +} registers_t; +int32_t syscall(registers_t* regs) { + switch(regs->eax) { + case 1: return sys_exit(regs->ebx); + case 3: return sys_read(regs->ebx, (void*)regs->ecx, regs->edx); + case 4: return sys_write(regs->ebx, (const void*)regs->ecx, regs->edx); + case 5: return sys_open((const char*)regs->ebx, regs->ecx); + case 6: return sys_close(regs->ebx); + case 12: return sys_brk(regs->ebx); + default: + return -1; + } +} \ No newline at end of file diff --git a/rocklibc/include/syscall.h b/rocklibc/include/syscall.h index 5838a1b..c7201d0 100644 --- a/rocklibc/include/syscall.h +++ b/rocklibc/include/syscall.h @@ -4,9 +4,11 @@ #include #include -#define SYS_EXIT 1 -#define SYS_READ 3 -#define SYS_WRITE 4 +#define SYS_EXIT 1 +#define SYS_READ 3 +#define SYS_WRITE 4 +#define SYS_PTY 20 +#define SYS_FORK 21 int write(int fd, const void *buf, size_t count); int read(int fd, void *buf, size_t count); @@ -19,4 +21,7 @@ void exit(int status) __attribute__((noreturn)); void *sbrk(intptr_t increment); int brk(void *addr); +int pty(int* slave); +int fork(); + #endif \ No newline at end of file diff --git a/rocklibc/src/syscall.c b/rocklibc/src/syscall.c index 8c981bb..e06c253 100644 --- a/rocklibc/src/syscall.c +++ b/rocklibc/src/syscall.c @@ -34,4 +34,12 @@ int write(int fd, const void* buf, size_t cnt) { int read(int fd, void* buf, size_t cnt) { return syscall3(SYS_READ, (uint32_t)fd, (uint32_t)buf, (uint32_t)cnt); +} + +int pty(int* slave) { + return syscall1(SYS_PTY, (uint32_t)slave); +} + +int fork() { + return syscall1(SYS_FORK, 0); } \ No newline at end of file