Add process scheduler, pipes, and separate terminal from shell
This commit is contained in:
+36
-24
@@ -5,8 +5,10 @@
|
||||
#include "src/kernel/panic.h"
|
||||
#include "src/kernel/path.h"
|
||||
#include "src/kernel/pic.h"
|
||||
#include "src/kernel/pipe.h"
|
||||
#include "src/kernel/process.h"
|
||||
#include "src/kernel/syscall.h"
|
||||
#include "src/kernel/timer.h"
|
||||
#include "src/kernel/tss.h"
|
||||
#include "src/kernel/util.h"
|
||||
#include "src/kernel/vga.h"
|
||||
@@ -44,46 +46,56 @@ void kernel_main() {
|
||||
idt_set_entry(0x0E, isr_page_fault, 0x8E);
|
||||
idt_set_entry(0x0D, isr_general_violation, 0x8E);
|
||||
idt_set_entry(46, isr_ata_primary, 0x8E);
|
||||
__asm__ volatile("sti");
|
||||
|
||||
gdt_init();
|
||||
syscall_init();
|
||||
|
||||
process_t *kernel = memory_allocate(sizeof(process_t));
|
||||
kernel->pml4 = (void *)KERNEL_VIRTUAL_PML4;
|
||||
kernel->root = fs_mount();
|
||||
kernel->pml4 = (uint64_t *)KERNEL_VIRTUAL_PML4;
|
||||
kernel->pid = 0;
|
||||
kernel->state = PROCESS_RUNNING;
|
||||
kernel->kernel_stack = kernel->kernel_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
|
||||
kernel->user_stack = kernel->user_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
|
||||
kernel->cwd = memory_allocate(sizeof(path_t));
|
||||
kernel->fds[0] = keyboard_init();
|
||||
kernel->fds[1] = vga_init();
|
||||
kernel->free_fd = 2;
|
||||
kernel->root = fs_mount();
|
||||
kernel->fds[kernel->free_fd++] = keyboard_init();
|
||||
kernel->fds[kernel->free_fd++] = vga_init();
|
||||
kernel->code = EXIT_CODE_OK;
|
||||
|
||||
fs_node_t *bin = path_open_file(kernel->root, kernel->cwd, "bin/terminal");
|
||||
current_process = kernel;
|
||||
tss.rsp0 = (uint64_t)kernel->kernel_stack;
|
||||
|
||||
ASSERT(bin, "kernel: terminal not found");
|
||||
fs_node_t *terminal_node = path_open_file(kernel->root, kernel->cwd, "bin/terminal");
|
||||
fs_node_t *shell_node = path_open_file(kernel->root, kernel->cwd, "bin/shell");
|
||||
|
||||
uint8_t *code = memory_allocate(bin->size);
|
||||
fs_read(bin, 0, bin->size, code);
|
||||
ASSERT(terminal_node, "kernel: terminal not found");
|
||||
ASSERT(shell_node, "kernel: shell not found");
|
||||
|
||||
process_t *terminal = process_create(kernel, code, bin->size);
|
||||
uint8_t *terminal_code = memory_allocate(terminal_node->size);
|
||||
fs_read(terminal_node, 0, terminal_node->size, terminal_code);
|
||||
fs_close(terminal_node);
|
||||
|
||||
memory_free(code);
|
||||
fs_close(bin);
|
||||
uint8_t *shell_code = memory_allocate(shell_node->size);
|
||||
fs_read(shell_node, 0, shell_node->size, shell_code);
|
||||
fs_close(shell_node);
|
||||
|
||||
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)KERNEL_VIRTUAL_PML4)) : "memory");
|
||||
process_t *terminal = process_create(kernel, terminal_code, terminal_node->size);
|
||||
memory_free(terminal_code);
|
||||
|
||||
uint8_t *stack = (uint8_t *)terminal->user_stack;
|
||||
process_t *shell = process_create(kernel, shell_code, shell_node->size);
|
||||
memory_free(shell_code);
|
||||
|
||||
stack -= sizeof(uint64_t);
|
||||
*(uint64_t *)stack = 0;
|
||||
uint64_t *terminal_stack = (uint64_t *)terminal->user_stack;
|
||||
*(--terminal_stack) = 0;
|
||||
terminal->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)terminal->user_stack - (uint64_t)terminal_stack));
|
||||
|
||||
terminal->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)terminal->user_stack - (uint64_t)stack));
|
||||
uint64_t *shell_stack = (uint64_t *)shell->user_stack;
|
||||
*(--shell_stack) = 0;
|
||||
shell->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)shell->user_stack - (uint64_t)shell_stack));
|
||||
|
||||
current_process = terminal;
|
||||
tss.rsp0 = (uint64_t)current_process->kernel_stack;
|
||||
process_switch_to(&kernel->kernel_rsp, terminal->kernel_rsp, VIRT_TO_PHYS(terminal->pml4));
|
||||
pipe_init(&(terminal->fds[terminal->free_fd++]), &(shell->fds[0]));
|
||||
|
||||
fs_unmount((fs_node_t *)kernel->root);
|
||||
timer_init();
|
||||
|
||||
outw(0x604, 0x2000); // TODO: parse ACPI tables for real hardware
|
||||
__asm__ volatile("cli; hlt");
|
||||
__asm__ volatile("sti; hlt");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
#include "src/kernel/pipe.h"
|
||||
#include "src/kernel/stream.h"
|
||||
#include "src/lib/memory.h"
|
||||
|
||||
#define PIPE_BUFFER_SIZE 65536
|
||||
|
||||
typedef struct {
|
||||
char buffer[PIPE_BUFFER_SIZE];
|
||||
uint64_t begin;
|
||||
uint64_t end;
|
||||
uint8_t write_closed;
|
||||
uint8_t read_closed;
|
||||
} pipe_t;
|
||||
|
||||
typedef struct {
|
||||
stream_t stream;
|
||||
pipe_t *pipe;
|
||||
} pipe_read_stream_t;
|
||||
|
||||
typedef struct {
|
||||
stream_t stream;
|
||||
pipe_t *pipe;
|
||||
} pipe_write_stream_t;
|
||||
|
||||
static uint64_t null_read(__attribute__((unused)) const stream_t *self, __attribute__((unused)) uint64_t max,
|
||||
__attribute__((unused)) char *to) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t null_write(__attribute__((unused)) const stream_t *self, __attribute__((unused)) const char *from,
|
||||
__attribute__((unused)) uint64_t bytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t pipe_read(const stream_t *self, uint64_t max, char *to) {
|
||||
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe;
|
||||
|
||||
uint64_t available = (pipe->end + PIPE_BUFFER_SIZE - pipe->begin) % PIPE_BUFFER_SIZE;
|
||||
uint64_t to_read = available < max ? available : max;
|
||||
|
||||
if (pipe->write_closed && !to_read) {
|
||||
return (uint64_t)-1;
|
||||
}
|
||||
|
||||
if (!to_read) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pipe->begin + to_read <= PIPE_BUFFER_SIZE) {
|
||||
memory_copy(pipe->buffer + pipe->begin, to_read, to);
|
||||
} else {
|
||||
uint64_t chunk = PIPE_BUFFER_SIZE - pipe->begin;
|
||||
memory_copy(pipe->buffer + pipe->begin, chunk, to);
|
||||
memory_copy(pipe->buffer, to_read - chunk, to + chunk);
|
||||
}
|
||||
|
||||
pipe->begin = (pipe->begin + to_read) % PIPE_BUFFER_SIZE;
|
||||
return to_read;
|
||||
}
|
||||
|
||||
static uint64_t pipe_write(const stream_t *self, const char *from, uint64_t bytes) {
|
||||
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe;
|
||||
|
||||
if (pipe->read_closed) {
|
||||
return (uint64_t)-1;
|
||||
}
|
||||
|
||||
uint64_t available = PIPE_BUFFER_SIZE - (pipe->end + PIPE_BUFFER_SIZE - pipe->begin) % PIPE_BUFFER_SIZE;
|
||||
uint64_t to_write = available < bytes ? available : bytes;
|
||||
|
||||
if (to_write == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pipe->end + to_write <= PIPE_BUFFER_SIZE) {
|
||||
memory_copy(from, to_write, pipe->buffer + pipe->end);
|
||||
} else {
|
||||
uint64_t chunk = PIPE_BUFFER_SIZE - pipe->end;
|
||||
memory_copy(from, chunk, pipe->buffer + pipe->end);
|
||||
memory_copy(from + chunk, to_write - chunk, pipe->buffer);
|
||||
}
|
||||
|
||||
pipe->end = (pipe->end + to_write) % PIPE_BUFFER_SIZE;
|
||||
return to_write;
|
||||
}
|
||||
|
||||
static void read_close(stream_t *self) {
|
||||
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe;
|
||||
|
||||
pipe->read_closed = 1;
|
||||
|
||||
if (pipe->read_closed && pipe->write_closed) {
|
||||
memory_free(pipe);
|
||||
}
|
||||
memory_free(self);
|
||||
}
|
||||
|
||||
static void write_close(stream_t *self) {
|
||||
pipe_t *pipe = ((pipe_write_stream_t *)self)->pipe;
|
||||
|
||||
pipe->write_closed = 1;
|
||||
|
||||
if (pipe->read_closed && pipe->write_closed) {
|
||||
memory_free(pipe);
|
||||
}
|
||||
memory_free(self);
|
||||
}
|
||||
|
||||
void pipe_init(stream_t **write, stream_t **read) {
|
||||
pipe_t *pipe = memory_allocate(sizeof(pipe_t));
|
||||
|
||||
pipe_write_stream_t *ws = memory_allocate(sizeof(pipe_write_stream_t));
|
||||
ws->stream.read = null_read;
|
||||
ws->stream.write = pipe_write;
|
||||
ws->stream.close = write_close;
|
||||
ws->pipe = pipe;
|
||||
|
||||
pipe_read_stream_t *rs = memory_allocate(sizeof(pipe_read_stream_t));
|
||||
rs->stream.read = pipe_read;
|
||||
rs->stream.write = null_write;
|
||||
rs->stream.close = read_close;
|
||||
rs->pipe = pipe;
|
||||
|
||||
*write = (stream_t *)ws;
|
||||
*read = (stream_t *)rs;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/kernel/stream.h"
|
||||
|
||||
void pipe_init(stream_t **write, stream_t **read);
|
||||
+73
-3
@@ -1,17 +1,35 @@
|
||||
#include "src/kernel/process.h"
|
||||
#include "src/kernel/memory.h"
|
||||
#include "src/kernel/panic.h"
|
||||
#include "src/kernel/path.h"
|
||||
#include "src/kernel/tss.h"
|
||||
#include "src/kernel/util.h"
|
||||
#include "src/lib/layout.h"
|
||||
#include "src/lib/memory.h"
|
||||
#include "src/lib/util.h"
|
||||
|
||||
#define MAX_PROCESSES 256
|
||||
|
||||
static process_t *processes[MAX_PROCESSES];
|
||||
static uint64_t free_pid = 1;
|
||||
|
||||
process_t *current_process;
|
||||
|
||||
process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size) {
|
||||
ASSERT(size <= USER_VIRTUAL_HEAP - USER_VIRTUAL_CODE, "process_create: binary too big");
|
||||
if (size >= USER_VIRTUAL_HEAP - USER_VIRTUAL_CODE) {
|
||||
return NUL;
|
||||
}
|
||||
|
||||
process_t *proc = memory_allocate(sizeof(process_t));
|
||||
uint64_t free_pi;
|
||||
for (free_pi = 0; free_pi <= MAX_PROCESSES; free_pi++) {
|
||||
if (!processes[free_pi]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (free_pi == MAX_PROCESSES) {
|
||||
return NUL;
|
||||
}
|
||||
|
||||
process_t *proc = processes[free_pi] = memory_allocate(sizeof(process_t));
|
||||
|
||||
proc->parent = parent;
|
||||
|
||||
@@ -56,6 +74,8 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t
|
||||
cwd->stack[i] = fs_open_again(parent->cwd->stack[i]);
|
||||
}
|
||||
|
||||
proc->pid = free_pid++;
|
||||
proc->state = PROCESS_RUNNING;
|
||||
proc->cwd = cwd;
|
||||
proc->root = parent->root;
|
||||
proc->fds[0] = parent->fds[0];
|
||||
@@ -66,6 +86,49 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t
|
||||
return proc;
|
||||
}
|
||||
|
||||
process_t *process_get(uint64_t pid) {
|
||||
for (uint64_t i = 0; i < MAX_PROCESSES; i++) {
|
||||
if (processes[i] && processes[i]->pid == pid) {
|
||||
return processes[i];
|
||||
}
|
||||
}
|
||||
return NUL;
|
||||
}
|
||||
|
||||
void process_next() {
|
||||
process_t *next = NUL;
|
||||
uint64_t cpi = 0;
|
||||
|
||||
for (uint64_t i = 0; i < MAX_PROCESSES; i++) {
|
||||
if (processes[i] == current_process) {
|
||||
cpi = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint64_t i = 1; i <= MAX_PROCESSES; i++) {
|
||||
process_t *candidate = processes[(cpi + i) % MAX_PROCESSES];
|
||||
if (candidate && candidate->state == PROCESS_RUNNING) {
|
||||
next = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!next) {
|
||||
outw(0x604, 0x2000);
|
||||
__asm__ volatile("cli; hlt");
|
||||
}
|
||||
|
||||
if (next == current_process) {
|
||||
return;
|
||||
}
|
||||
|
||||
process_t *prev = current_process;
|
||||
tss.rsp0 = (uint64_t)next->kernel_stack;
|
||||
current_process = next;
|
||||
process_switch_to(&prev->kernel_rsp, next->kernel_rsp, VIRT_TO_PHYS(next->pml4));
|
||||
}
|
||||
|
||||
void process_destroy(process_t *proc) {
|
||||
for (uint64_t i = 2; i < MAX_FDS; i++) {
|
||||
if (proc->fds[i]) {
|
||||
@@ -117,5 +180,12 @@ void process_destroy(process_t *proc) {
|
||||
|
||||
memory_free(proc->kernel_stack - PAGE_SIZE);
|
||||
|
||||
for (uint64_t i = 0; i < MAX_PROCESSES; i++) {
|
||||
if (processes[i] == proc) {
|
||||
processes[i] = NUL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memory_free(proc);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
|
||||
#define MAX_FDS 16
|
||||
|
||||
typedef enum {
|
||||
PROCESS_RUNNING,
|
||||
PROCESS_WAITING,
|
||||
PROCESS_ZOMBIE,
|
||||
} process_state_t;
|
||||
|
||||
typedef struct process {
|
||||
const struct process *parent;
|
||||
uint64_t *pml4;
|
||||
@@ -16,6 +22,9 @@ typedef struct process {
|
||||
void *kernel_rsp;
|
||||
void *user_stack;
|
||||
void *user_rsp;
|
||||
uint64_t pid;
|
||||
process_state_t state;
|
||||
uint64_t waiting_for;
|
||||
const fs_node_t *root;
|
||||
const path_t *cwd;
|
||||
stream_t *fds[MAX_FDS];
|
||||
@@ -31,6 +40,10 @@ extern void process_trampoline();
|
||||
|
||||
process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size);
|
||||
|
||||
process_t *process_get(uint64_t pid);
|
||||
|
||||
extern void process_switch_to(void **save_rsp, void *new_rsp, void *new_pml4_phys);
|
||||
|
||||
void process_next();
|
||||
|
||||
void process_destroy(process_t *proc);
|
||||
|
||||
+29
-8
@@ -94,7 +94,7 @@ static uint64_t chdir(const char *path) {
|
||||
return current_process->cwd->depth;
|
||||
}
|
||||
|
||||
static exit_code_t spawn(const char *path, uint64_t argc, const char **argv) {
|
||||
static uint64_t spawn(const char *path, uint64_t argc, const char **argv) {
|
||||
fs_node_t *node = path_open_file(current_process->root, current_process->cwd, path);
|
||||
if (!node) {
|
||||
return EXIT_CODE_NOT_FOUND;
|
||||
@@ -138,15 +138,25 @@ static exit_code_t spawn(const char *path, uint64_t argc, const char **argv) {
|
||||
|
||||
child->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)child->user_stack - (uint64_t)stack));
|
||||
|
||||
process_t *parent = current_process;
|
||||
current_process = child;
|
||||
tss.rsp0 = (uint64_t)child->kernel_stack;
|
||||
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)current_process->pml4)) : "memory");
|
||||
|
||||
process_switch_to(&parent->kernel_rsp, child->kernel_rsp, VIRT_TO_PHYS(child->pml4));
|
||||
return child->pid;
|
||||
}
|
||||
|
||||
static exit_code_t wait(uint64_t pid) {
|
||||
process_t *child = process_get(pid);
|
||||
if (!child) {
|
||||
return EXIT_CODE_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
current_process->state = PROCESS_WAITING;
|
||||
current_process->waiting_for = pid;
|
||||
while (child->state != PROCESS_ZOMBIE) {
|
||||
process_next();
|
||||
}
|
||||
current_process->state = PROCESS_RUNNING;
|
||||
|
||||
exit_code_t code = child->code;
|
||||
tss.rsp0 = (uint64_t)parent->kernel_stack;
|
||||
current_process = parent;
|
||||
process_destroy(child);
|
||||
|
||||
return code;
|
||||
@@ -174,8 +184,17 @@ static uint64_t close(uint64_t fd) {
|
||||
}
|
||||
|
||||
static void exit(exit_code_t code) {
|
||||
current_process->state = PROCESS_ZOMBIE;
|
||||
current_process->code = code;
|
||||
process_switch_to(¤t_process->kernel_rsp, current_process->parent->kernel_rsp, VIRT_TO_PHYS(current_process->parent->pml4));
|
||||
|
||||
if (current_process->parent) {
|
||||
process_t *parent = (process_t *)current_process->parent;
|
||||
if (parent->state == PROCESS_WAITING && parent->waiting_for == current_process->pid) {
|
||||
parent->state = PROCESS_RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
process_next();
|
||||
}
|
||||
|
||||
uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3, __attribute__((unused)) uint64_t arg4) {
|
||||
@@ -190,6 +209,8 @@ uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t
|
||||
return chdir((const char *)arg1);
|
||||
case SYSCALL_SPAWN:
|
||||
return spawn((const char *)arg1, arg2, (const char **)arg3);
|
||||
case SYSCALL_WAIT:
|
||||
return wait(arg1);
|
||||
case SYSCALL_OPEN:
|
||||
return open((const char *)arg1);
|
||||
case SYSCALL_CLOSE:
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define SYSCALL_GETCWD 2
|
||||
#define SYSCALL_CHDIR 3
|
||||
#define SYSCALL_SPAWN 4
|
||||
#define SYSCALL_WAIT 5
|
||||
#define SYSCALL_OPEN 6
|
||||
#define SYSCALL_CLOSE 7
|
||||
#define SYSCALL_EXIT 60
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
bits 64
|
||||
extern timer_handler
|
||||
|
||||
global timer_entry
|
||||
timer_entry:
|
||||
push rax
|
||||
push rcx
|
||||
push rdx
|
||||
push rsi
|
||||
push rdi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
|
||||
call timer_handler
|
||||
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rax
|
||||
|
||||
iretq
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "src/kernel/timer.h"
|
||||
#include "src/kernel/idt.h"
|
||||
#include "src/kernel/process.h"
|
||||
#include "src/kernel/util.h"
|
||||
|
||||
extern void timer_entry();
|
||||
|
||||
void timer_init() {
|
||||
// PIT channel 0, rate generator, ~100Hz
|
||||
outb(0x43, 0x36);
|
||||
outb(0x40, 0xA9); // divisor low byte (11932 for ~100Hz)
|
||||
outb(0x40, 0x2E); // divisor high byte
|
||||
outb(0x21, inb(0x21) & ~0x01); // unmask IRQ0
|
||||
idt_set_entry(32, timer_entry, 0x8E);
|
||||
}
|
||||
|
||||
void timer_handler() {
|
||||
outb(0x20, 0x20);
|
||||
process_next();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void timer_init();
|
||||
Reference in New Issue
Block a user