Compare commits

...

17 Commits

  1. 4
      .gdbinit
  2. 12
      build.sh
  3. 420
      meson.build
  4. 35
      src/bootloader.asm
  5. 259
      src/fat16.c
  6. 17
      src/fat16.h
  7. 26
      src/fs.c
  8. 25
      src/fs.h
  9. 48
      src/kernel.c
  10. 6
      src/kernel/ata.c
  11. 0
      src/kernel/ata.h
  12. 767
      src/kernel/fat16.c
  13. 26
      src/kernel/fat16.h
  14. 42
      src/kernel/fs.c
  15. 34
      src/kernel/fs.h
  16. 67
      src/kernel/gdt.c
  17. 12
      src/kernel/gdt.h
  18. 2
      src/kernel/idt.c
  19. 0
      src/kernel/idt.h
  20. 103
      src/kernel/kernel.c
  21. 0
      src/kernel/kernel_entry.asm
  22. 172
      src/kernel/keyboard.c
  23. 6
      src/kernel/keyboard.h
  24. 2
      src/kernel/linker.ld
  25. 120
      src/kernel/memory.c
  26. 22
      src/kernel/memory.h
  27. 8
      src/kernel/panic.c
  28. 2
      src/kernel/panic.h
  29. 98
      src/kernel/path.c
  30. 21
      src/kernel/path.h
  31. 4
      src/kernel/pic.c
  32. 0
      src/kernel/pic.h
  33. 134
      src/kernel/pipe.c
  34. 5
      src/kernel/pipe.h
  35. 50
      src/kernel/process.asm
  36. 191
      src/kernel/process.c
  37. 47
      src/kernel/process.h
  38. 40
      src/kernel/stream.h
  39. 57
      src/kernel/syscall.asm
  40. 250
      src/kernel/syscall.c
  41. 7
      src/kernel/syscall.h
  42. 28
      src/kernel/timer.asm
  43. 20
      src/kernel/timer.c
  44. 3
      src/kernel/timer.h
  45. 9
      src/kernel/tss.c
  46. 20
      src/kernel/tss.h
  47. 2
      src/kernel/util.h
  48. 181
      src/kernel/vga.c
  49. 10
      src/kernel/vga.h
  50. 22
      src/keyboard.c
  51. 9
      src/keyboard.h
  52. 26
      src/lib/layout.h
  53. 60
      src/lib/memory.c
  54. 8
      src/lib/memory.h
  55. 65
      src/lib/string.c
  56. 4
      src/lib/string.h
  57. 17
      src/lib/syscall.h
  58. 15
      src/lib/util.h
  59. 107
      src/memory.c
  60. 8
      src/panic.c
  61. 397
      src/terminal.c
  62. 3
      src/terminal.h
  63. 50
      src/user/app/cat/cat.c
  64. 54
      src/user/app/cp/cp.c
  65. 14
      src/user/app/echo/echo.c
  66. 51
      src/user/app/ls/ls.c
  67. 23
      src/user/app/mkdir/mkdir.c
  68. 21
      src/user/app/rm/rm.c
  69. 187
      src/user/app/shell/shell.c
  70. 202
      src/user/app/terminal/terminal.c
  71. 56
      src/user/app/wc/wc.c
  72. 10
      src/user/linker.ld
  73. 12
      src/user/start.asm
  74. 85
      src/user/syscall.asm
  75. 34
      src/user/syscall.h
  76. 48
      src/vga.c
  77. 16
      src/vga.h

@ -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

