Compare commits
17 Commits
bfb74dd57a
...
14c3262c14
| Author | SHA1 | Date |
|---|---|---|
|
|
14c3262c14 | 4 days ago |
|
|
85cfbb2050 | 5 days ago |
|
|
4ad32dda17 | 6 days ago |
|
|
1ff38ffad4 | 6 days ago |
|
|
1148596a81 | 3 weeks ago |
|
|
01cd3f528c | 1 month ago |
|
|
325dc6339a | 1 month ago |
|
|
415f78fd40 | 1 month ago |
|
|
452125782e | 1 month ago |
|
|
9761e3e870 | 1 month ago |
|
|
bcf2e5bac6 | 1 month ago |
|
|
968bb02aa8 | 1 month ago |
|
|
03b3ddac73 | 1 month ago |
|
|
1c935cf154 | 1 month ago |
|
|
b32c12b311 | 1 month ago |
|
|
2d4f76a5dd | 2 months ago |
|
|
c8edeab750 | 2 months ago |
@ -1,4 +1,6 @@ |
||||
target remote localhost:1234 |
||||
set architecture i386:x86-64 |
||||
set disassembly-flavor intel |
||||
display/i ($cs * 16 + $rip) |
||||
display/i $rip |
||||
|
||||
break *0x0000000000400000 |
||||
|
||||
@ -1,259 +0,0 @@ |
||||
#include "src/fat16.h" |
||||
#include "src/ata.h" |
||||
#include "src/memory.h" |
||||
#include "src/string.h" |
||||
#include "src/util.h" |
||||
#include <stdint.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; |
||||
} |
||||
|
||||
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"); |
||||
ASSERT(0, "fat16_unmount: not implemented"); |
||||
} |
||||
@ -1,17 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include "src/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); |
||||
|
||||
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); |
||||
@ -1,26 +0,0 @@ |
||||
#include "src/fs.h" |
||||
#include "src/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); |
||||
} |
||||
|
||||
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); |
||||
} |
||||
@ -1,25 +0,0 @@ |
||||
#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); |
||||
|
||||
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); |
||||
@ -1,48 +0,0 @@ |
||||
#include "src/idt.h" |
||||
#include "src/keyboard.h" |
||||
#include "src/memory.h" |
||||
#include "src/pic.h" |
||||
#include "src/terminal.h" |
||||
#include "src/util.h" |
||||
#include "src/vga.h" |
||||
|
||||
__attribute__((interrupt)) void isr_divide_by_zero([[maybe_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([[maybe_unused]] struct interrupt_frame *frame) { |
||||
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: page fault", 0x4F); |
||||
while (1) |
||||
; |
||||
} |
||||
|
||||
__attribute__((interrupt)) void isr_general_violation([[maybe_unused]] struct interrupt_frame *frame) { |
||||
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: general violation", 0x4F); |
||||
while (1) |
||||
; |
||||
} |
||||
|
||||
__attribute__((interrupt)) void isr_ata_primary([[maybe_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"); |
||||
|
||||
memory_init(); |
||||
keyboard_init(); |
||||
terminal_init(); |
||||
|
||||
while (1) |
||||
; |
||||
} |
||||
@ -1,6 +1,6 @@ |
||||
#include "src/ata.h" |
||||
#include "src/panic.h" |
||||
#include "src/util.h" |
||||
#include "src/kernel/ata.h" |
||||
#include "src/kernel/panic.h" |
||||
#include "src/kernel/util.h" |
||||
|
||||
#define BSY 0b10000000 |
||||
#define DF 0b00100000 |
||||
@ -0,0 +1,767 @@ |
||||
#include "src/kernel/fat16.h" |
||||
#include "src/kernel/ata.h" |
||||
#include "src/kernel/fs.h" |
||||
#include "src/kernel/panic.h" |
||||
#include "src/kernel/stream.h" |
||||
#include "src/lib/memory.h" |
||||
#include "src/lib/string.h" |
||||
#include "src/lib/syscall.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; |
||||
|
||||
// Assuming one persistently mounted partition.
|
||||
static fat16_bpb_t *bpb = NUL; |
||||
static uint32_t data_start; |
||||
static uint16_t *fat = NUL; |
||||
|
||||
typedef struct { |
||||
fs_node_t base; |
||||
uint16_t dir_cluster; |
||||
uint16_t dir_index; |
||||
uint16_t first_cluster; |
||||
} fat16_node_t; |
||||
|
||||
#define MAX_NODES 256 |
||||
|
||||
static fat16_node_t nodes[MAX_NODES]; |
||||
|
||||
static fs_node_t *node_create(uint16_t dir_cluster, uint16_t dir_index, const char *name, uint8_t is_dir, uint16_t first_cluster, |
||||
uint32_t size) { |
||||
for (uint64_t i = 0; i < MAX_NODES; i++) { |
||||
if (!nodes[i].base.refs) { |
||||
nodes[i].base.type = FAT16; |
||||
nodes[i].base.refs = 1; |
||||
memory_copy(name, FILENAME_SIZE_LIMIT + 1, nodes[i].base.name); |
||||
nodes[i].base.size = size; |
||||
nodes[i].base.is_dir = is_dir; |
||||
nodes[i].base.removed = 0; |
||||
nodes[i].dir_cluster = dir_cluster; |
||||
nodes[i].dir_index = dir_index; |
||||
nodes[i].first_cluster = first_cluster; |
||||
return (fs_node_t *)&nodes[i]; |
||||
} |
||||
} |
||||
return NUL; |
||||
} |
||||
|
||||
static fs_node_t *node_get(uint32_t dir_cluster, uint16_t dir_index) { |
||||
for (uint64_t i = 0; i < MAX_NODES; i++) { |
||||
if (nodes[i].base.refs && nodes[i].dir_cluster == dir_cluster && nodes[i].dir_index == dir_index) { |
||||
return (fs_node_t *)&nodes[i]; |
||||
} |
||||
} |
||||
return NUL; |
||||
} |
||||
|
||||
static fs_node_t *node_use(uint16_t dir_cluster, uint16_t dir_index, const char *name, uint8_t is_dir, uint16_t first_cluster, |
||||
uint32_t size) { |
||||
fs_node_t *result = node_get(dir_cluster, dir_index); |
||||
if (!result) { |
||||
result = node_create(dir_cluster, dir_index, name, is_dir, first_cluster, size); |
||||
} else { |
||||
ASSERT(result->refs < UINT32_MAX, "node_use: too many references"); |
||||
result->refs++; |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
static void node_free(fs_node_t *node) { |
||||
if (node->refs) { |
||||
node->refs--; |
||||
} |
||||
} |
||||
|
||||
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++]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void ensure_bpb() { |
||||
if (bpb) { |
||||
return; |
||||
} |
||||
|
||||
uint8_t sector[512]; |
||||
ata_read_sectors(FIRST_PARTITION_SECTOR, 1, §or); |
||||
bpb = memory_allocate(sizeof(fat16_bpb_t)); |
||||
memory_copy(sector + 11, sizeof(fat16_bpb_t), bpb); |
||||
data_start = 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; |
||||
} |
||||
|
||||
static void ensure_fat() { |
||||
if (fat) { |
||||
return; |
||||
} |
||||
|
||||
ensure_bpb(); |
||||
|
||||
ASSERT(bpb->sectors_per_fat < 256, "ensure_fat: big FAT not implemented"); |
||||
|
||||
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 uint16_t allocate_cluster() { |
||||
ensure_fat(); |
||||
for (uint16_t i = 2; i < bpb->sectors_per_fat * SECTOR_SIZE / 2; i++) { |
||||
if (fat[i] == 0x0000) { |
||||
fat[i] = 0xFFFF; |
||||
return i; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static void flush_fat() { |
||||
ensure_fat(); |
||||
ata_write_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors, (uint8_t)bpb->sectors_per_fat, fat); |
||||
} |
||||
|
||||
static uint16_t read_directory(uint16_t dir_cluster, fat16_dir_entry_t **entries) { |
||||
ensure_bpb(); |
||||
|
||||
if (!dir_cluster) { |
||||
uint8_t sectors = (uint8_t)((sizeof(fat16_dir_entry_t) * bpb->root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE); |
||||
*entries = memory_allocate(sectors * SECTOR_SIZE); |
||||
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat, sectors, *entries); |
||||
return bpb->root_entry_count; |
||||
} else { |
||||
ensure_fat(); |
||||
|
||||
uint16_t next_cluster = dir_cluster; |
||||
uint16_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); |
||||
|
||||
uint8_t *chunk = (uint8_t *)*entries; |
||||
next_cluster = dir_cluster; |
||||
while (next_cluster < 0xFFF8) { |
||||
ata_read_sectors(data_start + 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 cluster_count * bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t); |
||||
} |
||||
|
||||
return (uint16_t)-1; |
||||
} |
||||
|
||||
static fs_node_t *open_entry(uint16_t dir_cluster, const fat16_dir_entry_t *entries, uint16_t index) { |
||||
const fat16_dir_entry_t *entry = &entries[index]; |
||||
|
||||
if (!entry->name[0]) { |
||||
return NUL; |
||||
} |
||||
|
||||
char name[FILENAME_SIZE_LIMIT + 1]; |
||||
from_8_3(entry->name, entry->ext, name); |
||||
|
||||
return node_use(dir_cluster, index, name, entry->attributes & ATTRIBUTE_SUBDIRECTORY, entry->first_cluster, entry->size); |
||||
} |
||||
|
||||
static void write_directory(uint16_t dir_cluster, const fat16_dir_entry_t *entries, uint16_t count) { |
||||
ensure_bpb(); |
||||
|
||||
ASSERT((count * sizeof(fat16_dir_entry_t)) % (bpb->sectors_per_cluster * SECTOR_SIZE) == 0, |
||||
"write_directory: entries must be aligned to clusters"); |
||||
|
||||
if (!dir_cluster) { |
||||
ata_write_sectors(FIRST_PARTITION_SECTOR + bpb->reserved_sectors + bpb->fats_count * bpb->sectors_per_fat, |
||||
(uint8_t)((sizeof(fat16_dir_entry_t) * bpb->root_entry_count + SECTOR_SIZE - 1) / SECTOR_SIZE), entries); |
||||
} else { |
||||
ensure_fat(); |
||||
|
||||
uint8_t *chunk = (uint8_t *)entries; |
||||
uint16_t next_cluster = dir_cluster; |
||||
while (count) { |
||||
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, chunk); |
||||
chunk += bpb->sectors_per_cluster * SECTOR_SIZE; |
||||
count -= bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t); |
||||
if (count && fat[next_cluster] >= 0xFFF8) { |
||||
fat[next_cluster] = allocate_cluster(); |
||||
} |
||||
next_cluster = fat[next_cluster]; |
||||
} |
||||
flush_fat(); |
||||
} |
||||
} |
||||
|
||||
fs_node_t *fat16_open_root() { |
||||
ensure_bpb(); |
||||
|
||||
return node_use(0, UINT16_MAX, "", 1, 0, sizeof(fat16_dir_entry_t) * bpb->root_entry_count); |
||||
} |
||||
|
||||
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name, uint64_t flags) { |
||||
if (!directory || directory->type != FAT16 || !directory->is_dir || directory->removed) { |
||||
return NUL; |
||||
} |
||||
|
||||
uint16_t dir_cluster = ((fat16_node_t *)directory)->first_cluster; |
||||
|
||||
char name_8_3[12]; |
||||
to_8_3(name, name_8_3); |
||||
|
||||
fat16_dir_entry_t *entries; |
||||
uint16_t entries_count = read_directory(dir_cluster, &entries); |
||||
|
||||
fat16_dir_entry_t *entry = entries; |
||||
uint16_t index = 0; |
||||
while (index < entries_count && 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++; |
||||
index++; |
||||
} |
||||
|
||||
fs_node_t *result = open_entry(dir_cluster, entries, index); |
||||
|
||||
if (result && (flags & OPEN_EXCLUSIVE)) { |
||||
memory_free(entries); |
||||
return NUL; |
||||
} |
||||
|
||||
if (!result && (flags & OPEN_CREATE)) { |
||||
index = 0; |
||||
while (index < entries_count && entries[index].name[0]) { |
||||
index++; |
||||
} |
||||
if (index >= entries_count) { |
||||
if (!dir_cluster) { |
||||
memory_free(entries); |
||||
return NUL; |
||||
} else { |
||||
uint16_t new_entries_count = entries_count + bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t); |
||||
fat16_dir_entry_t *new_entries = memory_allocate(new_entries_count * sizeof(fat16_dir_entry_t)); |
||||
memory_copy(entries, entries_count * sizeof(fat16_dir_entry_t), new_entries); |
||||
memory_free(entries); |
||||
entries_count = new_entries_count; |
||||
entries = new_entries; |
||||
} |
||||
} |
||||
|
||||
memory_copy(name_8_3, 11, &entries[index]); |
||||
|
||||
if (flags & OPEN_DIRECTORY && !(flags & OPEN_FILE)) { |
||||
entries[index].attributes = ATTRIBUTE_SUBDIRECTORY; |
||||
entries[index].first_cluster = allocate_cluster(); |
||||
fat16_dir_entry_t *inner_entries = memory_allocate(bpb->sectors_per_cluster * SECTOR_SIZE); |
||||
memory_copy(". ", 11, inner_entries[0].name); |
||||
inner_entries[0].attributes = ATTRIBUTE_SUBDIRECTORY; |
||||
inner_entries[0].first_cluster = entries[index].first_cluster; |
||||
memory_copy(".. ", 11, inner_entries[1].name); |
||||
inner_entries[1].attributes = ATTRIBUTE_SUBDIRECTORY; |
||||
inner_entries[1].first_cluster = dir_cluster; |
||||
write_directory(entries[index].first_cluster, inner_entries, bpb->sectors_per_cluster * SECTOR_SIZE / sizeof(fat16_dir_entry_t)); |
||||
memory_free(inner_entries); |
||||
} |
||||
|
||||
write_directory(dir_cluster, entries, entries_count); |
||||
|
||||
result = open_entry(dir_cluster, entries, index); |
||||
} |
||||
|
||||
memory_free(entries); |
||||
|
||||
if (!result) { |
||||
return NUL; |
||||
} |
||||
|
||||
if (result->is_dir && !(flags & OPEN_DIRECTORY)) { |
||||
return NUL; |
||||
} |
||||
|
||||
if (!result->is_dir && !(flags & OPEN_FILE)) { |
||||
return NUL; |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
fs_node_t *fat16_open_at(const fs_node_t *directory, uint16_t index, uint64_t flags) { |
||||
if (!directory || directory->type != FAT16 || !directory->is_dir || directory->removed) { |
||||
return NUL; |
||||
} |
||||
|
||||
uint16_t dir_cluster = ((fat16_node_t *)directory)->first_cluster; |
||||
|
||||
fat16_dir_entry_t *entries; |
||||
uint16_t entries_count = read_directory(dir_cluster, &entries); |
||||
|
||||
uint16_t ei = 0, vi = 0; |
||||
while (ei < entries_count && entries[ei].name[0]) { |
||||
if ((uint8_t)entries[ei].name[0] != 0xE5 && (uint8_t)entries[ei].attributes != 0x0F) { |
||||
if (vi == index) { |
||||
break; |
||||
} |
||||
vi++; |
||||
} |
||||
ei++; |
||||
} |
||||
|
||||
fs_node_t *result = open_entry(dir_cluster, entries, ei); |
||||
|
||||
memory_free(entries); |
||||
|
||||
if (result && (flags & OPEN_EXCLUSIVE)) { |
||||
return NUL; |
||||
} |
||||
|
||||
if (!result) { |
||||
return NUL; |
||||
} |
||||
|
||||
if (result->is_dir && !(flags & OPEN_DIRECTORY)) { |
||||
return NUL; |
||||
} |
||||
|
||||
if (!result->is_dir && !(flags & OPEN_FILE)) { |
||||
return NUL; |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
fs_node_t *fat16_open_again(fs_node_t *source) { |
||||
ASSERT(source->refs < UINT32_MAX, "fat16_open_again: too many references"); |
||||
source->refs++; |
||||
return source; |
||||
} |
||||
|
||||
uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to) { |
||||
if (!file || file->type != FAT16 || file->is_dir) { |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
if (file->removed) { |
||||
return 0; |
||||
} |
||||
|
||||
if (offset >= file->size) { |
||||
return 0; |
||||
} |
||||
|
||||
if (offset + bytes >= file->size) { |
||||
bytes = file->size - offset; |
||||
} |
||||
|
||||
ensure_fat(); |
||||
|
||||
uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE; |
||||
uint32_t next_cluster = ((fat16_node_t *)file)->first_cluster; |
||||
|
||||
while (offset >= cluster_size) { |
||||
next_cluster = fat[next_cluster]; |
||||
offset -= cluster_size; |
||||
} |
||||
|
||||
uint32_t readden = 0; |
||||
|
||||
uint8_t *cursor = to; |
||||
|
||||
uint8_t *tmp = memory_allocate(cluster_size); |
||||
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); |
||||
uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset; |
||||
memory_copy(tmp + offset, prefix_size, cursor); |
||||
bytes -= prefix_size; |
||||
readden += prefix_size; |
||||
cursor += prefix_size; |
||||
next_cluster = fat[next_cluster]; |
||||
|
||||
while (bytes >= cluster_size) { |
||||
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor); |
||||
bytes -= cluster_size; |
||||
readden += cluster_size; |
||||
cursor += cluster_size; |
||||
next_cluster = fat[next_cluster]; |
||||
} |
||||
|
||||
if (bytes) { |
||||
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); |
||||
memory_copy(tmp, bytes, cursor); |
||||
readden += bytes; |
||||
} |
||||
|
||||
memory_free(tmp); |
||||
|
||||
return readden; |
||||
} |
||||
|
||||
uint64_t fat16_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes) { |
||||
if (!file || file->type != FAT16 || file->is_dir) { |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
if (file->removed) { |
||||
return 0; |
||||
} |
||||
|
||||
ensure_fat(); |
||||
|
||||
fat16_node_t *fat_file = (fat16_node_t *)file; |
||||
|
||||
if (!fat_file->first_cluster) { |
||||
fat_file->first_cluster = allocate_cluster(); |
||||
} |
||||
|
||||
uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE; |
||||
uint32_t next_cluster = fat_file->first_cluster; |
||||
|
||||
uint32_t allocated = cluster_size; |
||||
while (allocated < offset + bytes) { |
||||
if (fat[next_cluster] >= 0xFFF8) { |
||||
fat[next_cluster] = allocate_cluster(); |
||||
} |
||||
next_cluster = fat[next_cluster]; |
||||
allocated += cluster_size; |
||||
} |
||||
|
||||
flush_fat(); |
||||
|
||||
cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE; |
||||
next_cluster = fat_file->first_cluster; |
||||
|
||||
while (offset >= cluster_size) { |
||||
next_cluster = fat[next_cluster]; |
||||
offset -= cluster_size; |
||||
} |
||||
|
||||
uint32_t written = 0; |
||||
|
||||
const uint8_t *cursor = from; |
||||
|
||||
uint8_t *tmp = memory_allocate(cluster_size); |
||||
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); |
||||
uint32_t prefix_size = bytes <= cluster_size - offset ? bytes : cluster_size - offset; |
||||
memory_copy(cursor, prefix_size, tmp + offset); |
||||
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); |
||||
bytes -= prefix_size; |
||||
written += prefix_size; |
||||
cursor += prefix_size; |
||||
next_cluster = fat[next_cluster]; |
||||
|
||||
while (bytes >= cluster_size) { |
||||
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, cursor); |
||||
bytes -= cluster_size; |
||||
written += cluster_size; |
||||
cursor += cluster_size; |
||||
next_cluster = fat[next_cluster]; |
||||
} |
||||
|
||||
if (bytes) { |
||||
ata_read_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); |
||||
memory_copy(cursor, bytes, tmp); |
||||
ata_write_sectors(data_start + bpb->sectors_per_cluster * (next_cluster - 2), bpb->sectors_per_cluster, tmp); |
||||
written += bytes; |
||||
} |
||||
|
||||
memory_free(tmp); |
||||
|
||||
if (offset + written > file->size) { |
||||
file->size = offset + written; |
||||
|
||||
fat16_dir_entry_t *entries; |
||||
uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries); |
||||
entries[fat_file->dir_index].first_cluster = fat_file->first_cluster; |
||||
entries[fat_file->dir_index].size = file->size; |
||||
write_directory(fat_file->dir_cluster, entries, entries_count); |
||||
memory_free(entries); |
||||
} |
||||
|
||||
return written; |
||||
} |
||||
|
||||
uint64_t fat16_truncate(fs_node_t *file, uint32_t size) { |
||||
if (!file || file->type != FAT16 || file->is_dir) { |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
if (file->removed) { |
||||
return 0; |
||||
} |
||||
|
||||
if (file->size == size) { |
||||
return size; |
||||
} else if (file->size < size) { |
||||
uint32_t diff = size - file->size; |
||||
uint8_t *tmp = memory_allocate(diff); |
||||
uint64_t written = fat16_write(file, file->size, tmp, diff); |
||||
memory_free(tmp); |
||||
return written == (uint64_t)-1 ? (uint64_t)-1 : file->size + written; |
||||
} else if (file->size > size) { |
||||
fat16_node_t *fat_file = (fat16_node_t *)file; |
||||
|
||||
uint32_t cluster_size = bpb->sectors_per_cluster * SECTOR_SIZE; |
||||
uint32_t prev_cluster = 0; |
||||
uint32_t next_cluster = fat_file->first_cluster; |
||||
|
||||
ensure_fat(); |
||||
uint32_t allocated = 0; |
||||
while (allocated < size) { |
||||
prev_cluster = next_cluster; |
||||
next_cluster = fat[next_cluster]; |
||||
allocated += cluster_size; |
||||
} |
||||
if (prev_cluster) { |
||||
fat[prev_cluster] = 0xFFFF; |
||||
} else { |
||||
fat_file->first_cluster = 0; |
||||
} |
||||
do { |
||||
uint32_t swap = fat[next_cluster]; |
||||
fat[next_cluster] = 0; |
||||
next_cluster = swap; |
||||
} while (next_cluster < 0xFFF8); |
||||
flush_fat(); |
||||
|
||||
file->size = size; |
||||
fat16_dir_entry_t *entries; |
||||
uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries); |
||||
entries[fat_file->dir_index].first_cluster = fat_file->first_cluster; |
||||
entries[fat_file->dir_index].size = file->size; |
||||
write_directory(fat_file->dir_cluster, entries, entries_count); |
||||
memory_free(entries); |
||||
|
||||
return size; |
||||
} |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
uint64_t fat16_remove(fs_node_t *file) { |
||||
if (!file || file->type != FAT16) { |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
if (file->removed) { |
||||
return 0; |
||||
} |
||||
|
||||
fat16_node_t *fat_file = (fat16_node_t *)file; |
||||
|
||||
if (file->is_dir) { |
||||
fat16_dir_entry_t *subentries; |
||||
uint16_t subentries_count = read_directory(fat_file->first_cluster, &subentries); |
||||
uint16_t ei = 0, vi = 0; |
||||
while (ei < subentries_count && subentries[ei].name[0]) { |
||||
if ((uint8_t)subentries[ei].name[0] != 0xE5 && (uint8_t)subentries[ei].attributes != 0x0F) { |
||||
vi++; |
||||
} |
||||
ei++; |
||||
} |
||||
memory_free(subentries); |
||||
if (vi > 2) { |
||||
return (uint64_t)-1; |
||||
} |
||||
} |
||||
|
||||
file->removed = 1; |
||||
|
||||
if (fat_file->first_cluster) { |
||||
ensure_fat(); |
||||
uint64_t next_cluster = fat_file->first_cluster; |
||||
do { |
||||
uint32_t swap = fat[next_cluster]; |
||||
fat[next_cluster] = 0; |
||||
next_cluster = swap; |
||||
} while (next_cluster < 0xFFF8); |
||||
flush_fat(); |
||||
} |
||||
|
||||
fat16_dir_entry_t *entries; |
||||
uint16_t entries_count = read_directory(fat_file->dir_cluster, &entries); |
||||
entries[fat_file->dir_index].name[0] = 0xE5; |
||||
entries[fat_file->dir_index].first_cluster = 0; |
||||
entries[fat_file->dir_index].size = 0; |
||||
write_directory(fat_file->dir_cluster, entries, entries_count); |
||||
memory_free(entries); |
||||
|
||||
return 1; |
||||
} |
||||
|
||||
typedef struct { |
||||
stream_t stream; |
||||
fs_node_t *node; |
||||
uint32_t offset; |
||||
} fat16_file_stream_t; |
||||
|
||||
static uint64_t file_stream_write(stream_t *self, const char *from, uint64_t bytes) { |
||||
if (!self) { |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; |
||||
uint64_t written = fat16_write(ffs->node, ffs->offset, from, bytes > UINT32_MAX ? UINT32_MAX : (uint32_t)bytes); |
||||
if (written != (uint64_t)-1) { |
||||
ffs->offset += written; |
||||
} |
||||
return written; |
||||
} |
||||
|
||||
static uint64_t file_stream_read(stream_t *self, uint64_t max, char *to) { |
||||
if (!self) { |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; |
||||
uint64_t readden = fat16_read(ffs->node, ffs->offset, max > UINT32_MAX ? UINT32_MAX : (uint32_t)max, to); |
||||
if (readden != (uint64_t)-1) { |
||||
ffs->offset += readden; |
||||
} |
||||
return readden; |
||||
} |
||||
|
||||
static uint64_t file_stream_truncate(stream_t *self, uint64_t size) { |
||||
if (!self) { |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; |
||||
uint64_t resized = fat16_truncate(ffs->node, size > UINT32_MAX ? UINT32_MAX : (uint32_t)size); |
||||
if (resized != (uint64_t)-1 && resized < ffs->offset) { |
||||
ffs->offset = (uint32_t)resized; |
||||
} |
||||
return resized; |
||||
} |
||||
|
||||
static void file_stream_close(stream_t *self) { |
||||
if (!self) { |
||||
return; |
||||
} |
||||
|
||||
fat16_file_stream_t *ffs = (fat16_file_stream_t *)self; |
||||
fat16_close(ffs->node); |
||||
memory_free(self); |
||||
} |
||||
|
||||
typedef struct { |
||||
stream_t stream; |
||||
fs_node_t *node; |
||||
uint16_t index; |
||||
} fat16_directory_stream_t; |
||||
|
||||
static uint64_t directory_stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from, |
||||
__attribute__((unused)) uint64_t bytes) { |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
static uint64_t directory_stream_read(stream_t *self, uint64_t max, char *to) { |
||||
if (!self) { |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
fat16_directory_stream_t *fds = (fat16_directory_stream_t *)self; |
||||
fs_node_t *node = fat16_open_at(fds->node, fds->index++, OPEN_FILE | OPEN_DIRECTORY); |
||||
if (!node) { |
||||
return 0; |
||||
} |
||||
uint64_t length = string_length(node->name); |
||||
uint64_t to_read = length > max ? max : length; |
||||
memory_copy(node->name, to_read, to); |
||||
return to_read; |
||||
} |
||||
|
||||
static uint64_t directory_stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) { |
||||
return (uint64_t)-1; |
||||
} |
||||
|
||||
static void directory_stream_close(stream_t *self) { |
||||
if (!self) { |
||||
return; |
||||
} |
||||
|
||||
fat16_directory_stream_t *fds = (fat16_directory_stream_t *)self; |
||||
fat16_close(fds->node); |
||||
memory_free(self); |
||||
} |
||||
|
||||
stream_t *fat16_open_stream(fs_node_t *source) { |
||||
if (!source) { |
||||
return NUL; |
||||
} |
||||
|
||||
if (!source->is_dir) { |
||||
fat16_file_stream_t *result = memory_allocate(sizeof(fat16_file_stream_t)); |
||||
result->stream.read = file_stream_read; |
||||
result->stream.write = file_stream_write; |
||||
result->stream.truncate = file_stream_truncate; |
||||
result->stream.close = file_stream_close; |
||||
result->node = fat16_open_again(source); |
||||
result->offset = 0; |
||||
return (stream_t *)result; |
||||
} else { |
||||
fat16_directory_stream_t *result = memory_allocate(sizeof(fat16_directory_stream_t)); |
||||
result->stream.read = directory_stream_read; |
||||
result->stream.write = directory_stream_write; |
||||
result->stream.truncate = directory_stream_truncate; |
||||
result->stream.close = directory_stream_close; |
||||
result->node = fat16_open_again(source); |
||||
result->index = 0; |
||||
return (stream_t *)result; |
||||
} |
||||
} |
||||
|
||||
void fat16_close(fs_node_t *node) { |
||||
if (!node || node->type != FAT16) { |
||||
return; |
||||
} |
||||
|
||||
node_free(node); |
||||
} |
||||
@ -0,0 +1,26 @@ |
||||
#pragma once |
||||
|
||||
#include "src/kernel/fs.h" |
||||
#include "src/kernel/stream.h" |
||||
|
||||
#define FAT16 1 |
||||
|
||||
fs_node_t *fat16_open_root(); |
||||
|
||||
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name, uint64_t flags); |
||||
|
||||
fs_node_t *fat16_open_at(const fs_node_t *directory, uint16_t index, uint64_t flags); |
||||
|
||||
fs_node_t *fat16_open_again(fs_node_t *source); |
||||
|
||||
stream_t *fat16_open_stream(fs_node_t *source); |
||||
|
||||
uint64_t fat16_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to); |
||||
|
||||
uint64_t fat16_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes); |
||||
|
||||
uint64_t fat16_truncate(fs_node_t *file, uint32_t size); |
||||
|
||||
uint64_t fat16_remove(fs_node_t *file); |
||||
|
||||
void fat16_close(fs_node_t *node); |
||||
@ -0,0 +1,42 @@ |
||||
#include "src/kernel/fs.h" |
||||
#include "src/kernel/fat16.h" |
||||
|
||||
fs_node_t *fs_open_root() { |
||||
return fat16_open_root(); |
||||
} |
||||
|
||||
fs_node_t *fs_open_by(const fs_node_t *directory, const char *name, uint64_t flags) { |
||||
return fat16_open_by(directory, name, flags); |
||||
} |
||||
|
||||
fs_node_t *fs_open_at(const fs_node_t *directory, uint16_t index, uint64_t flags) { |
||||
return fat16_open_at(directory, index, flags); |
||||
} |
||||
|
||||
fs_node_t *fs_open_again(fs_node_t *source) { |
||||
return fat16_open_again(source); |
||||
} |
||||
|
||||
stream_t *fs_open_stream(fs_node_t *source) { |
||||
return fat16_open_stream(source); |
||||
} |
||||
|
||||
void fs_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to) { |
||||
fat16_read(file, offset, bytes, to); |
||||
} |
||||
|
||||
void fs_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes) { |
||||
fat16_write(file, offset, from, bytes); |
||||
} |
||||
|
||||
void fs_truncate(fs_node_t *file, uint32_t size) { |
||||
fat16_truncate(file, size); |
||||
} |
||||
|
||||
void fs_remove(fs_node_t *file) { |
||||
fat16_remove(file); |
||||
} |
||||
|
||||
void fs_close(fs_node_t *node) { |
||||
fat16_close(node); |
||||
} |
||||
@ -0,0 +1,34 @@ |
||||
#pragma once |
||||
|
||||
#include "src/kernel/stream.h" |
||||
|
||||
#define FILENAME_SIZE_LIMIT 255 |
||||
|
||||
typedef struct fs_node { |
||||
uint8_t type; |
||||
uint32_t refs; |
||||
char name[FILENAME_SIZE_LIMIT + 1]; |
||||
uint32_t size; |
||||
uint8_t is_dir; |
||||
uint8_t removed; |
||||
} fs_node_t; |
||||
|
||||
fs_node_t *fs_open_root(); |
||||
|
||||
fs_node_t *fs_open_by(const fs_node_t *directory, const char *name, uint64_t flags); |
||||
|
||||
fs_node_t *fs_open_at(const fs_node_t *directory, uint16_t index, uint64_t flags); |
||||
|
||||
fs_node_t *fs_open_again(fs_node_t *source); |
||||
|
||||
stream_t *fs_open_stream(fs_node_t *source); |
||||
|
||||
void fs_read(const fs_node_t *file, uint32_t offset, uint32_t bytes, void *to); |
||||
|
||||
void fs_write(fs_node_t *file, uint32_t offset, const void *from, uint32_t bytes); |
||||
|
||||
void fs_truncate(fs_node_t *file, uint32_t size); |
||||
|
||||
void fs_remove(fs_node_t *file); |
||||
|
||||
void fs_close(fs_node_t *node); |
||||
@ -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(); |
||||
@ -1,4 +1,4 @@ |
||||
#include "src/idt.h" |
||||
#include "src/kernel/idt.h" |
||||
|
||||
struct idt_entry { |
||||
uint16_t offset_low; |
||||
@ -0,0 +1,103 @@ |
||||
#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/pipe.h" |
||||
#include "src/kernel/process.h" |
||||
#include "src/kernel/syscall.h" |
||||
#include "src/kernel/timer.h" |
||||
#include "src/kernel/tss.h" |
||||
#include "src/kernel/util.h" |
||||
#include "src/kernel/vga.h" |
||||
#include "src/lib/layout.h" |
||||
#include "src/lib/memory.h" |
||||
#include "src/lib/syscall.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); |
||||
|
||||
gdt_init(); |
||||
syscall_init(); |
||||
|
||||
process_t *kernel = memory_allocate(sizeof(process_t)); |
||||
kernel->pml4 = (uint64_t *)KERNEL_VIRTUAL_PML4; |
||||
kernel->pid = 0; |
||||
kernel->state = PROCESS_RUNNING; |
||||
kernel->kernel_stack = kernel->kernel_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE; |
||||
kernel->user_stack = kernel->user_rsp = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE; |
||||
kernel->cwd = memory_allocate(sizeof(path_t)); |
||||
kernel->fds[STDIN] = keyboard_init(); |
||||
kernel->fds[STDOUT] = vga_init(); |
||||
kernel->fds[STDERR] = kernel->fds[STDOUT]; |
||||
kernel->free_fd = STDERR + 1; |
||||
kernel->code = EXIT_CODE_OK; |
||||
|
||||
current_process = kernel; |
||||
tss.rsp0 = (uint64_t)kernel->kernel_stack; |
||||
|
||||
fs_node_t *terminal_node = path_open_node(kernel->cwd, "bin/terminal", OPEN_FILE); |
||||
fs_node_t *shell_node = path_open_node(kernel->cwd, "bin/shell", OPEN_FILE); |
||||
|
||||
ASSERT(terminal_node, "kernel: terminal not found"); |
||||
ASSERT(shell_node, "kernel: shell not found"); |
||||
|
||||
uint8_t *terminal_code = memory_allocate(terminal_node->size); |
||||
fs_read(terminal_node, 0, terminal_node->size, terminal_code); |
||||
fs_close(terminal_node); |
||||
|
||||
uint8_t *shell_code = memory_allocate(shell_node->size); |
||||
fs_read(shell_node, 0, shell_node->size, shell_code); |
||||
fs_close(shell_node); |
||||
|
||||
process_t *terminal = process_create(kernel, terminal_code, terminal_node->size, STDIN, STDOUT); |
||||
memory_free(terminal_code); |
||||
|
||||
process_t *shell = process_create(kernel, shell_code, shell_node->size, STDIN, STDOUT); |
||||
memory_free(shell_code); |
||||
|
||||
uint64_t *terminal_stack = (uint64_t *)terminal->user_stack; |
||||
*(--terminal_stack) = 0; |
||||
terminal->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)terminal->user_stack - (uint64_t)terminal_stack)); |
||||
|
||||
uint64_t *shell_stack = (uint64_t *)shell->user_stack; |
||||
*(--shell_stack) = 0; |
||||
shell->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)shell->user_stack - (uint64_t)shell_stack)); |
||||
|
||||
pipe_init(&(terminal->fds[terminal->free_fd++]), &(shell->fds[0])); |
||||
|
||||
timer_init(); |
||||
|
||||
__asm__ volatile("sti; hlt"); |
||||
} |
||||
@ -0,0 +1,172 @@ |
||||
#include "src/kernel/keyboard.h" |
||||
#include "src/kernel/idt.h" |
||||
#include "src/kernel/process.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 uint64_t stream_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from, |
||||
__attribute__((unused)) uint64_t bytes) { |
||||
return 0; |
||||
} |
||||
|
||||
static uint64_t stream_read(__attribute__((unused)) stream_t *self, uint64_t max, char *to) { |
||||
while (!buffer_length) { |
||||
__asm__ volatile("sti"); |
||||
process_next(); |
||||
__asm__ volatile("cli"); |
||||
} |
||||
|
||||
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 uint64_t stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) { |
||||
return size; |
||||
} |
||||
|
||||
static void stream_close(__attribute__((unused)) stream_t *self) { |
||||
} |
||||
|
||||
static stream_t stream = {stream_write, stream_read, stream_truncate, stream_close}; |
||||
|
||||
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(); |
||||
@ -1,7 +1,7 @@ |
||||
ENTRY(kernel_main) |
||||
|
||||
SECTIONS { |
||||
. = 0x0000000000020000; |
||||
. = 0xFFFFFFFF80020000; |
||||
|
||||
.text : { |
||||
*(.text) |
||||
@ -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,98 @@ |
||||
#include "src/kernel/path.h" |
||||
#include "src/kernel/fs.h" |
||||
#include "src/kernel/stream.h" |
||||
#include "src/lib/memory.h" |
||||
#include "src/lib/string.h" |
||||
#include "src/lib/syscall.h" |
||||
#include "src/lib/util.h" |
||||
|
||||
path_t *path_open(const path_t *base, const char *path, uint64_t flags) { |
||||
if (!base || !path) { |
||||
return NUL; |
||||
} |
||||
|
||||
uint64_t length = string_length(path); |
||||
char *path_own = memory_allocate(length + 1); |
||||
memory_copy(path, length + 1, path_own); |
||||
|
||||
path_t *result = memory_allocate(sizeof(path_t)); |
||||
|
||||
char *path_components[PATH_DEPTH]; |
||||
uint8_t path_length = (uint8_t)string_split(path_own, '/', PATH_DEPTH, path_components); |
||||
if (!string_empty(path_components[0])) { |
||||
for (uint8_t i = 0; i < base->depth; i++) { |
||||
result->stack[result->depth++] = fs_open_again(base->stack[i]); |
||||
} |
||||
} |
||||
|
||||
for (uint8_t i = 0; i < path_length; i++) { |
||||
if (!result->depth) { |
||||
result->stack[result->depth++] = fs_open_root(); |
||||
} |
||||
|
||||
if (string_empty(path_components[i]) || string_equal(path_components[i], ".")) { |
||||
if (i == path_length - 1 && result->depth) { |
||||
// TODO Apply flags to current top of stack.
|
||||
} |
||||
continue; |
||||
} else if (string_equal(path_components[i], "..")) { |
||||
if (result->depth > 1) { |
||||
fs_close(result->stack[--result->depth]); |
||||
} |
||||
} else { |
||||
fs_node_t *next = fs_open_by(result->stack[result->depth - 1], path_components[i], |
||||
i < path_length - 1 ? (flags & ~(uint64_t)OPEN_EXCLUSIVE) | OPEN_DIRECTORY : flags); |
||||
if (!next || result->depth >= PATH_DEPTH) { |
||||
path_close(result); |
||||
memory_free(path_own); |
||||
return NUL; |
||||
} else { |
||||
result->stack[result->depth++] = next; |
||||
} |
||||
} |
||||
} |
||||
|
||||
memory_free(path_own); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
path_t *path_open_again(const path_t *path) { |
||||
if (!path) { |
||||
return NUL; |
||||
} |
||||
|
||||
path_t *p = memory_allocate(sizeof(path_t)); |
||||
p->depth = path->depth; |
||||
for (uint8_t i = 0; i < path->depth; i++) { |
||||
p->stack[i] = fs_open_again(path->stack[i]); |
||||
} |
||||
return p; |
||||
} |
||||
|
||||
fs_node_t *path_open_node(const path_t *base, const char *path, uint64_t flags) { |
||||
path_t *p = path_open(base, path, flags); |
||||
if (!p) { |
||||
return NUL; |
||||
} |
||||
fs_node_t *n = fs_open_again(p->stack[p->depth - 1]); |
||||
path_close(p); |
||||
return n; |
||||
} |
||||
|
||||
stream_t *path_open_stream(const path_t *base, const char *path, uint64_t flags) { |
||||
path_t *p = path_open(base, path, flags); |
||||
if (!p) { |
||||
return NUL; |
||||
} |
||||
stream_t *s = fs_open_stream(p->stack[p->depth - 1]); |
||||
path_close(p); |
||||
return s; |
||||
} |
||||
|
||||
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,21 @@ |
||||
#pragma once |
||||
|
||||
#include "src/kernel/fs.h" |
||||
#include "src/kernel/stream.h" |
||||
|
||||
#define PATH_DEPTH 255 |
||||
|
||||
typedef struct { |
||||
fs_node_t *stack[PATH_DEPTH]; |
||||
uint8_t depth; |
||||
} path_t; |
||||
|
||||
path_t *path_open(const path_t *base, const char *path, uint64_t flags); |
||||
|
||||
path_t *path_open_again(const path_t *path); |
||||
|
||||
fs_node_t *path_open_node(const path_t *base, const char *path, uint64_t flags); |
||||
|
||||
stream_t *path_open_stream(const path_t *base, const char *path, uint64_t flags); |
||||
|
||||
void path_close(path_t *path); |
||||
@ -1,5 +1,5 @@ |
||||
#include "src/pic.h" |
||||
#include "src/util.h" |
||||
#include "src/kernel/pic.h" |
||||
#include "src/kernel/util.h" |
||||
|
||||
static void pic_remap() { |
||||
uint8_t mask1 = inb(0x21); |
||||
@ -0,0 +1,134 @@ |
||||
#include "src/kernel/pipe.h" |
||||
#include "src/kernel/process.h" |
||||
#include "src/kernel/stream.h" |
||||
#include "src/lib/memory.h" |
||||
|
||||
#define PIPE_BUFFER_SIZE 65536 |
||||
|
||||
typedef struct { |
||||
char buffer[PIPE_BUFFER_SIZE]; |
||||
uint64_t begin; |
||||
uint64_t end; |
||||
uint8_t write_closed; |
||||
uint8_t read_closed; |
||||
} pipe_t; |
||||
|
||||
typedef struct { |
||||
stream_t stream; |
||||
pipe_t *pipe; |
||||
} pipe_read_stream_t; |
||||
|
||||
typedef struct { |
||||
stream_t stream; |
||||
pipe_t *pipe; |
||||
} pipe_write_stream_t; |
||||
|
||||
static uint64_t null_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max, |
||||
__attribute__((unused)) char *to) { |
||||
return 0; |
||||
} |
||||
|
||||
static uint64_t null_write(__attribute__((unused)) stream_t *self, __attribute__((unused)) const char *from, |
||||
__attribute__((unused)) uint64_t bytes) { |
||||
return 0; |
||||
} |
||||
|
||||
static uint64_t pipe_read(stream_t *self, uint64_t max, char *to) { |
||||
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe; |
||||
|
||||
while (pipe->begin == pipe->end) { |
||||
if (pipe->write_closed) { |
||||
return 0; |
||||
} |
||||
__asm__ volatile("sti"); |
||||
process_next(); |
||||
__asm__ volatile("cli"); |
||||
} |
||||
|
||||
uint64_t available = (pipe->end + PIPE_BUFFER_SIZE - pipe->begin) % PIPE_BUFFER_SIZE; |
||||
uint64_t to_read = available < max ? available : max; |
||||
|
||||
if (pipe->begin + to_read <= PIPE_BUFFER_SIZE) { |
||||
memory_copy(pipe->buffer + pipe->begin, to_read, to); |
||||
} else { |
||||
uint64_t chunk = PIPE_BUFFER_SIZE - pipe->begin; |
||||
memory_copy(pipe->buffer + pipe->begin, chunk, to); |
||||
memory_copy(pipe->buffer, to_read - chunk, to + chunk); |
||||
} |
||||
|
||||
pipe->begin = (pipe->begin + to_read) % PIPE_BUFFER_SIZE; |
||||
return to_read; |
||||
} |
||||
|
||||
static uint64_t pipe_write(stream_t *self, const char *from, uint64_t bytes) { |
||||
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe; |
||||
|
||||
uint64_t written = 0; |
||||
while (written < bytes) { |
||||
while (((pipe->end + 1) % PIPE_BUFFER_SIZE) == pipe->begin) { |
||||
if (pipe->read_closed) { |
||||
return written; |
||||
} |
||||
__asm__ volatile("sti"); |
||||
process_next(); |
||||
__asm__ volatile("cli"); |
||||
} |
||||
|
||||
uint64_t available = PIPE_BUFFER_SIZE - (pipe->end + PIPE_BUFFER_SIZE - pipe->begin) % PIPE_BUFFER_SIZE - 1; |
||||
uint64_t to_write = available < bytes - written ? available : bytes - written; |
||||
|
||||
if (pipe->end + to_write <= PIPE_BUFFER_SIZE) { |
||||
memory_copy(from, to_write, pipe->buffer + pipe->end); |
||||
} else { |
||||
uint64_t chunk = PIPE_BUFFER_SIZE - pipe->end; |
||||
memory_copy(from, chunk, pipe->buffer + pipe->end); |
||||
memory_copy(from + chunk, to_write - chunk, pipe->buffer); |
||||
} |
||||
|
||||
pipe->end = (pipe->end + to_write) % PIPE_BUFFER_SIZE; |
||||
written += to_write; |
||||
} |
||||
|
||||
return written; |
||||
} |
||||
|
||||
static void read_close(stream_t *self) { |
||||
pipe_t *pipe = ((pipe_read_stream_t *)self)->pipe; |
||||
|
||||
pipe->read_closed = 1; |
||||
|
||||
if (pipe->read_closed && pipe->write_closed) { |
||||
memory_free(pipe); |
||||
} |
||||
memory_free(self); |
||||
} |
||||
|
||||
static void write_close(stream_t *self) { |
||||
pipe_t *pipe = ((pipe_write_stream_t *)self)->pipe; |
||||
|
||||
pipe->write_closed = 1; |
||||
|
||||
if (pipe->read_closed && pipe->write_closed) { |
||||
memory_free(pipe); |
||||
} |
||||
memory_free(self); |
||||
} |
||||
|
||||
void pipe_init(stream_t **write, stream_t **read) { |
||||
pipe_t *pipe = memory_allocate(sizeof(pipe_t)); |
||||
|
||||
pipe_write_stream_t *ws = memory_allocate(sizeof(pipe_write_stream_t)); |
||||
ws->stream.read = null_read; |
||||
ws->stream.write = pipe_write; |
||||
ws->stream.close = write_close; |
||||
ws->pipe = pipe; |
||||
|
||||
pipe_read_stream_t *rs = memory_allocate(sizeof(pipe_read_stream_t)); |
||||
rs->stream.read = pipe_read; |
||||
rs->stream.write = null_write; |
||||
rs->stream.close = read_close; |
||||
rs->pipe = pipe; |
||||
|
||||
*write = (stream_t *)ws; |
||||
*read = (stream_t *)rs; |
||||
} |
||||
@ -0,0 +1,5 @@ |
||||
#pragma once |
||||
|
||||
#include "src/kernel/stream.h" |
||||
|
||||
void pipe_init(stream_t **write, stream_t **read); |
||||
@ -0,0 +1,50 @@ |
||||
bits 64 |
||||
|
||||
global process_trampoline |
||||
process_trampoline: |
||||
extern current_process |
||||
mov rax, [current_process] |
||||
mov rdx, [rax + 40] ; current_process->user_rsp |
||||
|
||||
; Keep in sync with `gdt.h` and `layout.h` |
||||
mov rax, 0x0000000000400000 |
||||
mov rbx, 0x2B |
||||
mov rcx, 0x202 |
||||
mov rsi, 0x23 |
||||
|
||||
push rsi |
||||
push rdx |
||||
push rcx |
||||
push rbx |
||||
push rax |
||||
iretq |
||||
|
||||
global process_switch_to |
||||
; process_switch_to(uint64_t *save_rsp, uint64_t new_rsp, uint64_t new_pml4_phys) |
||||
process_switch_to: |
||||
; save callee-saved registers of the OUTGOING process |
||||
push rbp |
||||
push rbx |
||||
push r12 |
||||
push r13 |
||||
push r14 |
||||
push r15 |
||||
|
||||
; save current rsp into *save_rsp (rdi) |
||||
mov [rdi], rsp |
||||
|
||||
; switch address space |
||||
mov cr3, rdx ; rdx = new_pml4_phys (3rd arg) |
||||
|
||||
; switch to incoming process's stack |
||||
mov rsp, rsi ; rsi = new_rsp (2nd arg) |
||||
|
||||
; restore callee-saved registers of the INCOMING process |
||||
pop r15 |
||||
pop r14 |
||||
pop r13 |
||||
pop r12 |
||||
pop rbx |
||||
pop rbp |
||||
|
||||
ret |
||||
@ -0,0 +1,191 @@ |
||||
#include "src/kernel/process.h" |
||||
#include "src/kernel/memory.h" |
||||
#include "src/kernel/path.h" |
||||
#include "src/kernel/tss.h" |
||||
#include "src/kernel/util.h" |
||||
#include "src/lib/layout.h" |
||||
#include "src/lib/memory.h" |
||||
#include "src/lib/util.h" |
||||
|
||||
#define MAX_PROCESSES 256 |
||||
|
||||
static process_t *processes[MAX_PROCESSES]; |
||||
static uint64_t free_pid = 1; |
||||
|
||||
process_t *current_process; |
||||
|
||||
process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size, uint64_t stdin, uint64_t stdout) { |
||||
if (size >= USER_VIRTUAL_HEAP - USER_VIRTUAL_CODE) { |
||||
return NUL; |
||||
} |
||||
|
||||
uint64_t free_pi; |
||||
for (free_pi = 0; free_pi <= MAX_PROCESSES; free_pi++) { |
||||
if (!processes[free_pi]) { |
||||
break; |
||||
} |
||||
} |
||||
if (free_pi == MAX_PROCESSES) { |
||||
return NUL; |
||||
} |
||||
|
||||
if (stdin >= MAX_FDS || !parent->fds[stdin] || stdout > MAX_FDS || !parent->fds[stdout]) { |
||||
return NUL; |
||||
} |
||||
|
||||
process_t *proc = processes[free_pi] = memory_allocate(sizeof(process_t)); |
||||
|
||||
proc->parent = parent; |
||||
|
||||
proc->kernel_stack = (uint8_t *)memory_allocate(PAGE_SIZE) + PAGE_SIZE; |
||||
|
||||
uint64_t *kstackv = (uint64_t *)((uint8_t *)proc->kernel_stack); |
||||
*(--kstackv) = (uint64_t)process_trampoline; // "return address" for switch_to's ret
|
||||
*(--kstackv) = 0; // r15
|
||||
*(--kstackv) = 0; // r14
|
||||
*(--kstackv) = 0; // r13
|
||||
*(--kstackv) = 0; // r12
|
||||
*(--kstackv) = 0; // rbx
|
||||
*(--kstackv) = 0; // rbp
|
||||
proc->kernel_rsp = kstackv; |
||||
|
||||
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); |
||||
|
||||
void *ustackp = memory_page_allocate(); |
||||
proc->user_stack = PHYS_TO_VIRT((uint64_t)ustackp + PAGE_SIZE); |
||||
memory_page_map(proc->pml4, (void *)USER_VIRTUAL_STACK, ustackp, 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_WRITABLE | PAGE_USER); // TODO Restrict writeability of code.
|
||||
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->pid = free_pid++; |
||||
proc->state = PROCESS_RUNNING; |
||||
proc->cwd = path_open_again(parent->cwd); |
||||
proc->fds[STDIN] = parent->fds[stdin]; |
||||
proc->fds[STDOUT] = parent->fds[stdout]; |
||||
proc->fds[STDERR] = parent->fds[STDERR]; |
||||
proc->free_fd = STDERR + 1; |
||||
proc->code = EXIT_CODE_OK; |
||||
|
||||
return proc; |
||||
} |
||||
|
||||
process_t *process_get(uint64_t pid) { |
||||
for (uint64_t i = 0; i < MAX_PROCESSES; i++) { |
||||
if (processes[i] && processes[i]->pid == pid) { |
||||
return processes[i]; |
||||
} |
||||
} |
||||
return NUL; |
||||
} |
||||
|
||||
void process_next() { |
||||
process_t *next = NUL; |
||||
uint64_t cpi = 0; |
||||
|
||||
for (uint64_t i = 0; i < MAX_PROCESSES; i++) { |
||||
if (processes[i] == current_process) { |
||||
cpi = i; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
for (uint64_t i = 1; i <= MAX_PROCESSES; i++) { |
||||
process_t *candidate = processes[(cpi + i) % MAX_PROCESSES]; |
||||
if (candidate && candidate->state == PROCESS_RUNNING) { |
||||
next = candidate; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (!next) { |
||||
outw(0x604, 0x2000); |
||||
__asm__ volatile("cli; hlt"); |
||||
} |
||||
|
||||
if (next == current_process) { |
||||
return; |
||||
} |
||||
|
||||
process_t *prev = current_process; |
||||
tss.rsp0 = (uint64_t)next->kernel_stack; |
||||
current_process = next; |
||||
__asm__ volatile("cli"); |
||||
process_switch_to(&prev->kernel_rsp, next->kernel_rsp, VIRT_TO_PHYS(next->pml4)); |
||||
__asm__ volatile("sti"); |
||||
} |
||||
|
||||
void process_destroy(process_t *proc) { |
||||
for (uint64_t i = STDERR + 1; i < MAX_FDS; i++) { |
||||
if (proc->fds[i]) { |
||||
proc->fds[i]->close(proc->fds[i]); |
||||
proc->fds[i] = NUL; |
||||
} |
||||
} |
||||
|
||||
path_close((path_t *)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(VIRT_TO_PHYS(proc->pml4)); |
||||
|
||||
memory_free(proc->kernel_stack - PAGE_SIZE); |
||||
|
||||
for (uint64_t i = 0; i < MAX_PROCESSES; i++) { |
||||
if (processes[i] == proc) { |
||||
processes[i] = NUL; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
memory_free(proc); |
||||
} |
||||
@ -0,0 +1,47 @@ |
||||
#pragma once |
||||
|
||||
#include "src/kernel/path.h" |
||||
#include "src/kernel/stream.h" |
||||
#include "src/lib/util.h" |
||||
|
||||
#define USER_RFLAGS 0x202 |
||||
|
||||
#define MAX_FDS 16 |
||||
|
||||
typedef enum { |
||||
PROCESS_RUNNING, |
||||
PROCESS_WAITING, |
||||
PROCESS_ZOMBIE, |
||||
} process_state_t; |
||||
|
||||
typedef struct process { |
||||
const struct process *parent; |
||||
uint64_t *pml4; |
||||
void *kernel_stack; |
||||
void *kernel_rsp; |
||||
void *user_stack; |
||||
void *user_rsp; |
||||
uint64_t pid; |
||||
process_state_t state; |
||||
uint64_t waiting_for; |
||||
const path_t *cwd; |
||||
stream_t *fds[MAX_FDS]; |
||||
uint64_t free_fd; |
||||
exit_code_t code; |
||||
} process_t; |
||||
|
||||
typedef void (*app_t)(process_t *proc, uint8_t argc, char **argv); |
||||
|
||||
extern process_t *current_process; |
||||
|
||||
extern void process_trampoline(); |
||||
|
||||
process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t size, uint64_t stdin, uint64_t stdout); |
||||
|
||||
process_t *process_get(uint64_t pid); |
||||
|
||||
extern void process_switch_to(void **save_rsp, void *new_rsp, void *new_pml4_phys); |
||||
|
||||
void process_next(); |
||||
|
||||
void process_destroy(process_t *proc); |
||||
@ -0,0 +1,40 @@ |
||||
#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 uint64_t (*stream_write_t)(stream_t *self, const char *from, uint64_t bytes); |
||||
typedef uint64_t (*stream_read_t)(stream_t *self, uint64_t max, char *to); |
||||
typedef uint64_t (*stream_truncate_t)(stream_t *self, uint64_t size); |
||||
typedef void (*stream_close_t)(stream_t *self); |
||||
|
||||
typedef struct stream { |
||||
stream_write_t write; |
||||
stream_read_t read; |
||||
stream_truncate_t truncate; |
||||
stream_close_t close; |
||||
} stream_t; |
||||
@ -0,0 +1,57 @@ |
||||
bits 64 |
||||
|
||||
extern tss |
||||
extern syscall_dispatch |
||||
extern current_process |
||||
|
||||
global syscall_entry |
||||
|
||||
syscall_entry: |
||||
mov [rel user_rsp_tmp], rsp |
||||
|
||||
mov rsp, [tss + 4] |
||||
|
||||
push rax |
||||
push rcx |
||||
mov rax, [rel current_process] |
||||
mov rcx, [rel user_rsp_tmp] |
||||
mov [rax + 40], rcx ; current_process->user_rsp |
||||
pop rcx |
||||
pop rax |
||||
|
||||
push rcx |
||||
push r11 |
||||
push rbp |
||||
push rbx |
||||
push r12 |
||||
push r13 |
||||
push r14 |
||||
push r15 |
||||
|
||||
mov r9, r8 ; arg5 |
||||
mov r8, r10 ; arg4 |
||||
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 |
||||
|
||||
push rax |
||||
mov rax, [rel current_process] |
||||
mov rax, [rax + 40] ; current_process->user_rsp |
||||
mov [rel user_rsp_tmp], rax |
||||
pop rax |
||||
|
||||
mov rsp, [rel user_rsp_tmp] |
||||
o64 sysret |
||||
|
||||
user_rsp_tmp: dq 0 |
||||
@ -0,0 +1,250 @@ |
||||
#include "src/kernel/syscall.h" |
||||
#include "src/kernel/fs.h" |
||||
#include "src/kernel/gdt.h" |
||||
#include "src/kernel/path.h" |
||||
#include "src/kernel/pipe.h" |
||||
#include "src/kernel/process.h" |
||||
#include "src/kernel/stream.h" |
||||
#include "src/lib/layout.h" |
||||
#include "src/lib/memory.h" |
||||
#include "src/lib/string.h" |
||||
#include "src/lib/syscall.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 uint64_t read(uint64_t fd, uint64_t max, char *to) { |
||||
if (fd >= MAX_FDS || !current_process->fds[fd]) { |
||||
return (uint64_t)-1; |
||||
} |
||||
return current_process->fds[fd]->read(current_process->fds[fd], max, to); |
||||
} |
||||
|
||||
static uint64_t write(uint64_t fd, const char *from, uint64_t bytes) { |
||||
if (fd >= MAX_FDS || !current_process->fds[fd]) { |
||||
return (uint64_t)-1; |
||||
} |
||||
return current_process->fds[fd]->write(current_process->fds[fd], from, bytes); |
||||
} |
||||
|
||||
static uint64_t getcwd(uint64_t max, char *to) { |
||||
if (max == 0) { |
||||
return 0; |
||||
} |
||||
|
||||
if (max == 1) { |
||||
to[0] = '\0'; |
||||
return 0; |
||||
} |
||||
|
||||
if (max == 2 || !current_process->cwd->depth) { |
||||
to[0] = '/'; |
||||
to[1] = '\0'; |
||||
return 1; |
||||
} |
||||
|
||||
uint64_t i = 0; |
||||
for (uint8_t j = 0; j < current_process->cwd->depth; j++) { |
||||
uint64_t l = string_length(current_process->cwd->stack[j]->name); |
||||
if (i + l + 1 > max) { |
||||
break; |
||||
} |
||||
to[i++] = '/'; |
||||
memory_copy(current_process->cwd->stack[j]->name, l, to + i); |
||||
i += l; |
||||
} |
||||
|
||||
to[i] = '\0'; |
||||
return i; |
||||
} |
||||
|
||||
static uint64_t chdir(const char *path) { |
||||
path_t *cwd = path_open(current_process->cwd, path, OPEN_DIRECTORY); |
||||
if (!cwd) { |
||||
return (uint64_t)-1; |
||||
} |
||||
path_close((path_t *)current_process->cwd); |
||||
current_process->cwd = cwd; |
||||
return current_process->cwd->depth; |
||||
} |
||||
|
||||
static uint64_t spawn(const char *path, uint64_t argc, const char **argv, uint64_t stdin, uint64_t stdout) { |
||||
fs_node_t *node = path_open_node(current_process->cwd, path, OPEN_FILE); |
||||
if (!node) { |
||||
return EXIT_CODE_NOT_FOUND; |
||||
} |
||||
uint8_t *bin = memory_allocate(node->size); |
||||
fs_read(node, 0, node->size, bin); |
||||
|
||||
uint64_t size = 0; |
||||
uint64_t sizes[16]; |
||||
uint64_t offsets[16]; |
||||
for (uint64_t i = 0; i < argc; i++) { |
||||
offsets[i] = size; |
||||
size += (sizes[i] = string_length(argv[i]) + 1); |
||||
} |
||||
char *blob = memory_allocate(size); |
||||
for (uint64_t i = 0; i < argc; i++) { |
||||
memory_copy(argv[i], sizes[i], blob + offsets[i]); |
||||
} |
||||
|
||||
process_t *child = process_create(current_process, bin, node->size, stdin, stdout); |
||||
|
||||
memory_free(bin); |
||||
fs_close(node); |
||||
|
||||
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)KERNEL_VIRTUAL_PML4)) : "memory"); |
||||
|
||||
uint8_t *stack = (uint8_t *)child->user_stack; |
||||
|
||||
stack -= size; |
||||
memory_copy(blob, size, stack); |
||||
memory_free(blob); |
||||
|
||||
stack = (uint8_t *)((uint64_t)stack & ~7ULL); |
||||
|
||||
stack -= argc * sizeof(char *); |
||||
for (uint64_t i = 0; i < argc; i++) { |
||||
((char **)stack)[i] = (char *)(USER_VIRTUAL_STACK_TOP - size + offsets[i]); |
||||
} |
||||
stack -= sizeof(uint64_t); |
||||
*(uint64_t *)stack = argc; |
||||
|
||||
child->user_rsp = (void *)(USER_VIRTUAL_STACK_TOP - ((uint64_t)child->user_stack - (uint64_t)stack)); |
||||
|
||||
__asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)current_process->pml4)) : "memory"); |
||||
|
||||
return child->pid; |
||||
} |
||||
|
||||
static exit_code_t wait(uint64_t pid) { |
||||
process_t *child = process_get(pid); |
||||
if (!child) { |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
current_process->state = PROCESS_WAITING; |
||||
current_process->waiting_for = pid; |
||||
while (child->state != PROCESS_ZOMBIE) { |
||||
process_next(); |
||||
} |
||||
current_process->state = PROCESS_RUNNING; |
||||
|
||||
exit_code_t code = child->code; |
||||
process_destroy(child); |
||||
|
||||
return code; |
||||
} |
||||
|
||||
static uint64_t open(const char *path, uint64_t flags) { |
||||
if (current_process->free_fd >= MAX_FDS) { |
||||
return (uint64_t)-1; |
||||
} |
||||
stream_t *stream = path_open_stream(current_process->cwd, path, flags); |
||||
if (!stream) { |
||||
return (uint64_t)-1; |
||||
} |
||||
current_process->fds[current_process->free_fd++] = stream; |
||||
return current_process->free_fd - 1; |
||||
} |
||||
|
||||
static uint64_t close(uint64_t fd) { |
||||
if (fd >= MAX_FDS || !current_process->fds[fd]) { |
||||
return (uint64_t)-1; |
||||
} |
||||
current_process->fds[fd]->close(current_process->fds[fd]); |
||||
current_process->fds[fd] = NUL; |
||||
return 0; |
||||
} |
||||
|
||||
static uint64_t truncate(uint64_t fd, uint64_t size) { |
||||
if (fd >= MAX_FDS || !current_process->fds[fd]) { |
||||
return (uint64_t)-1; |
||||
} |
||||
return current_process->fds[fd]->truncate(current_process->fds[fd], size); |
||||
} |
||||
|
||||
static uint64_t remove(const char *path) { |
||||
fs_node_t *node = path_open_node(current_process->cwd, path, OPEN_FILE | OPEN_DIRECTORY); |
||||
fs_remove(node); |
||||
fs_close(node); |
||||
return 0; |
||||
} |
||||
|
||||
static uint64_t pipe(uint64_t *write_fd, uint64_t *read_fd) { |
||||
if (current_process->free_fd + 2 >= MAX_FDS) { |
||||
return (uint64_t)-1; |
||||
} |
||||
*write_fd = current_process->free_fd++; |
||||
*read_fd = current_process->free_fd++; |
||||
pipe_init(¤t_process->fds[*write_fd], ¤t_process->fds[*read_fd]); |
||||
return 0; |
||||
} |
||||
|
||||
static void exit(exit_code_t code) { |
||||
current_process->state = PROCESS_ZOMBIE; |
||||
current_process->code = code; |
||||
|
||||
if (current_process->parent) { |
||||
process_t *parent = (process_t *)current_process->parent; |
||||
if (parent->state == PROCESS_WAITING && parent->waiting_for == current_process->pid) { |
||||
parent->state = PROCESS_RUNNING; |
||||
} |
||||
} |
||||
|
||||
process_next(); |
||||
} |
||||
|
||||
uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) { |
||||
switch (func) { |
||||
case SYSCALL_READ: |
||||
return read(arg1, arg2, (char *)arg3); |
||||
case SYSCALL_WRITE: |
||||
return write(arg1, (const char *)arg2, arg3); |
||||
case SYSCALL_GETCWD: |
||||
return getcwd(arg1, (char *)arg2); |
||||
case SYSCALL_CHDIR: |
||||
return chdir((const char *)arg1); |
||||
case SYSCALL_SPAWN: |
||||
return spawn((const char *)arg1, arg2, (const char **)arg3, arg4, arg5); |
||||
case SYSCALL_WAIT: |
||||
return wait(arg1); |
||||
case SYSCALL_OPEN: |
||||
return open((const char *)arg1, arg2); |
||||
case SYSCALL_CLOSE: |
||||
return close(arg1); |
||||
case SYSCALL_TRUNCATE: |
||||
return truncate(arg1, arg2); |
||||
case SYSCALL_REMOVE: |
||||
return remove((char *)arg1); |
||||
case SYSCALL_PIPE: |
||||
return pipe((uint64_t *)arg1, (uint64_t *)arg2); |
||||
case SYSCALL_EXIT: |
||||
exit(arg1); |
||||
return 0; |
||||
default: |
||||
return (uint64_t)-1; |
||||
} |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
#pragma once |
||||
|
||||
#include <stdint.h> |
||||
|
||||
void syscall_init(); |
||||
|
||||
uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5); |
||||
@ -0,0 +1,28 @@ |
||||
bits 64 |
||||
extern timer_handler |
||||
|
||||
global timer_entry |
||||
timer_entry: |
||||
push rax |
||||
push rcx |
||||
push rdx |
||||
push rsi |
||||
push rdi |
||||
push r8 |
||||
push r9 |
||||
push r10 |
||||
push r11 |
||||
|
||||
call timer_handler |
||||
|
||||
pop r11 |
||||
pop r10 |
||||
pop r9 |
||||
pop r8 |
||||
pop rdi |
||||
pop rsi |
||||
pop rdx |
||||
pop rcx |
||||
pop rax |
||||
|
||||
iretq |
||||
@ -0,0 +1,20 @@ |
||||
#include "src/kernel/timer.h" |
||||
#include "src/kernel/idt.h" |
||||
#include "src/kernel/process.h" |
||||
#include "src/kernel/util.h" |
||||
|
||||
extern void timer_entry(); |
||||
|
||||
void timer_init() { |
||||
// PIT channel 0, rate generator, ~100Hz
|
||||
outb(0x43, 0x36); |
||||
outb(0x40, 0xA9); // divisor low byte (11932 for ~100Hz)
|
||||
outb(0x40, 0x2E); // divisor high byte
|
||||
outb(0x21, inb(0x21) & ~0x01); // unmask IRQ0
|
||||
idt_set_entry(32, timer_entry, 0x8E); |
||||
} |
||||
|
||||
void timer_handler() { |
||||
outb(0x20, 0x20); |
||||
process_next(); |
||||
} |
||||
@ -0,0 +1,3 @@ |
||||
#pragma once |
||||
|
||||
void timer_init(); |
||||
@ -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,181 @@ |
||||
#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 uint64_t stream_write(__attribute__((unused)) stream_t *self, const char *from, uint64_t bytes) { |
||||
uint64_t left = bytes; |
||||
while (left--) { |
||||
on_char_received(*from++); |
||||
} |
||||
return bytes; |
||||
} |
||||
|
||||
static uint64_t stream_read(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t max, |
||||
__attribute__((unused)) char *to) { |
||||
return 0; |
||||
} |
||||
|
||||
static uint64_t stream_truncate(__attribute__((unused)) stream_t *self, __attribute__((unused)) uint64_t size) { |
||||
return size; |
||||
} |
||||
|
||||
static void stream_close(__attribute__((unused)) stream_t *self) { |
||||
} |
||||
|
||||
static stream_t stream = {stream_write, stream_read, stream_truncate, stream_close}; |
||||
|
||||
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); |
||||
uint16_t prev_offset = offset; |
||||
offset = row * VGA_WIDTH + col; |
||||
while (*str) { |
||||
on_char_received(*str++); |
||||
} |
||||
offset = prev_offset; |
||||
} |
||||
@ -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); |
||||
@ -1,22 +0,0 @@ |
||||
#include "src/keyboard.h" |
||||
#include "src/idt.h" |
||||
#include "src/util.h" |
||||
|
||||
static keyboard_handler_t current_handler = 0; |
||||
|
||||
__attribute__((interrupt)) static void isr_keyboard([[maybe_unused]] struct interrupt_frame *frame) { |
||||
uint8_t scancode = inb(0x60); |
||||
outb(0x20, 0x20); |
||||
if (current_handler) { |
||||
current_handler(scancode); |
||||
} |
||||
} |
||||
|
||||
void keyboard_init() { |
||||
outb(0x21, inb(0x21) & ~0x02); |
||||
idt_set_entry(33, isr_keyboard, 0x8E); |
||||
} |
||||
|
||||
void keyboard_set_handler(keyboard_handler_t handler) { |
||||
current_handler = handler; |
||||
} |
||||
@ -1,9 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <stdint.h> |
||||
|
||||
void keyboard_init(); |
||||
|
||||
typedef void (*keyboard_handler_t)(uint8_t scancode); |
||||
|
||||
void keyboard_set_handler(keyboard_handler_t handler); |
||||
@ -0,0 +1,26 @@ |
||||
#pragma once |
||||
|
||||
#define MEMORY_SIZE 0x08000000 |
||||
#define PAGE_SIZE 4096 |
||||
#define PAGE_COUNT (MEMORY_SIZE / PAGE_SIZE) |
||||
|
||||
#define HEAP_PAGE_COUNT 256 |
||||
#define HEAP_SIZE (HEAP_PAGE_COUNT * PAGE_SIZE) |
||||
|
||||
#define KERNEL_VIRTUAL_BASE 0xFFFFFFFF80000000ULL |
||||
#define PHYS_TO_VIRT(phys) ((void *)((uint64_t)(phys) + KERNEL_VIRTUAL_BASE)) |
||||
#define VIRT_TO_PHYS(virt) ((void *)((uint64_t)(virt) - KERNEL_VIRTUAL_BASE)) |
||||
|
||||
#define KERNEL_VIRTUAL_PML4 (KERNEL_VIRTUAL_BASE + 0x10000) |
||||
#define KERNEL_VIRTUAL_CODE (KERNEL_VIRTUAL_PML4 + 0x10000) |
||||
#define KERNEL_VIRTUAL_HEAP (KERNEL_VIRTUAL_CODE + 0x100000) |
||||
#define KERNEL_VIRTUAL_UNUSED (KERNEL_VIRTUAL_HEAP + HEAP_SIZE) |
||||
#define KERNEL_VIRTUAL_STACK (KERNEL_VIRTUAL_BASE + 0xF00000 - PAGE_SIZE) |
||||
#define KERNEL_VIRTUAL_STACK_TOP (KERNEL_VIRTUAL_STACK + PAGE_SIZE) |
||||
|
||||
#define USER_VIRTUAL_BASE 0x0000000000000000ULL |
||||
#define USER_VIRTUAL_CODE (USER_VIRTUAL_BASE + 0x400000) |
||||
#define USER_VIRTUAL_HEAP (USER_VIRTUAL_CODE + 0x100000) |
||||
#define USER_VIRTUAL_UNUSED (USER_VIRTUAL_HEAP + HEAP_SIZE) |
||||
#define USER_VIRTUAL_STACK (0x0000700000000000ULL - PAGE_SIZE) |
||||
#define USER_VIRTUAL_STACK_TOP (USER_VIRTUAL_STACK + PAGE_SIZE) |
||||
@ -0,0 +1,60 @@ |
||||
#include "src/lib/memory.h" |
||||
#include "src/lib/layout.h" |
||||
#include "src/lib/util.h" |
||||
|
||||
#ifdef KERNEL |
||||
#define HEAP_VIRTUAL_BASE KERNEL_VIRTUAL_HEAP |
||||
#else |
||||
#define HEAP_VIRTUAL_BASE USER_VIRTUAL_HEAP |
||||
#endif |
||||
|
||||
typedef struct free_chunk { |
||||
uint64_t size; // header + data
|
||||
struct free_chunk *next; |
||||
} free_chunk_t; |
||||
|
||||
static free_chunk_t *free_list; |
||||
|
||||
void *memory_allocate(uint64_t size) { |
||||
if (!free_list) { |
||||
free_list = (free_chunk_t *)HEAP_VIRTUAL_BASE; |
||||
free_list->size = HEAP_SIZE; |
||||
free_list->next = NUL; |
||||
} |
||||
|
||||
size = sizeof(free_chunk_t) + ((size + 7) & (uint64_t)~7); |
||||
|
||||
free_chunk_t *prev = NUL; |
||||
free_chunk_t *curr = free_list; |
||||
|
||||
while (curr) { |
||||
if (curr->size >= size + (sizeof(free_chunk_t) + 8)) { |
||||
free_chunk_t *remainder = (free_chunk_t *)((uint8_t *)curr + size); |
||||
remainder->size = curr->size - size; |
||||
remainder->next = curr->next; |
||||
curr->size = size; |
||||
curr->next = remainder; |
||||
} |
||||
if (curr->size >= size) { |
||||
if (prev) { |
||||
prev->next = curr->next; |
||||
} else { |
||||
free_list = curr->next; |
||||
} |
||||
void *result = (void *)((uint8_t *)curr + sizeof(free_chunk_t)); |
||||
memory_set(0, size - sizeof(free_chunk_t), result); |
||||
return result; |
||||
} |
||||
prev = curr; |
||||
curr = curr->next; |
||||
} |
||||
|
||||
return NUL; |
||||
} |
||||
|
||||
void memory_free(void *pointer) { |
||||
free_chunk_t *chunk = (free_chunk_t *)((uint8_t *)pointer - sizeof(free_chunk_t)); |
||||
chunk->next = free_list; |
||||
free_list = chunk; |
||||
// TODO Merge adjacent free chunks.
|
||||
} |
||||
@ -0,0 +1,17 @@ |
||||
#define SYSCALL_READ 0 |
||||
#define SYSCALL_WRITE 1 |
||||
#define SYSCALL_GETCWD 2 |
||||
#define SYSCALL_CHDIR 3 |
||||
#define SYSCALL_SPAWN 4 |
||||
#define SYSCALL_WAIT 5 |
||||
#define SYSCALL_OPEN 6 |
||||
#define SYSCALL_CLOSE 7 |
||||
#define SYSCALL_TRUNCATE 8 |
||||
#define SYSCALL_REMOVE 9 |
||||
#define SYSCALL_PIPE 10 |
||||
#define SYSCALL_EXIT 60 |
||||
|
||||
#define OPEN_CREATE 0b0001 |
||||
#define OPEN_EXCLUSIVE 0b0010 |
||||
#define OPEN_FILE 0b0100 |
||||
#define OPEN_DIRECTORY 0b1000 |
||||
@ -0,0 +1,15 @@ |
||||
#pragma once |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#define NUL 0 // TODO Fix VSCode thinking `NULL` conflicts with some other definition.
|
||||
|
||||
#define STDIN 0 |
||||
#define STDOUT 1 |
||||
#define STDERR 2 |
||||
|
||||
typedef uint64_t exit_code_t; |
||||
|
||||
#define EXIT_CODE_OK 0 |
||||
#define EXIT_CODE_NOT_FOUND ((uint64_t)-2) |
||||
#define EXIT_CODE_GENERAL_FAILURE ((uint64_t)-1) |
||||
@ -1,107 +0,0 @@ |
||||
#include "src/memory.h" |
||||
#include "src/panic.h" |
||||
#include "src/util.h" |
||||
#include <stdint.h> |
||||
|
||||
#define MEMORY_SIZE 134217728 |
||||
#define PAGE_SIZE 4096 |
||||
#define PAGE_COUNT (MEMORY_SIZE / PAGE_SIZE) |
||||
#define MAP_UNIT 64 |
||||
#define FULL_UNIT 0xFFFFFFFFFFFFFFFF |
||||
|
||||
// Claim 2MB for bootloader, kernel, and stuff.
|
||||
static uint16_t free_page = MAP_UNIT * 8; |
||||
static uint64_t allocation[PAGE_COUNT / MAP_UNIT] = {FULL_UNIT, FULL_UNIT, FULL_UNIT, FULL_UNIT, |
||||
FULL_UNIT, FULL_UNIT, FULL_UNIT, FULL_UNIT}; |
||||
|
||||
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)); |
||||
} |
||||
} |
||||
|
||||
static 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); |
||||
} |
||||
|
||||
[[maybe_unused]] static 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; |
||||
} |
||||
} |
||||
|
||||
#define HEAP_PAGE_COUNT 256 |
||||
|
||||
typedef struct free_chunk { |
||||
uint64_t size; // header + data
|
||||
struct free_chunk *next; |
||||
} free_chunk_t; |
||||
|
||||
static free_chunk_t *free_list; |
||||
|
||||
void memory_init() { |
||||
free_list = memory_page_allocate(); |
||||
for (uint64_t i = 1; i < HEAP_PAGE_COUNT; i++) { |
||||
memory_page_allocate(); // TODO Map the pages.
|
||||
} |
||||
free_list->size = HEAP_PAGE_COUNT * PAGE_SIZE; |
||||
free_list->next = NUL; |
||||
} |
||||
|
||||
void *memory_allocate(uint64_t size) { |
||||
size = sizeof(free_chunk_t) + (size + 7) & (uint64_t)~7; |
||||
|
||||
free_chunk_t *prev = NUL; |
||||
free_chunk_t *curr = free_list; |
||||
|
||||
while (curr) { |
||||
if (curr->size >= size + (sizeof(free_chunk_t) + 8)) { |
||||
free_chunk_t *remainder = (free_chunk_t *)((uint8_t *)curr + size); |
||||
remainder->size = curr->size - size; |
||||
remainder->next = curr->next; |
||||
curr->size = size; |
||||
curr->next = remainder; |
||||
} |
||||
if (curr->size >= size) { |
||||
if (prev) { |
||||
prev->next = curr->next; |
||||
} else { |
||||
free_list = curr->next; |
||||
} |
||||
void *result = (void *)((uint8_t *)curr + sizeof(free_chunk_t)); |
||||
memory_set(0, size - sizeof(free_chunk_t), result); |
||||
return result; |
||||
} |
||||
prev = curr; |
||||
curr = curr->next; |
||||
} |
||||
|
||||
ASSERT(0, "memory_heap_allocate: out of heap memory"); |
||||
return 0; |
||||
} |
||||
|
||||
void memory_free(void *pointer) { |
||||
free_chunk_t *chunk = (free_chunk_t *)((uint8_t *)pointer - sizeof(free_chunk_t)); |
||||
chunk->next = free_list; |
||||
free_list = chunk; |
||||
// TODO Merge adjacent free chunks.
|
||||
} |
||||
@ -1,8 +0,0 @@ |
||||
#include "src/panic.h" |
||||
#include "src/vga.h" |
||||
|
||||
void kernel_panic(const char *msg, [[maybe_unused]] const char *file, [[maybe_unused]] int line) { |
||||
vga_set_string(0, VGA_HEIGHT - 1, msg, 0x28); |
||||
while (1) |
||||
; |
||||
} |
||||
@ -1,397 +0,0 @@ |
||||
#include "src/terminal.h" |
||||
#include "src/fs.h" |
||||
#include "src/keyboard.h" |
||||
#include "src/memory.h" |
||||
#include "src/string.h" |
||||
#include "src/util.h" |
||||
#include "src/vga.h" |
||||
#include <stdint.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 PROMPT_PREFIX 2 |
||||
#define PROMPT_LENGTH 255 |
||||
|
||||
fs_node_t *root; |
||||
|
||||
static char prompt[PROMPT_LENGTH + 1]; |
||||
static char prompt_swap[PROMPT_LENGTH + 1]; |
||||
static uint8_t prompt_row = 0; |
||||
static uint8_t prompt_length = 0; |
||||
static uint8_t prompt_offset = 0; |
||||
static char prompt_buf[VGA_WIDTH + 1] = "$ "; |
||||
|
||||
static void render_prompt() { |
||||
uint8_t offset = PROMPT_PREFIX + prompt_offset < VGA_WIDTH ? 0 : PROMPT_PREFIX + prompt_offset - VGA_WIDTH + 1; |
||||
uint8_t length = PROMPT_PREFIX + prompt_length < VGA_WIDTH ? prompt_length : VGA_WIDTH - PROMPT_PREFIX; |
||||
prompt_buf[1] = PROMPT_PREFIX + prompt_offset < VGA_WIDTH ? ' ' : '<'; |
||||
memory_copy(prompt + offset, length, prompt_buf + PROMPT_PREFIX); |
||||
if (PROMPT_PREFIX + prompt_length > VGA_WIDTH && prompt_offset < prompt_length - 1) { |
||||
prompt_buf[VGA_WIDTH - 1] = '>'; |
||||
} |
||||
prompt_buf[PROMPT_PREFIX + length] = '\0'; |
||||
|
||||
vga_clear(prompt_row, 1); |
||||
vga_set_string(prompt_row, 0, prompt_buf, 0x0F); |
||||
vga_set_cursor(prompt_row, PROMPT_PREFIX + prompt_offset - offset); |
||||
} |
||||
|
||||
static void on_home_pressed() { |
||||
if (keyboard_state || !prompt_offset) { |
||||
return; |
||||
} |
||||
|
||||
prompt_offset = 0; |
||||
render_prompt(); |
||||
} |
||||
|
||||
static void on_left_pressed() { |
||||
if (keyboard_state || !prompt_offset) { |
||||
return; |
||||
} |
||||
|
||||
prompt_offset--; |
||||
render_prompt(); |
||||
} |
||||
|
||||
static void on_right_pressed() { |
||||
if (keyboard_state || prompt_offset == prompt_length) { |
||||
return; |
||||
} |
||||
|
||||
prompt_offset++; |
||||
render_prompt(); |
||||
} |
||||
|
||||
static void on_end_pressed() { |
||||
if (keyboard_state || prompt_offset == prompt_length) { |
||||
return; |
||||
} |
||||
|
||||
prompt_offset = prompt_length; |
||||
render_prompt(); |
||||
} |
||||
|
||||
static void advance_row(uint8_t rows) { |
||||
while (rows--) { |
||||
if (prompt_row == VGA_HEIGHT - 1) { |
||||
vga_copy(1, VGA_HEIGHT - 1, 0); |
||||
} else { |
||||
prompt_row++; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void print_line(const char *string) { |
||||
advance_row(1); |
||||
vga_clear(prompt_row - 1, 1); |
||||
vga_set_string(prompt_row - 1, 0, string, 0x0F); |
||||
} |
||||
|
||||
static void help(uint8_t argc, [[maybe_unused]] char **argv) { |
||||
if (argc > 1) { |
||||
print_line("help: accepts no arguments"); |
||||
} |
||||
|
||||
advance_row(4); |
||||
vga_set_string(prompt_row - 4, 0, "Available commands:", 0x0F); |
||||
vga_set_string(prompt_row - 3, 0, "help", 0x0F); |
||||
vga_set_string(prompt_row - 2, 0, "ls DIR", 0x0F); |
||||
vga_set_string(prompt_row - 1, 0, "cat FILE", 0x0F); |
||||
} |
||||
|
||||
static void ls(uint8_t argc, char **argv) { |
||||
if (argc != 2) { |
||||
print_line("ls: requires a single path"); |
||||
return; |
||||
} |
||||
|
||||
char *path_components[16]; |
||||
uint64_t path_length = string_split(argv[1], '/', 16, path_components); |
||||
if (!string_empty(path_components[0])) { |
||||
print_line("ls: relative paths not supported"); |
||||
return; |
||||
} |
||||
|
||||
fs_node_t *prev = NUL; |
||||
fs_node_t *curr = root; |
||||
uint8_t i = 1; |
||||
while (i < path_length) { |
||||
if (string_empty(path_components[i])) { |
||||
i++; |
||||
continue; |
||||
} |
||||
|
||||
if (!curr->is_dir) { |
||||
print_line("ls: can not navigate into a file"); |
||||
fs_close(curr); |
||||
return; |
||||
} |
||||
|
||||
prev = curr; |
||||
curr = fs_open_by(curr, path_components[i]); |
||||
if (prev != root) { |
||||
fs_close(prev); |
||||
} |
||||
|
||||
if (!curr) { |
||||
print_line("ls: invalid path"); |
||||
return; |
||||
} |
||||
i++; |
||||
} |
||||
|
||||
if (!curr->is_dir) { |
||||
print_line("ls: can not list file"); |
||||
fs_close(curr); |
||||
return; |
||||
} |
||||
|
||||
uint8_t j = 0; |
||||
fs_node_t *item = fs_open_at(curr, j++); |
||||
while (item) { |
||||
print_line(item->name); |
||||
fs_close(item); |
||||
item = fs_open_at(curr, j++); |
||||
} |
||||
if (curr != root) { |
||||
fs_close(curr); |
||||
} |
||||
} |
||||
|
||||
static void cat(uint8_t argc, char **argv) { |
||||
if (argc != 2) { |
||||
print_line("cat: requires a single path"); |
||||
return; |
||||
} |
||||
|
||||
char *path_components[16]; |
||||
uint64_t path_length = string_split(argv[1], '/', 16, path_components); |
||||
if (!string_empty(path_components[0])) { |
||||
print_line("cat: relative paths not supported"); |
||||
return; |
||||
} |
||||
|
||||
fs_node_t *prev = NUL; |
||||
fs_node_t *curr = root; |
||||
uint8_t i = 1; |
||||
while (i < path_length) { |
||||
if (string_empty(path_components[i])) { |
||||
i++; |
||||
continue; |
||||
} |
||||
|
||||
if (!curr->is_dir) { |
||||
print_line("cat: can not navigate into a file"); |
||||
fs_close(curr); |
||||
return; |
||||
} |
||||
|
||||
prev = curr; |
||||
curr = fs_open_by(curr, path_components[i]); |
||||
if (prev != root) { |
||||
fs_close(prev); |
||||
} |
||||
|
||||
if (!curr) { |
||||
print_line("cat: invalid path"); |
||||
return; |
||||
} |
||||
i++; |
||||
} |
||||
|
||||
if (curr->is_dir) { |
||||
print_line("cat: can not print a directory"); |
||||
if (curr != root) { |
||||
fs_close(curr); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
char *content = memory_allocate(curr->size + 1); |
||||
fs_read(curr, 0, curr->size, content); |
||||
content[curr->size] = '\0'; |
||||
|
||||
char **lines = memory_allocate(curr->size * sizeof(char *)); |
||||
uint64_t lines_count = string_split(content, '\n', curr->size, lines); |
||||
|
||||
for (uint64_t i = 0; i < lines_count; i++) { |
||||
print_line(lines[i]); |
||||
} |
||||
|
||||
memory_free(lines); |
||||
memory_free(content); |
||||
fs_close(curr); |
||||
} |
||||
|
||||
static void on_enter_pressed() { |
||||
prompt[prompt_length] = '\0'; |
||||
|
||||
char *argv[16]; |
||||
uint8_t argc = (uint8_t)string_split(prompt, ' ', 16, argv); |
||||
|
||||
advance_row(1); |
||||
|
||||
if (string_equal(argv[0], "help")) { |
||||
help(argc, argv); |
||||
} else if (string_equal(argv[0], "ls")) { |
||||
ls(argc, argv); |
||||
} else if (string_equal(argv[0], "cat")) { |
||||
cat(argc, argv); |
||||
} else { |
||||
print_line("Unknown command"); |
||||
} |
||||
prompt_length = prompt_offset = 0; |
||||
render_prompt(); |
||||
} |
||||
|
||||
static void on_cancel_pressed() { |
||||
advance_row(1); |
||||
prompt_length = prompt_offset = 0; |
||||
render_prompt(); |
||||
} |
||||
|
||||
static void on_character_pressed(uint8_t code) { |
||||
if (keyboard_state && (keyboard_state & (KEYBOARD_STATE_LCTRL | KEYBOARD_STATE_RCTRL)) == keyboard_state) { |
||||
switch (code) { |
||||
case 0x2E: // C
|
||||
on_cancel_pressed(); |
||||
} |
||||
return; |
||||
} |
||||
|
||||
if (keyboard_state & ~(KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) || prompt_length >= PROMPT_LENGTH) { |
||||
return; |
||||
} |
||||
|
||||
if (prompt_offset == prompt_length) { |
||||
prompt[prompt_offset + 1] = '\0'; |
||||
} else { |
||||
memory_copy(prompt, prompt_length + 1, prompt_swap); |
||||
memory_copy(prompt_swap + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset + 1); |
||||
} |
||||
prompt[prompt_offset] = (keyboard_state & (KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) ? scancode_shifted : scancode_normal)[code]; |
||||
prompt_offset++; |
||||
prompt_length++; |
||||
render_prompt(); |
||||
} |
||||
|
||||
static void on_backspace_pressed() { |
||||
if (keyboard_state || !prompt_offset) { |
||||
return; |
||||
} |
||||
|
||||
if (prompt_offset == prompt_length) { |
||||
prompt[prompt_offset - 1] = '\0'; |
||||
} else { |
||||
memory_copy(prompt, prompt_length + 1, prompt_swap); |
||||
memory_copy(prompt_swap + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset - 1); |
||||
} |
||||
prompt_offset--; |
||||
prompt_length--; |
||||
render_prompt(); |
||||
} |
||||
|
||||
static void on_delete_pressed() { |
||||
if (keyboard_state || prompt_offset == prompt_length) { |
||||
return; |
||||
} |
||||
|
||||
prompt_offset++; |
||||
on_backspace_pressed(); |
||||
} |
||||
|
||||
static void terminal_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 ? on_backspace_pressed() : 0; |
||||
break; |
||||
case 0x1C: |
||||
pressed ? on_enter_pressed() : 0; |
||||
break; |
||||
default: |
||||
pressed &&scancode_normal[code] ? on_character_pressed(code) : 0; |
||||
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 ? on_home_pressed() : 0; |
||||
break; |
||||
case 0x4B: |
||||
pressed ? on_left_pressed() : 0; |
||||
break; |
||||
case 0x4D: |
||||
pressed ? on_right_pressed() : 0; |
||||
break; |
||||
case 0x4F: |
||||
pressed ? on_end_pressed() : 0; |
||||
break; |
||||
case 0x53: |
||||
pressed ? on_delete_pressed() : 0; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void terminal_init() { |
||||
root = fs_mount(); |
||||
|
||||
keyboard_set_handler(terminal_on_key); |
||||
vga_clear(0, VGA_HEIGHT); |
||||
char *greeting = string_format("Welcome to FreywarOS v%s!", VERSION); |
||||
vga_set_string(0, 0, greeting, 0x0F); |
||||
memory_free(greeting); |
||||
prompt_row += 2; |
||||
render_prompt(); |
||||
} |
||||
@ -1,3 +0,0 @@ |
||||
#pragma once |
||||
|
||||
void terminal_init(); |
||||
@ -0,0 +1,50 @@ |
||||
#include "src/lib/syscall.h" |
||||
#include "src/lib/util.h" |
||||
#include "src/user/syscall.h" |
||||
|
||||
#define BLOCK_SIZE 65536 |
||||
|
||||
exit_code_t pass(uint64_t fd) { |
||||
static char buffer[BLOCK_SIZE]; |
||||
uint64_t bytes; |
||||
while ((bytes = read(fd, BLOCK_SIZE, buffer))) { |
||||
if (bytes == (uint64_t)-1) { |
||||
ERR_S("cat: could not read file\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
write(STDOUT, buffer, bytes); |
||||
} |
||||
|
||||
return EXIT_CODE_OK; |
||||
} |
||||
|
||||
exit_code_t cat(const char *path) { |
||||
uint64_t fd = open(path, OPEN_FILE); |
||||
|
||||
if (fd == (uint64_t)-1) { |
||||
ERR_S("cat: path does not exist\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
exit_code_t code = pass(fd); |
||||
|
||||
close(fd); |
||||
|
||||
return code; |
||||
} |
||||
|
||||
exit_code_t main(uint64_t argc, const char **argv) { |
||||
if (argc == 1) { |
||||
return pass(0); |
||||
} |
||||
|
||||
uint64_t code = EXIT_CODE_OK; |
||||
for (uint64_t i = 1; i < argc; i++) { |
||||
exit_code_t c = cat(argv[i]); |
||||
if (c != EXIT_CODE_OK) { |
||||
code = c; |
||||
} |
||||
} |
||||
|
||||
return code; |
||||
} |
||||
@ -0,0 +1,54 @@ |
||||
#include "src/lib/syscall.h" |
||||
#include "src/lib/util.h" |
||||
#include "src/user/syscall.h" |
||||
|
||||
#define BLOCK_SIZE 65536 |
||||
|
||||
exit_code_t main(uint64_t argc, const char **argv) { |
||||
if (argc != 3) { |
||||
ERR_S("cp: requires source and target\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
uint64_t source = open(argv[1], OPEN_FILE); |
||||
if (source == (uint64_t)-1) { |
||||
ERR_S("cp: source does not exist\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
uint64_t target = open(argv[2], OPEN_FILE | OPEN_CREATE); |
||||
if (target == (uint64_t)-1) { |
||||
ERR_S("cp: target path does not exist\n"); |
||||
close(source); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
if (truncate(target, 0) == (uint64_t)-1) { |
||||
ERR_S("cp: could not write file\n"); |
||||
close(source); |
||||
close(target); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
static char buffer[BLOCK_SIZE]; |
||||
uint64_t bytes; |
||||
while ((bytes = read(source, BLOCK_SIZE, buffer))) { |
||||
if (bytes == (uint64_t)-1) { |
||||
ERR_S("cp: could not read file\n"); |
||||
close(source); |
||||
close(target); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
if (write(target, buffer, bytes) == (uint64_t)-1) { |
||||
ERR_S("cp: could not write file\n"); |
||||
close(source); |
||||
close(target); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
} |
||||
|
||||
close(source); |
||||
close(target); |
||||
|
||||
return EXIT_CODE_OK; |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
#include "src/lib/util.h" |
||||
#include "src/user/syscall.h" |
||||
|
||||
exit_code_t main(uint64_t argc, const char **argv) { |
||||
for (uint64_t i = 1; i < argc; i++) { |
||||
if (i > 1) { |
||||
OUT_S(" "); |
||||
} |
||||
OUT_D(argv[i]); |
||||
} |
||||
OUT_S("\n"); |
||||
|
||||
return EXIT_CODE_OK; |
||||
} |
||||
@ -0,0 +1,51 @@ |
||||
#include "src/lib/syscall.h" |
||||
#include "src/lib/util.h" |
||||
#include "src/user/syscall.h" |
||||
|
||||
#define BLOCK_SIZE 256 |
||||
|
||||
exit_code_t ls(const char *path) { |
||||
uint64_t fd = open(path, OPEN_DIRECTORY); |
||||
|
||||
if (fd == (uint64_t)-1) { |
||||
ERR_S("ls: path does not exist\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
char buffer[BLOCK_SIZE]; |
||||
uint64_t bytes; |
||||
while ((bytes = read(fd, BLOCK_SIZE, buffer))) { |
||||
if (bytes == (uint64_t)-1) { |
||||
ERR_S("ls: could not read directory\n"); |
||||
close(fd); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
write(STDOUT, buffer, bytes); |
||||
OUT_S("\n"); |
||||
} |
||||
|
||||
close(fd); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
exit_code_t main(uint64_t argc, const char **argv) { |
||||
if (argc == 1) { |
||||
return ls("."); |
||||
} |
||||
|
||||
exit_code_t code = EXIT_CODE_OK; |
||||
for (uint64_t i = 1; i < argc; i++) { |
||||
if (argc > 2) { |
||||
OUT_S("\n"); |
||||
OUT_D(argv[i]); |
||||
OUT_S(":\n"); |
||||
} |
||||
exit_code_t c = ls(argv[i]); |
||||
if (c != EXIT_CODE_OK) { |
||||
code = c; |
||||
} |
||||
} |
||||
|
||||
return code; |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
#include "src/lib/syscall.h" |
||||
#include "src/lib/util.h" |
||||
#include "src/user/syscall.h" |
||||
|
||||
exit_code_t main(uint64_t argc, const char **argv) { |
||||
if (argc < 2) { |
||||
ERR_S("mkdir: requires target(s)\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
exit_code_t code = EXIT_CODE_OK; |
||||
for (uint64_t i = 1; i < argc; i++) { |
||||
uint64_t fd = open(argv[i], OPEN_DIRECTORY | OPEN_CREATE | OPEN_EXCLUSIVE); |
||||
if (fd == (uint64_t)-1) { |
||||
ERR_S("mkdir: could not create directory\n"); |
||||
code = EXIT_CODE_GENERAL_FAILURE; |
||||
} else { |
||||
close(fd); |
||||
} |
||||
} |
||||
|
||||
return code; |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
#include "src/lib/syscall.h" |
||||
#include "src/lib/util.h" |
||||
#include "src/user/syscall.h" |
||||
|
||||
exit_code_t main(uint64_t argc, const char **argv) { |
||||
if (argc < 2) { |
||||
ERR_S("rm: requires one or more arguments\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
uint64_t code = EXIT_CODE_OK; |
||||
for (uint64_t i = 1; i < argc; i++) { |
||||
uint64_t removed = remove(argv[i]); |
||||
if (removed == (uint64_t)-1) { |
||||
ERR_S("rm: could not remove file\n"); |
||||
code = EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
} |
||||
|
||||
return code; |
||||
} |
||||
@ -0,0 +1,187 @@ |
||||
#include "src/lib/memory.h" |
||||
#include "src/lib/string.h" |
||||
#include "src/lib/util.h" |
||||
#include "src/user/syscall.h" |
||||
|
||||
static exit_code_t help(uint8_t argc, char **argv, uint64_t stdin, uint64_t stdout); |
||||
static exit_code_t cd(uint8_t argc, char **argv, uint64_t stdin, uint64_t stdout); |
||||
|
||||
#define PATH "/bin/" |
||||
|
||||
#define WRITE_S(o, s) write(o, s, sizeof(s) - 1); |
||||
#define WRITE_D(o, s) write(o, s, string_length(s)); |
||||
|
||||
typedef exit_code_t (*app_t)(uint8_t argc, char **argv, uint64_t stdin, uint64_t stdout); |
||||
|
||||
typedef struct { |
||||
const char *name; |
||||
app_t app; |
||||
} app_entry_t; |
||||
|
||||
static app_entry_t builtins[] = { |
||||
{"help", help}, |
||||
{"cd", cd}, |
||||
}; |
||||
|
||||
static exit_code_t help(uint8_t argc, __attribute__((unused)) char **argv, __attribute__((unused)) uint64_t stdin, uint64_t stdout) { |
||||
if (argc > 1) { |
||||
ERR_S("help: expects no arguments\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
WRITE_S(stdout, "Available commands:\n"); |
||||
for (uint64_t i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) { |
||||
WRITE_D(stdout, builtins[i].name); |
||||
WRITE_S(stdout, "\n"); |
||||
} |
||||
|
||||
return EXIT_CODE_OK; |
||||
} |
||||
|
||||
static exit_code_t cd(uint8_t argc, char **argv, __attribute__((unused)) uint64_t stdin, __attribute__((unused)) uint64_t stdout) { |
||||
if (argc != 2) { |
||||
ERR_S("cd: requires a single path\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
if (chdir(argv[1]) == (uint64_t)-1) { |
||||
ERR_S("cd: path does not exist\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
return EXIT_CODE_OK; |
||||
} |
||||
|
||||
static void print_prompt() { |
||||
char path[256]; |
||||
uint8_t pl = getcwd(256, path); |
||||
char *components[16]; |
||||
uint8_t cl = string_split(path, '/', 16, components); |
||||
|
||||
OUT_S("[") |
||||
OUT_D(pl == 1 ? "/" : components[cl - 1]); |
||||
OUT_S("]$ "); |
||||
} |
||||
|
||||
static void free_fds(uint64_t *fds, uint8_t fds_count) { |
||||
for (uint8_t i = 0; i < fds_count; i++) { |
||||
close(fds[i]); |
||||
} |
||||
} |
||||
|
||||
static void kill_pids(__attribute__((unused)) uint64_t *pids, __attribute__((unused)) uint8_t pid_count) { |
||||
// TODO.
|
||||
} |
||||
|
||||
static void execute(char *command) { |
||||
OUT_S("\n"); |
||||
|
||||
if (string_equal(command, "^C")) { |
||||
print_prompt(); |
||||
return; |
||||
} |
||||
|
||||
if (string_equal(command, "^D")) { |
||||
exit(EXIT_CODE_OK); |
||||
return; |
||||
} |
||||
|
||||
char *subcommands[16]; |
||||
uint8_t subcommands_count = (uint8_t)string_split(command, '|', 16, subcommands); |
||||
|
||||
uint64_t fds[32]; |
||||
uint8_t fds_count = 0; |
||||
|
||||
uint64_t pids[16]; |
||||
uint8_t pids_count = 0; |
||||
|
||||
for (uint8_t i = 0; i < subcommands_count; i++) { |
||||
string_trim(subcommands[i], ' ', &subcommands[i]); |
||||
|
||||
char *argv[16]; |
||||
uint8_t argc = (uint8_t)string_split(subcommands[i], ' ', 16, argv); |
||||
|
||||
uint64_t stdin = 0; |
||||
uint64_t stdout = 1; |
||||
|
||||
if (i > 0) { |
||||
stdin = fds[fds_count - 1]; |
||||
} |
||||
if (i < subcommands_count - 1) { |
||||
if (pipe(&fds[fds_count], &fds[fds_count + 1]) == (uint64_t)-1) { |
||||
kill_pids(pids, pids_count); |
||||
free_fds(fds, fds_count); |
||||
print_prompt(); |
||||
return; |
||||
} |
||||
fds_count += 2; |
||||
stdout = fds[fds_count - 2]; |
||||
} |
||||
|
||||
uint8_t j; |
||||
for (j = 0; j < sizeof(builtins) / sizeof(app_entry_t); j++) { |
||||
if (string_equal(argv[0], builtins[j].name)) { |
||||
builtins[j].app(argc, argv, stdin, stdout); |
||||
pids[pids_count++] = 0; |
||||
break; |
||||
} |
||||
} |
||||
if (j == sizeof(builtins) / sizeof(app_entry_t)) { |
||||
uint64_t size = sizeof(PATH) + string_length(argv[0]) + 1; |
||||
char *path = memory_allocate(size); |
||||
memory_copy(PATH, sizeof(PATH), path); |
||||
memory_copy(argv[0], size - sizeof(PATH), path + sizeof(PATH) - 1); |
||||
uint64_t pid = spawn(path, argc, (const char **)argv, stdin, stdout); |
||||
memory_free(path); |
||||
|
||||
if (pid == (uint64_t)-1) { |
||||
kill_pids(pids, pids_count); |
||||
free_fds(fds, fds_count); |
||||
print_prompt(); |
||||
return; |
||||
} else { |
||||
pids[pids_count++] = pid; |
||||
} |
||||
} |
||||
} |
||||
|
||||
for (uint8_t i = 0; i < pids_count; i++) { |
||||
if (pids[i]) { |
||||
waitpid(pids[i]); |
||||
} |
||||
if (i > 0) { |
||||
close(fds[(i - 1) * 2 + 1]); |
||||
} |
||||
if (i < pids_count - 1) { |
||||
close(fds[i * 2]); |
||||
} |
||||
} |
||||
|
||||
print_prompt(); |
||||
} |
||||
|
||||
exit_code_t main() { |
||||
ERR_S("Welcome to FreywarOS v" VERSION "!\n\n"); |
||||
|
||||
print_prompt(); |
||||
|
||||
char line[256]; |
||||
uint64_t offset; |
||||
|
||||
char chunk[64]; |
||||
uint64_t received; |
||||
|
||||
while ((received = read(0, sizeof(chunk), chunk))) { |
||||
for (uint64_t i = 0; i < received; i++) { |
||||
if (chunk[i] == '\n') { |
||||
line[offset] = '\0'; |
||||
execute(line); |
||||
offset = 0; |
||||
} else if (offset < 255) { |
||||
line[offset++] = chunk[i]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return EXIT_CODE_OK; |
||||
} |
||||
@ -0,0 +1,202 @@ |
||||
#include "src/lib/memory.h" |
||||
#include "src/lib/util.h" |
||||
#include "src/user/syscall.h" |
||||
|
||||
#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; |
||||
|
||||
static void on_home_pressed() { |
||||
if (!prompt_offset) { |
||||
return; |
||||
} |
||||
|
||||
write(STDOUT, bs, prompt_offset); |
||||
prompt_offset = 0; |
||||
} |
||||
|
||||
static void on_left_pressed() { |
||||
if (!prompt_offset) { |
||||
return; |
||||
} |
||||
|
||||
write(STDOUT, bs, 1); |
||||
prompt_offset--; |
||||
} |
||||
|
||||
static void on_right_pressed() { |
||||
if (prompt_offset == prompt_length) { |
||||
return; |
||||
} |
||||
|
||||
write(STDOUT, prompt + prompt_offset, 1); |
||||
prompt_offset++; |
||||
} |
||||
|
||||
static void on_end_pressed() { |
||||
if (prompt_offset == prompt_length) { |
||||
return; |
||||
} |
||||
|
||||
write(STDOUT, prompt + prompt_offset, prompt_length - prompt_offset); |
||||
prompt_offset = prompt_length; |
||||
} |
||||
|
||||
static void on_enter_pressed() { |
||||
prompt[prompt_length] = '\n'; |
||||
|
||||
write(3, prompt, prompt_length + 1); |
||||
|
||||
prompt_length = prompt_offset = 0; |
||||
} |
||||
|
||||
static void on_cancel_pressed() { |
||||
OUT_S("^C\n"); |
||||
write(3, "^C\n", 3); |
||||
prompt_length = prompt_offset = 0; |
||||
} |
||||
|
||||
static void on_disconnect_pressed() { |
||||
OUT_S("^D\n"); |
||||
write(3, "^D\n", 3); |
||||
exit(EXIT_CODE_OK); |
||||
} |
||||
|
||||
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; |
||||
write(STDOUT, prompt + prompt_offset, tail); |
||||
write(STDOUT, ws, 1); // erase the character past the end
|
||||
write(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++; |
||||
|
||||
write(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--; |
||||
|
||||
write(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; |
||||
} |
||||
} |
||||
|
||||
uint64_t main() { |
||||
memory_set('\b', PROMPT_LENGTH, (char *)bs); |
||||
memory_set(' ', PROMPT_LENGTH, (char *)ws); |
||||
|
||||
char c; |
||||
while (read(STDIN, 1, &c)) { |
||||
on_char_received(c); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
@ -0,0 +1,56 @@ |
||||
#include "src/lib/string.h" |
||||
#include "src/lib/syscall.h" |
||||
#include "src/lib/util.h" |
||||
#include "src/user/syscall.h" |
||||
|
||||
#define BLOCK_SIZE 65536 |
||||
|
||||
exit_code_t pass(uint64_t fd) { |
||||
static char buffer[BLOCK_SIZE]; |
||||
uint64_t bytes; |
||||
uint64_t total = 0; |
||||
while ((bytes = read(fd, BLOCK_SIZE, buffer))) { |
||||
if (bytes == (uint64_t)-1) { |
||||
ERR_S("wc: could not read file\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} else { |
||||
total += bytes; |
||||
} |
||||
} |
||||
|
||||
uint64_t l = string_format("%d\n", BLOCK_SIZE, buffer, total); |
||||
write(STDOUT, buffer, l); |
||||
|
||||
return EXIT_CODE_OK; |
||||
} |
||||
|
||||
exit_code_t wc(const char *path) { |
||||
uint64_t fd = open(path, OPEN_FILE); |
||||
|
||||
if (fd == (uint64_t)-1) { |
||||
OUT_S("wc: path does not exist\n"); |
||||
return EXIT_CODE_GENERAL_FAILURE; |
||||
} |
||||
|
||||
exit_code_t code = pass(fd); |
||||
|
||||
close(fd); |
||||
|
||||
return code; |
||||
} |
||||
|
||||
exit_code_t main(uint64_t argc, const char **argv) { |
||||
if (argc == 1) { |
||||
return pass(0); |
||||
} |
||||
|
||||
uint64_t code = EXIT_CODE_OK; |
||||
for (uint64_t i = 1; i < argc; i++) { |
||||
exit_code_t c = wc(argv[i]); |
||||
if (c != EXIT_CODE_OK) { |
||||
code = c; |
||||
} |
||||
} |
||||
|
||||
return code; |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
ENTRY(_start) |
||||
|
||||
SECTIONS { |
||||
. = 0x0000000000400000; |
||||
|
||||
.text : { *(.text) } |
||||
.rodata : { *(.rodata) } |
||||
.data : { *(.data) } |
||||
.bss : { *(.bss) } |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
bits 64 |
||||
|
||||
extern main |
||||
|
||||
global _start |
||||
_start: |
||||
pop rdi |
||||
mov rsi, rsp |
||||
call main |
||||
mov rdi, rax ; return value |
||||
mov rax, 60 ; exit syscall |
||||
syscall |
||||
@ -0,0 +1,85 @@ |
||||
bits 64 |
||||
|
||||
global read |
||||
read: |
||||
mov rax, 0 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global write |
||||
write: |
||||
mov rax, 1 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global getcwd |
||||
getcwd: |
||||
mov rax, 2 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global chdir |
||||
chdir: |
||||
mov rax, 3 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global spawn |
||||
spawn: |
||||
mov rax, 4 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global waitpid |
||||
waitpid: |
||||
mov rax, 5 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global open |
||||
open: |
||||
mov rax, 6 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global close |
||||
close: |
||||
mov rax, 7 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global truncate |
||||
truncate: |
||||
mov rax, 8 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global remove |
||||
remove: |
||||
mov rax, 9 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global pipe |
||||
pipe: |
||||
mov rax, 10 |
||||
mov r10, rcx |
||||
syscall |
||||
ret |
||||
|
||||
global exit |
||||
exit: |
||||
mov rax, 60 |
||||
mov r10, rcx |
||||
syscall |
||||
; never returns |
||||
@ -0,0 +1,34 @@ |
||||
#pragma once |
||||
|
||||
#include "src/lib/util.h" |
||||
#include "src/lib/string.h" |
||||
|
||||
#define OUT_S(s) write(STDOUT, s, sizeof(s) - 1); |
||||
#define OUT_D(s) write(STDOUT, s, string_length(s)); |
||||
|
||||
#define ERR_S(s) write(STDERR, s, sizeof(s) - 1); |
||||
#define ERR_D(s) write(STDERR, s, string_length(s)); |
||||
|
||||
uint64_t read(int64_t fd, uint64_t max, void *to); |
||||
|
||||
uint64_t write(int64_t fd, const void *from, uint64_t bytes); |
||||
|
||||
uint64_t getcwd(uint64_t max, void *to); |
||||
|
||||
uint64_t chdir(const char *path); |
||||
|
||||
uint64_t spawn(const char *path, uint64_t argc, const char **argv, uint64_t stdin, uint64_t stdout); |
||||
|
||||
exit_code_t waitpid(uint64_t pid); |
||||
|
||||
uint64_t open(const char *path, uint64_t flags); |
||||
|
||||
uint64_t close(uint64_t fd); |
||||
|
||||
uint64_t truncate(uint64_t fd, uint64_t size); |
||||
|
||||
uint64_t remove(const char *path); |
||||
|
||||
uint64_t pipe(uint64_t *write_fd, uint64_t *read_fd); |
||||
|
||||
void exit(exit_code_t code); |
||||
@ -1,48 +0,0 @@ |
||||
#include "src/vga.h" |
||||
#include "src/memory.h" |
||||
#include "src/panic.h" |
||||
#include "src/util.h" |
||||
|
||||
static uint16_t *vga = (uint16_t *)0x000B8000; |
||||
|
||||
void vga_set_cursor(uint8_t row, uint8_t col) { |
||||
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_cursor: invalid coordinates") |
||||
uint16_t pos = row * VGA_WIDTH + col; |
||||
outb(0x3D4, 0x0F); |
||||
outb(0x3D5, pos & 0xFF); |
||||
outb(0x3D4, 0x0E); |
||||
outb(0x3D5, (pos >> 8) & 0xFF); |
||||
} |
||||
|
||||
void vga_set_char(uint8_t row, uint8_t col, char c, uint8_t color) { |
||||
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_char: invalid coordinates"); |
||||
|
||||
vga[row * VGA_WIDTH + col] = (uint16_t)(((uint16_t)color << 8) | (uint16_t)c); |
||||
} |
||||
|
||||
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color) { |
||||
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates") |
||||
while (*str) { |
||||
vga_set_char(row, col++, *str++, color); |
||||
if (col >= VGA_WIDTH) { |
||||
col = 0; |
||||
row++; |
||||
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates") |
||||
} |
||||
} |
||||
} |
||||
|
||||
void vga_copy(uint8_t src_row, uint8_t rows, uint8_t dst_row) { |
||||
ASSERT(src_row < VGA_HEIGHT && dst_row < VGA_HEIGHT && src_row + rows <= VGA_HEIGHT && dst_row + rows <= VGA_HEIGHT, |
||||
"vga_copy: invalid copy dimensions"); |
||||
memory_move(vga + src_row * VGA_WIDTH, rows * VGA_WIDTH * 2, vga + dst_row * VGA_WIDTH); |
||||
} |
||||
|
||||
void vga_clear(uint8_t row, uint8_t rows) { |
||||
ASSERT(row < VGA_HEIGHT && rows <= VGA_HEIGHT && row + rows <= VGA_HEIGHT, "vga_clear: invalid clear dimensions"); |
||||
|
||||
uint16_t *dst = vga + row * VGA_WIDTH; |
||||
uint16_t fill = 0x0F20; |
||||
uint32_t count = VGA_WIDTH * rows; |
||||
__asm__ volatile("rep stosw" : "=D"(dst), "=c"(count) : "D"(dst), "a"(fill), "c"(count) : "memory"); |
||||
} |
||||
@ -1,16 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <stdint.h> |
||||
|
||||
#define VGA_WIDTH 80 |
||||
#define VGA_HEIGHT 25 |
||||
|
||||
void vga_set_cursor(uint8_t row, uint8_t col); |
||||
|
||||
void vga_set_char(uint8_t row, uint8_t col, char c, uint8_t color); |
||||
|
||||
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color); |
||||
|
||||
void vga_copy(uint8_t src_row, uint8_t rows, uint8_t dst_row); |
||||
|
||||
void vga_clear(uint8_t row, uint8_t rows); |
||||
Loading…
Reference in new issue