Add first C user program

This commit is contained in:
2026-06-11 20:19:24 +03:00
parent b32c12b311
commit 1c935cf154
55 changed files with 365 additions and 276 deletions
+30
View File
@@ -0,0 +1,30 @@
#include "src/kernel/app/cat.h"
#include "src/kernel/fs.h"
#include "src/kernel/path.h"
#include "src/lib/memory.h"
void cat(process_t *proc, uint8_t argc, char **argv) {
if (argc != 2) {
WRITE_S("cat: requires a single path\n");
return;
}
path_t *path = path_open(proc->root, proc->cwd, argv[1]);
if (!path) {
WRITE_S("cat: invalid path\n");
return;
}
fs_node_t *curr = path->depth ? path->stack[path->depth - 1] : proc->root;
if (curr->is_dir) {
WRITE_S("cat: can not print a directory\n");
path_close(path);
return;
}
char *content = memory_allocate(curr->size + 1);
fs_read(curr, 0, curr->size, content);
proc->stdout->write(proc->stdout, content, curr->size);
memory_free(content);
path_close(path);
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "src/kernel/process.h"
void cat(process_t *proc, uint8_t argc, char **argv);
+34
View File
@@ -0,0 +1,34 @@
#include "src/kernel/app/ls.h"
#include "src/kernel/fs.h"
#include "src/kernel/path.h"
#include <stdint.h>
void ls(process_t *proc, uint8_t argc, char **argv) {
if (argc > 2) {
WRITE_S("ls: requires a single path\n");
return;
}
path_t *path = path_open(proc->root, proc->cwd, argc == 1 ? "." : argv[1]);
if (!path) {
WRITE_S("ls: invalid path\n");
return;
}
fs_node_t *curr = path->depth ? path->stack[path->depth - 1] : proc->root;
if (!curr->is_dir) {
WRITE_S("ls: can not list a file\n");
path_close(path);
return;
}
uint8_t j = 0;
fs_node_t *item = fs_open_at(curr, j++);
while (item) {
WRITE_D(item->name);
WRITE_S("\n");
fs_close(item);
item = fs_open_at(curr, j++);
}
path_close(path);
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "src/kernel/process.h"
void ls(process_t *proc, uint8_t argc, char **argv);
+283
View File
@@ -0,0 +1,283 @@
#include "src/kernel/app/terminal.h"
#include "src/kernel/app/cat.h"
#include "src/kernel/app/ls.h"
#include "src/kernel/fs.h"
#include "src/kernel/path.h"
#include "src/kernel/process.h"
#include "src/lib/memory.h"
#include "src/lib/string.h"
#include "src/lib/util.h"
static void help(process_t *proc, uint8_t argc, char **argv);
static void cd(process_t *proc, uint8_t argc, char **argv);
#define PROMPT_LENGTH 255
static const char bs[PROMPT_LENGTH + 1];
static const char ws[PROMPT_LENGTH + 1];
static char prompt[PROMPT_LENGTH + 1];
static uint8_t prompt_length = 0;
static uint8_t prompt_offset = 0;
process_t *proc;
static uint8_t active = 1;
typedef struct {
const char *name;
app_t app;
} app_entry_t;
static app_entry_t apps[] = {
{"help", help},
{"cd", cd},
{"ls", ls},
{"cat", cat},
};
static void help(process_t *proc, uint8_t argc, __attribute__((unused)) char **argv) {
if (argc > 1) {
WRITE_S("help: accepts no arguments\n");
}
WRITE_S("Available commands:\n");
for (uint64_t i = 0; i < sizeof(apps) / sizeof(app_entry_t); i++) {
WRITE_D(apps[i].name);
WRITE_S("\n");
}
}
static void cd(process_t *proc, uint8_t argc, char **argv) {
if (argc != 2) {
WRITE_S("cd: requires a single path\n");
return;
}
path_t *new = path_open(proc->root, proc->cwd, argv[1]);
if (!new) {
WRITE_S("cd: path does not exist\n");
return;
}
path_close(proc->cwd);
proc->cwd = new;
}
static void print_prompt() {
WRITE_S("[");
fs_node_t *curr = proc->cwd->depth ? proc->cwd->stack[proc->cwd->depth - 1] : proc->root;
WRITE_D(curr->name);
WRITE_S("]$ ");
}
static void on_home_pressed() {
if (!prompt_offset) {
return;
}
proc->stdout->write(proc->stdout, bs, prompt_offset);
prompt_offset = 0;
}
static void on_left_pressed() {
if (!prompt_offset) {
return;
}
proc->stdout->write(proc->stdout, bs, 1);
prompt_offset--;
}
static void on_right_pressed() {
if (prompt_offset == prompt_length) {
return;
}
proc->stdout->write(proc->stdout, prompt + prompt_offset, 1);
prompt_offset++;
}
static void on_end_pressed() {
if (prompt_offset == prompt_length) {
return;
}
proc->stdout->write(proc->stdout, prompt + prompt_offset, prompt_length - prompt_offset);
prompt_offset = prompt_length;
}
static void on_enter_pressed() {
prompt[prompt_length] = '\0';
char *argv[16];
uint8_t argc = (uint8_t)string_split(prompt, ' ', 16, argv);
WRITE_S("\n");
uint8_t i;
for (i = 0; i < sizeof(apps) / sizeof(app_entry_t); i++) {
if (string_equal(argv[0], apps[i].name)) {
apps[i].app(proc, argc, argv);
break;
}
}
if (i == sizeof(apps) / sizeof(app_entry_t)) {
WRITE_S("Unknown command\n");
}
prompt_length = prompt_offset = 0;
print_prompt();
}
static void on_cancel_pressed() {
WRITE_S("^C\n");
prompt_length = prompt_offset = 0;
print_prompt();
}
static void on_disconnect_pressed() {
WRITE_S("^D\n");
active = 0;
}
static void on_ctrl_character_pressed(char c) {
if (c == 'c') {
on_cancel_pressed();
} else if (c == 'd') {
on_disconnect_pressed();
}
}
static void redraw_from_cursor() {
uint64_t tail = prompt_length - prompt_offset;
proc->stdout->write(proc->stdout, prompt + prompt_offset, tail);
proc->stdout->write(proc->stdout, ws, 1); // erase the character past the end
proc->stdout->write(proc->stdout, bs, tail + 1); // move back to cursor position
}
static void on_character_pressed(char c) {
if (prompt_offset >= PROMPT_LENGTH)
return;
if (prompt_offset != prompt_length) {
memory_move(prompt + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset + 1);
}
prompt[prompt_offset] = c;
prompt_length++;
prompt_offset++;
proc->stdout->write(proc->stdout, &c, 1); // emit the character itself
redraw_from_cursor();
}
static void on_backspace_pressed() {
if (!prompt_offset)
return;
if (prompt_offset != prompt_length) {
memory_move(prompt + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset - 1);
}
prompt_offset--;
prompt_length--;
proc->stdout->write(proc->stdout, bs, 1);
redraw_from_cursor();
}
static void on_delete_pressed() {
if (prompt_offset == prompt_length)
return;
memory_move(prompt + prompt_offset + 1, prompt_length - prompt_offset - 1, prompt + prompt_offset);
prompt_length--;
redraw_from_cursor();
}
typedef enum {
PARSE_NORMAL,
PARSE_ESC,
PARSE_CSI,
} parse_state_t;
static parse_state_t parser_state = PARSE_NORMAL;
static char parser_csi_param[8];
static uint8_t parser_csi_len;
static void on_char_received(char c) {
switch (parser_state) {
case PARSE_NORMAL:
if (c == '\x1B') {
parser_state = PARSE_ESC;
} else if (c == '\b' || c == '\x7F') {
on_backspace_pressed();
} else if (c == '\n' || c == '\r') {
on_enter_pressed();
} else if (c >= '\x01' && c <= '\x1A') {
on_ctrl_character_pressed(c + 'a' - 1);
} else if (c >= ' ') {
on_character_pressed(c);
}
break;
case PARSE_ESC:
if (c == '[') {
parser_state = PARSE_CSI;
parser_csi_len = 0;
} else {
parser_state = PARSE_NORMAL;
}
break;
case PARSE_CSI:
if ((c >= '0' && c <= '9') || c == ';') {
if (parser_csi_len < sizeof(parser_csi_param) - 1) {
parser_csi_param[parser_csi_len++] = c;
}
} else {
parser_state = PARSE_NORMAL;
parser_csi_param[parser_csi_len] = '\0';
switch (c) {
case 'C':
on_right_pressed();
break;
case 'D':
on_left_pressed();
break;
case 'H':
on_home_pressed();
break;
case 'F':
on_end_pressed();
break;
case '~':
if (parser_csi_param[0] == '3') {
on_delete_pressed();
}
break;
}
}
break;
}
}
void terminal(process_t *p, __attribute__((unused)) uint8_t argc, __attribute__((unused)) char **argv) {
memory_set('\b', PROMPT_LENGTH, (char *)bs);
memory_set(' ', PROMPT_LENGTH, (char *)ws);
proc = p;
WRITE_D("Welcome to FreywarOS v" VERSION "!\n\n");
print_prompt();
while (active) {
char c;
if (proc->stdin->read(proc->stdin, 1, &c)) {
on_char_received(c);
}
}
proc = NUL;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "src/kernel/process.h"
void terminal(process_t *proc, uint8_t argc, char **argv);
+70
View File
@@ -0,0 +1,70 @@
#include "src/kernel/ata.h"
#include "src/kernel/panic.h"
#include "src/kernel/util.h"
#define BSY 0b10000000
#define DF 0b00100000
#define DRQ 0b00001000
#define ERR 0b00000001
#define READ_SECTORS 0x20
#define WRITE_SECTORS 0x30
#define FLUSH_CACHE 0xE7
#define SECTOR_WORDS 256
void ata_read_sectors(uint32_t index, uint8_t count, void *to) {
while (inb(0x1F7) & BSY) {
}
outb(0x1F2, count);
outb(0x1F3, index & 0xFF);
index = index >> 8;
outb(0x1F4, index & 0xFF);
index = index >> 8;
outb(0x1F5, index & 0xFF);
index = index >> 8;
outb(0x1F6, 0b11100000 | (index & 0b00001111));
outb(0x1F7, READ_SECTORS);
while (count--) {
while (!(inb(0x1F7) & DRQ)) {
}
insw(0x1F0, to, SECTOR_WORDS);
to = (uint16_t *)to + SECTOR_WORDS;
}
ASSERT(!(inb(0x1F7) & (ERR | DF)), "ata_read_sectors: read error");
}
void ata_write_sectors(uint32_t index, uint8_t count, const void *from) {
while (inb(0x1F7) & BSY) {
}
outb(0x1F2, count);
outb(0x1F3, index & 0xFF);
index = index >> 8;
outb(0x1F4, index & 0xFF);
index = index >> 8;
outb(0x1F5, index & 0xFF);
index = index >> 8;
outb(0x1F6, 0b11100000 | (index & 0b00001111));
outb(0x1F7, WRITE_SECTORS);
while (count--) {
while (!(inb(0x1F7) & DRQ)) {
}
outsw(0x1F0, from, SECTOR_WORDS);
from = (const uint16_t *)from + SECTOR_WORDS;
}
outb(0x1F7, FLUSH_CACHE);
while (inb(0x1F7) & BSY) {
}
ASSERT(!(inb(0x1F7) & (ERR | DF)), "ata_write_sectors: write error");
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
void ata_read_sectors(uint32_t index, uint8_t count, void *to);
void ata_write_sectors(uint32_t index, uint8_t count, const void *from);
+267
View File
@@ -0,0 +1,267 @@
#include "src/kernel/fat16.h"
#include "src/kernel/ata.h"
#include "src/kernel/fs.h"
#include "src/kernel/panic.h"
#include "src/lib/memory.h"
#include "src/lib/string.h"
#include "src/lib/util.h"
#define SECTOR_SIZE 512
#define FIRST_PARTITION_SECTOR 2048
#define ATTRIBUTE_SUBDIRECTORY 0x10
typedef struct __attribute__((packed)) {
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t fats_count;
uint16_t root_entry_count;
uint16_t total_sectors_16;
uint8_t media_type;
uint16_t sectors_per_fat;
} fat16_bpb_t;
typedef struct __attribute__((packed)) {
char name[8];
char ext[3];
uint8_t attributes;
uint8_t reserved[10];
uint16_t modified_time;
uint16_t modified_date;
uint16_t first_cluster;
uint32_t size;
} fat16_dir_entry_t;
typedef struct {
fs_node_t base;
fat16_dir_entry_t entry;
} fat16_node_t;
static fat16_bpb_t bpb; // Assuming one partition.
static fs_node_t *fs = NUL;
static uint16_t *fat = NUL;
static void to_8_3(const char *name, char *output) {
memory_set(' ', 11, output);
output[11] = '\0';
const char *c = name;
uint64_t i = 0;
uint8_t ext = 0;
while (*c) {
if (*c == '.') {
i = 8;
ext = 1;
} else if (i < (!ext ? 8 : 11)) {
output[i++] = *c >= 'a' && *c <= 'z' ? *c - 32 : *c;
}
c++;
}
}
static void from_8_3(const char *name, const char *extension, char *output) {
memory_set(0, 13, output);
uint16_t ni = 0, ei = 0, oi = 0;
while (ni < 8 && name[ni] != ' ') {
output[oi++] = name[ni++];
}
if (extension[ei] != ' ') {
output[oi++] = '.';
while (ei < 3 && extension[ei] != ' ') {
output[oi++] = extension[ei++];
}
}
}
fs_node_t *fat16_mount() { // Assuming one partition.
uint8_t sector[512];
ata_read_sectors(FIRST_PARTITION_SECTOR, 1, &sector);
memory_copy(sector + 11, sizeof(fat16_bpb_t), &bpb);
fat16_node_t *node = memory_allocate(sizeof(fat16_node_t));
node->base.type = FAT16;
node->base.name[0] = node->entry.name[0] = '/';
node->base.size = node->entry.size = sizeof(fat16_dir_entry_t) * bpb.root_entry_count;
node->base.is_dir = 1;
return fs = (fs_node_t *)node;
}
static void ensure_fat() {
ASSERT(bpb.sectors_per_fat < 256, "ensure_fat: big FAT not implemented")
if (!fat) {
fat = memory_allocate(bpb.sectors_per_fat * SECTOR_SIZE);
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors, (uint8_t)bpb.sectors_per_fat, fat);
}
}
static fat16_dir_entry_t *load_directory(fat16_node_t *directory) {
fat16_dir_entry_t *entries;
if (!directory->entry.first_cluster) {
uint8_t sectors = (uint8_t)((directory->base.size + SECTOR_SIZE - 1) / SECTOR_SIZE);
entries = memory_allocate(sectors * SECTOR_SIZE + sizeof(fat16_dir_entry_t)); // One extra as null terminator.
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.fats_count * bpb.sectors_per_fat, sectors, entries);
} else {
ensure_fat();
uint16_t next_cluster = directory->entry.first_cluster;
uint32_t cluster_count = 0;
while (next_cluster < 0xFFF8) {
cluster_count++;
next_cluster = fat[next_cluster];
}
entries =
memory_allocate(cluster_count * bpb.sectors_per_cluster * SECTOR_SIZE + sizeof(fat16_dir_entry_t)); // One extra as null terminator.
uint8_t *chunk = (uint8_t *)entries;
next_cluster = directory->entry.first_cluster;
while (next_cluster < 0xFFF8) {
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
bpb.sectors_per_cluster * (next_cluster - 2),
bpb.sectors_per_cluster, chunk);
chunk += bpb.sectors_per_cluster * SECTOR_SIZE;
next_cluster = fat[next_cluster];
}
}
return entries;
}
static fs_node_t *open_entry(const char *name, const fat16_dir_entry_t *entry) {
if (!entry->name[0]) {
return NUL;
}
fat16_node_t *result = memory_allocate(sizeof(fat16_node_t));
memory_copy((char *)name, string_length(name) + 1, &(result->base.name));
result->base.type = FAT16;
result->base.size = entry->size;
result->base.is_dir = entry->attributes & ATTRIBUTE_SUBDIRECTORY;
memory_copy((fat16_dir_entry_t *)entry, sizeof(fat16_dir_entry_t), (uint8_t *)result + sizeof(fs_node_t));
return (fs_node_t *)result;
}
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name) {
ASSERT(directory->type == FAT16, "fat16_open_by: directory is not FAT16");
ASSERT(directory->is_dir, "fat16_open_by: directory is not a directory");
char name_8_3[12];
to_8_3(name, name_8_3);
fat16_dir_entry_t *entries = load_directory((fat16_node_t *)directory);
fat16_dir_entry_t *entry = entries;
while (entry->name[0]) {
if ((uint8_t)entry->name[0] != 0xE5 && (uint8_t)entry->attributes != 0x0F && bytes_equal(name_8_3, (char *)entry, 11)) {
break;
}
entry++;
}
fs_node_t *result = open_entry(name, entry);
memory_free(entries);
return result;
}
fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index) {
ASSERT(directory->type == FAT16, "fat16_open_at: directory is not FAT16");
ASSERT(directory->is_dir, "fat16_open_at: directory is not a directory");
fat16_dir_entry_t *entries = load_directory((fat16_node_t *)directory);
uint64_t ei = 0, vi = 0;
while (entries[ei].name[0]) {
if ((uint8_t)entries[ei].name[0] != 0xE5 && (uint8_t)entries[ei].attributes != 0x0F) {
if (vi == index) {
break;
}
vi++;
}
ei++;
}
if (!entries[ei].name[0]) {
return NUL;
}
char name[13];
from_8_3(entries[ei].name, entries[ei].ext, name);
fs_node_t *result = open_entry(name, entries + ei);
memory_free(entries);
return result;
}
fs_node_t *fat16_open_again(const fs_node_t *source) {
fs_node_t *result = memory_allocate(sizeof(fat16_node_t));
memory_copy(source, sizeof(fat16_node_t), result);
return result;
}
void fat16_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to) {
ASSERT(file->type == FAT16, "fat16_read: file is not FAT16");
ASSERT(!file->is_dir, "fat16_read: can not read directory");
ASSERT(file->size >= offset + size, "fat16_read: offset/size are out of bounds");
ensure_fat();
fat16_node_t *fat_file = (fat16_node_t *)file;
uint32_t cluster_size = bpb.sectors_per_cluster * SECTOR_SIZE;
uint32_t next_cluster = fat_file->entry.first_cluster;
// Assuming filesystem is correct. TODO Check for real.
while (offset >= cluster_size) {
next_cluster = fat[next_cluster];
offset -= cluster_size;
}
uint8_t *cursor = to;
uint8_t *tmp = memory_allocate(cluster_size);
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
bpb.sectors_per_cluster * (next_cluster - 2),
bpb.sectors_per_cluster, tmp);
uint64_t prefix_size = size <= cluster_size - offset ? size : cluster_size - offset;
memory_copy(tmp + offset, prefix_size, cursor);
size -= prefix_size;
cursor += prefix_size;
next_cluster = fat[next_cluster];
while (size >= cluster_size) {
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
bpb.sectors_per_cluster * (next_cluster - 2),
bpb.sectors_per_cluster, cursor);
size -= cluster_size;
cursor += cluster_size;
next_cluster = fat[next_cluster];
}
if (size) {
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
bpb.sectors_per_cluster * (next_cluster - 2),
bpb.sectors_per_cluster, tmp);
memory_copy(tmp, size, cursor);
}
memory_free(tmp);
}
void fat16_close(fs_node_t *node) {
ASSERT(node->type == FAT16, "fat16_close: node is not FAT16");
ASSERT(node != fs, "fat16_close: can not unmount FS");
memory_free(node);
}
void fat16_unmount(fs_node_t *node) {
ASSERT(node->type == FAT16, "fat16_unmount: node is not FAT16");
ASSERT(node == fs, "fat16_unmount: node is not filesystem");
memory_free(node);
fs = NUL;
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include "src/kernel/fs.h"
#define FAT16 1
fs_node_t *fat16_mount();
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name);
fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index);
fs_node_t *fat16_open_again(const fs_node_t *source);
void fat16_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to);
void fat16_close(fs_node_t *node);
void fat16_unmount(fs_node_t *fs);
+30
View File
@@ -0,0 +1,30 @@
#include "src/kernel/fs.h"
#include "src/kernel/fat16.h"
fs_node_t *fs_mount() {
return fat16_mount();
}
fs_node_t *fs_open_by(const fs_node_t *directory, const char *name) {
return fat16_open_by(directory, name);
}
fs_node_t *fs_open_at(const fs_node_t *directory, uint64_t index) {
return fat16_open_at(directory, index);
}
fs_node_t *fs_open_again(const fs_node_t *source) {
return fat16_open_again(source);
}
void fs_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to) {
fat16_read(file, offset, size, to);
}
void fs_close(fs_node_t *node) {
fat16_close(node);
}
void fs_unmount(fs_node_t *fs) {
fat16_unmount(fs);
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <stdint.h>
#define FILENAME_SIZE_LIMIT 255
typedef struct fs_node {
char name[FILENAME_SIZE_LIMIT + 1];
uint32_t size;
uint8_t is_dir;
uint8_t type;
} fs_node_t;
fs_node_t *fs_mount();
fs_node_t *fs_open_by(const fs_node_t *directory, const char *name);
fs_node_t *fs_open_at(const fs_node_t *directory, uint64_t index);
fs_node_t *fs_open_again(const fs_node_t *source);
void fs_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to);
void fs_close(fs_node_t *node);
void fs_unmount(fs_node_t *fs);
+67
View File
@@ -0,0 +1,67 @@
#include "src/kernel/gdt.h"
#include "src/kernel/tss.h"
typedef struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_mid;
uint8_t access;
uint8_t flags_limit_high;
uint8_t base_high;
} __attribute__((packed)) gdt_entry_t;
typedef struct {
gdt_entry_t base;
uint32_t base_upper;
uint32_t reserved;
} __attribute__((packed)) gdt_system_entry_t;
typedef struct {
gdt_entry_t gdt[6];
gdt_system_entry_t tss_entry;
} __attribute__((packed)) gdt_table_t;
typedef struct {
uint16_t limit;
uint64_t offset;
} __attribute__((packed)) gdt_descriptor_t;
static gdt_table_t gdt;
static gdt_descriptor_t desc;
static void set_entry(gdt_entry_t *e, uint8_t access, uint8_t flags) {
e->limit_low = 0xFFFF;
e->base_low = 0;
e->base_mid = 0;
e->access = access;
e->flags_limit_high = flags | 0x0F;
e->base_high = 0;
}
static void set_tss_entry(void *base) {
uint64_t ibase = (uint64_t)base;
gdt.tss_entry.base.limit_low = sizeof(tss_t) - 1;
gdt.tss_entry.base.base_low = ibase & 0xFFFF;
gdt.tss_entry.base.base_mid = (ibase >> 16) & 0xFF;
gdt.tss_entry.base.access = 0x89; // present, type=TSS available
gdt.tss_entry.base.flags_limit_high = 0x00;
gdt.tss_entry.base.base_high = (ibase >> 24) & 0xFF;
gdt.tss_entry.base_upper = ibase >> 32;
gdt.tss_entry.reserved = 0;
}
void gdt_init() {
gdt.gdt[0] = (gdt_entry_t){0};
set_entry(&gdt.gdt[1], 0x9A, 0xCF); // present, ring 0, code, executable, readable, 32-bit, 4KB granularity
set_entry(&gdt.gdt[2], 0x9A, 0xA0); // present, ring 0, code, executable, readable, 64-bit
set_entry(&gdt.gdt[3], 0x92, 0x00); // present, ring 0, data, writable
set_entry(&gdt.gdt[4], 0xF2, 0x00); // present, ring 3, data, writable
set_entry(&gdt.gdt[5], 0xFA, 0xA0); // present, ring 3, code, executable, readable, 64-bit
set_tss_entry(&tss);
desc.limit = sizeof(gdt) - 1;
desc.offset = (uint64_t)&gdt;
__asm__ volatile("lgdt %0" : : "m"(desc));
__asm__ volatile("ltr %0" : : "r"((uint16_t)TSS_SEL));
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#define OBSOLETE_CS 0x08
#define KERNEL_CS (OBSOLETE_CS + 0x08)
#define KERNEL_DS (KERNEL_CS + 0x08)
#define USER_DS_BASE (KERNEL_DS + 0x08)
#define USER_DS (USER_DS_BASE | 3)
#define USER_CS_BASE (USER_DS_BASE + 0x08)
#define USER_CS (USER_CS_BASE | 3)
#define TSS_SEL (USER_CS_BASE + 0x08)
void gdt_init();
+40
View File
@@ -0,0 +1,40 @@
#include "src/kernel/idt.h"
struct idt_entry {
uint16_t offset_low;
uint16_t selector;
uint8_t ist;
uint8_t flags;
uint16_t offset_mid;
uint32_t offset_high;
uint32_t zero;
} __attribute__((packed));
struct idt_descriptor {
uint16_t size;
uint64_t offset;
} __attribute__((packed));
static struct idt_entry idt[256];
static struct idt_descriptor idt_desc;
static void idt_load() {
idt_desc.size = sizeof(idt) - 1;
idt_desc.offset = (uint64_t)&idt;
__asm__ volatile("lidt %0" : : "m"(idt_desc));
}
inline void idt_init() {
idt_load();
}
void idt_set_entry(int vector, void (*handler)(struct interrupt_frame *), uint8_t flags) {
uint64_t addr = (uint64_t)handler;
idt[vector].offset_low = addr & 0xFFFF;
idt[vector].selector = 0x0010;
idt[vector].ist = 0;
idt[vector].flags = flags;
idt[vector].offset_mid = (addr >> 16) & 0xFFFF;
idt[vector].offset_high = (addr >> 32) & 0xFFFFFFFF;
idt[vector].zero = 0;
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
struct interrupt_frame {
uint64_t ip;
uint64_t cs;
uint64_t flags;
uint64_t sp;
uint64_t ss;
};
void idt_init();
void idt_set_entry(int vector, void (*handler)(struct interrupt_frame *), uint8_t flags);
+77
View File
@@ -0,0 +1,77 @@
#include "src/kernel/fs.h"
#include "src/kernel/gdt.h"
#include "src/kernel/idt.h"
#include "src/kernel/keyboard.h"
#include "src/kernel/panic.h"
#include "src/kernel/path.h"
#include "src/kernel/pic.h"
#include "src/kernel/process.h"
#include "src/kernel/syscall.h"
#include "src/kernel/util.h"
#include "src/kernel/vga.h"
#include "src/lib/memory.h"
__attribute__((interrupt)) void isr_divide_by_zero(__attribute__((unused)) struct interrupt_frame *frame) {
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: divide by zero", 0x4F);
while (1)
;
}
__attribute__((interrupt)) void isr_page_fault(__attribute__((unused)) struct interrupt_frame *frame) {
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: page fault", 0x4F);
while (1)
;
}
__attribute__((interrupt)) void isr_general_violation(__attribute__((unused)) struct interrupt_frame *frame) {
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: general violation", 0x4F);
while (1)
;
}
__attribute__((interrupt)) void isr_ata_primary(__attribute__((unused)) struct interrupt_frame *frame) {
outb(0x20, 0x20);
outb(0xA0, 0x20);
}
void kernel_main() {
pic_init();
idt_init();
outb(0x21, inb(0x21) | 0x01); // mask out timer interrupt
idt_set_entry(0, isr_divide_by_zero, 0x8E);
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 *kp = memory_allocate(sizeof(process_t));
kp->root = fs_mount();
kp->cwd = memory_allocate(sizeof(path_t));
kp->stdin = keyboard_init();
kp->stdout = vga_init();
fs_node_t *hello = fs_open_by(kp->root, "HELLO.BIN");
ASSERT(hello, "kernel: hello.bin not found");
uint8_t *hello_code = memory_allocate(hello->size);
fs_read(hello, 0, hello->size, hello_code);
process_t *proc = process_create(kp, hello_code, hello->size);
memory_free(hello_code);
fs_close(hello);
process_run(proc);
while (1)
;
fs_unmount(kp->root);
outw(0x604, 0x2000); // TODO: parse ACPI tables for real hardware
__asm__ volatile("cli; hlt");
}
+3
View File
@@ -0,0 +1,3 @@
bits 64
extern kernel_main
jmp kernel_main
+161
View File
@@ -0,0 +1,161 @@
#include "src/kernel/keyboard.h"
#include "src/kernel/idt.h"
#include "src/kernel/stream.h"
#include "src/kernel/util.h"
#include "src/lib/memory.h"
#define KEYBOARD_STATE_LSHIFT 0b00000001
#define KEYBOARD_STATE_RSHIFT 0b00000010
#define KEYBOARD_STATE_LCTRL 0b00000100
#define KEYBOARD_STATE_RCTRL 0b00001000
#define KEYBOARD_STATE_LALT 0b00010000
#define KEYBOARD_STATE_RALT 0b00100000
#define KEYBOARD_STATE_SEQ 0b01000000
static uint8_t keyboard_state = 0;
// clang-format off
static const char scancode_normal[128] = {
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u',
'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x',
'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static const char scancode_shifted[128] = {
0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U',
'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X',
'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0
};
// clang-format on
#define BUFFER_SIZE 256
static char buffer[BUFFER_SIZE];
static uint16_t buffer_offset = 0;
static uint16_t buffer_length = 0;
static void append(char c) {
buffer[(buffer_offset + buffer_length) % BUFFER_SIZE] = c;
if (buffer_length < BUFFER_SIZE) {
buffer_length++;
}
}
static void append_sequence(const char *s) {
while (*s) {
append(*s);
s++;
}
}
static void stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from,
__attribute__((unused)) uint64_t size) {
}
static uint64_t stream_read(__attribute__((unused)) stream_t *self, uint64_t max, char *to) {
if (max == 0 || buffer_length == 0) {
return 0;
}
uint64_t size = buffer_length > max ? max : buffer_length;
if (buffer_offset + size < BUFFER_SIZE) {
memory_copy(buffer + buffer_offset, size, to);
} else {
uint64_t chunk_0 = BUFFER_SIZE - buffer_offset;
memory_copy(buffer + buffer_offset, chunk_0, to);
memory_copy(buffer, size - chunk_0, to + chunk_0);
}
buffer_length -= size;
buffer_offset = (buffer_offset + size) % BUFFER_SIZE;
return size;
}
static stream_t stream = {stream_write, stream_read};
static void on_key(uint8_t scancode) {
const uint8_t pressed = !(scancode & 0x80);
const uint8_t code = scancode & ~0x80;
if (!(keyboard_state & KEYBOARD_STATE_SEQ)) {
switch (code) {
case 0x60:
keyboard_state = keyboard_state | KEYBOARD_STATE_SEQ;
break;
case 0x2A:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LSHIFT : keyboard_state & ~KEYBOARD_STATE_LSHIFT;
break;
case 0x36:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RSHIFT : keyboard_state & ~KEYBOARD_STATE_RSHIFT;
break;
case 0x38:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LALT : keyboard_state & ~KEYBOARD_STATE_LALT;
break;
case 0x1D:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LCTRL : keyboard_state & ~KEYBOARD_STATE_LCTRL;
break;
case 0x0E:
pressed ? append('\b') : 0;
break;
case 0x1C:
pressed ? append('\n') : 0;
break;
default:
if (pressed && scancode_normal[code]) {
if (keyboard_state && (keyboard_state & (KEYBOARD_STATE_LCTRL | KEYBOARD_STATE_RCTRL)) == keyboard_state) {
if ((scancode_normal[code] >= '0' && scancode_normal[code] <= '9') ||
(scancode_normal[code] >= 'a' && scancode_normal[code] <= 'z')) {
append(scancode_normal[code] - 'a' + 1);
}
} else if (keyboard_state && (keyboard_state & (KEYBOARD_STATE_LALT | KEYBOARD_STATE_RALT)) == keyboard_state) {
// TODO Alt combinations.
} else {
append((keyboard_state & (KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) ? scancode_shifted : scancode_normal)[code]);
}
}
break;
}
} else {
keyboard_state = keyboard_state & ~KEYBOARD_STATE_SEQ;
switch (code) {
case 0x38:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RALT : keyboard_state & ~KEYBOARD_STATE_RALT;
break;
case 0x1D:
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RCTRL : keyboard_state & ~KEYBOARD_STATE_RCTRL;
break;
case 0x47:
pressed ? append_sequence(STREAM_SEQ_HOME) : 0;
break;
case 0x4B:
pressed ? append_sequence(STREAM_SEQ_LEFT) : 0;
break;
case 0x4D:
pressed ? append_sequence(STREAM_SEQ_RIGHT) : 0;
break;
case 0x4F:
pressed ? append_sequence(STREAM_SEQ_END) : 0;
break;
case 0x53:
pressed ? append_sequence(STREAM_SEQ_DELETE) : 0;
break;
}
}
}
__attribute__((interrupt)) static void isr_keyboard(__attribute__((unused)) struct interrupt_frame *frame) {
uint8_t scancode = inb(0x60);
outb(0x20, 0x20);
on_key(scancode);
}
stream_t *keyboard_init() {
outb(0x21, inb(0x21) & ~0x02);
idt_set_entry(33, isr_keyboard, 0x8E);
return &stream;
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "src/kernel/stream.h"
#include <stdint.h>
stream_t *keyboard_init();
+17
View File
@@ -0,0 +1,17 @@
ENTRY(kernel_main)
SECTIONS {
. = 0xFFFFFFFF80020000;
.text : {
*(.text)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}
+120
View File
@@ -0,0 +1,120 @@
#include "src/kernel/memory.h"
#include "src/kernel/panic.h"
#include "src/lib/layout.h"
#include "src/lib/memory.h"
#define MAP_UNIT 64
#define FULL_UNIT 0xFFFFFFFFFFFFFFFF
// TODO keep aligned with `src/lib/layout.h`.
static uint16_t free_page = MAP_UNIT * 12;
static uint64_t allocation[PAGE_COUNT / MAP_UNIT] = {
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
FULL_UNIT,
[59] = 0x8000000000000000ULL,
};
static uint8_t get_allocated(uint16_t page) {
return !!allocation[page / MAP_UNIT] & ((uint64_t)1 << (page % MAP_UNIT));
}
static void set_allocated(uint16_t page, uint8_t allocated) {
if (allocated) {
allocation[page / MAP_UNIT] |= ((uint64_t)1 << (page % MAP_UNIT));
} else {
allocation[page / MAP_UNIT] &= ~((uint64_t)1 << (page % MAP_UNIT));
}
}
void *memory_page_allocate() {
ASSERT(free_page < PAGE_COUNT, "memory_page_allocate: out of memory");
const uint16_t page = free_page;
set_allocated(page, 1);
for (uint16_t unit = free_page / MAP_UNIT; unit < PAGE_COUNT / MAP_UNIT; unit++) {
if (allocation[unit] != FULL_UNIT) {
uint16_t bit = (uint16_t)__builtin_ctzll(~allocation[unit]);
free_page = unit * MAP_UNIT + bit;
break;
}
}
return (void *)((uint64_t)page * PAGE_SIZE);
}
void memory_page_free(void *address) {
uint16_t page = (uint16_t)((uint64_t)address / PAGE_SIZE);
ASSERT(get_allocated(page), "memory_page_free: page not allocated")
set_allocated(page, 0);
if (page < free_page) {
free_page = page;
}
}
void memory_page_map(uint64_t *pml4, void *virt, void *phys, uint64_t flags) {
const uint64_t ivirt = (uint64_t)virt;
const uint64_t pml4_index = (ivirt >> 39) & 0x1FF;
const uint64_t pdpt_index = (ivirt >> 30) & 0x1FF;
const uint64_t pd_index = (ivirt >> 21) & 0x1FF;
const uint64_t pt_index = (ivirt >> 12) & 0x1FF;
if (!(pml4[pml4_index] & PAGE_PRESENT)) {
uint64_t *pdpt = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pdpt));
pml4[pml4_index] = (uint64_t)pdpt | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
}
uint64_t *pdpt = PHYS_TO_VIRT(pml4[pml4_index] & ~(uint64_t)0xFFF);
if (!(pdpt[pdpt_index] & PAGE_PRESENT)) {
uint64_t *pd = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pd));
pdpt[pdpt_index] = (uint64_t)pd | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
}
uint64_t *pd = PHYS_TO_VIRT(pdpt[pdpt_index] & ~(uint64_t)0xFFF);
ASSERT(!(pd[pd_index] & 0x80), "memory_page_map: huge page in PD");
if (!(pd[pd_index] & PAGE_PRESENT)) {
uint64_t *pt = memory_page_allocate();
memory_set(0, PAGE_SIZE, PHYS_TO_VIRT(pt));
pd[pd_index] = (uint64_t)pt | PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER;
}
uint64_t *pt = PHYS_TO_VIRT(pd[pd_index] & ~(uint64_t)0xFFF);
pt[pt_index] = (uint64_t)phys | flags | PAGE_PRESENT;
}
void memory_page_unmap(uint64_t *pml4, void *virt) {
const uint64_t ivirt = (uint64_t)virt;
const uint64_t pml4_index = (ivirt >> 39) & 0x1FF;
const uint64_t pdpt_index = (ivirt >> 30) & 0x1FF;
const uint64_t pd_index = (ivirt >> 21) & 0x1FF;
const uint64_t pt_index = (ivirt >> 12) & 0x1FF;
if (!(pml4[pml4_index] & PAGE_PRESENT)) {
return;
}
uint64_t *pdpt = PHYS_TO_VIRT(pml4[pml4_index] & ~(uint64_t)0xFFF);
if (!(pdpt[pdpt_index] & PAGE_PRESENT)) {
return;
}
uint64_t *pd = PHYS_TO_VIRT(pdpt[pdpt_index] & ~(uint64_t)0xFFF);
if (!(pd[pd_index] & PAGE_PRESENT)) {
return;
}
uint64_t *pt = PHYS_TO_VIRT(pd[pd_index] & ~(uint64_t)0xFFF);
memory_page_free((void *)(pt[pt_index] & ~(uint64_t)0xFFF));
pt[pt_index] = 0;
__asm__ volatile("invlpg (%0)" : : "r"(virt) : "memory");
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <stdint.h>
#define PAGE_PRESENT 0x01
#define PAGE_WRITABLE 0x02
#define PAGE_USER 0x04
#define PAGE_PWT 0x08
#define PAGE_PCD 0x10
#define PAGE_ACCESSED 0x20
#define PAGE_DIRTY 0x40
#define PAGE_HUGE 0x80
#define PAGE_GLOBAL 0x100
#define PAGE_NX (1ULL << 63)
void *memory_page_allocate();
void memory_page_free(void *page);
void memory_page_map(uint64_t *pml4, void *virt, void *phys, uint64_t flags);
void memory_page_unmap(uint64_t *pml4, void *virt);
+8
View File
@@ -0,0 +1,8 @@
#include "src/kernel/panic.h"
#include "src/kernel/vga.h"
void kernel_panic(const char *msg, __attribute__((unused)) const char *file, __attribute__((unused)) int line) {
vga_set_string(0, VGA_HEIGHT - 1, msg, 0x28);
while (1)
;
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#define ASSERT(cond, msg) \
if (!(cond)) { \
kernel_panic(msg " (assertion: " #cond ")", __FILE__, __LINE__); \
}
void kernel_panic(const char *msg, const char *file, int line);
+45
View File
@@ -0,0 +1,45 @@
#include "src/kernel/path.h"
#include "src/kernel/fs.h"
#include "src/lib/memory.h"
#include "src/lib/string.h"
#include "src/lib/util.h"
path_t *path_open(const fs_node_t *root, const path_t *source, char *path) {
path_t *result = memory_allocate(sizeof(path_t));
char *path_components[PATH_DEPTH];
uint8_t path_length = (uint8_t)string_split(path, '/', PATH_DEPTH, path_components);
if (!string_empty(path_components[0])) {
for (uint8_t i = 0; i < source->depth; i++) {
result->stack[result->depth++] = fs_open_again(source->stack[i]);
}
}
for (uint8_t i = 0; i < path_length; i++) {
if (string_empty(path_components[i]) || string_equal(path_components[i], ".")) {
continue;
} else if (string_equal(path_components[i], "..")) {
if (result->depth) {
fs_close(result->stack[--result->depth]);
}
} else {
const fs_node_t *prev = result->depth ? result->stack[result->depth - 1] : root;
fs_node_t *next = prev->is_dir ? fs_open_by(prev, path_components[i]) : NUL;
if (!next || result->depth >= PATH_DEPTH) {
path_close(result);
return NUL;
} else {
result->stack[result->depth++] = next;
}
}
}
return result;
}
void path_close(path_t *path) {
for (uint64_t i = 0; i < path->depth; i++) {
fs_close(path->stack[i]);
}
memory_free(path);
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include "src/kernel/fs.h"
#include <stdint.h>
#define PATH_DEPTH 255
typedef struct {
fs_node_t *stack[PATH_DEPTH];
uint8_t depth;
} path_t;
path_t *path_open(const fs_node_t *root, const path_t *source, char *path);
void path_close(path_t *path);
+35
View File
@@ -0,0 +1,35 @@
#include "src/kernel/pic.h"
#include "src/kernel/util.h"
static void pic_remap() {
uint8_t mask1 = inb(0x21);
uint8_t mask2 = inb(0xA1);
outb(0x20, 0x11);
io_wait();
outb(0xA0, 0x11);
io_wait();
outb(0x21, 0x20);
io_wait();
outb(0xA1, 0x28);
io_wait();
outb(0x21, 0x04);
io_wait();
outb(0xA1, 0x02);
io_wait();
outb(0x21, 0x01);
io_wait();
outb(0xA1, 0x01);
io_wait();
outb(0x21, mask1);
outb(0xA1, mask2);
io_wait();
}
inline void pic_init() {
pic_remap();
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void pic_init();
+114
View File
@@ -0,0 +1,114 @@
#include "src/kernel/process.h"
#include "src/kernel/gdt.h"
#include "src/kernel/memory.h"
#include "src/kernel/panic.h"
#include "src/kernel/path.h"
#include "src/kernel/tss.h"
#include "src/lib/layout.h"
#include "src/lib/memory.h"
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");
process_t *proc = memory_allocate(sizeof(process_t));
proc->kernel_stack = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE;
proc->pml4 = PHYS_TO_VIRT(memory_page_allocate());
memory_set(0, PAGE_SIZE / 2, proc->pml4);
memory_copy((uint64_t *)KERNEL_VIRTUAL_PML4 + 256, PAGE_SIZE / 2, proc->pml4 + 256);
memory_page_map(proc->pml4, (void *)USER_VIRTUAL_STACK, memory_page_allocate(), PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER);
for (uint64_t page = 0; size > 0; page++) {
void *p = memory_page_allocate();
uint64_t s = size < PAGE_SIZE ? size : PAGE_SIZE;
memory_page_map(proc->pml4, (void *)(USER_VIRTUAL_CODE + page * PAGE_SIZE), p, PAGE_PRESENT | PAGE_USER);
memory_copy(code, s, PHYS_TO_VIRT(p));
code += s;
size -= s;
}
for (uint64_t page = 0; page < HEAP_PAGE_COUNT; page++) {
memory_page_map(proc->pml4, (void *)(USER_VIRTUAL_HEAP + page * PAGE_SIZE), memory_page_allocate(),
PAGE_PRESENT | PAGE_WRITABLE | PAGE_USER);
}
proc->cwd = memory_allocate(sizeof(path_t));
proc->cwd->depth = parent->cwd->depth;
for (uint8_t i = 0; i < parent->cwd->depth; i++) {
proc->cwd->stack[i] = fs_open_again(parent->cwd->stack[i]);
}
proc->root = parent->root;
proc->stdin = parent->stdin;
proc->stdout = parent->stdout;
return proc;
}
void process_run(process_t *proc) {
current_process = proc;
tss.rsp0 = (uint64_t)proc->kernel_stack;
__asm__ volatile("mov %0, %%cr3" : : "r"(VIRT_TO_PHYS(proc->pml4)) : "memory");
__asm__ volatile("push %0\n" // ss
"push %1\n" // rsp
"push %2\n" // rflags
"push %3\n" // cs
"push %4\n" // rip
"iretq"
:
: "r"((uint64_t)USER_DS), "r"(USER_VIRTUAL_STACK + PAGE_SIZE), "r"((uint64_t)USER_RFLAGS), "r"((uint64_t)USER_CS),
"r"(USER_VIRTUAL_CODE)
: "memory");
}
void process_destroy(process_t *proc) {
path_close(proc->cwd);
for (uint16_t pml4i = 0; pml4i < 256; pml4i++) {
if (!(proc->pml4[pml4i] & PAGE_PRESENT)) {
continue;
}
uint64_t *pdpt = PHYS_TO_VIRT(proc->pml4[pml4i] & ~(uint64_t)0xFFF);
for (uint16_t pdpti = 0; pdpti < 512; pdpti++) {
if (!(pdpt[pdpti] & PAGE_PRESENT)) {
continue;
}
uint64_t *pd = PHYS_TO_VIRT(pdpt[pdpti] & ~(uint64_t)0xFFF);
for (uint16_t pdi = 0; pdi < 512; pdi++) {
if (!(pd[pdi] & PAGE_PRESENT)) {
continue;
}
if (pd[pdi] & PAGE_HUGE) {
memory_page_free((void *)(pd[pdi] & ~(uint64_t)0xFFF));
continue;
}
uint64_t *pt = PHYS_TO_VIRT(pd[pdi] & ~(uint64_t)0xFFF);
for (uint16_t pti = 0; pti < 512; pti++) {
if (!(pt[pti] & PAGE_PRESENT)) {
continue;
}
memory_page_free((void *)(pt[pti] & ~(uint64_t)0xFFF));
}
memory_page_free((void *)(pd[pdi] & ~(uint64_t)0xFFF));
}
memory_page_free((void *)(pdpt[pdpti] & ~(uint64_t)0xFFF));
}
memory_page_free((void *)(proc->pml4[pml4i] & ~(uint64_t)0xFFF));
}
memory_page_free((void *)VIRT_TO_PHYS(proc->pml4));
memory_free(proc->kernel_stack);
memory_free(proc);
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "src/kernel/fs.h"
#include "src/kernel/path.h"
#include "src/kernel/stream.h"
#include "src/lib/string.h"
#define USER_RFLAGS 0x202
typedef struct process {
uint64_t *pml4;
void *kernel_stack;
fs_node_t *root;
path_t *cwd;
stream_t *stdin;
stream_t *stdout;
} process_t;
typedef void (*app_t)(process_t *proc, uint8_t argc, char **argv);
extern process_t *current_process;
process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size);
void process_run(process_t *proc);
void process_destroy(process_t *proc);
#define WRITE_S(s) proc->stdout->write(proc->stdout, s, sizeof(s) - 1);
#define WRITE_D(s) proc->stdout->write(proc->stdout, s, string_length(s));
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include <stdint.h>
#define STREAM_SEQ_UP "\x1B[A"
#define STREAM_SEQ_DOWN "\x1B[B"
#define STREAM_SEQ_RIGHT "\x1B[C"
#define STREAM_SEQ_LEFT "\x1B[D"
#define STREAM_SEQ_HOME "\x1B[H"
#define STREAM_SEQ_END "\x1B[F"
#define STREAM_SEQ_INSERT "\x1B[2~"
#define STREAM_SEQ_DELETE "\x1B[3~"
#define STREAM_SEQ_PAGE_UP "\x1B[5~"
#define STREAM_SEQ_PAGE_DOWN "\x1B[6~"
#define STREAM_SEQ_F1 "\x1B[11~"
#define STREAM_SEQ_F2 "\x1B[12~"
#define STREAM_SEQ_F3 "\x1B[13~"
#define STREAM_SEQ_F4 "\x1B[14~"
#define STREAM_SEQ_F5 "\x1B[15~"
#define STREAM_SEQ_F6 "\x1B[16~"
#define STREAM_SEQ_F7 "\x1B[17~"
#define STREAM_SEQ_F8 "\x1B[18~"
#define STREAM_SEQ_F9 "\x1B[19~"
#define STREAM_SEQ_F10 "\x1B[1A~"
#define STREAM_SEQ_F11 "\x1B[1B~"
#define STREAM_SEQ_F12 "\x1B[1C~"
typedef struct stream stream_t;
typedef void (*stream_write_t)(stream_t *self, const char *from, uint64_t size);
typedef uint64_t (*stream_read_t)(stream_t *self, uint64_t max, char *to);
typedef struct stream {
stream_write_t write;
stream_read_t read;
} stream_t;
+39
View File
@@ -0,0 +1,39 @@
bits 64
extern tss
extern syscall_dispatch
global syscall_entry
syscall_entry:
mov [user_rsp_tmp], rsp
mov rsp, [tss + 4]
push rcx
push r11
push rbp
push rbx
push r12
push r13
push r14
push r15
mov rcx, rdx ; arg3
mov rdx, rsi ; arg2
mov rsi, rdi ; arg1
mov rdi, rax ; syscall number
call syscall_dispatch
pop r15
pop r14
pop r13
pop r12
pop rbx
pop rbp
pop r11
pop rcx
mov rsp, [rel user_rsp_tmp]
o64 sysret
user_rsp_tmp: dq 0
+61
View File
@@ -0,0 +1,61 @@
#include "src/kernel/syscall.h"
#include "src/kernel/gdt.h"
#include "src/kernel/process.h"
#include "src/lib/layout.h"
#include "src/lib/util.h"
#define MSR_EFER 0xC0000080
#define MSR_STAR 0xC0000081
#define MSR_LSTAR 0xC0000082
#define MSR_FMASK 0xC0000084
static inline uint64_t rdmsr(uint32_t msr) {
uint32_t lo, hi;
__asm__ volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
return ((uint64_t)hi << 32) | lo;
}
static inline void wrmsr(uint32_t msr, uint64_t value) {
__asm__ volatile("wrmsr" : : "c"(msr), "a"((uint32_t)value), "d"((uint32_t)(value >> 32)));
}
extern void syscall_entry();
void syscall_init() {
wrmsr(MSR_EFER, rdmsr(MSR_EFER) | 0x01);
wrmsr(MSR_STAR, ((uint64_t)KERNEL_DS << 48) | ((uint64_t)KERNEL_CS << 32));
wrmsr(MSR_LSTAR, (uint64_t)syscall_entry);
wrmsr(MSR_FMASK, 0x200);
}
static void write(uint64_t fd, uint64_t data, uint64_t bytes) {
(void)fd;
current_process->stdout->write(current_process->stdout, (const char *)data, bytes);
}
static void exit() {
__asm__ volatile("mov %0, %%cr3" : : "r"(KERNEL_VIRTUAL_PML4) : "memory");
process_destroy(current_process);
current_process = NUL;
while (1)
;
// shutdown for now
// outw(0x604, 0x2000);
// __asm__ volatile("cli; hlt");
}
void syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3) {
switch (func) {
case SYSCALL_WRITE:
write(arg1, arg2, arg3);
break;
case SYSCALL_EXIT:
exit();
break;
default:
break;
}
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <stdint.h>
#define SYSCALL_WRITE 1
#define SYSCALL_EXIT 60
void syscall_init();
void syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3);
+9
View File
@@ -0,0 +1,9 @@
#include "src/kernel/tss.h"
tss_t tss = {
.iopb_offset = sizeof(tss_t) // points past end of TSS = no I/O permissions
};
void tss_set_kernel_stack(void *rsp0) {
tss.rsp0 = (uint64_t)rsp0;
}
+20
View File
@@ -0,0 +1,20 @@
// tss.h
#pragma once
#include <stdint.h>
typedef struct __attribute__((packed)) {
uint32_t reserved0;
uint64_t rsp0;
uint64_t rsp1;
uint64_t rsp2;
uint64_t reserved1;
uint64_t ist[7];
uint64_t reserved2;
uint16_t reserved3;
uint16_t iopb_offset;
} tss_t;
extern tss_t tss;
void tss_set_kernel_stack(void *rsp0);
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <stdint.h>
static inline uint8_t inb(uint16_t port) {
uint8_t value;
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static inline void outb(uint16_t port, uint8_t value) {
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
}
static inline uint16_t inw(uint16_t port) {
uint16_t value;
__asm__ volatile("inw %1, %0" : "=a"(value) : "Nd"(port));
return value;
}
static inline void outw(uint16_t port, uint16_t value) {
__asm__ volatile("outw %0, %1" : : "a"(value), "Nd"(port));
}
static inline void insw(uint16_t port, void *to, uint32_t count) {
__asm__ volatile("rep insw" : "=D"(to), "=c"(count) : "d"(port), "D"(to), "c"(count) : "memory");
}
static inline void outsw(uint16_t port, const void *buffer, uint32_t count) {
__asm__ volatile("rep outsw" : "=S"(buffer), "=c"(count) : "d"(port), "S"(buffer), "c"(count) : "memory");
}
static inline void io_wait() {
outb(0x80, 0x00);
}
+170
View File
@@ -0,0 +1,170 @@
#include "src/kernel/vga.h"
#include "src/kernel/panic.h"
#include "src/kernel/util.h"
#include "src/lib/memory.h"
static uint16_t *vga = (uint16_t *)0xFFFFFFFF800B8000;
static uint16_t color = (uint16_t)0x0F << 8;
static uint16_t offset = 0;
static void set_char(char c) {
vga[offset] = color | (uint16_t)c;
}
static void set_cursor() {
outb(0x3D4, 0x0F);
outb(0x3D5, offset & 0xFF);
outb(0x3D4, 0x0E);
outb(0x3D5, (offset >> 8) & 0xFF);
}
static void scroll() {
memory_move(vga + VGA_WIDTH, 2 * VGA_WIDTH * (VGA_HEIGHT - 1), vga);
for (offset = VGA_WIDTH * (VGA_HEIGHT - 1); offset < VGA_WIDTH * VGA_HEIGHT; offset++) {
set_char(' ');
}
offset = VGA_WIDTH * (VGA_HEIGHT - 1);
}
static void advance() {
offset++;
if (offset >= VGA_WIDTH * VGA_HEIGHT) {
scroll();
}
}
static void clear() {
uint16_t *ptr = vga;
uint16_t fill = 0x0F20;
uint32_t count = VGA_WIDTH * VGA_HEIGHT;
__asm__ volatile("rep stosw" : "=D"(ptr), "=c"(count) : "D"(ptr), "a"(fill), "c"(count) : "memory");
offset = 0;
set_cursor();
}
typedef enum {
PARSE_NORMAL,
PARSE_ESC,
PARSE_CSI,
} parse_state_t;
static parse_state_t parser_state = PARSE_NORMAL;
static char parser_csi_param[8];
static uint8_t parser_csi_len;
static void on_char_received(char c) {
switch (parser_state) {
case PARSE_NORMAL:
switch (c) {
case '\x1B':
parser_state = PARSE_ESC;
break;
case '\b':
case '\x7F':
if (offset > 0) {
offset--;
set_cursor();
}
break;
case '\r':
offset -= offset % VGA_WIDTH;
set_cursor();
break;
case '\n':
if (offset / VGA_WIDTH == VGA_HEIGHT - 1) {
scroll();
} else {
offset += VGA_WIDTH;
}
offset -= offset % VGA_WIDTH;
set_cursor();
break;
default:
if (c >= '\x01' && c <= '\x1A') {
set_char('^');
advance();
set_char(c + 'A' - 1);
advance();
set_cursor();
} else if (c >= ' ') {
set_char(c);
advance();
set_cursor();
}
break;
}
break;
case PARSE_ESC:
if (c == '[') {
parser_state = PARSE_CSI;
parser_csi_len = 0;
} else {
parser_state = PARSE_NORMAL;
}
break;
case PARSE_CSI:
if ((c >= '0' && c <= '9') || c == ';') {
if (parser_csi_len < sizeof(parser_csi_param) - 1) {
parser_csi_param[parser_csi_len++] = c;
}
} else {
parser_state = PARSE_NORMAL;
parser_csi_param[parser_csi_len] = '\0';
switch (c) {
case 'C':
if (offset < VGA_WIDTH * VGA_HEIGHT - 1) {
offset++;
}
set_cursor();
break;
case 'D':
if (offset > 0) {
offset--;
}
set_cursor();
break;
case 'H':
offset -= offset % VGA_WIDTH;
set_cursor();
break;
case 'F':
offset -= offset % VGA_WIDTH;
offset += VGA_WIDTH - 1;
set_cursor();
break;
}
}
break;
}
}
static void stream_write(__attribute__((unused)) stream_t *self, const char *from, uint64_t size) {
while (size--) {
on_char_received(*from++);
}
}
static uint64_t stream_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max,
__attribute__((unused)) char *to) {
return 0;
}
static stream_t stream = {stream_write, stream_read};
stream_t *vga_init() {
clear();
return &stream;
}
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t c) {
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates")
color = (uint16_t)((uint16_t)c << 8);
offset = row * VGA_WIDTH + col;
while (*str) {
on_char_received(*str++);
}
}
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "src/kernel/stream.h"
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
stream_t *vga_init();
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color);