@ -13,5 +13,15 @@ dd if=/dev/zero of=build/os.img bs="${sector_size}" count="$((partition_offset +
dd if=build/mbr.bin of=build/os.img bs="${sector_size}" count=1 conv=notrunc
dd if=build/bootloader.bin of=build/os.img bs="${sector_size}" seek=1 count="$((partition_offset - 1))" conv=notrunc
mkfs.fat -F 16 --offset "${partition_offset}" build/os.img
mcopy -i build/os.img@@"$((partition_offset * sector_size))" src ::src
mcopy -i build/os.img@@"$((partition_offset * sector_size))" -s src ::src
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/kernel.bin ::kernel.bin
mmd -i build/os.img@@"$((partition_offset * sector_size))" ::bin
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/terminal ::bin/terminal
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/shell ::bin/shell
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/echo ::bin/echo
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/ls ::bin/ls
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/cat ::bin/cat
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/cp ::bin/cp
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/mkdir ::bin/mkdir
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/rm ::bin/rm
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/wc ::bin/wc

@ -33,46 +33,79 @@ custom_target(
kernel_entry_o = custom_target(
'kernel_entry',
input: 'src/kernel_entry.asm',
input: 'src/kernel/kernel_entry.asm',
output: 'kernel_entry.o',
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
)
cc = meson.get_compiler('c')
timer_o = custom_target(
'timer',
input: 'src/kernel/timer.asm',
output: 'timer.o',
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
)
process_o = custom_target(
'process',
input: 'src/kernel/process.asm',
output: 'process.o',
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
)
kernel_sources = files(
'src/ata.c',
'src/fat16.c',
'src/fs.c',
'src/idt.c',
'src/kernel.c',
'src/keyboard.c',
'src/memory.c',
'src/panic.c',
'src/pic.c',
'src/string.c',
'src/terminal.c',
'src/vga.c',
syscall_o = custom_target(
'syscall',
input: 'src/kernel/syscall.asm',
output: 'syscall.o',
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
)
cc = meson.get_compiler('c')
lib_sources = files([
'src/lib/memory.c',
'src/lib/string.c',
])
kernel_sources = files([
'src/kernel/ata.c',
'src/kernel/fat16.c',
'src/kernel/fs.c',
'src/kernel/gdt.c',
'src/kernel/idt.c',
'src/kernel/kernel.c',
'src/kernel/keyboard.c',
'src/kernel/memory.c',
'src/kernel/panic.c',
'src/kernel/path.c',
'src/kernel/pic.c',
'src/kernel/pipe.c',
'src/kernel/process.c',
'src/kernel/syscall.c',
'src/kernel/timer.c',
'src/kernel/tss.c',
'src/kernel/vga.c',
])
kernel_elf = executable(
'kernel.elf',
sources: [kernel_entry_o, kernel_sources],
sources: [kernel_entry_o, timer_o, process_o, syscall_o, lib_sources, kernel_sources],
c_args: [
'-ffreestanding',
'-nostdlib',
'-nostartfiles',
'-mno-red-zone',
'-mgeneral-regs-only',
'-mcmodel=kernel',
'-Wno-unused-command-line-argument',
'-Wconversion',
'-DKERNEL="yes"',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/linker.ld',
'-T', meson.project_source_root() / 'src/kernel/linker.ld',
'-nostdlib',
],
link_depends: 'src/linker.ld',
link_depends: 'src/kernel/linker.ld',
)
custom_target(
@ -82,3 +115,354 @@ custom_target(
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
terminal_start = custom_target(
'terminal_start',
input: 'src/user/start.asm',
output: 'terminal_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
terminal_syscall = custom_target(
'terminal_syscall',
input: 'src/user/syscall.asm',
output: 'terminal_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
terminal_elf = executable(
'terminal.elf',
sources: [terminal_start, terminal_syscall, lib_sources, 'src/user/app/terminal/terminal.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'terminal',
input: terminal_elf,
output: 'terminal',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
shell_start = custom_target(
'shell_start',
input: 'src/user/start.asm',
output: 'shell_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
shell_syscall = custom_target(
'shell_syscall',
input: 'src/user/syscall.asm',
output: 'shell_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
shell_elf = executable(
'shell.elf',
sources: [shell_start, shell_syscall, lib_sources, 'src/user/app/shell/shell.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'shell',
input: shell_elf,
output: 'shell',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
echo_start = custom_target(
'echo_start',
input: 'src/user/start.asm',
output: 'echo_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
echo_syscall = custom_target(
'echo_syscall',
input: 'src/user/syscall.asm',
output: 'echo_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
echo_elf = executable(
'echo.elf',
sources: [echo_start, echo_syscall, lib_sources, 'src/user/app/echo/echo.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'echo',
input: echo_elf,
output: 'echo',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
ls_start = custom_target(
'ls_start',
input: 'src/user/start.asm',
output: 'ls_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
ls_syscall = custom_target(
'ls_syscall',
input: 'src/user/syscall.asm',
output: 'ls_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
ls_elf = executable(
'ls.elf',
sources: [ls_start, ls_syscall, lib_sources, 'src/user/app/ls/ls.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'ls',
input: ls_elf,
output: 'ls',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
cat_start = custom_target(
'cat_start',
input: 'src/user/start.asm',
output: 'cat_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
cat_syscall = custom_target(
'cat_syscall',
input: 'src/user/syscall.asm',
output: 'cat_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
cat_elf = executable(
'cat.elf',
sources: [cat_start, cat_syscall, lib_sources, 'src/user/app/cat/cat.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'cat',
input: cat_elf,
output: 'cat',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
cp_start = custom_target(
'cp_start',
input: 'src/user/start.asm',
output: 'cp_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
cp_syscall = custom_target(
'cp_syscall',
input: 'src/user/syscall.asm',
output: 'cp_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
cp_elf = executable(
'cp.elf',
sources: [cp_start, cp_syscall, lib_sources, 'src/user/app/cp/cp.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'cp',
input: cp_elf,
output: 'cp',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
mkdir_start = custom_target(
'mkdir_start',
input: 'src/user/start.asm',
output: 'mkdir_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
mkdir_syscall = custom_target(
'mkdir_syscall',
input: 'src/user/syscall.asm',
output: 'mkdir_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
mkdir_elf = executable(
'mkdir.elf',
sources: [mkdir_start, mkdir_syscall, lib_sources, 'src/user/app/mkdir/mkdir.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'mkdir',
input: mkdir_elf,
output: 'mkdir',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
rm_start = custom_target(
'rm_start',
input: 'src/user/start.asm',
output: 'rm_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
rm_syscall = custom_target(
'rm_syscall',
input: 'src/user/syscall.asm',
output: 'rm_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
rm_elf = executable(
'rm.elf',
sources: [rm_start, rm_syscall, lib_sources, 'src/user/app/rm/rm.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'rm',
input: rm_elf,
output: 'rm',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)
wc_start = custom_target(
'wc_start',
input: 'src/user/start.asm',
output: 'wc_start.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
wc_syscall = custom_target(
'wc_syscall',
input: 'src/user/syscall.asm',
output: 'wc_syscall.o',
command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'],
)
wc_elf = executable(
'wc.elf',
sources: [wc_start, wc_syscall, lib_sources, 'src/user/app/wc/wc.c'],
c_args: [
'-ffreestanding',
'-nostdlib',
'-fno-stack-protector',
'-mcmodel=large',
'-DVERSION="' + meson.project_version() + '"',
],
link_args: [
'-T', meson.project_source_root() / 'src/user/linker.ld',
'-nostdlib',
],
link_depends: 'src/user/linker.ld',
)
custom_target(
'wc',
input: wc_elf,
output: 'wc',
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
build_by_default: true,
)

@ -224,30 +224,30 @@ jmp load_gdt
gdt_start:
gdt_0: dq 0x0000000000000000
gdt_1:
gdt_32_code:
.limit_low: dw 0xFFFF
.base_low: dw 0x0000
.base_middle: db 0x00
.access: db 10011010b ; present, ring 0, code, executable, readable
.flags: db 11001111b ; 32-bit, 4KB granularity
.base_high: db 0x00
gdt_1_end:
gdt_2:
gdt_32_code_end:
gdt_64_code:
.limit_low: dw 0xFFFF
.base_low: dw 0x0000
.base_middle: db 0x00
.access: db 10011010b ; present, ring 0, code, executable, readable
.access: db 10011010b ; present, ring 0, code, executable, readable
.flags: db 10101111b ; long mode (L bit)
.base_high: db 0x00
gdt_2_end:
gdt_3:
gdt_64_code_end:
gdt_data:
.limit_low: dw 0xFFFF
.base_low: dw 0x0000
.base_middle: db 0x00
.access: db 10010010b ; present, ring 0, data, writable
.flags: db 00000000b
.base_high: db 0x00
gdt_3_end:
gdt_data_end:
gdt_end:
gdt_descriptor:
@ -276,23 +276,30 @@ protected_mode:
mov ss, ax
mov esp, 0x00090000
; zero page table memory at 0x00010000 (3 pages = 768 dwords)
; zero page table memory
mov edi, 0x00010000
mov ecx, 768
mov ecx, 256 * 5
xor eax, eax
rep stosd
; PML4[0] -> PDPT at 0x00011000
mov dword [0x00010000], 0x00011003
; PDPT[0] -> PD at 0x00012000
mov dword [0x00011000], 0x00012003
; PML4[511] -> PDPT at 0x00012000
mov dword [0x00010FF8], 0x00012003
; PML4[0].PDPT[0] -> PD at 0x00013000
mov dword [0x00011000], 0x00013003
; PML4[511].PDPT[510] -> PD at 0x00014000
mov dword [0x00012FF0], 0x00014003
; PD: fill 64 entries, each mapping 2MB
mov edi, 0x00012000
mov edi, 0x00013000
mov eax, 0x00000083 ; present, writable, huge page, base 0
mov ecx, 64
.fill_pd:
mov ebx, edi
add ebx, 0x1000
mov dword [edi], eax
mov dword [ebx], eax
add edi, 8
add eax, 0x00200000 ; next 2MB
loop .fill_pd
@ -327,11 +334,11 @@ long_mode:
mov ds, ax
mov es, ax
mov ss, ax
mov rsp, 0x0000000000090000
mov rsp, 0xFFFFFFFF80F00000
xor rax, rax
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
jmp 0x00020000
jmp 0xFFFFFFFF80020000

@ -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, &sector);
memory_copy(sector + 11, sizeof(fat16_bpb_t), &bpb);
fat16_node_t *node = memory_allocate(sizeof(fat16_node_t));
node->base.type = FAT16;
node->base.name[0] = node->entry.name[0] = '/';
node->base.size = node->entry.size = sizeof(fat16_dir_entry_t) * bpb.root_entry_count;
node->base.is_dir = 1;
return fs = (fs_node_t *)node;
}
static void ensure_fat() {
ASSERT(bpb.sectors_per_fat<256, "ensure_fat: big FAT not implemented")
if (!fat) {
fat = memory_allocate(bpb.sectors_per_fat * SECTOR_SIZE);
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors, (uint8_t)bpb.sectors_per_fat, fat);
}
}
static fat16_dir_entry_t *load_directory(fat16_node_t *directory) {
fat16_dir_entry_t *entries;
if (!directory->entry.first_cluster) {
uint8_t sectors = (uint8_t)((directory->base.size + SECTOR_SIZE - 1) / SECTOR_SIZE);
entries = memory_allocate(sectors * SECTOR_SIZE + sizeof(fat16_dir_entry_t)); // One extra as null terminator.
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.fats_count * bpb.sectors_per_fat, sectors, entries);
} else {
ensure_fat();
uint16_t next_cluster = directory->entry.first_cluster;
uint32_t cluster_count = 0;
while (next_cluster < 0xFFF8) {
cluster_count++;
next_cluster = fat[next_cluster];
}
entries =
memory_allocate(cluster_count * bpb.sectors_per_cluster * SECTOR_SIZE + sizeof(fat16_dir_entry_t)); // One extra as null terminator.
uint8_t *chunk = (uint8_t *)entries;
next_cluster = directory->entry.first_cluster;
while (next_cluster < 0xFFF8) {
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
bpb.sectors_per_cluster * (next_cluster - 2),
bpb.sectors_per_cluster, chunk);
chunk += bpb.sectors_per_cluster * SECTOR_SIZE;
next_cluster = fat[next_cluster];
}
}
return entries;
}
static fs_node_t *open_entry(const char *name, const fat16_dir_entry_t *entry) {
if (!entry->name[0]) {
return NUL;
}
fat16_node_t *result = memory_allocate(sizeof(fat16_node_t));
memory_copy((char *)name, string_length(name) + 1, &(result->base.name));
result->base.type = FAT16;
result->base.size = entry->size;
result->base.is_dir = entry->attributes & ATTRIBUTE_SUBDIRECTORY;
memory_copy((fat16_dir_entry_t *)entry, sizeof(fat16_dir_entry_t), (uint8_t *)result + sizeof(fs_node_t));
return (fs_node_t *)result;
}
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name) {
ASSERT(directory->type == FAT16, "fat16_open_by: directory is not FAT16");
ASSERT(directory->is_dir, "fat16_open_by: directory is not a directory");
char name_8_3[12];
to_8_3(name, name_8_3);
fat16_dir_entry_t *entries = load_directory((fat16_node_t *)directory);
fat16_dir_entry_t *entry = entries;
while (entry->name[0]) {
if ((uint8_t)entry->name[0] != 0xE5 && (uint8_t)entry->attributes != 0x0F && bytes_equal(name_8_3, (char *)entry, 11)) {
break;
}
entry++;
}
fs_node_t *result = open_entry(name, entry);
memory_free(entries);
return result;
}
fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index) {
ASSERT(directory->type == FAT16, "fat16_open_at: directory is not FAT16");
ASSERT(directory->is_dir, "fat16_open_at: directory is not a directory");
fat16_dir_entry_t *entries = load_directory((fat16_node_t *)directory);
uint64_t ei = 0, vi = 0;
while (entries[ei].name[0]) {
if ((uint8_t)entries[ei].name[0] != 0xE5 && (uint8_t)entries[ei].attributes != 0x0F) {
if (vi == index) {
break;
}
vi++;
}
ei++;
}
if (!entries[ei].name[0]) {
return NUL;
}
char name[13];
from_8_3(entries[ei].name, entries[ei].ext, name);
fs_node_t *result = open_entry(name, entries + ei);
memory_free(entries);
return result;
}
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, &sector);
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)
;
}

@ -2,7 +2,7 @@
#define ASSERT(cond, msg) \
if (!(cond)) { \
kernel_panic(msg " (assertion: " #cond ")", __FILE__, __LINE__); \
kernel_panic(msg " (assertion: " #cond ")", __FILE__, __LINE__); \
}
void kernel_panic(const char *msg, const char *file, int line);

@ -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(&current_process->fds[*write_fd], &current_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);

@ -2,8 +2,6 @@
#include <stdint.h>
#define NUL 0 // TODO Fix VSCode thinking `NULL` conflicts with some other definition.
static inline uint8_t inb(uint16_t port) {
uint8_t value;
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));

@ -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.
}

@ -1,10 +1,7 @@
#pragma once
#include "src/panic.h"
#include <stdint.h>
void memory_init();
void *memory_allocate(uint64_t size);
void memory_free(void *pointer);
@ -19,12 +16,13 @@ static inline void memory_move(void *from, uint64_t bytes, void *to) {
} else if (to < from) {
__asm__ volatile("rep movsb" : "=D"(to), "=S"(from), "=c"(bytes) : "D"(to), "S"(from), "c"(bytes) : "memory");
} else {
ASSERT(0, "memory_move: forward overlapping move not implemented");
// TODO ASSERT(0, "memory_move: forward overlapping move not implemented");
}
}
static inline void memory_copy(const void *from, uint64_t bytes, void *to) {
ASSERT((uint64_t)to + bytes <= (uint64_t)from || (uint64_t)to >= (uint64_t)from + bytes, "memory_copy: overlapping backward copy");
// TODO ASSERT((uint64_t)to + bytes <= (uint64_t)from || (uint64_t)to >= (uint64_t)from + bytes, "memory_copy: overlapping backward
// copy");
__asm__ volatile("rep movsb" : "=D"(to), "=S"(from), "=c"(bytes) : "D"(to), "S"(from), "c"(bytes) : "memory");
}

@ -1,7 +1,5 @@
#include "src/string.h"
#include "src/memory.h"
#include "src/lib/string.h"
#include <stdarg.h>
#include <stdint.h>
uint8_t bytes_equal(const char *l, const char *r, uint64_t count) {
while (count--) {
@ -35,6 +33,30 @@ uint8_t string_equal(const char *l, const char *r) {
return *l == *r;
}
uint64_t string_trim(char *string, char character, char **result) {
while (*string == character) {
string++;
}
*result = string;
if (*string == '\0') {
return 0;
}
char *end = string;
while (*end != '\0') {
end++;
}
end--;
while (string < end && *end == character) {
*end = '\0';
end--;
}
return (uint64_t)(end - string + 1);
}
uint64_t string_split(char *string, char separator, uint64_t max, char **result) {
uint64_t count = 1;
*result = string;
@ -164,41 +186,39 @@ uint64_t string_uint_to_hex(uint64_t value, uint64_t max, char *result) {
return i;
}
char *string_format(const char *format, ...) {
uint64_t string_format(const char *format, uint64_t max, char *output, ...) {
va_list args;
va_start(args, format);
uint64_t limit = 1023;
va_start(args, output);
char *result = memory_allocate(limit + 1);
uint64_t limit = max - 1;
uint64_t fi = 0, ri = 0;
while (format[fi] && ri < limit) {
if (format[fi] != '%') {
result[ri++] = format[fi++];
output[ri++] = format[fi++];
} else {
switch (format[fi + 1]) {
case 'c':
result[ri++] = (char)va_arg(args, int);
output[ri++] = (char)va_arg(args, int);
fi += 2;
break;
case 'd':
string_int_to_decimal(va_arg(args, int64_t), limit - ri, result + ri);
while (result[ri]) {
string_int_to_decimal(va_arg(args, int64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
case 'u':
string_uint_to_decimal(va_arg(args, uint64_t), limit - ri, result + ri);
while (result[ri]) {
string_uint_to_decimal(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
break;
case 'x':
string_uint_to_hex(va_arg(args, uint64_t), limit - ri, result + ri);
while (result[ri]) {
string_uint_to_hex(va_arg(args, uint64_t), limit - ri, output + ri);
while (output[ri]) {
ri++;
}
fi += 2;
@ -206,25 +226,26 @@ char *string_format(const char *format, ...) {
case 's': {
char *s = va_arg(args, char *);
while (*s && ri < limit) {
result[ri++] = *s;
output[ri++] = *s;
s++;
}
fi += 2;
break;
}
case '%': {
result[ri++] = '%';
output[ri++] = '%';
fi += 2;
break;
}
default:
result[ri++] = format[fi++];
result[ri++] = format[fi++];
output[ri++] = format[fi++];
output[ri++] = format[fi++];
break;
}
}
}
result[ri] = '\0';
output[ri] = '\0';
va_end(args);
return result;
return ri;
}

@ -10,6 +10,8 @@ uint64_t string_length(const char *s);
uint8_t string_equal(const char *l, const char *r);
uint64_t string_trim(char *string, char character, char **result);
uint64_t string_split(char *string, char separator, uint64_t max, char **result);
uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result);
@ -20,4 +22,4 @@ uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result);
uint64_t string_uint_to_hex(uint64_t value, uint64_t max, char *result);
char *string_format(const char *format, ...);
uint64_t string_format(const char *format, uint64_t max, char *output, ...);

@ -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…
Cancel
Save