Add process scheduler, pipes, and separate terminal from shell
This commit is contained in:
@@ -17,6 +17,7 @@ mcopy -i build/os.img@@"$((partition_offset * sector_size))" -s src ::src
|
|||||||
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/kernel.bin ::kernel.bin
|
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/kernel.bin ::kernel.bin
|
||||||
mmd -i build/os.img@@"$((partition_offset * sector_size))" ::bin
|
mmd -i build/os.img@@"$((partition_offset * sector_size))" ::bin
|
||||||
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/terminal ::bin/terminal
|
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/terminal ::bin/terminal
|
||||||
|
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/shell ::bin/shell
|
||||||
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/echo ::bin/echo
|
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/echo ::bin/echo
|
||||||
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/ls ::bin/ls
|
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/ls ::bin/ls
|
||||||
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/cat ::bin/cat
|
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/cat ::bin/cat
|
||||||
|
|||||||
+49
-1
@@ -38,6 +38,13 @@ kernel_entry_o = custom_target(
|
|||||||
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
|
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
timer_o = custom_target(
|
||||||
|
'timer',
|
||||||
|
input: 'src/kernel/timer.asm',
|
||||||
|
output: 'timer.o',
|
||||||
|
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
|
||||||
process_o = custom_target(
|
process_o = custom_target(
|
||||||
'process',
|
'process',
|
||||||
input: 'src/kernel/process.asm',
|
input: 'src/kernel/process.asm',
|
||||||
@@ -71,15 +78,17 @@ kernel_sources = files([
|
|||||||
'src/kernel/panic.c',
|
'src/kernel/panic.c',
|
||||||
'src/kernel/path.c',
|
'src/kernel/path.c',
|
||||||
'src/kernel/pic.c',
|
'src/kernel/pic.c',
|
||||||
|
'src/kernel/pipe.c',
|
||||||
'src/kernel/process.c',
|
'src/kernel/process.c',
|
||||||
'src/kernel/syscall.c',
|
'src/kernel/syscall.c',
|
||||||
|
'src/kernel/timer.c',
|
||||||
'src/kernel/tss.c',
|
'src/kernel/tss.c',
|
||||||
'src/kernel/vga.c',
|
'src/kernel/vga.c',
|
||||||
])
|
])
|
||||||
|
|
||||||
kernel_elf = executable(
|
kernel_elf = executable(
|
||||||
'kernel.elf',
|
'kernel.elf',
|
||||||
sources: [kernel_entry_o, process_o, syscall_o, lib_sources, kernel_sources],
|
sources: [kernel_entry_o, timer_o, process_o, syscall_o, lib_sources, kernel_sources],
|
||||||
c_args: [
|
c_args: [
|
||||||
'-ffreestanding',
|
'-ffreestanding',
|
||||||
'-nostdlib',
|
'-nostdlib',
|
||||||
@@ -146,6 +155,45 @@ custom_target(
|
|||||||
build_by_default: true,
|
build_by_default: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
shell_start = custom_target(
|
||||||
|
'shell_start',
|
||||||
|
input: 'src/user/start.asm',
|
||||||
|
output: 'shell_start.o',
|
||||||
|
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
|
||||||
|
)
|
||||||
|
|
||||||
|
shell_syscall = custom_target(
|
||||||
|
'shell_syscall',
|
||||||
|
input: 'src/user/syscall.asm',
|
||||||
|
output: 'shell_syscall.o',
|
||||||
|
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
|
||||||
|
)
|
||||||
|
|
||||||
|
shell_elf = executable(
|
||||||
|
'shell.elf',
|
||||||
|
sources: [shell_start, shell_syscall, lib_sources, 'src/user/app/shell/shell.c'],
|
||||||
|
c_args: [
|
||||||
|
'-ffreestanding',
|
||||||
|
'-nostdlib',
|
||||||
|
'-fno-stack-protector',
|
||||||
|
'-mcmodel=large',
|
||||||
|
'-DVERSION="' + meson.project_version() + '"',
|
||||||
|
],
|
||||||
|
link_args: [
|
||||||
|
'-T', meson.project_source_root() / 'src/user/linker.ld',
|
||||||
|
'-nostdlib',
|
||||||
|
],
|
||||||
|
link_depends: 'src/user/linker.ld',
|
||||||
|
)
|
||||||
|
|
||||||
|
custom_target(
|
||||||
|
'shell',
|
||||||
|
input: shell_elf,
|
||||||
|
output: 'shell',
|
||||||
|
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
|
||||||
|
build_by_default: true,
|
||||||
|
)
|
||||||
|
|
||||||
echo_start = custom_target(
|
echo_start = custom_target(
|
||||||
'echo_start',
|
'echo_start',
|
||||||
input: 'src/user/start.asm',
|
input: 'src/user/start.asm',
|
||||||
|
|||||||
+36
-24
@@ -5,8 +5,10 @@
|
|||||||
#include "src/kernel/panic.h"
|
#include "src/kernel/panic.h"
|
||||||
#include "src/kernel/path.h"
|
#include "src/kernel/path.h"
|
||||||
#include "src/kernel/pic.h"
|
#include "src/kernel/pic.h"
|
||||||
|
#include "src/kernel/pipe.h"
|
||||||
#include "src/kernel/process.h"
|
#include "src/kernel/process.h"
|
||||||
#include "src/kernel/syscall.h"
|
#include "src/kernel/syscall.h"
|
||||||
|
#include "src/kernel/timer.h"
|
||||||
#include "src/kernel/tss.h"
|
#include "src/kernel/tss.h"
|
||||||
#include "src/kernel/util.h"
|
#include "src/kernel/util.h"
|
||||||
#include "src/kernel/vga.h"
|
#include "src/kernel/vga.h"
|
||||||
@@ -44,46 +46,56 @@ void kernel_main() {
|
|||||||
idt_set_entry(0x0E, isr_page_fault, 0x8E);
|
idt_set_entry(0x0E, isr_page_fault, 0x8E);
|
||||||
idt_set_entry(0x0D, isr_general_violation, 0x8E);
|
idt_set_entry(0x0D, isr_general_violation, 0x8E);
|
||||||
idt_set_entry(46, isr_ata_primary, 0x8E);
|
idt_set_entry(46, isr_ata_primary, 0x8E);
|
||||||
__asm__ volatile("sti");
|
|
||||||
|
|
||||||
gdt_init();
|
gdt_init();
|
||||||
syscall_init();
|
syscall_init();
|
||||||
|
|
||||||
process_t *kernel = memory_allocate(sizeof(process_t));
|
process_t *kernel = memory_allocate(sizeof(process_t));
|
||||||
kernel->pml4 = (void *)KERNEL_VIRTUAL_PML4;
|
kernel->pml4 = (uint64_t *)KERNEL_VIRTUAL_PML4;
|
||||||
kernel->root = fs_mount();
|
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->cwd = memory_allocate(sizeof(path_t));
|
||||||
kernel->fds[0] = keyboard_init();
|
kernel->root = fs_mount();
|
||||||
kernel->fds[1] = vga_init();
|
kernel->fds[kernel->free_fd++] = keyboard_init();
|
||||||
kernel->free_fd = 2;
|
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);
|
ASSERT(terminal_node, "kernel: terminal not found");
|
||||||
fs_read(bin, 0, bin->size, code);
|
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);
|
uint8_t *shell_code = memory_allocate(shell_node->size);
|
||||||
fs_close(bin);
|
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 *terminal_stack = (uint64_t *)terminal->user_stack;
|
||||||
*(uint64_t *)stack = 0;
|
*(--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;
|
pipe_init(&(terminal->fds[terminal->free_fd++]), &(shell->fds[0]));
|
||||||
tss.rsp0 = (uint64_t)current_process->kernel_stack;
|
|
||||||
process_switch_to(&kernel->kernel_rsp, terminal->kernel_rsp, VIRT_TO_PHYS(terminal->pml4));
|
|
||||||
|
|
||||||
fs_unmount((fs_node_t *)kernel->root);
|
timer_init();
|
||||||
|
|
||||||
outw(0x604, 0x2000); // TODO: parse ACPI tables for real hardware
|
__asm__ volatile("sti; hlt");
|
||||||
__asm__ volatile("cli; 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/process.h"
|
||||||
#include "src/kernel/memory.h"
|
#include "src/kernel/memory.h"
|
||||||
#include "src/kernel/panic.h"
|
|
||||||
#include "src/kernel/path.h"
|
#include "src/kernel/path.h"
|
||||||
|
#include "src/kernel/tss.h"
|
||||||
|
#include "src/kernel/util.h"
|
||||||
#include "src/lib/layout.h"
|
#include "src/lib/layout.h"
|
||||||
#include "src/lib/memory.h"
|
#include "src/lib/memory.h"
|
||||||
#include "src/lib/util.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 *current_process;
|
||||||
|
|
||||||
process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size) {
|
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;
|
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]);
|
cwd->stack[i] = fs_open_again(parent->cwd->stack[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proc->pid = free_pid++;
|
||||||
|
proc->state = PROCESS_RUNNING;
|
||||||
proc->cwd = cwd;
|
proc->cwd = cwd;
|
||||||
proc->root = parent->root;
|
proc->root = parent->root;
|
||||||
proc->fds[0] = parent->fds[0];
|
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;
|
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) {
|
void process_destroy(process_t *proc) {
|
||||||
for (uint64_t i = 2; i < MAX_FDS; i++) {
|
for (uint64_t i = 2; i < MAX_FDS; i++) {
|
||||||
if (proc->fds[i]) {
|
if (proc->fds[i]) {
|
||||||
@@ -117,5 +180,12 @@ void process_destroy(process_t *proc) {
|
|||||||
|
|
||||||
memory_free(proc->kernel_stack - PAGE_SIZE);
|
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);
|
memory_free(proc);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
|
|
||||||
#define MAX_FDS 16
|
#define MAX_FDS 16
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PROCESS_RUNNING,
|
||||||
|
PROCESS_WAITING,
|
||||||
|
PROCESS_ZOMBIE,
|
||||||
|
} process_state_t;
|
||||||
|
|
||||||
typedef struct process {
|
typedef struct process {
|
||||||
const struct process *parent;
|
const struct process *parent;
|
||||||
uint64_t *pml4;
|
uint64_t *pml4;
|
||||||
@@ -16,6 +22,9 @@ typedef struct process {
|
|||||||
void *kernel_rsp;
|
void *kernel_rsp;
|
||||||
void *user_stack;
|
void *user_stack;
|
||||||
void *user_rsp;
|
void *user_rsp;
|
||||||
|
uint64_t pid;
|
||||||
|
process_state_t state;
|
||||||
|
uint64_t waiting_for;
|
||||||
const fs_node_t *root;
|
const fs_node_t *root;
|
||||||
const path_t *cwd;
|
const path_t *cwd;
|
||||||
stream_t *fds[MAX_FDS];
|
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_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);
|
extern void process_switch_to(void **save_rsp, void *new_rsp, void *new_pml4_phys);
|
||||||
|
|
||||||
|
void process_next();
|
||||||
|
|
||||||
void process_destroy(process_t *proc);
|
void process_destroy(process_t *proc);
|
||||||
|
|||||||
+29
-8
@@ -94,7 +94,7 @@ static uint64_t chdir(const char *path) {
|
|||||||
return current_process->cwd->depth;
|
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);
|
fs_node_t *node = path_open_file(current_process->root, current_process->cwd, path);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return EXIT_CODE_NOT_FOUND;
|
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));
|
child->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)child->user_stack - (uint64_t)stack));
|
||||||
|
|
||||||
process_t *parent = current_process;
|
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)current_process->pml4)) : "memory");
|
||||||
current_process = child;
|
|
||||||
tss.rsp0 = (uint64_t)child->kernel_stack;
|
|
||||||
|
|
||||||
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;
|
exit_code_t code = child->code;
|
||||||
tss.rsp0 = (uint64_t)parent->kernel_stack;
|
|
||||||
current_process = parent;
|
|
||||||
process_destroy(child);
|
process_destroy(child);
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
@@ -174,8 +184,17 @@ static uint64_t close(uint64_t fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void exit(exit_code_t code) {
|
static void exit(exit_code_t code) {
|
||||||
|
current_process->state = PROCESS_ZOMBIE;
|
||||||
current_process->code = code;
|
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) {
|
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);
|
return chdir((const char *)arg1);
|
||||||
case SYSCALL_SPAWN:
|
case SYSCALL_SPAWN:
|
||||||
return spawn((const char *)arg1, arg2, (const char **)arg3);
|
return spawn((const char *)arg1, arg2, (const char **)arg3);
|
||||||
|
case SYSCALL_WAIT:
|
||||||
|
return wait(arg1);
|
||||||
case SYSCALL_OPEN:
|
case SYSCALL_OPEN:
|
||||||
return open((const char *)arg1);
|
return open((const char *)arg1);
|
||||||
case SYSCALL_CLOSE:
|
case SYSCALL_CLOSE:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#define SYSCALL_GETCWD 2
|
#define SYSCALL_GETCWD 2
|
||||||
#define SYSCALL_CHDIR 3
|
#define SYSCALL_CHDIR 3
|
||||||
#define SYSCALL_SPAWN 4
|
#define SYSCALL_SPAWN 4
|
||||||
|
#define SYSCALL_WAIT 5
|
||||||
#define SYSCALL_OPEN 6
|
#define SYSCALL_OPEN 6
|
||||||
#define SYSCALL_CLOSE 7
|
#define SYSCALL_CLOSE 7
|
||||||
#define SYSCALL_EXIT 60
|
#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();
|
||||||
+1
-1
@@ -22,7 +22,7 @@ void *memory_allocate(uint64_t size) {
|
|||||||
free_list->next = NUL;
|
free_list->next = NUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = sizeof(free_chunk_t) + (size + 7) & (uint64_t)~7;
|
size = sizeof(free_chunk_t) + ((size + 7) & (uint64_t)~7);
|
||||||
|
|
||||||
free_chunk_t *prev = NUL;
|
free_chunk_t *prev = NUL;
|
||||||
free_chunk_t *curr = free_list;
|
free_chunk_t *curr = free_list;
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
#include "src/lib/memory.h"
|
||||||
|
#include "src/lib/string.h"
|
||||||
|
#include "src/lib/util.h"
|
||||||
|
#include "src/user/syscall.h"
|
||||||
|
|
||||||
|
static void help(uint8_t argc, char **argv);
|
||||||
|
static void cd(uint8_t argc, char **argv);
|
||||||
|
|
||||||
|
#define PATH "/bin/"
|
||||||
|
|
||||||
|
#define PRINT_S(s) write(1, s, sizeof(s) - 1);
|
||||||
|
#define PRINT_D(s) write(1, s, string_length(s));
|
||||||
|
|
||||||
|
typedef void (*app_t)(uint8_t argc, char **argv);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
app_t app;
|
||||||
|
} app_entry_t;
|
||||||
|
|
||||||
|
static app_entry_t builtins[] = {
|
||||||
|
{"help", help},
|
||||||
|
{"cd", cd},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void help(uint8_t argc, __attribute__((unused)) char **argv) {
|
||||||
|
if (argc > 1) {
|
||||||
|
PRINT_S("help: expects no arguments");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRINT_S("Available commands:\n");
|
||||||
|
for (uint64_t i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) {
|
||||||
|
PRINT_D(builtins[i].name);
|
||||||
|
PRINT_S("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cd(uint8_t argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
PRINT_S("cd: requires a single path\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chdir(argv[1]) == (uint64_t)-1) {
|
||||||
|
PRINT_S("cd: path does not exist\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_prompt() {
|
||||||
|
char path[256];
|
||||||
|
uint8_t pl = getcwd(256, path);
|
||||||
|
char *components[16];
|
||||||
|
uint8_t cl = string_split(path, '/', 16, components);
|
||||||
|
|
||||||
|
PRINT_S("[")
|
||||||
|
PRINT_D(pl == 1 ? "/" : components[cl - 1]);
|
||||||
|
PRINT_S("]$ ");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void execute(char *command) {
|
||||||
|
char *argv[16];
|
||||||
|
uint8_t argc = (uint8_t)string_split(command, ' ', 16, argv);
|
||||||
|
|
||||||
|
PRINT_S("\n");
|
||||||
|
|
||||||
|
if (string_equal(command, "^C")) {
|
||||||
|
print_prompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string_equal(command, "^D")) {
|
||||||
|
exit(EXIT_CODE_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t i;
|
||||||
|
for (i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) {
|
||||||
|
if (string_equal(argv[0], builtins[i].name)) {
|
||||||
|
builtins[i].app(argc, argv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == sizeof(builtins) / sizeof(app_entry_t)) {
|
||||||
|
uint64_t size = sizeof(PATH) + string_length(argv[0]) + 1;
|
||||||
|
char *path = memory_allocate(size);
|
||||||
|
memory_copy(PATH, sizeof(PATH), path);
|
||||||
|
memory_copy(argv[0], size - sizeof(PATH), path + sizeof(PATH) - 1);
|
||||||
|
uint64_t pid = spawn(path, argc, (const char **)argv);
|
||||||
|
memory_free(path);
|
||||||
|
|
||||||
|
if (pid == EXIT_CODE_NOT_FOUND) {
|
||||||
|
PRINT_S("shell: program not found\n");
|
||||||
|
} else {
|
||||||
|
waitpid(pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t main() {
|
||||||
|
PRINT_D("Welcome to FreywarOS v" VERSION "!\n\n");
|
||||||
|
|
||||||
|
print_prompt();
|
||||||
|
|
||||||
|
char line[256];
|
||||||
|
uint64_t offset;
|
||||||
|
|
||||||
|
char chunk[64];
|
||||||
|
uint64_t received;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if ((received = read(0, sizeof(chunk), chunk))) {
|
||||||
|
for (uint64_t i = 0; i < received; i++) {
|
||||||
|
if (chunk[i] == '\n') {
|
||||||
|
line[offset] = '\0';
|
||||||
|
execute(line);
|
||||||
|
offset = 0;
|
||||||
|
} else if (offset < 255) {
|
||||||
|
line[offset++] = chunk[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,13 +1,6 @@
|
|||||||
#include "src/lib/memory.h"
|
#include "src/lib/memory.h"
|
||||||
#include "src/lib/string.h"
|
|
||||||
#include "src/lib/util.h"
|
#include "src/lib/util.h"
|
||||||
#include "src/user/syscall.h"
|
#include "src/user/syscall.h"
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
static void help(uint8_t argc, char **argv);
|
|
||||||
static void cd(uint8_t argc, char **argv);
|
|
||||||
|
|
||||||
#define PATH "/bin/"
|
|
||||||
|
|
||||||
#define PRINT_S(s) write(1, s, sizeof(s) - 1);
|
#define PRINT_S(s) write(1, s, sizeof(s) - 1);
|
||||||
#define PRINT_D(s) write(1, s, string_length(s));
|
#define PRINT_D(s) write(1, s, string_length(s));
|
||||||
@@ -21,53 +14,6 @@ static char prompt[PROMPT_LENGTH + 1];
|
|||||||
static uint8_t prompt_length = 0;
|
static uint8_t prompt_length = 0;
|
||||||
static uint8_t prompt_offset = 0;
|
static uint8_t prompt_offset = 0;
|
||||||
|
|
||||||
typedef void (*app_t)(uint8_t argc, char **argv);
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char *name;
|
|
||||||
app_t app;
|
|
||||||
} app_entry_t;
|
|
||||||
|
|
||||||
static app_entry_t builtins[] = {
|
|
||||||
{"help", help},
|
|
||||||
{"cd", cd},
|
|
||||||
};
|
|
||||||
|
|
||||||
static void help(uint8_t argc, __attribute__((unused)) char **argv) {
|
|
||||||
if (argc) {
|
|
||||||
PRINT_S("help: expects no arguments");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PRINT_S("Available commands:\n");
|
|
||||||
for (uint64_t i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) {
|
|
||||||
PRINT_D(builtins[i].name);
|
|
||||||
PRINT_S("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void cd(uint8_t argc, char **argv) {
|
|
||||||
if (argc != 2) {
|
|
||||||
PRINT_S("cd: requires a single path\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chdir(argv[1]) == (uint64_t)-1) {
|
|
||||||
PRINT_S("cd: path does not exist\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void print_prompt() {
|
|
||||||
char path[PROMPT_LENGTH];
|
|
||||||
uint8_t pl = getcwd(PROMPT_LENGTH, path);
|
|
||||||
char *components[16];
|
|
||||||
uint8_t cl = string_split(path, '/', 16, components);
|
|
||||||
|
|
||||||
PRINT_S("[")
|
|
||||||
PRINT_D(pl == 1 ? "/" : components[cl - 1]);
|
|
||||||
PRINT_S("]$ ");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void on_home_pressed() {
|
static void on_home_pressed() {
|
||||||
if (!prompt_offset) {
|
if (!prompt_offset) {
|
||||||
return;
|
return;
|
||||||
@@ -105,42 +51,22 @@ static void on_end_pressed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void on_enter_pressed() {
|
static void on_enter_pressed() {
|
||||||
prompt[prompt_length] = '\0';
|
prompt[prompt_length] = '\n';
|
||||||
|
|
||||||
char *argv[16];
|
write(2, prompt, prompt_length + 1);
|
||||||
uint8_t argc = (uint8_t)string_split(prompt, ' ', 16, argv);
|
|
||||||
|
|
||||||
PRINT_S("\n");
|
|
||||||
|
|
||||||
uint8_t i;
|
|
||||||
for (i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) {
|
|
||||||
if (string_equal(argv[0], builtins[i].name)) {
|
|
||||||
builtins[i].app(argc, argv);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == sizeof(builtins) / sizeof(app_entry_t)) {
|
|
||||||
uint64_t size = sizeof(PATH) + string_length(argv[0]) + 1;
|
|
||||||
char *path = memory_allocate(size);
|
|
||||||
memory_copy(PATH, sizeof(PATH), path);
|
|
||||||
memory_copy(argv[0], size - sizeof(PATH), path + sizeof(PATH) - 1);
|
|
||||||
if (spawn(path, argc, (const char **)argv) == EXIT_CODE_NOT_FOUND) {
|
|
||||||
PRINT_S("terminal: program not found\n");
|
|
||||||
}
|
|
||||||
memory_free(path);
|
|
||||||
}
|
|
||||||
prompt_length = prompt_offset = 0;
|
prompt_length = prompt_offset = 0;
|
||||||
print_prompt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_cancel_pressed() {
|
static void on_cancel_pressed() {
|
||||||
PRINT_S("^C\n");
|
PRINT_S("^C\n");
|
||||||
|
write(2, "^C\n", 3);
|
||||||
prompt_length = prompt_offset = 0;
|
prompt_length = prompt_offset = 0;
|
||||||
print_prompt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_disconnect_pressed() {
|
static void on_disconnect_pressed() {
|
||||||
PRINT_S("^D\n");
|
PRINT_S("^D\n");
|
||||||
|
write(2, "^D\n", 3);
|
||||||
exit(EXIT_CODE_OK);
|
exit(EXIT_CODE_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,10 +196,6 @@ uint64_t main() {
|
|||||||
memory_set('\b', PROMPT_LENGTH, (char *)bs);
|
memory_set('\b', PROMPT_LENGTH, (char *)bs);
|
||||||
memory_set(' ', PROMPT_LENGTH, (char *)ws);
|
memory_set(' ', PROMPT_LENGTH, (char *)ws);
|
||||||
|
|
||||||
PRINT_D("Welcome to FreywarOS v" VERSION "!\n\n");
|
|
||||||
|
|
||||||
print_prompt();
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
char c;
|
char c;
|
||||||
if (read(0, 1, &c)) {
|
if (read(0, 1, &c)) {
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ spawn:
|
|||||||
syscall
|
syscall
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
global waitpid
|
||||||
|
waitpid:
|
||||||
|
mov rax, 5
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
|
||||||
global open
|
global open
|
||||||
open:
|
open:
|
||||||
mov rax, 6
|
mov rax, 6
|
||||||
|
|||||||
+3
-1
@@ -11,7 +11,9 @@ uint64_t getcwd(uint64_t max, void *to);
|
|||||||
|
|
||||||
uint64_t chdir(const char *path);
|
uint64_t chdir(const char *path);
|
||||||
|
|
||||||
exit_code_t spawn(const char *path, uint64_t argc, const char **argv);
|
uint64_t spawn(const char *path, uint64_t argc, const char **argv);
|
||||||
|
|
||||||
|
exit_code_t waitpid(uint64_t pid);
|
||||||
|
|
||||||
uint64_t open(const char *path);
|
uint64_t open(const char *path);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user