Add first C user program
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/kernel/process.h"
|
||||
|
||||
void cat(process_t *proc, uint8_t argc, char **argv);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/kernel/process.h"
|
||||
|
||||
void ls(process_t *proc, uint8_t argc, char **argv);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/kernel/process.h"
|
||||
|
||||
void terminal(process_t *proc, uint8_t argc, char **argv);
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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, §or);
|
||||
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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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");
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
bits 64
|
||||
extern kernel_main
|
||||
jmp kernel_main
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "src/kernel/stream.h"
|
||||
#include <stdint.h>
|
||||
|
||||
stream_t *keyboard_init();
|
||||
@@ -0,0 +1,17 @@
|
||||
ENTRY(kernel_main)
|
||||
|
||||
SECTIONS {
|
||||
. = 0xFFFFFFFF80020000;
|
||||
|
||||
.text : {
|
||||
*(.text)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void pic_init();
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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++);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user