summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/assembly-programming.md28
-rw-r--r--doc/internal/gdt.txt5
-rw-r--r--doc/internal/mem.txt23
-rw-r--r--doc/internal/ple.txt12
-rw-r--r--doc/ints.txt52
-rw-r--r--doc/keys.txt42
-rw-r--r--fs-skel/sys/startup.rc2
-rw-r--r--makefile51
-rw-r--r--qemu-debug.gdb2
-rw-r--r--src/boot.asm49
-rw-r--r--src/kernel/boot.h3
-rw-r--r--src/kernel/drive.c23
-rw-r--r--src/kernel/drive.h21
-rw-r--r--src/kernel/elf-link.ld4
-rw-r--r--src/kernel/elf.c116
-rw-r--r--src/kernel/elf.h9
-rw-r--r--src/kernel/fat.c53
-rw-r--r--src/kernel/fat.h1
-rw-r--r--src/kernel/ide.c32
-rw-r--r--src/kernel/idt.c116
-rw-r--r--src/kernel/idt.h9
-rw-r--r--src/kernel/isrs.asm94
-rw-r--r--src/kernel/main.c58
-rw-r--r--src/kernel/main2.c18
-rw-r--r--src/kernel/mem.c63
-rw-r--r--src/kernel/mem.h13
-rw-r--r--src/kernel/paging.c130
-rw-r--r--src/kernel/paging.h17
-rw-r--r--src/kernel/pci.c8
-rw-r--r--src/kernel/plef.c32
-rw-r--r--src/kernel/plef.h21
-rw-r--r--src/kernel/pmap.c127
-rw-r--r--src/kernel/pmap.h17
-rw-r--r--src/kernel/serial.c5
-rw-r--r--src/kernel/task.c90
-rw-r--r--src/kernel/task.h26
-rw-r--r--src/kernel/util.c2
-rw-r--r--src/kernel/util.h2
-rw-r--r--src/kernel/vga.c17
-rw-r--r--src/kernel/vga.h1
-rw-r--r--src/lib.ld17
-rw-r--r--src/user-elf.ld26
-rw-r--r--src/user/elf.ld8
-rw-r--r--src/user/hello/hello.asm15
-rw-r--r--src/user/include/canyo/file.h9
-rw-r--r--src/user/include/knob/block.h8
-rw-r--r--src/user/include/knob/env.h9
-rw-r--r--src/user/include/knob/file.h17
-rw-r--r--src/user/include/knob/format.h9
-rw-r--r--src/user/include/knob/heap.h9
-rw-r--r--src/user/include/knob/quit.h7
-rw-r--r--src/user/include/knob/user.h12
-rw-r--r--src/user/include/pland.h157
-rw-r--r--src/user/include/pland/syscall.h139
-rw-r--r--src/user/init/main.c29
-rw-r--r--src/user/knob/entry.asm23
-rw-r--r--src/user/knob/env.c3
-rw-r--r--src/user/knob/file.c82
-rw-r--r--src/user/knob/format.c13
-rw-r--r--src/user/knob/heap.c112
-rw-r--r--src/user/knob/quit.c29
-rw-r--r--src/user/knob/user.c134
-rw-r--r--src/user/libcanyo/file.c15
63 files changed, 1713 insertions, 563 deletions
diff --git a/doc/assembly-programming.md b/doc/assembly-programming.md
deleted file mode 100644
index 78f81ac..0000000
--- a/doc/assembly-programming.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# Assembly Programming on Portland
-
-Assembly programming on Portland OS works pretty similarly to other OS's, but the linking phase varies in order to get the proper Portland Executable format for the output.
-
-## Portland-specific quirks
-
-The linker script used in the "Compiling and linking" section only recognizes `.text`, `.rodata`, `.data` and `.bss` sections, so please only use these. All of these except `.bss` are treated the same when the executable is loaded, so it doesn't really matter which section you put anything in except for alignment considerations, but it is still helpful for debugging and for readability to put things into the proper sections. This linker script provides a symbol `__pl_length` at the end of the `.data` section, used for generating the PLE header, so don't name any symbols this.
-
-## Portland ABI
-
-At program startup¸ we are in 32-bit protected mode in ring 3. The data and stack segments are equal, and these and the code segment refer to a region of memory containing `.text`, `.rodata` and `.data` (all three loaded verbatim from the executable) at address zero, followed by an uninitialized region of memory the size of `.bss`. The stack pointer is initialized to the end of this region, and the instruction pointer is initialized to the `_entry` label.
-
-See `ints.txt` for information about system calls.
-
-## Compiling and linking
-
-The following commands are used to compile and link an (NASM) assembly program into a Portland Executable. Replace the parts in all caps (except the `-O` argument to `objcopy`) with the relevant file names. The `user_elf.ld` file can be found at `src/user-elf.ld` in the kernel repository.
-
-```
-nasm -f elf32 SOURCE_FILE_1.asm -o SOURCE_FILE_1.o
-nasm -f elf32 SOURCE_FILE_2.asm -o SOURCE_FILE_2.o
-...
-
-ld -T user_elf.ld SOURCE_FILE_1.o SOURCE_FILE_2.o ... -o PROGRAM.elf
-objcopy -O binary PROGRAM.elf PROGRAM.ple
-```
-
-After running each of these commands, `PROGRAM.elf` is a regular 32-bit ELF file containing the compiled program, with symbol table and all, useful for debugging. The `.ple_head` section contains the PLE header placed at the start of the executable by `objcopy`, and can be ignored when examining the ELF file. The `PROGRAM.ple` file is an executable file that can be run by Portland OS. \ No newline at end of file
diff --git a/doc/internal/gdt.txt b/doc/internal/gdt.txt
deleted file mode 100644
index 26f7be9..0000000
--- a/doc/internal/gdt.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-0x08: 0x0000.0000 - 0x0003.7fff (code)
-0x10: 0x0000.0000 - 0x1fff.ffff (data)
-0x18: task
-0x20: user code
-0x28: user data \ No newline at end of file
diff --git a/doc/internal/mem.txt b/doc/internal/mem.txt
index 0819fb7..c5f5e8b 100644
--- a/doc/internal/mem.txt
+++ b/doc/internal/mem.txt
@@ -1,17 +1,28 @@
-0x0000.4000 - 0x0000.4003 (4): bootloader info
+0x0000.4000 - 0x0000.4007 (8): bootloader info
0x0 byte: support flags
0x80: PCI
+ 0x40: PAE
0x1 byte: PCI "hardware characteristics"
0x2 byte: PCI minor
0x3 byte: PCI major
0x4 byte: last PCI bus
-0x0000.4200 - 0x0000.42ff (256): VESA info
-0x0000.4300 - 0x0000.43ff (256): VBE strings
+ 0x6 word: BIOS memory map length
-0x0001.0000 - 0x0001.1fff (8k): memory map
+0x0000.4f98 - 0x0000.4fff (104): TSS
+0x0000.5000 - 0x0000.5fff (4k): kernel page directory
-0x0003.0000 - 0x0003.7fff (32k): kernel
+0x0001.0000 - 0x0001.ffff (64k): BIOS memory map
+
+0x0003.0000 - 0x0003.7fff (32k): kernel text, data, rodata
0x0003.8000 - 0x0003.ffff (32k): kernel stack
+0x0004.0000 - 0x0005.ffff (128k): pagemap
+
+0x000a.0000 - 0x000f.ffff (384k): VGA and BIOS memory
+
+0x0040.0000 - 0x007f.ffff (4M): kernel page tables
+
+0x00f0.0000 - 0x00ff.ffff (1M): ISA memory
-0x1000.0000 - 0x1fff.ffff (256M): dynamic memory \ No newline at end of file
+0x0400.0000 - 0x07ff.ffff (64M): kernel heap, bss pages
+0x0800.0000 - 0xffff.ffff (rest): user pages, hardware memory \ No newline at end of file
diff --git a/doc/internal/ple.txt b/doc/internal/ple.txt
deleted file mode 100644
index 1bcceb4..0000000
--- a/doc/internal/ple.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-Portland Executable Format
-
-0x00 magic dword 0xb9ba4c50
-0x04 minor version word 0x0000
-0x06 major version word 0x0000
-0x08 payload file offset dword
-0x0c payload length dword
-0x10 bss length dword
-0x14 virtual entry point dword
-
-Payload loaded at start of cs=ds=ss.
-bss after payload, esp set to end of bss. \ No newline at end of file
diff --git a/doc/ints.txt b/doc/ints.txt
index 1da08fd..2a97049 100644
--- a/doc/ints.txt
+++ b/doc/ints.txt
@@ -1,30 +1,32 @@
-int 0x30 - exit task
-int 0x31 - yield to scheduler
-int 0x33 - extend data section by eax bytes
- actual amount extended returned in eax
+quit task is int 0x38
-int 0x32 - system call
+
+yield task is int 0x39
+eax is cleared on yield
+
+
+syscall is int 0x30
system call number in eax
args in ebx, ecx, edx, esi, edi
result in eax
+ note: do not assume eax is unmodified if there is no value to be returned
+modifies ecx, edx
+
+file system calls have units of bytes unless otherwise specified
+functions returning handles or pointers use 0 to indicate error
+functions returning "success" use 1 for success and 0 for failure
+see keys.txt for the return type of the "get key" system call
+
+invalid system call numbers change eax to -1, and have no other effect.
+
- function | eax | eax out | ebx | ecx | edx | esi | edi
----------------|-----|-----------|---------------|--------|--------|-----|-----
- vga_blank | 0x0 | | | | | |
- vga_set_color | 0x1 | | color | | | |
- vga_printch | 0x2 | | char | | | |
- vga_printsz | 0x3 | | sz string | | | |
- vga_printsn | 0x4 | | non-sz string | length | | |
- | | | | | | |
- fs_open | 0x5 | handle | path | | | |
- fs_open_root | 0x6 | handle | | | | |
- fs_new | 0x7 | handle | path | | | |
- fs_close | 0x8 | | handle | | | |
- fs_delete | 0x9 | | path | | | |
- fs_exists | 0xa | does | path | | | |
- fs_seek | 0xb | seeked by | handle | by | | |
- fs_tell | 0xc | position | handle | | | |
- fs_read | 0xd | read | handle | max | buffer | |
- fs_write | 0xe | written | handle | max | buffer | |
- | | | | | | |
- plef_run | 0xf | handle | image path | | | | \ No newline at end of file
+ function | eax | eax out | ebx | ecx | edx | esi | edi
+---------------|-----|---------------|---------------|-------------|-------|--------|-----
+ open file | 0x0 | handle | drive number | path | | |
+ close file | 0x1 | | handle | | | |
+ file read | 0x2 | read | handle | file offset | count | buffer |
+ get file size | 0x3 | size | handle | | | |
+ start task | 0x4 | success | drive number | path | | |
+ log string | 0x5 | | sz string | | | |
+ get key | 0x6 | keycode | | | | |
+ allocate ram | 0x7 | start pointer | pages | | | | \ No newline at end of file
diff --git a/doc/keys.txt b/doc/keys.txt
new file mode 100644
index 0000000..a083eba
--- /dev/null
+++ b/doc/keys.txt
@@ -0,0 +1,42 @@
+keycodes are 32-bit integers.
+
+the low byte indicates the key itself. for printable characters (and keys with
+reasonable translations to ascii control codes), this is the ascii code. for
+other ones, something in the range of 0x80 to 0xff is used, seen in table 1.
+
+the top 24 bits indicate several flags. these are seen in table 2, where bit 0
+is the lowest bit of the second lowest byte of the keycode, and bit 23 is the
+highest bit of the highest byte of the keycode.
+
+the "get key" system call returns 0 if there is not a key available. it is
+recommended to make the system call and, if it returns 0, yield to the scheduler
+and then loop back to making the system call. this way your task does not block
+the cpu while waiting for keyboard input. see the "get_key_char" function in
+user.c of the "knob" library for an example of this.
+
+
+table 1:
+
+ code | key
+------|-----
+ 0x80 |
+ .... |
+ 0xff |
+
+table 2:
+
+bit 0: left shift
+bit 1: right shift
+bit 2: caps lock
+bit 3: insert
+bit 4: num lock
+bit 5: scroll lock
+bit 6: left alt
+bit 7: right alt
+bit 8: left control
+bit 9: right control
+bit 10: left meta
+bit 11: right meta
+
+bits 12-23 are reserved for future versions. in this version, they should be set
+to zero when giving a keycode, and should be ignored when recieving a keycode. \ No newline at end of file
diff --git a/fs-skel/sys/startup.rc b/fs-skel/sys/startup.rc
index 1535d72..209542a 100644
--- a/fs-skel/sys/startup.rc
+++ b/fs-skel/sys/startup.rc
@@ -1 +1 @@
-/bin/hello.ple \ No newline at end of file
+/bin/shell.ple \ No newline at end of file
diff --git a/makefile b/makefile
index 502a3cc..c6706ed 100644
--- a/makefile
+++ b/makefile
@@ -1,4 +1,4 @@
-disk: kernel boot skel hello
+disk: kernel boot skel init #psch
mkdir -p obj out
/sbin/mkfs.fat -C -f 1 -F 16 -n "PORTLAND OS" -R 65 -s 1 -S 512 obj/shadow.img 8192
echo -n -e '\xeb\x3c' > obj/jmp.bin
@@ -15,41 +15,68 @@ debug: disk
gdb -x qemu-debug.gdb
clean:
- rm -r obj out
+ rm -r obj out || true
skel:
mkdir -p out/fs
cp -r fs-skel/* out/fs/
kgccargs = -Wall -Wsuggest-attribute=pure -Wsuggest-attribute=const -Wsuggest-attribute=malloc -m32 -Og -ffreestanding -fno-asynchronous-unwind-tables
-ugccargs = ${kgccargs} -Isrc/libc/inc
+ugccargs = ${kgccargs} -Isrc/user/include
nasmargs = -f elf32
+partlink = -r -m elf_i386
-hello:
- mkdir -p obj/hello out/fs/bin
- nasm ${nasmargs} src/user/hello/hello.asm -o obj/hello/hello.o
- ld -T src/user-elf.ld obj/hello/hello.o -o obj/hello.elf
- objcopy -O binary obj/hello.elf out/fs/bin/hello.ple
+init: knob
+ mkdir -p obj/init out/fs/bin
-init:
- #TODO
+ gcc ${ugccargs} -c src/user/init/main.c -o obj/init/main.o
+
+ ld -T src/user/elf.ld obj/init/main.o obj/knob.o -o obj/init.elf
+ cp obj/init.elf out/fs/bin/init.elf
+
+psch: knob
+ mkdir -p obj/psch out/fs/bin
+
+ gcc ${ugccargs} -c src/user/psch/main.c -o obj/psch/main.o
+ gcc ${ugccargs} -c src/user/psch/data.c -o obj/psch/data.o
+
+ ld -T src/user/elf.ld obj/psch/*.o obj/knob.o -o obj/psch.elf
+ cp obj/psch.elf out/fs/bin/psch.elf
+
+knob:
+ mkdir -p obj/knob
+
+ gcc ${ugccargs} -c src/user/knob/env.c -o obj/knob/env.o
+ gcc ${ugccargs} -c src/user/knob/file.c -o obj/knob/file.o
+ gcc ${ugccargs} -c src/user/knob/format.c -o obj/knob/format.o
+ gcc ${ugccargs} -c src/user/knob/heap.c -o obj/knob/heap.o
+ gcc ${ugccargs} -c src/user/knob/quit.c -o obj/knob/quit.o
+ gcc ${ugccargs} -c src/user/knob/user.c -o obj/knob/user.o
+ nasm ${nasmargs} src/user/knob/entry.asm -o obj/knob/entry.o
+
+ ld ${partlink} obj/knob/*.o -o obj/knob.o
kernel:
mkdir -p obj/kernel out
+
gcc ${kgccargs} -c src/kernel/drive.c -o obj/kernel/drive.o
gcc ${kgccargs} -c src/kernel/fat.c -o obj/kernel/fat.o
gcc ${kgccargs} -c src/kernel/ide.c -o obj/kernel/ide.o
+ gcc ${kgccargs} -c src/kernel/idt.c -o obj/kernel/idt.o
gcc ${kgccargs} -c src/kernel/log.c -o obj/kernel/log.o
gcc ${kgccargs} -c src/kernel/main.c -o obj/kernel/main.o
gcc ${kgccargs} -c src/kernel/main2.c -o obj/kernel/main2.o
- gcc ${kgccargs} -c src/kernel/mem.c -o obj/kernel/mem.o
+ gcc ${kgccargs} -c src/kernel/pmap.c -o obj/kernel/pmap.o
+ gcc ${kgccargs} -c src/kernel/paging.c -o obj/kernel/paging.o
gcc ${kgccargs} -c src/kernel/panic.c -o obj/kernel/panic.o
gcc ${kgccargs} -c src/kernel/pci.c -o obj/kernel/pci.o
- gcc ${kgccargs} -c src/kernel/plef.c -o obj/kernel/plef.o
+ gcc ${kgccargs} -c src/kernel/elf.c -o obj/kernel/elf.o
gcc ${kgccargs} -c src/kernel/serial.c -o obj/kernel/serial.o
gcc ${kgccargs} -c src/kernel/task.c -o obj/kernel/task.o
gcc ${kgccargs} -c src/kernel/util.c -o obj/kernel/util.o
gcc ${kgccargs} -c src/kernel/vga.c -o obj/kernel/vga.o
+ nasm ${nasmargs} src/kernel/isrs.asm -o obj/kernel/isrs.o
+
ld -T src/kernel/elf-link.ld obj/kernel/*.o -o obj/kernel.elf
objcopy -O binary obj/kernel.elf out/kernel.bin
diff --git a/qemu-debug.gdb b/qemu-debug.gdb
index 72b935e..06e4914 100644
--- a/qemu-debug.gdb
+++ b/qemu-debug.gdb
@@ -1,7 +1,9 @@
target remote | qemu-system-i386 -m 768 -S -gdb stdio out/disk.img
add-symbol-file obj/kernel.elf
+add-symbol-file obj/init.elf
set disassembly-flavor intel
layout reg
break main
break panic
+break _before_start_task
cont
diff --git a/src/boot.asm b/src/boot.asm
index c74145c..28d1a89 100644
--- a/src/boot.asm
+++ b/src/boot.asm
@@ -10,6 +10,7 @@ pci_ver equ 0x4002
last_pci_bus equ 0x4004
pci_support equ 0x80
+pae_support equ 0x40
in al, 0x92
or al, 0x02
@@ -46,6 +47,34 @@ pci_support equ 0x80
mov byte [last_pci_bus], cl
no_pci:
+ mov eax, 1
+ cpuid
+
+ test dl, 0x40
+ jz no_pae
+
+ or byte [support_flags], pae_support
+
+no_pae:
+ mov ax, 0x1000
+ mov es, ax
+ xor di, di
+
+ mov edx, 0x534d4150
+ xor ebx, ebx
+
+mmap_loop:
+ mov eax, 0xe820
+ mov ecx, 24
+ int 0x15
+
+ jc mmap_end
+ add di, 24
+ test ebx, ebx
+ jnz mmap_loop
+mmap_end:
+ mov word [0x4006], di
+
cli
lgdt [gdt]
@@ -54,36 +83,36 @@ no_pci:
or al, 0x01
mov cr0, eax
- jmp 0x08:pmode
+ jmp 0x10:pmode
bits 32
pmode:
- mov ax, 0x10
+ mov ax, 0x18
mov ds, ax
mov ss, ax
mov esp, 0x00040000
xor ebp, ebp
+
call kernel_segment * 16
halt:
hlt
jmp halt
-times $$ + 0x018a - $ db 0
+times $$ + 510 - 0x3e - $ - (gdt.e - gdt) db 0
-;0x7dc8
gdt:
dw .e - .t
dd .t
.t:
- dq 0x0000_0000_0000_0000
- dq 0x00c0_9a00_0000_0037
- dq 0x00c1_9200_0000_ffff
- dq 0x0000_0000_0000_0000;TODO: task
- dq 0x0000_0000_0000_0000
- dq 0x0000_0000_0000_0000
+ dq 0x0000_0000_0000_0000;0x00: null
+ dq 0x0040_8900_4f98_0068;0x08: tss, 0x4f98
+ dq 0x00cf_9a00_0000_ffff;0x10: kernel code
+ dq 0x00cf_9200_0000_ffff;0x18: kernel data
+ dq 0x00cf_fa00_0000_ffff;0x20: user code
+ dq 0x00cf_f200_0000_ffff;0x28: user data
.e:
dw 0xaa55 \ No newline at end of file
diff --git a/src/kernel/boot.h b/src/kernel/boot.h
index b3d3e76..9774573 100644
--- a/src/kernel/boot.h
+++ b/src/kernel/boot.h
@@ -4,7 +4,8 @@
#include <stdint.h>
enum {
- BIS_PCI = 0x80
+ BIS_PCI = 0x80,
+ BIS_PAE = 0x40
};
enum {
diff --git a/src/kernel/drive.c b/src/kernel/drive.c
index 38fef38..c3aa5b2 100644
--- a/src/kernel/drive.c
+++ b/src/kernel/drive.c
@@ -2,23 +2,27 @@
#include "panic.h"
#include "fat.h"
-uint8_t n_drives = 0;
+uint8_t n_drives;
struct drive drives[256];
+void init_drives() {
+ n_drives = 0;
+}
+
__attribute__ ((const))
-static drive_file_id_t unknown_get_file(const struct drive *d, const char *path) {
+static file_id_t unknown_get_file(const struct drive *d, const char *path) {
return 0;
}
-static void unknown_free_file(const struct drive *d, drive_file_id_t fid) {
+static void unknown_free_file(const struct drive *d, file_id_t fid) {
panic("Free file called on unknown file system");
}
-static void unknown_load_sector(const struct drive *d, drive_file_id_t fid, uint32_t sector, void *at) {
+static void unknown_load_sector(const struct drive *d, file_id_t fid, uint32_t sector, void *at) {
panic("Load sector called on unknown file system");
}
-static uint32_t unknown_get_file_length(const struct drive *d, drive_file_id_t fid) {
+static uint32_t unknown_get_file_length(const struct drive *d, file_id_t fid) {
panic("Get file length called on unknown file system");
}
@@ -32,7 +36,7 @@ static uint32_t unknown_enumerate_dir(const struct drive *d, const char *path, s
return 0;
}
-static void determine_fs(struct drive *d) {
+static inline void determine_fs(struct drive *d) {
if (try_fat_init_drive(d))
return;
@@ -45,7 +49,12 @@ static void determine_fs(struct drive *d) {
d->get_free_sectors = &unknown_get_free_sectors;
}
+//drive should be ready before this.
+//determine_fs and its children
+//do not need to make ready or done
void commit_drive(struct drive data) {
determine_fs(&data);
- drives[n_drives++] = data;
+ drives[n_drives] = data;
+ data.done(drives + n_drives);
+ ++n_drives;
} \ No newline at end of file
diff --git a/src/kernel/drive.h b/src/kernel/drive.h
index 8eb6955..b02b4ad 100644
--- a/src/kernel/drive.h
+++ b/src/kernel/drive.h
@@ -4,7 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
-typedef uint8_t drive_file_id_t;
+typedef uint8_t file_id_t;
typedef uint8_t fs_id_t;
typedef uint8_t drive_id_t;
@@ -22,23 +22,26 @@ struct drive {
char *drive_type;
char *fs_type;
- uint8_t (*read_sectors) (const struct drive *d, uint32_t start, uint32_t count, void *buffer);
- uint8_t (*write_sectors)(const struct drive *d, uint32_t start, uint32_t count, const void *buffer);
+ uint32_t (*read_sectors) (const struct drive *d, uint32_t start, uint32_t count, void *buffer);
+ uint32_t (*write_sectors)(const struct drive *d, uint32_t start, uint32_t count, const void *buffer);
+ void (*ready) (const struct drive *d);
+ void (*done) (const struct drive *d);
uint32_t n_sectors;
drive_id_t drive_id;
- drive_file_id_t (*get_file) (const struct drive *d, const char *path);
- void (*free_file) (const struct drive *d, drive_file_id_t fid);
- void (*load_sector) (const struct drive *d, drive_file_id_t fid, uint32_t sector, void *at);
- uint32_t (*get_file_length) (const struct drive *d, drive_file_id_t fid);
- uint32_t (*enumerate_dir) (const struct drive *d, const char *path, struct directory_content_info *info, uint32_t max);
- uint32_t (*get_free_sectors)(const struct drive *d);
+ file_id_t (*get_file) (const struct drive *d, const char *path);
+ void (*free_file) (const struct drive *d, file_id_t fid);
+ void (*load_sector) (const struct drive *d, file_id_t fid, uint32_t sector, void *at);
+ uint32_t (*get_file_length) (const struct drive *d, file_id_t fid);
+ uint32_t (*enumerate_dir) (const struct drive *d, const char *path, struct directory_content_info *info, uint32_t max);
+ uint32_t (*get_free_sectors)(const struct drive *d);
fs_id_t fs_id;
};
extern uint8_t n_drives;
extern struct drive drives[MAX_DRIVES];
+void init_drives();
void commit_drive(struct drive data);
#endif \ No newline at end of file
diff --git a/src/kernel/elf-link.ld b/src/kernel/elf-link.ld
index a0d60f9..10abac0 100644
--- a/src/kernel/elf-link.ld
+++ b/src/kernel/elf-link.ld
@@ -13,9 +13,9 @@ SECTIONS {
.data : {
*(.data)
}
- . = 0x10000000;
+ . = 0x04000000;
.bss : {
*(.bss)
- kernel_bss_end = .;
+ _kernel_bss_end = .;
}
} \ No newline at end of file
diff --git a/src/kernel/elf.c b/src/kernel/elf.c
new file mode 100644
index 0000000..8cd659c
--- /dev/null
+++ b/src/kernel/elf.c
@@ -0,0 +1,116 @@
+#include <stdint.h>
+#include "drive.h"
+#include "elf.h"
+#include "task.h"
+#include "util.h"
+#include "paging.h"
+#include "pmap.h"
+
+#define ELF_MAGIC 0x464c457f
+
+enum {
+ ELF_32 = 1,
+ ELF_64 = 2
+};
+
+enum {
+ LITTLE_ENDIAN = 1,
+ BIG_ENDIAN = 2
+};
+
+enum {
+ //...
+ ARCH_X86 = 0x03
+ //...
+};
+
+struct elf_header {
+ uint32_t magic;
+ uint8_t word_size;
+ uint8_t endianness;
+ uint8_t elf_version;
+ uint8_t target_os;//ignored
+ uint8_t os_abi_version;//should be zero
+ uint8_t reserved[7];
+ uint16_t object_type;//TODO
+ uint16_t architecture;//should be ARCH_X86
+ uint32_t elf_version_2;
+ uint32_t entry_vma;
+ uint32_t phtable_fa;
+ uint32_t shtable_fa;
+ uint32_t flags;
+ uint16_t eh_size;
+ uint16_t phentry_size;
+ uint16_t phtable_count;
+ uint16_t shentry_size;
+ uint16_t shtable_count;
+ uint16_t sh_names_entry;
+} __attribute__ ((packed));
+
+enum {
+ PT_UNUSED = 0,
+ PT_LOADME = 1,
+ PT_DYNLINK = 2,
+ PT_INTERP = 3,
+ PT_COMMENT = 4,
+ PT_SHARED = 5,
+ PT_PHTABLE = 6,
+ PT_TL_TMPL = 7
+};
+
+enum {
+ PH_WRITABLE = 0x02
+};
+
+struct ph_entry {
+ uint32_t type;
+ uint32_t fa;
+ uint32_t vma;//must be page-aligned
+ uint32_t pma;//ignored
+ uint32_t fs;
+ uint32_t vms;
+ uint32_t flags;
+ uint32_t align;
+} __attribute__ ((packed));
+
+bool try_elf_run(const struct drive *d, const char *path) {
+ file_id_t h = d->get_file(d, path);
+ if (!h)
+ return false;
+
+ struct elf_header ehead;
+
+ fmcpy(&ehead, d, h, 0, sizeof(struct elf_header));
+
+ if ((ehead.magic != ELF_MAGIC) ||
+ (ehead.word_size != ELF_32) ||
+ (ehead.endianness != LITTLE_ENDIAN) ||
+ ehead.os_abi_version) {
+ d->free_file(d, h);
+ return false;
+ }
+
+ uint32_t phtable_size = ehead.phentry_size * ehead.phtable_count;
+ uint16_t phtable_pages = (phtable_size - 1) / 4096 + 1;
+
+ void *phtable = allocate_kernel_pages(phtable_pages);
+ fmcpy(phtable, d, h, ehead.phtable_fa, phtable_size);
+
+ void *pd = new_task_pd();
+
+ for (uint32_t phi = 0; phi < ehead.phtable_count; ++phi) {
+ struct ph_entry *entry = phtable + phi * ehead.phentry_size;
+ if (entry->type != PT_LOADME)
+ continue;
+ void *pma = pd_user_allocate(pd, entry->vma, (entry->vms - 1) / 4096 + 1, entry->flags & PH_WRITABLE);
+ fmcpy(pma, d, h, entry->fa, entry->fs);
+ }
+
+ free_pages(phtable, phtable_pages);
+
+ struct task_state tstate;
+ tstate.page_directory = pd;
+ tstate.ret_addr = ehead.entry_vma;
+ new_task(tstate);
+ return true;
+} \ No newline at end of file
diff --git a/src/kernel/elf.h b/src/kernel/elf.h
new file mode 100644
index 0000000..09aa05c
--- /dev/null
+++ b/src/kernel/elf.h
@@ -0,0 +1,9 @@
+#ifndef ELF_H
+#define ELF_H
+
+#include <stdint.h>
+#include "drive.h"
+
+bool try_elf_run(const struct drive *d, const char *path);
+
+#endif \ No newline at end of file
diff --git a/src/kernel/fat.c b/src/kernel/fat.c
index ff01d7d..1b6c52d 100644
--- a/src/kernel/fat.c
+++ b/src/kernel/fat.c
@@ -3,7 +3,7 @@
#include "util.h"
#include "ata.h"
#include "fat.h"
-#include "mem.h"
+#include "pmap.h"
#define MAX_FAT_DRIVES 16
#define MAX_OPEN_FILES_PER_DRIVE 32
@@ -81,14 +81,14 @@ struct fat_drive_info {
};
static struct fat_drive_info infos[MAX_FAT_DRIVES];
-static uint8_t next_id = 0;
+static uint8_t next_id;
static uint8_t fat_driver_buffer[512];
static struct fat_info *next_fi;
static void alloc_next_fi() {
if (!((uint32_t)(next_fi = (struct fat_info *)((uint32_t)next_fi + 64)) & 0xfff))
- if (!(next_fi = allocate_pages(1)))
+ if (!(next_fi = allocate_kernel_pages(1)))
panic("Out of memory in FAT driver.");
}
@@ -109,8 +109,10 @@ static void load_cluster(uint16_t c, void *to) {
cur_drive->read_sectors(cur_drive, s, 1, to);
}
+__attribute__ ((pure))
static uint16_t next_cluster(uint16_t c) {
- panic("TODO: compute next sector (or 0 for none)");
+ uint16_t found = infos[cur_id].fat[c];
+ return (found < 2) || (found >= 0xfff0) ? 0 : found;
}
static const uint8_t this_dir[] = {'.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
@@ -182,7 +184,7 @@ static const char *split_path(const char *path, uint8_t *fat_name_buffer) {
}
else
panic("Bad path in FAT16 driver");
- else if ((fi == 8) || (fi == 11))
+ else if (((fi == 8) && (path[pi - 1] != EXT_SEP_CHAR)) || (fi == 11))
panic("Bad path in FAT16 driver");
else {
fat_name_buffer[fi++] = (uint8_t)path[pi++];
@@ -209,13 +211,17 @@ static bool try_load_from_path(const struct drive *d, const char *path) {
return true;
}
-static drive_file_id_t fat_get_file(const struct drive *d, const char *path) {
+static file_id_t fat_get_file(const struct drive *d, const char *path) {
+ d->ready(d);
struct open_file_info *open_files = infos[d->drive_id].open_files - 1;
- for (drive_file_id_t n = 1; n != MAX_OPEN_FILES_PER_DRIVE + 1; ++n)
+ for (file_id_t n = 1; n != MAX_OPEN_FILES_PER_DRIVE + 1; ++n)
if (!open_files[n].di_sector) {
- if (!try_load_from_path(d, path))
+ if (!try_load_from_path(d, path)) {
+ d->done(d);
return 0;
+ }
+ d->done(d);
open_files[n].di_sector = cur_sect;
open_files[n].di_number = cur_dir - (struct directory_entry *)fat_driver_buffer;
open_files[n].start_cluster = cur_dir->first_cluster;
@@ -226,22 +232,26 @@ static drive_file_id_t fat_get_file(const struct drive *d, const char *path) {
panic("Maximum number of files open reached for FAT drive.");
}
-static void fat_free_file(const struct drive *d, drive_file_id_t fid) {
+static void fat_free_file(const struct drive *d, file_id_t fid) {
infos[d->drive_id].open_files[fid - 1].di_sector = 0;
}
-static void fat_load_sector(const struct drive *d, drive_file_id_t fid, uint32_t sector, void *at) {
+static void fat_load_sector(const struct drive *d, file_id_t fid, uint32_t sector, void *at) {
cur_drive = d;
cur_id = d->drive_id;
cur_fdi = &infos[cur_id];
+
uint16_t c = cur_fdi->open_files[fid - 1].start_cluster;
for (uint32_t i = 0; i < sector; ++i)
c = next_cluster(c);
+
+ d->ready(d);
load_cluster(c, at);
+ d->done(d);
}
__attribute__ ((pure))
-static uint32_t fat_get_file_length(const struct drive *d, drive_file_id_t fid) {
+static uint32_t fat_get_file_length(const struct drive *d, file_id_t fid) {
return infos[d->drive_id].open_files[fid - 1].length;
}
@@ -288,8 +298,10 @@ static uint32_t enumerate_root(const struct drive *d, struct directory_content_i
d->read_sectors(d, sect, 1, entry);
}
- if (!*(uint8_t *)entry || (info == fill + max))
+ if (!*(uint8_t *)entry || (info == fill + max)) {
+ d->done(d);
return fill - info;
+ }
if (entry-> attrib & FA_LABEL) {
++entry;
@@ -306,11 +318,15 @@ static uint32_t enumerate_root(const struct drive *d, struct directory_content_i
}
static uint32_t fat_enumerate_dir(const struct drive *d, const char *path, struct directory_content_info *info, uint32_t max) {
+ d->ready(d);
+
if (!*path)
return enumerate_root(d, info, max);
- if (!try_load_from_path(d, path))
+ if (!try_load_from_path(d, path)) {
+ d->done(d);
return 0;
+ }
uint16_t cluster = cur_dir->first_cluster;
load_cluster(cluster, fat_driver_buffer);
@@ -323,8 +339,10 @@ static uint32_t fat_enumerate_dir(const struct drive *d, const char *path, struc
load_cluster(cluster = next_cluster(cluster), fat_driver_buffer);
}
- if (!*(uint8_t *)entry || (fill == info + max))
+ if (!*(uint8_t *)entry || (fill == info + max)) {
+ d->done(d);
return fill - info;
+ }
if (check_fat_names(entry->name, this_dir) || check_fat_names(entry->name, parent_dir)) {
++entry;
@@ -341,7 +359,8 @@ static uint32_t fat_enumerate_dir(const struct drive *d, const char *path, struc
}
void init_fat() {
- next_fi = allocate_pages(1);
+ next_fi = allocate_kernel_pages(1);
+ next_id = 0;
}
bool try_fat_init_drive(struct drive *d) {
@@ -366,7 +385,7 @@ bool try_fat_init_drive(struct drive *d) {
d->fs_id = next_id;
infos[next_id].fi = next_fi;
- infos[next_id].fat = allocate_pages(((next_fi->sectors_per_fat - 1) >> 3) + 1);
+ infos[next_id].fat = allocate_kernel_pages(((next_fi->sectors_per_fat - 1) >> 3) + 1);
infos[next_id].root_start = next_fi->reserved_sectors +
next_fi->sectors_per_fat * next_fi->fats;
infos[next_id].data_start = infos[next_id].root_start +
@@ -374,7 +393,7 @@ bool try_fat_init_drive(struct drive *d) {
d->read_sectors(d, next_fi->reserved_sectors, next_fi->sectors_per_fat, infos[next_id].fat);
struct open_file_info *open_files = infos[next_id].open_files - 1;
- for (drive_file_id_t i = 0; i < MAX_OPEN_FILES_PER_DRIVE; ++i)
+ for (file_id_t i = 0; i < MAX_OPEN_FILES_PER_DRIVE; ++i)
open_files[i].di_sector = 0;
alloc_next_fi();
diff --git a/src/kernel/fat.h b/src/kernel/fat.h
index cadb073..3ca9cc3 100644
--- a/src/kernel/fat.h
+++ b/src/kernel/fat.h
@@ -2,7 +2,6 @@
#define FAT_H
#include <stdbool.h>
-#include <stdint.h>
#include "drive.h"
void init_fat();
diff --git a/src/kernel/ide.c b/src/kernel/ide.c
index a4ba34c..70b1dee 100644
--- a/src/kernel/ide.c
+++ b/src/kernel/ide.c
@@ -15,7 +15,7 @@ struct ide_drive_info {
};
static struct ide_drive_info ide_drives[MAX_IDE_DRIVES];
-static drive_id_t n_ide_drives = 0;
+static drive_id_t n_ide_drives;
typedef uint16_t spinner_t;
@@ -53,7 +53,7 @@ static uint8_t wait_for_data_ready_not_busy(uint16_t base_port) {
panic("Spun out in IDE driver.");
}
-static uint8_t ide_ata_rs(const struct drive *d, uint32_t start, uint32_t count, void *buffer) {
+static uint32_t ide_ata_rs(const struct drive *d, uint32_t start, uint32_t count, void *buffer) {
if (start >= d->n_sectors)
return 0;
if (start + count > d->n_sectors)
@@ -91,21 +91,29 @@ static uint8_t ide_ata_rs(const struct drive *d, uint32_t start, uint32_t count,
return count;
}
-static uint8_t ide_ata_ws(const struct drive *d, uint32_t start, uint32_t count, const void *buffer) {
+static uint32_t ide_ata_ws(const struct drive *d, uint32_t start, uint32_t count, const void *buffer) {
panic("IDE ATA writing not implemented yet");
return 0;
}
-static uint8_t ide_atapi_rs(const struct drive *d, uint32_t start, uint32_t count, void *buffer) {
- //panic("IDE ATAPI reading not implemented yet");
- return 0;
+static uint32_t ide_atapi_rs(const struct drive *d, uint32_t start, uint32_t count, void *buffer) {
+ if (start >= d->n_sectors)
+ return 0;
+ if (start + count > d->n_sectors)
+ count = d->n_sectors - start;
+ if (!count)
+ return 0;
+
+
}
-static uint8_t ide_atapi_ws(const struct drive *d, uint32_t start, uint32_t count, const void *buffer) {
+static uint32_t ide_atapi_ws(const struct drive *d, uint32_t start, uint32_t count, const void *buffer) {
panic("IDE ATAPI writing not implemented yet");
return 0;
}
+static void nop(const struct drive *d) { }
+
struct id_space {
uint8_t skip1[120];
uint32_t max_lba;//120
@@ -132,9 +140,13 @@ static void test_drive(uint16_t base_port, uint16_t alt_port, bool slave) {
outb(base_port | ATA_REG_CMD, ATA_CMD_ID);
- if (!inb(base_port | ATA_REG_STATUS))
+ uint8_t status = inb(base_port | ATA_REG_STATUS);
+ if (!status || (status == 0x7f))
return;
+ next_d.ready = &nop;
+ next_d.done = &nop;
+
struct id_space ids;
if (!(wait_for_error_or_ready(base_port) & ATA_STATUS_ERROR)) {
@@ -157,7 +169,7 @@ static void test_drive(uint16_t base_port, uint16_t alt_port, bool slave) {
next_d.write_sectors = &ide_atapi_ws;
//TODO: issue proper identify command
- ids.max_lba = -1;
+ return;
}
else if (code == 0xc33c) {
next_d.drive_type = "IDE SATA";
@@ -177,6 +189,8 @@ static void test_drive(uint16_t base_port, uint16_t alt_port, bool slave) {
}
void init_ide() {
+ n_ide_drives = 0;
+
uint16_t check_from = 0;
struct pci_device *device;
//double parentheses to let gcc know this assignment isn't a mistake
diff --git a/src/kernel/idt.c b/src/kernel/idt.c
new file mode 100644
index 0000000..460cfb0
--- /dev/null
+++ b/src/kernel/idt.c
@@ -0,0 +1,116 @@
+#include "drive.h"
+#include "elf.h"
+#include "util.h"
+#include "idt.h"
+#include "log.h"
+#include "panic.h"
+#include "task.h"
+#include "paging.h"
+
+enum {
+ IDT_PRESENT = 0x80,
+
+ IDT_INT = 0x0e,
+ IDT_TRAP = 0x0f
+};
+
+struct idt_entry {
+ uint16_t addr_low;
+ uint16_t cs;
+ uint8_t zero;
+ uint8_t flags;
+ uint16_t addr_high;
+} __attribute__ ((packed));
+
+struct idt_entry idt[256];
+
+struct {
+ uint16_t limit;
+ uint32_t start;
+} __attribute__ ((packed)) idtr = {
+ .limit = 256 * sizeof(struct idt_entry) - 1,
+ .start = (uint32_t)idt
+};
+
+//file handles as (drive_number << 8) + file_id_t
+
+static uint32_t sc_open_file(uint32_t drive_number, char *path) {
+ return (drive_number << 8) + drives[drive_number].get_file(drives + drive_number, path);
+}
+
+static void sc_close_file(uint32_t handle) {
+ drives[handle >> 8].free_file(drives + (handle >> 8), handle & 0xff);
+}
+
+static uint32_t sc_file_get_size(uint32_t handle) {
+ return drives[handle >> 8].get_file_length(drives + (handle >> 8), handle & 0xff);
+}
+
+static uint32_t sc_file_read(uint32_t handle, uint32_t file_offset, uint32_t count, void *buffer) {
+ uint32_t len = sc_file_get_size(handle);
+ if (file_offset + count > len)
+ count = len - file_offset;
+ fmcpy(buffer, drives + (handle >> 8), handle & 0xff, file_offset, count);
+ return count;
+}
+
+static bool sc_start_task(uint32_t drive_number, char *path) {
+ switch_to_kernel_cr3();
+ bool result = try_elf_run(drives + drive_number, path);
+ switch_to_task_cr3();
+ return result;
+}
+
+static void sc_log_string(char *sz) {
+ logsz(sz);
+}
+
+static char sc_get_key() {
+ panic("TODO: get key system call");
+}
+
+static void *sc_allocate_ram(uint32_t pages) {
+//switch_to_kernel_cr3();
+ void *result = pd_user_allocate_anywhere_writable(active_task->page_directory, pages);
+//switch_to_task_cr3();
+}
+
+void const *syscall_table[] = {
+ &sc_open_file,
+ &sc_close_file,
+ &sc_file_read,
+ &sc_file_get_size,
+ &sc_start_task,
+ &sc_log_string,
+ &sc_get_key,
+ &sc_allocate_ram
+};
+
+extern void syscall_isr;
+extern void quit_isr;
+extern void yield_isr;
+
+void register_int(uint8_t n, void *isr, uint8_t dpl) {
+ idt[n].addr_low = (uint32_t)isr & 0xffff;
+ idt[n].addr_high = (uint32_t)isr >> 16;
+ idt[n].cs = 0x10;
+ idt[n].flags = IDT_PRESENT | (dpl << 5) | IDT_INT;
+}
+
+void init_idt() {
+ for (uint16_t i = 0; i < 256; ++i) {
+ idt[i].flags = 0;
+ idt[i].zero = 0;
+ }
+
+ register_int(0x30, &syscall_isr, 3);
+ register_int(0x38, &quit_isr, 3);
+ register_int(0x39, &yield_isr, 3);
+
+ outb(0x0021, 0xff);
+ outb(0x00a1, 0xff);
+
+ asm volatile (
+ "lidt %0"
+ : : "m" (idtr) : "al");
+} \ No newline at end of file
diff --git a/src/kernel/idt.h b/src/kernel/idt.h
new file mode 100644
index 0000000..6815de9
--- /dev/null
+++ b/src/kernel/idt.h
@@ -0,0 +1,9 @@
+#ifndef IDT_H
+#define IDT_H
+
+#include <stdint.h>
+
+void init_idt();
+void enable_idt();
+
+#endif \ No newline at end of file
diff --git a/src/kernel/isrs.asm b/src/kernel/isrs.asm
new file mode 100644
index 0000000..163ddbe
--- /dev/null
+++ b/src/kernel/isrs.asm
@@ -0,0 +1,94 @@
+bits 32
+
+global syscall_isr
+global quit_isr
+global yield_isr
+global _start_user_mode
+
+extern syscall_table
+extern active_task
+
+extern delete_task
+extern advance_active_task
+
+n_syscalls equ 8
+
+section .text
+syscall_isr:
+ cmp eax, n_syscalls
+ jge .bad
+
+ mov eax, dword [syscall_table + eax * 4]
+
+ push edi
+ push esi
+ push edx
+ push ecx
+ push ebx
+
+ call eax
+
+ add esp, 20
+
+ iret
+
+.bad:
+ mov eax, -1
+ iret
+
+quit_isr:
+ push dword [active_task]
+ call delete_task
+ push yield_isr.return_to_task
+ jmp advance_active_task
+
+yield_isr:
+ mov eax, dword [active_task]
+
+ mov dword [eax + 8], ebx
+ mov dword [eax + 12], ecx
+ mov dword [eax + 16], edx
+ mov dword [eax + 20], esi
+ mov dword [eax + 24], edi
+ mov dword [eax + 28], ebp
+
+ mov edx, dword [esp]
+ mov dword [eax], edx
+
+ mov edx, cr3
+ mov dword [eax + 4], edx
+
+ mov edx, dword [esp + 12]
+ mov dword [eax + 4], edx
+
+ call advance_active_task
+
+.return_to_task:
+ mov eax, dword [active_task]
+
+ mov edx, dword [eax]
+ mov dword [esp], edx
+
+ mov edx, dword [eax + 4]
+ mov cr3, edx
+
+ mov edx, dword [eax + 4]
+ mov dword [esp + 24], edx
+
+ mov ebx, dword [eax + 8]
+ mov ecx, dword [eax + 12]
+ mov edx, dword [eax + 16]
+ mov esi, dword [eax + 20]
+ mov edi, dword [eax + 24]
+ mov ebp, dword [eax + 28]
+
+_before_start_task:
+ iret
+
+_start_user_mode:
+ push dword 0x2b
+ sub esp, 4
+ push dword 0x00000200;interrupt flag
+ push dword 0x23
+ sub esp, 4
+ jmp yield_isr.return_to_task \ No newline at end of file
diff --git a/src/kernel/main.c b/src/kernel/main.c
index 0c57ef3..eb57a01 100644
--- a/src/kernel/main.c
+++ b/src/kernel/main.c
@@ -1,21 +1,30 @@
#include <stdint.h>
#include "serial.h"
#include "panic.h"
+#include "paging.h"
#include "boot.h"
#include "util.h"
#include "fat.h"
#include "ide.h"
-#include "mem.h"
+#include "idt.h"
+#include "pmap.h"
+#include "task.h"
#include "pci.h"
#include "log.h"
+#include "elf.h"
+void reset_tree();
void tree(struct drive *d);
+void _start_user_mode() __attribute__ ((noreturn));
+
__attribute__ ((noreturn))
void main() {
char nbuf[11];
- init_mmap();
+ init_pagemap();
+ init_paging();
+ init_tasks();
init_serial();
init_log();
@@ -57,6 +66,8 @@ void main() {
init_fat();
//other fs drivers
+ init_drives();
+
init_ide();
//other drive drivers
@@ -97,13 +108,44 @@ void main() {
logch('\n');
- u32_dec(pages_left * 4, nbuf);
+ for (uint8_t n = 0; n < n_drives; ++n) {
+ if (drives[n].get_free_sectors(drives + n) == -1)
+ continue;
+
+ u32_dec(n, nbuf);
+ logsz("sd");
+ logsz(nbuf);
+ logsz(" tree:\n");
+
+ reset_tree();
+ tree(drives + n);
+ logch('\n');
+ }
+
+ if (!try_elf_run(drives, "BIN/INIT.ELF"))
+ panic("Failed to load init program.");
+
+ if (BOOT_INFO->support_flags & BIS_PAE)
+ logsz("Processor supports PAE (but Portland OS does not yet).\n\n");
+ else
+ logsz("Processor does not support PAE.\n\n");
+
+ u32_dec(kernel_pages_left * 4, nbuf);
+ logsz(nbuf);
+ logsz("k / ");
+ u32_dec(max_kernel_pages * 4, nbuf);
+ logsz(nbuf);
+ logsz("k kernel heap free.\n");
+ u32_dec(user_pages_left * 4, nbuf);
+ logsz(nbuf);
+ logsz("k / ");
+ u32_dec(max_user_pages * 4, nbuf);
logsz(nbuf);
- logsz("k dynamic memory free.\n\n");
+ logsz("k user memory free.\n");
- logsz("sd0 root:\n");
- tree(&drives[0]);
+//while (1)
+// asm ("hlt");
- while (1)
- asm ("hlt");
+ init_idt();
+ _start_user_mode();
} \ No newline at end of file
diff --git a/src/kernel/main2.c b/src/kernel/main2.c
index 2b85592..27ef8de 100644
--- a/src/kernel/main2.c
+++ b/src/kernel/main2.c
@@ -5,11 +5,21 @@
static char nbuf2[11];
-static char path_builder[200] = "";
-static uint8_t path_builder_len = 0;
+static char path_builder[200];
+static uint8_t path_builder_len;
-static char indent_builder[20] = " ";
-static uint8_t indent_builder_len = 2;
+static char indent_builder[20];
+static uint8_t indent_builder_len;
+
+void reset_tree() {
+ path_builder[0] = '\0';
+ path_builder_len = 0;
+
+ indent_builder[0] = ' ';
+ indent_builder[1] = ' ';
+ indent_builder[2] = '\0';
+ indent_builder_len = 2;
+}
void tree(struct drive *d) {
struct directory_content_info infos[100];
diff --git a/src/kernel/mem.c b/src/kernel/mem.c
deleted file mode 100644
index d3baad3..0000000
--- a/src/kernel/mem.c
+++ /dev/null
@@ -1,63 +0,0 @@
-#include <stdint.h>
-#include "panic.h"
-#include "mem.h"
-
-#define DYNAMIC_START (0x10000000)
-#define DYNAMIC_END (DYNAMIC_START + 65536 * 4096)
-#define PAGE_TO_ADDR(n) ((void *)(DYNAMIC_START | ((n) << 12)))
-#define ADDR_TO_PAGE(n) (((uint32_t)(n) & ~DYNAMIC_START) >> 12)
-
-#define MMAP_START (0x00010000)
-#define PAGE_USED(n) ((*(uint8_t *)(MMAP_START + (n >> 3)) >> (n & 7)) & 1)
-#define CLEAR_PAGE(n) *(uint8_t *)(MMAP_START + (n >> 3)) &= ~(1 << (n & 7))
-#define SET_PAGE(n) *(uint8_t *)(MMAP_START + (n >> 3)) |= 1 << (n & 7)
-
-extern const void kernel_bss_end;
-
-uint16_t pages_left;
-
-void init_mmap() {
- volatile uint8_t *end_ptr = (uint8_t *)(DYNAMIC_END - 1);
- uint8_t end_val = *end_ptr;
- *end_ptr = (uint8_t)~end_val;
- if (*end_ptr != (uint8_t)~end_val)
- panic("Not enough memory. Must have at least 512MiB.");
-
- for (uint32_t *m = (uint32_t *)MMAP_START; m < (uint32_t *)(MMAP_START + (65536 / 8)); ++m)
- *m = 0;
-
- uint16_t kernel_bss_pages = (((uint32_t)&kernel_bss_end - DYNAMIC_START - 1) >> 12) + 1;
- for (uint16_t i = 0; i < kernel_bss_pages; ++i)
- SET_PAGE(i);
-
- pages_left = 65536 - kernel_bss_pages;
-}
-
-//very inneficient algorithm, just returns first hole big enough.
-//a smarter algorithm might pick the smallest one available,
-//and go by bytes (or dwords) instead of bits where possible.
-void *allocate_pages(uint16_t n) {
- uint16_t run = 0;
-
- for (uint32_t page = 0; page < 65536; ++page) {
- if (PAGE_USED(page))
- run = 0;
- else if (++run == n) {
- uint16_t start = page - run + 1;
- for (uint32_t i = start; i <= page; ++i)
- SET_PAGE(i);
- pages_left -= n;
- return PAGE_TO_ADDR(start);
- }
- }
-
- return 0;
-}
-
-//in the future, change this to go by bytes or dwords instead of bits.
-void free_pages(const void *ptr, uint16_t n) {
- uint16_t page = ADDR_TO_PAGE(ptr);
- for (uint32_t i = page; i < page + n; ++i)
- CLEAR_PAGE(i);
- pages_left += n;
-} \ No newline at end of file
diff --git a/src/kernel/mem.h b/src/kernel/mem.h
deleted file mode 100644
index 9a9ae79..0000000
--- a/src/kernel/mem.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef MEM_H
-#define MEM_H
-
-#include <stdint.h>
-
-extern uint16_t pages_left;
-
-void init_mmap();
-
-void *allocate_pages(uint16_t n) __attribute__ ((malloc));
-void free_pages(const void *ptr, uint16_t n);
-
-#endif \ No newline at end of file
diff --git a/src/kernel/paging.c b/src/kernel/paging.c
new file mode 100644
index 0000000..b92e037
--- /dev/null
+++ b/src/kernel/paging.c
@@ -0,0 +1,130 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include "pmap.h"
+#include "paging.h"
+#include "boot.h"
+#include "panic.h"
+#include "task.h"
+
+enum {
+ PE_ADDR_MASK = 0xfffff000,
+
+ PT_GLOBAL = 0x100,
+ PD_LARGE = 0x080,
+ PT_DIRTY = 0x040,
+ PE_ACCESSED = 0x020,
+ PE_NO_CACHE = 0x010,
+ PE_WRTHCH = 0x008,
+ PE_USER = 0x004,
+ PE_WRITABLE = 0x002,
+ PE_PRESENT = 0x001
+};
+
+//TODO:
+// try_elf_run needs to call these new functions.
+
+void *new_pd() {
+ uint32_t *pd = allocate_kernel_pages(1);
+ for (uint16_t i = 0; i < 1024; ++i)
+ pd[i] = 0;
+ return pd;
+}
+
+void free_pd(void *pd) {
+ uint32_t *pd_32 = pd;
+ for (uint16_t i = 0; i < 1024; ++i)
+ if (pd_32[i] & PE_PRESENT)
+ free_pages((void *)(pd_32[i] & PE_ADDR_MASK), 1);
+ free_pages(pd, 1);
+}
+
+void pd_map(void *pd, uint32_t physical_addr, uint32_t virtual_addr, bool writable) {
+ uint32_t *ptp = (uint32_t *)pd + (virtual_addr >> 22);
+ if (!(*ptp & PE_PRESENT))
+ *ptp = (uint32_t)allocate_kernel_pages(1) | PE_USER | PE_WRITABLE | PE_PRESENT;
+ ((uint32_t *)(*ptp & PE_ADDR_MASK))[(virtual_addr >> 12) % 1024] = physical_addr | PE_USER | PE_PRESENT | (PE_WRITABLE * writable);
+}
+
+bool pd_is_mapped(void *pd, uint32_t vma) {
+ uint32_t pde = ((uint32_t *)pd)[vma >> 22];
+ return (pde & PE_PRESENT) && (((uint32_t *)(pde & PE_ADDR_MASK))[(vma >> 12) % 1024] & PE_PRESENT);
+}
+
+#define KERNEL_END (0x08000000)
+
+void free_task_pd(void *pd) {
+ uint32_t *pd_32 = pd;
+ for (uint16_t i = 0; i < 1024; ++i)
+ if (pd_32[i] & PE_PRESENT) {
+ uint32_t *pt_32 = (uint32_t *)(pd_32[i] & PE_ADDR_MASK);
+ if (i >= KERNEL_END >> 22)
+ for (uint16_t j = 0; j < 1024; ++j)
+ if (pt_32[j] & PE_PRESENT)
+ free_pages((void *)(pt_32[j] & PE_ADDR_MASK), 1);
+ free_pages(pt_32, 1);
+ }
+ free_pages(pd, 1);
+}
+
+void *new_task_pd() {
+ uint32_t *pd = new_pd();
+ for (uint32_t addr = 0; addr < KERNEL_END; addr += 4096)
+ pd_map(pd, addr, addr, false);
+ return pd;
+}
+
+void *pd_user_allocate(void *pd, uint32_t vma, uint32_t pages, bool writable) {
+ void *pma = allocate_user_pages(pages);
+ if (!pma)
+ panic("Could not allocate user pages.");
+ for (uint32_t i = 0; i < pages; ++i)
+ pd_map(pd, (uint32_t)pma + (i << 12), vma + (i << 12), writable);
+ return pma;
+}
+
+void *pd_user_allocate_anywhere_writable(void *pd, uint32_t pages) {
+ uint32_t run = 0;
+ for (void *vma = (void *)KERNEL_END; vma; vma += 4096) {
+ if (pd_is_mapped(pd, vma))
+ run = 0;
+ else if (++run == pages) {
+ vma -= (pages - 1) * 4096;
+ for (uint32_t i = 0; i < pages; ++i)
+ pd_map(pd, (uint32_t)allocate_user_pages(1), (uint32_t)vma + 4096 * i, true);
+ return vma;
+ }
+ }
+}
+
+#define KPAGE_DIR ((uint32_t *)0x00005000)
+#define KPAGE_TABLE_0 ((uint32_t *)0x00400000)
+
+void init_paging() {
+ //TODO: use PAE if possible
+
+ for (uint32_t i = 0; i < 1048576; ++i)
+ KPAGE_TABLE_0[i] = (i * 4096) | PE_WRITABLE | PE_PRESENT;
+ for (uint16_t i = 0; i < 1024; ++i)
+ KPAGE_DIR[i] = (uint32_t)(KPAGE_TABLE_0 + i * 1024) | PE_WRITABLE | PE_PRESENT;
+
+ asm volatile (
+ "mov $0x00005000, %%eax\n"
+ "mov %%eax, %%cr3\n"
+ "mov %%cr0, %%eax\n"
+ "or $0x80000000, %%eax\n"
+ "mov %%eax, %%cr0"
+ : : : "eax");
+}
+
+void switch_to_kernel_cr3() {
+ asm volatile (
+ "mov $0x00005000, %%eax\n"
+ "mov %%eax, %%cr3"
+ : : : "eax");
+}
+
+void switch_to_task_cr3() {
+ asm volatile (
+ "mov %0, %%cr3"
+ : : "a" (active_task->page_directory));
+} \ No newline at end of file
diff --git a/src/kernel/paging.h b/src/kernel/paging.h
new file mode 100644
index 0000000..72400e8
--- /dev/null
+++ b/src/kernel/paging.h
@@ -0,0 +1,17 @@
+#include <stdint.h>
+#include <stdbool.h>
+
+void init_paging();
+
+void *new_pd();
+void free_pd(void *pd);
+void pd_map(void *pd, uint32_t physical_addr, uint32_t virtual_addr, bool writable);
+void switch_pd(void *pd);
+
+void free_task_pd(void *pd);
+void *new_task_pd();
+void *pd_user_allocate(void *pd, uint32_t vma, uint32_t pages, bool writable);
+void *pd_user_allocate_anywhere_writable(void *pd, uint32_t pages);
+
+void switch_to_kernel_cr3();
+void switch_to_task_cr3(); \ No newline at end of file
diff --git a/src/kernel/pci.c b/src/kernel/pci.c
index 28ca6c6..9e33e49 100644
--- a/src/kernel/pci.c
+++ b/src/kernel/pci.c
@@ -1,7 +1,7 @@
#include "panic.h"
#include "boot.h"
#include "util.h"
-#include "mem.h"
+#include "pmap.h"
#include "pci.h"
enum {
@@ -9,7 +9,7 @@ enum {
PCP_READ = 0x0cfc
};
-uint16_t n_pci_devices = 0;
+uint16_t n_pci_devices;
static struct pci_device *pci_device_pages[256];
#define PCI_DEVICES_PER_PAGE (4096 / sizeof(struct pci_device))
@@ -31,7 +31,7 @@ struct pci_device *find_pci_device_from_class_and_subclass(uint8_t class, uint8_
static struct pci_device *next_pci_device() {
if (!(n_pci_devices % PCI_DEVICES_PER_PAGE))
- if (!(pci_device_pages[n_pci_devices / PCI_DEVICES_PER_PAGE] = allocate_pages(1)))
+ if (!(pci_device_pages[n_pci_devices / PCI_DEVICES_PER_PAGE] = allocate_kernel_pages(1)))
panic("Out of memory in PCI enumeration");
return nth_pci_device(n_pci_devices++);
}
@@ -68,6 +68,8 @@ void pci_init() {
if (!(BOOT_INFO->pci_hw_char & PHC_CS_M1))
panic("No PCI Mechanism 1 support");
+ n_pci_devices = 0;
+
for (uint32_t number = 0; number < (BOOT_INFO->last_pci_bus + 1) * 256; ++number)
pci_device_check(number);
} \ No newline at end of file
diff --git a/src/kernel/plef.c b/src/kernel/plef.c
deleted file mode 100644
index 710943f..0000000
--- a/src/kernel/plef.c
+++ /dev/null
@@ -1,32 +0,0 @@
-#include <stdint.h>
-#include "drive.h"
-#include "plef.h"
-#include "task.h"
-#include "util.h"
-
-#define PLEF_MAGIC 0xb9ba4c50
-
-task_handle plef_run(const struct drive *d, const char *path) {
- drive_file_id_t h = d->get_file(d, path);
- if (!h)
- return 0;
-
- uint8_t start[512];
- d->load_sector(d, h, 0, start);
-
- struct plef_header head = *(struct plef_header *)start;
- if ((head.magic != PLEF_MAGIC) || (head.version_high)) {
- d->free_file(d, h);
- return 0;
- }
-
- uint32_t payload_addr;
- segment_id cs = new_segment(true, head.payload_length + head.bss_length, &payload_addr);
- segment_id ds = mirror_segment(false, cs);
-
- fmcpy((void *)payload_addr, d, h, head.payload_offset, head.payload_length);
-
- d->free_file(d, h);
-
- return new_task(cs, ds, head.entry_point, head.payload_length + head.bss_length);
-} \ No newline at end of file
diff --git a/src/kernel/plef.h b/src/kernel/plef.h
deleted file mode 100644
index a14ea9b..0000000
--- a/src/kernel/plef.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef PLEF_H
-#define PLEF_H
-
-#include <stdint.h>
-#include "drive.h"
-
-typedef uint8_t task_handle;
-
-struct plef_header {
- uint32_t magic;
- uint16_t version_low;
- uint16_t version_high;
- uint32_t payload_offset;
- uint32_t payload_length;
- uint32_t bss_length;
- uint32_t entry_point;
-} __attribute__ ((packed));
-
-task_handle plef_run(const struct drive *d, const char *path);
-
-#endif \ No newline at end of file
diff --git a/src/kernel/pmap.c b/src/kernel/pmap.c
new file mode 100644
index 0000000..8634303
--- /dev/null
+++ b/src/kernel/pmap.c
@@ -0,0 +1,127 @@
+#include <stdint.h>
+#include "panic.h"
+#include "pmap.h"
+
+#define PAGEMAP_START (0x00040000)
+#define PAGEMAP_END (0x00060000)
+#define PAGE_USED(n) ((*(uint8_t *)(PAGEMAP_START + (n >> 3)) >> (n & 7)) & 1)
+#define CLEAR_PAGE(n) *(uint8_t *)(PAGEMAP_START + (n >> 3)) &= ~(1 << (n & 7))
+#define SET_PAGE(n) *(uint8_t *)(PAGEMAP_START + (n >> 3)) |= 1 << (n & 7)
+
+#define KBSS_START (0x04000000)
+#define USER_START (0x08000000)
+extern const void _kernel_bss_end;
+
+uint32_t kernel_pages_left;
+uint32_t user_pages_left;
+uint32_t max_kernel_pages;
+uint32_t max_user_pages;
+
+enum {
+ BMET_FREE = 1
+};
+
+enum {
+ BMEF_NON_VOLATILE = 0x02
+};
+
+struct bios_mmap_entry {
+ uint64_t base;
+ uint64_t length;
+ uint32_t type;
+ uint32_t flags;
+};
+
+void init_pagemap() {
+ for (uint32_t *i = (uint32_t *)(PAGEMAP_START + (KBSS_START >> 15)); i < (uint32_t *)PAGEMAP_END; ++i)
+ *i = 0xffffffff;
+
+ const struct bios_mmap_entry *mmap_p = (const struct bios_mmap_entry *)0x00010000;
+ const struct bios_mmap_entry *mmap_e = (const struct bios_mmap_entry *)(0x00010000 + *(uint16_t *)0x00004006);
+
+ for (; mmap_p < mmap_e; ++mmap_p) {
+ if (mmap_p->type != BMET_FREE)
+ continue;
+ if (mmap_p->base > 0xffffffff)
+ continue;
+
+ uint32_t base_page = ((mmap_p->base - 1) >> 12) + 1;
+
+ uint64_t end = mmap_p->base + mmap_p->length;
+ if (end > 0xffffffff)
+ end = 0x100000000;
+ uint32_t end_page = end >> 12;
+
+ for (uint32_t i = base_page; i < end_page; ++i)
+ CLEAR_PAGE(i);
+ }
+
+ uint32_t kernel_pages = (((uint32_t)&_kernel_bss_end - 1) >> 12) + 1;
+ for (uint32_t i = 0; i < kernel_pages; ++i)
+ SET_PAGE(i);
+
+ kernel_pages_left = 0;
+ for (uint32_t i = KBSS_START >> 12; i < USER_START >> 12; ++i)
+ if (!PAGE_USED(i))
+ ++kernel_pages_left;
+ max_kernel_pages = kernel_pages_left;
+
+ user_pages_left = 0;
+ for (uint32_t i = USER_START >> 12; i < 1048576; ++i)
+ if (!PAGE_USED(i))
+ ++user_pages_left;
+ max_user_pages = user_pages_left;
+}
+
+//very inneficient algorithm, just returns first hole big enough.
+//a smarter algorithm might pick the smallest one available,
+//and go by bytes (or dwords) instead of bits where possible.
+void *allocate_kernel_pages(uint32_t n) {
+ uint32_t run = 0;
+
+ for (uint32_t page = KBSS_START >> 12; page < USER_START >> 12; ++page) {
+ if (PAGE_USED(page))
+ run = 0;
+ else if (++run == n) {
+ uint32_t start = page - run + 1;
+ for (uint32_t i = start; i <= page; ++i)
+ SET_PAGE(i);
+ kernel_pages_left -= n;
+ return (void *)(start << 12);
+ }
+ }
+
+ return 0;
+}
+
+//very inneficient algorithm, just returns first hole big enough.
+//a smarter algorithm might pick the smallest one available,
+//and go by bytes (or dwords) instead of bits where possible.
+void *allocate_user_pages(uint32_t n) {
+ uint32_t run = 0;
+
+ for (uint32_t page = USER_START >> 12; page < 1048576; ++page) {
+ if (PAGE_USED(page))
+ run = 0;
+ else if (++run == n) {
+ uint32_t start = page - run + 1;
+ for (uint32_t i = start; i <= page; ++i)
+ SET_PAGE(i);
+ user_pages_left -= n;
+ return (void *)(start << 12);
+ }
+ }
+
+ return 0;
+}
+
+//in the future, change this to go by bytes or dwords instead of bits.
+void free_pages(const void *ptr, uint32_t n) {
+ uint32_t page = (uint32_t)ptr >> 12;
+ for (uint32_t i = page; i < page + n; ++i)
+ CLEAR_PAGE(i);
+ if ((uint32_t)ptr >= USER_START)
+ user_pages_left += n;
+ else
+ kernel_pages_left += n;
+} \ No newline at end of file
diff --git a/src/kernel/pmap.h b/src/kernel/pmap.h
new file mode 100644
index 0000000..1016de3
--- /dev/null
+++ b/src/kernel/pmap.h
@@ -0,0 +1,17 @@
+#ifndef PAGEMAP_H
+#define PAGEMAP_H
+
+#include <stdint.h>
+
+extern uint32_t kernel_pages_left;
+extern uint32_t user_pages_left;
+extern uint32_t max_user_pages;
+extern uint32_t max_kernel_pages;
+
+void init_pagemap();
+
+void *allocate_kernel_pages(uint32_t n) __attribute__ ((malloc));
+void *allocate_user_pages(uint32_t n) __attribute__ ((malloc));
+void free_pages(const void *ptr, uint32_t n);
+
+#endif \ No newline at end of file
diff --git a/src/kernel/serial.c b/src/kernel/serial.c
index 9ca8315..c492e1e 100644
--- a/src/kernel/serial.c
+++ b/src/kernel/serial.c
@@ -52,9 +52,7 @@ static const uint16_t ports[] = {
CP_1, CP_2, CP_3, CP_4
};
-static bool error[] = {
- false, false, false, false
-};
+static bool error[4];
void reset_error(enum serial_port n) {
error[n] = false;
@@ -62,6 +60,7 @@ void reset_error(enum serial_port n) {
void init_serial() {
for (enum serial_port i = COM1; i <= COM4; ++i) {
+ error[i] = false;
outb(ports[i] | CP_INT, 0);
outb(ports[i] | CP_LINE, CL_BAUD);
outb(ports[i] | CP_DIVLOW, 0x03);//38400
diff --git a/src/kernel/task.c b/src/kernel/task.c
index 3e64639..79b2f09 100644
--- a/src/kernel/task.c
+++ b/src/kernel/task.c
@@ -1,14 +1,92 @@
#include "panic.h"
#include "task.h"
+#include "paging.h"
-segment_id new_segment(bool is_code, uint32_t length, uint32_t *location_out) {
- panic("TODO: make new segment");
+struct tss {
+ struct tss *prev;
+
+ uint32_t esp0;
+ uint32_t ss0;
+ uint32_t esp1;
+ uint32_t ss1;
+ uint32_t esp2;
+ uint32_t ss2;
+
+ uint32_t cr3;
+ uint32_t eip;
+ uint32_t eflags;
+
+ uint32_t eax;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t ebx;
+ uint32_t esp;
+ uint32_t ebp;
+ uint32_t esi;
+ uint32_t edi;
+
+ uint32_t es;
+ uint32_t cs;
+ uint32_t ss;
+ uint32_t ds;
+ uint32_t fs;
+ uint32_t gs;
+
+ uint32_t ldt;
+ uint16_t trap;
+ uint16_t iomp;
+} __attribute__ ((packed));
+
+#define TSS ((struct tss *)0x00004f98)
+
+#define MAX_TASKS 64
+
+struct task_state tasks[MAX_TASKS];
+struct task_state *active_task;
+
+void init_tasks() {
+ active_task = tasks;
+
+ for (uint8_t i = 0; i < MAX_TASKS; ++i)
+ tasks[i].page_directory = 0;
+
+ TSS->ss0 = 0x18;
+ TSS->esp0 = 0x00040000;
+//TSS->cs = 0x13;
+//TSS->ds = 0x1b;
+//TSS->ss = 0x1b;
+ TSS->iomp = sizeof(struct tss);
+
+ asm volatile (
+ "mov $0x08, %%ax\n"
+ "ltr %%ax"
+ : : : "ax");
+}
+
+void new_task(struct task_state state) {
+ for (uint8_t n = 0; n < MAX_TASKS; ++n)
+ if (!tasks[n].page_directory) {
+ tasks[n] = state;
+ return;
+ }
+ panic("Maximum number of tasks reached.");
}
-segment_id mirror_segment(bool is_code, segment_id other) {
- panic("TODO: make new segment with same base and limit");
+void advance_active_task() {
+ struct task_state *prev_task = active_task;
+ do {
+ if (++active_task == tasks + MAX_TASKS)
+ active_task = tasks;
+ if (active_task == prev_task) {
+ logsz("No active tasks.\nHalting.");
+ while (1)
+ asm ("hlt");
+ }
+ }
+ while (!active_task->page_directory);
}
-task_handle new_task(segment_id cs, segment_id ds, uint32_t eip, uint32_t esp) {
- panic("TODO: add task to scheduler");
+void delete_task(struct task_state *state) {
+ free_task_pd(state->page_directory);
+ state->page_directory = 0;
} \ No newline at end of file
diff --git a/src/kernel/task.h b/src/kernel/task.h
index c3f2acd..838f27b 100644
--- a/src/kernel/task.h
+++ b/src/kernel/task.h
@@ -4,11 +4,27 @@
#include <stdbool.h>
#include <stdint.h>
-typedef uint8_t segment_id;
-typedef uint8_t task_handle;
+struct task_state {
+ uint32_t ret_addr;
+ void *page_directory;
+ //maybe put scheduling or priviledge information here?
-segment_id new_segment(bool is_code, uint32_t length, uint32_t *location_out);
-segment_id mirror_segment(bool is_code, segment_id other);
-task_handle new_task(segment_id cs, segment_id ds, uint32_t eip, uint32_t esp);
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t esp;
+} __attribute__ ((packed));
+
+extern struct task_state *active_task;
+
+void init_tasks();
+
+void new_task(struct task_state state);
+void advance_active_task();
+
+void delete_task(struct task_state *state);
#endif \ No newline at end of file
diff --git a/src/kernel/util.c b/src/kernel/util.c
index 29d7e3f..625622a 100644
--- a/src/kernel/util.c
+++ b/src/kernel/util.c
@@ -14,7 +14,7 @@ void memcpy(void *to, const void *from, uint32_t n) {
*(tpp++) = *(fpp++);
}
-void fmcpy(void *to, const struct drive *d, drive_file_id_t f, uint32_t from, uint32_t n) {
+void fmcpy(void *to, const struct drive *d, file_id_t f, uint32_t from, uint32_t n) {
uint8_t buf[512];
d->load_sector(d, f, from >> 9, buf);
uint16_t from_low = from & 511;
diff --git a/src/kernel/util.h b/src/kernel/util.h
index 324f379..9fa8002 100644
--- a/src/kernel/util.h
+++ b/src/kernel/util.h
@@ -42,7 +42,7 @@ static inline uint32_t ind(uint16_t port) {
}
void memcpy(void *to, const void *from, uint32_t n);
-void fmcpy(void *to, const struct drive *d, drive_file_id_t f, uint32_t from, uint32_t n);
+void fmcpy(void *to, const struct drive *d, file_id_t f, uint32_t from, uint32_t n);
void u32_dec(uint32_t n, char *b);
void u16_dec(uint16_t n, char *b);
diff --git a/src/kernel/vga.c b/src/kernel/vga.c
index 7506416..c03e5f0 100644
--- a/src/kernel/vga.c
+++ b/src/kernel/vga.c
@@ -2,9 +2,8 @@
#include <stdint.h>
#define VGA_COLUMNS 80
-#define VGA_LINES 25
#define VGA_START (uint16_t *)0x000b8000
-#define VGA_END (VGA_START + VGA_COLUMNS * VGA_LINES)
+#define VGA_END (VGA_START + VGA_COLUMNS * 25)
static uint16_t *cursor = VGA_START;
static uint16_t mask;
@@ -12,12 +11,12 @@ void vga_set_color(uint8_t new_color) {
mask = new_color << 8;
}
-void vga_scroll() {
+static void vga_scroll() {
for (uint32_t *i = (uint32_t *)VGA_START; i < (uint32_t *)(VGA_END - VGA_COLUMNS); ++i)
*i = *(i + VGA_COLUMNS / 2);
uint32_t f = (mask | (uint8_t)' ') * 0x00010001;
for (uint32_t *i = (uint32_t *)(VGA_END - VGA_COLUMNS); i < (uint32_t *)VGA_END; ++i)
- *i++ = f;
+ *i = f;
cursor -= VGA_COLUMNS;
}
@@ -30,12 +29,10 @@ void vga_blank() {
}
void vga_printch(char ch) {
- if (ch == '\n') {
- if ((cursor = cursor - (cursor - VGA_START) % VGA_COLUMNS + VGA_COLUMNS) == VGA_END)
- vga_scroll();
- return;
- }
- *cursor++ = mask | (uint8_t)ch;
+ if (ch == '\n')
+ cursor = ((cursor - VGA_START) / VGA_COLUMNS + 1) * VGA_COLUMNS + VGA_START;
+ else
+ *cursor++ = mask | (uint8_t)ch;
if (cursor == VGA_END)
vga_scroll();
} \ No newline at end of file
diff --git a/src/kernel/vga.h b/src/kernel/vga.h
index 703080f..bfc9fe6 100644
--- a/src/kernel/vga.h
+++ b/src/kernel/vga.h
@@ -5,7 +5,6 @@
void vga_set_color(uint8_t color);
void vga_blank();
-void vga_scroll();
void vga_printch(char ch);
#endif \ No newline at end of file
diff --git a/src/lib.ld b/src/lib.ld
deleted file mode 100644
index 81cc67b..0000000
--- a/src/lib.ld
+++ /dev/null
@@ -1,17 +0,0 @@
-OUTPUT_FORMAT(elf32-i386)
-OUTPUT_ARCH(i386)
-
-SECTIONS {
- .text : {
- *(.text)
- }
- .rodata : {
- *(.rodata)
- }
- .data : {
- *(.data)
- }
- .bss : {
- *(.bss)
- }
-}
diff --git a/src/user-elf.ld b/src/user-elf.ld
deleted file mode 100644
index 8b0c102..0000000
--- a/src/user-elf.ld
+++ /dev/null
@@ -1,26 +0,0 @@
-OUTPUT_FORMAT(elf32-i386)
-OUTPUT_ARCH(i386)
-
-SECTIONS {
- .ple_head : {
- /*magic*/ LONG(0xb9ba4c50)
- /*version*/ LONG(0x00000000)
- /*payload offset*/ LONG(SIZEOF(.ple_head))
- /*payload length*/ LONG(__pl_length)
- /*bss length*/ LONG(SIZEOF(.bss))
- /*entry point*/ LONG(_entry)
- }
- .text 0 : AT(SIZEOF(.ple_head)) {
- *(.text)
- }
- .rodata : {
- *(.rodata)
- }
- .data : {
- *(.data)
- __pl_length = .;
- }
- .bss ALIGN(0) : {
- *(.bss)
- }
-} \ No newline at end of file
diff --git a/src/user/elf.ld b/src/user/elf.ld
new file mode 100644
index 0000000..aaef517
--- /dev/null
+++ b/src/user/elf.ld
@@ -0,0 +1,8 @@
+OUTPUT_FORMAT(elf32-i386)
+OUTPUT_ARCH(i386)
+ENTRY(_entry)
+
+MEMORY {
+ kernel (!a) : ORIGIN = 0x00000000, LENGTH = 0x08000000
+ user (awx) : ORIGIN = 0x08000000, LENGTH = 0xf8000000
+} \ No newline at end of file
diff --git a/src/user/hello/hello.asm b/src/user/hello/hello.asm
deleted file mode 100644
index 345abb4..0000000
--- a/src/user/hello/hello.asm
+++ /dev/null
@@ -1,15 +0,0 @@
-global _entry
-
-section .text
-_entry:
- mov eax, 3
- mov ebx, msg
- int 0x32
-
- int 0x30
-
-section .rodata
-msg db "Hello, world!", 0x0a, 0x00
-
-section .bss
-stack resb 8 \ No newline at end of file
diff --git a/src/user/include/canyo/file.h b/src/user/include/canyo/file.h
deleted file mode 100644
index 5f09387..0000000
--- a/src/user/include/canyo/file.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef CANYO_FILE_H
-#define CANYO_FILE_H
-
-#include <stdint.h>
-#include <pland.h>
-
-uint32_t read_line(fs_handle handle, uint32_t max_length, void *buffer);
-
-#endif \ No newline at end of file
diff --git a/src/user/include/knob/block.h b/src/user/include/knob/block.h
new file mode 100644
index 0000000..53b3deb
--- /dev/null
+++ b/src/user/include/knob/block.h
@@ -0,0 +1,8 @@
+#ifndef KNOB_BLOCK_H
+#define KNOB_BLOCK_H
+
+#include <stdint.h>
+
+void blockcpy(void *to, const void *from, uint32_t count);
+
+#endif \ No newline at end of file
diff --git a/src/user/include/knob/env.h b/src/user/include/knob/env.h
new file mode 100644
index 0000000..6d750ae
--- /dev/null
+++ b/src/user/include/knob/env.h
@@ -0,0 +1,9 @@
+#ifndef KNOB_ENV_H
+#define KNOB_ENV_H
+
+#include <stdint.h>
+
+//not implemented yet
+extern uint32_t current_drive;
+
+#endif \ No newline at end of file
diff --git a/src/user/include/knob/file.h b/src/user/include/knob/file.h
new file mode 100644
index 0000000..6068077
--- /dev/null
+++ b/src/user/include/knob/file.h
@@ -0,0 +1,17 @@
+#ifndef KNOB_FILE_H
+#define KNOB_FILE_H
+
+#include <stdint.h>
+
+struct file;
+
+struct file *open_file(const char *path);
+void close_file(struct file *f);
+
+uint32_t read_from_file(struct file *f, uint32_t max, void *buf);
+uint32_t seek_file_to(struct file *f, uint32_t to);
+int32_t seek_file_by(struct file *f, int32_t by);
+
+uint32_t file_size(struct file *f) __attribute__ ((pure));
+
+#endif \ No newline at end of file
diff --git a/src/user/include/knob/format.h b/src/user/include/knob/format.h
new file mode 100644
index 0000000..16d3d83
--- /dev/null
+++ b/src/user/include/knob/format.h
@@ -0,0 +1,9 @@
+#ifndef KNOB_FORMAT_H
+#define KNOB_FORMAT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+bool try_sntoi(const char *s, uint32_t n, uint32_t *out);
+
+#endif \ No newline at end of file
diff --git a/src/user/include/knob/heap.h b/src/user/include/knob/heap.h
new file mode 100644
index 0000000..32dc44c
--- /dev/null
+++ b/src/user/include/knob/heap.h
@@ -0,0 +1,9 @@
+#ifndef KNOB_HEAP_H
+#define KNOB_HEAP_H
+
+#include <stdint.h>
+
+void *get_block(uint32_t bytes) __attribute__ ((malloc));
+void free_block(void *block);
+
+#endif \ No newline at end of file
diff --git a/src/user/include/knob/quit.h b/src/user/include/knob/quit.h
new file mode 100644
index 0000000..7b10d09
--- /dev/null
+++ b/src/user/include/knob/quit.h
@@ -0,0 +1,7 @@
+#ifndef KNOB_QUIT_H
+#define KNOB_QUIT_H
+
+void on_quit(void (*run_f)());
+void quit() __attribute__ ((noreturn));
+
+#endif \ No newline at end of file
diff --git a/src/user/include/knob/user.h b/src/user/include/knob/user.h
new file mode 100644
index 0000000..c4ec7db
--- /dev/null
+++ b/src/user/include/knob/user.h
@@ -0,0 +1,12 @@
+#ifndef KNOB_USER_H
+#define KNOB_USER_H
+
+#include <stdint.h>
+
+void tell_user_sz(const char *sz);
+void tell_user_n(uint32_t n);
+
+//return value and max_length both include null terminator
+uint32_t ask_user_line_sz(char *sz, uint32_t max_length);
+
+#endif \ No newline at end of file
diff --git a/src/user/include/pland.h b/src/user/include/pland.h
deleted file mode 100644
index f174a51..0000000
--- a/src/user/include/pland.h
+++ /dev/null
@@ -1,157 +0,0 @@
-#ifndef PLAND_H
-#define PLAND_H
-
-#include <stdint.h>
-#include <stdbool.h>
-
-typedef uint8_t fs_handle;
-typedef uint8_t task_handle;
-
-static inline void exit() __attribute__ ((noreturn)) {
- asm volatile ("int $0x30");
- __builtin_unreachable();
-}
-
-static inline void yield() {
- asm volatile ("int $0x31");
-}
-
-static inline uint32_t data_extend(uint32_t amount) {
- uint32_t actual_amount;
- asm volatile (
- "int $0x33"
- : "eax" (actual_amount) : "eax" (amount));
- return actual_amount;
-}
-
-static inline void vga_blank() {
- asm volatile (
- "xor %%eax, %%eax\n"
- "int $0x32"
- : : : "eax");
-}
-
-static inline void vga_set_color(uint8_t color) {
- asm volatile (
- "mov $0x1, %%eax\n"
- "int $0x32"
- : : "ebx" (color) : "eax");
-}
-
-static inline void vga_printch(uint8_t ch) {
- asm volatile (
- "mov $0x2, %%eax\n"
- "int $0x32"
- : : "ebx" (ch) : "eax");
-}
-
-static inline void vga_printsz(uint8_t *sz) {
- asm volatile (
- "mov $0x3, %%eax\n"
- "int $0x32"
- : : "ebx" (sz) : "eax");
-}
-
-static inline void vga_printsn(uint8_t *sn, uint8_t length) {
- asm volatile (
- "mov $0x4, %%eax\n"
- "int $0x32"
- : : "ebx" (sn), "ecx" (length) : "eax");
-}
-
-static inline fs_handle fs_open(uint8_t *path) {
- fs_handle handle;
- asm volatile (
- "mov $0x5, %%eax\n"
- "int $0x32"
- : "eax" (handle) : "ebx" (path));
- return handle;
-}
-
-static inline fs_handle fs_open_root() {
- fs_handle handle;
- asm volatile (
- "mov $0x6, %%eax\n"
- "int $0x32"
- : "eax" (handle));
- return handle;
-}
-
-static inline fs_handle fs_new(uint8_t *path) {
- fs_handle handle;
- asm volatile (
- "mov $0x7, %%eax\n"
- "int $0x32"
- : "eax" (handle) : "ebx" (path));
- return handle;
-}
-
-static inline void fs_close(fs_handle handle) {
- asm volatile (
- "mov $0x8, %%eax\n"
- "int $0x32"
- : : "ebx" (handle) : "eax");
-}
-
-static inline void fs_delete(uint8_t *path) {
- asm volatile (
- "mov $0x9, %%eax\n"
- "int $0x32"
- : : "ebx" (path) : "eax");
-}
-
-static inline bool fs_exists(uint8_t *path) {
- bool does;
- asm volatile (
- "mov $0xa, %%eax\n"
- "int $0x32"
- : "eax" (does) : "ebx" (path));
- return does;
-}
-
-static inline int32_t fs_seek(fs_handle handle, int32_t by) {
- int32_t seeked_by;
- asm volatile (
- "mov $0xb, %%eax\n"
- "int $0x32"
- : "eax" (seeked_by) : "ebx" (handle), "ecx" (by));
- return seeked_by;
-}
-
-static inline uint32_t fs_tell(fs_handle handle) {
- uint32_t position;
- asm volatile (
- "mov $0xc, %%eax\n"
- "int $0x32"
- : "eax" (position) : "ebx" (handle));
- return position;
-}
-
-static inline uint32_t fs_read(fs_handle handle, uint32_t max, void *buffer) {
- uint32_t read;
- asm volatile (
- "mov %0xd, %%eax\n"
- "int $0x32"
- : "eax" (read) : "ebx" (handle), "ecx" (max), "edx" (buffer) : "memory");
- return read;
-}
-
-static inline uint32_t fs_write(fs_handle handle, uint32_t max, void *buffer) {
- uint32_t written;
- asm volatile (
- "mov %0xe, %%eax\n"
- "int $0x32"
- : "eax" (written) : "ebx" (handle), "ecx" (max), "edx" (buffer));
- return written;
-}
-
-static inline task_handle plef_run(uint8_t *image_path) {
- task_handle handle;
- asm volatile (
- "mov %0xf, %%eax\n"
- "int $0x32"
- : "eax" (handle) : "ebx" (image_path));
- return handle;
-}
-
-#endif
diff --git a/src/user/include/pland/syscall.h b/src/user/include/pland/syscall.h
new file mode 100644
index 0000000..01f7151
--- /dev/null
+++ b/src/user/include/pland/syscall.h
@@ -0,0 +1,139 @@
+#ifndef PLAND_SYSCALL_H
+#define PLAND_SYSCALL_H
+
+#include <stdint.h>
+
+typedef uint32_t _file_handle_t;
+typedef uint32_t _task_handle_t;
+typedef uint32_t _drive_number_t;
+typedef enum {
+ _KEY_BACKSPACE = '\b',
+ _KEY_RETURN = '\n',
+ //etc.
+
+ _KEY_LSHIFT = 0x00000100,
+ _KEY_RSHIFT = 0x00000200,
+ _KEY_CAPS = 0x00000400,
+ _KEY_INSERT = 0x00000800,
+ _KEY_NUM = 0x00001000,
+ _KEY_SCROLL = 0x00002000,
+ _KEY_LALT = 0x00004000,
+ _KEY_RALT = 0x00008000,
+ _KEY_LCTRL = 0x00010000,
+ _KEY_RCTRL = 0x00020000,
+ _KEY_LMETA = 0x00040000,
+ _KEY_RMETA = 0x00080000,
+
+ _KEY_SHIFT = 0x00000300,
+ _KEY_SCAPS = 0x00000700,
+ _KEY_ALT = 0x0000c000,
+ _KEY_CTRL = 0x00030000,
+ _KEY_META = 0x000c0000,
+} _key_code_t;
+
+enum _scn {
+ _SCN_OPEN_FILE,
+ _SCN_CLOSE_FILE,
+ _SCN_FILE_READ,
+ _SCN_FILE_SIZE,
+ _SCN_START_TASK,
+ _SCN_LOG_STRING,
+ _SCN_GET_KEY,
+ _SCN_ALLOCATE_RAM
+};
+
+static inline uint32_t _sc0(enum _scn eax) {
+ volatile uint32_t out;
+ asm (
+ "int $0x30"
+ : "=a" (out) : "a" (eax) : "ecx", "edx");
+ return out;
+}
+
+static inline uint32_t _sc1(enum _scn eax, uint32_t ebx) {
+ volatile uint32_t out;
+ asm (
+ "int $0x30"
+ : "=a" (out) : "a" (eax), "b" (ebx) : "ecx", "edx");
+ return out;
+}
+
+static inline uint32_t _sc2(enum _scn eax, uint32_t ebx, uint32_t ecx) {
+ volatile uint32_t out;
+ asm (
+ "int $0x30"
+ : "=a" (out) : "a" (eax), "b" (ebx), "c" (ecx) : "edx");
+ return out;
+}
+
+static inline uint32_t _sc3(enum _scn eax, uint32_t ebx, uint32_t ecx, uint32_t edx) {
+ volatile uint32_t out;
+ asm (
+ "int $0x30"
+ : "=a" (out) : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx));
+ return out;
+}
+
+static inline uint32_t _sc4(enum _scn eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi) {
+ volatile uint32_t out;
+ asm (
+ "int $0x30"
+ : "=a" (out) : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi));
+ return out;
+}
+
+static inline uint32_t _sc5(enum _scn eax, uint32_t ebx, uint32_t ecx, uint32_t edx, uint32_t esi, uint32_t edi) {
+ volatile uint32_t out;
+ asm (
+ "int $0x30"
+ : "=a" (out) : "a" (eax), "b" (ebx), "c" (ecx), "d" (edx), "S" (esi), "D" (edi));
+ return out;
+}
+
+static inline void _yield_task() {
+ asm (
+ "int $0x39"
+ );
+}
+
+__attribute__ ((noreturn))
+static inline void _exit_task() {
+ asm (
+ "int $0x38"
+ );
+ __builtin_unreachable();
+}
+
+static inline _file_handle_t _open_file(_drive_number_t drive_number, const char *path) {
+ return _sc2(_SCN_OPEN_FILE, drive_number, (uint32_t)path);
+}
+
+static inline void _close_file(_file_handle_t handle) {
+ _sc1(_SCN_CLOSE_FILE, handle);
+}
+
+static inline uint32_t _file_read(_file_handle_t handle, uint32_t file_offset, uint32_t count, void *buffer) {
+ return _sc4(_SCN_FILE_READ, handle, file_offset, count, (uint32_t)buffer);
+}
+
+static inline uint32_t _file_size(_file_handle_t handle) {
+ return _sc1(_SCN_FILE_SIZE, handle);
+}
+
+static inline void _start_task(_drive_number_t drive_number, char *path) {
+ _sc2(_SCN_START_TASK, drive_number, (uint32_t)path);
+}
+
+static inline void _log_string(const char *sz) {
+ _sc1(_SCN_LOG_STRING, (uint32_t)sz);
+}
+
+static inline _key_code_t _get_key() {
+ return _sc0(_SCN_GET_KEY);
+}
+
+static inline void *_allocate_ram(uint32_t pages) {
+ return (void *)_sc1(_SCN_ALLOCATE_RAM, pages);
+}
+
+#endif \ No newline at end of file
diff --git a/src/user/init/main.c b/src/user/init/main.c
index 002fd92..9a411e7 100644
--- a/src/user/init/main.c
+++ b/src/user/init/main.c
@@ -1,16 +1,29 @@
-#include <pland.h>
-#include <canyo/file.h>
+#include <knob/user.h>
+#include <knob/file.h>
+#include <knob/heap.h>
void main() {
- fs_handle f = fs_open("sys/startup.rc");
+ tell_user_sz("\n\nThis is a userland program.\n");
+
+ tell_user_sz("Opening sd0:TEST.TXT.\n");
+ struct file *f = open_file("sd0:TEST.TXT");
if (!f) {
- vga_printsz("Couldn't open sys/startup.rc\n");
+ tell_user_sz("Failed to open.\n");
return;
}
- uint8_t line_buffer[128];
- while (read_line(f, 128, line_buffer))
- plef_run(line_buffer);
+ tell_user_sz("Length: ");
+ uint32_t size = file_size(f);
+ tell_user_n(size);
+ tell_user_sz(" bytes\n\nContents:\n");
+
+ char *buf = get_block(size + 1);
+ read_from_file(f, size, buf);
+ buf[size] = '\0';
+
+ close_file(f);
+
+ tell_user_sz(buf);
- fs_close(f);
+ tell_user_sz("\n\nGoodbye!\n");
}
diff --git a/src/user/knob/entry.asm b/src/user/knob/entry.asm
new file mode 100644
index 0000000..ae024c4
--- /dev/null
+++ b/src/user/knob/entry.asm
@@ -0,0 +1,23 @@
+bits 32
+
+global _entry
+
+extern main
+extern quit
+
+extern current_disk
+
+section .text
+_entry:
+ mov esp, stack
+
+ ;TODO: heap stuff?
+ ;TODO: determine current_disk
+ ;any further needed initialization
+
+ push quit
+ jmp main
+
+section .stack nobits alloc noexec write align=16
+resb 1024
+stack: \ No newline at end of file
diff --git a/src/user/knob/env.c b/src/user/knob/env.c
new file mode 100644
index 0000000..952ab86
--- /dev/null
+++ b/src/user/knob/env.c
@@ -0,0 +1,3 @@
+#include <stdint.h>
+
+uint32_t current_drive; \ No newline at end of file
diff --git a/src/user/knob/file.c b/src/user/knob/file.c
new file mode 100644
index 0000000..8ab7acd
--- /dev/null
+++ b/src/user/knob/file.c
@@ -0,0 +1,82 @@
+#include <pland/syscall.h>
+#include <knob/format.h>
+#include <knob/heap.h>
+#include <knob/env.h>
+
+struct file {
+ _file_handle_t handle;
+ uint32_t position;
+ uint32_t length;
+};
+
+static const char *try_remove_prefix(const char *path, uint8_t *dn_out) {
+ if ((path[0] != 's') || (path[1] != 'd'))
+ return 0;
+
+ const char *num_part = path + 2;
+ for (uint32_t i = 0; num_part[i]; ++i)
+ if (num_part[i] == ':') {
+
+ uint32_t dn_large;
+ if (!try_sntoi(num_part, i, &dn_large) || dn_large > 255)
+ return 0;
+
+ *dn_out = (uint8_t)dn_large;
+ return num_part + i + 1;
+ }
+
+ return 0;
+}
+
+struct file *open_file(const char *path) {
+ uint8_t dn;
+ const char *path_part = try_remove_prefix(path, &dn);
+ if (path_part)
+ path = path_part;
+ else
+ dn = current_drive;
+
+ _file_handle_t h = _open_file(dn, path);
+ if (!h)
+ return 0;
+
+ struct file *f = get_block(sizeof(struct file));
+ f->handle = h;
+ f->position = 0;
+ f->length = _file_size(h);
+
+ return f;
+}
+
+void close_file(struct file *f) {
+ _close_file(f->handle);
+ free_block(f);
+}
+
+uint32_t read_from_file(struct file *f, uint32_t max, void *buf) {
+ if (f->position + max > f->length)
+ max = f->length - f->position;
+
+ uint32_t read = _file_read(f->handle, f->position, max, buf);
+
+ f->position += read;
+ return read;
+}
+
+uint32_t seek_file_to(struct file *f, uint32_t to) {
+ if (to > f->length)
+ to = f->length;
+ return f->position = to;
+}
+
+int32_t seek_file_by(struct file *f, int32_t by) {
+ uint32_t old = f->position;
+ uint32_t to = old + by > f->length ? f->length : old + by;
+ f->position = to;
+ return to - old;
+}
+
+__attribute__ ((pure))
+uint32_t file_size(struct file *f) {
+ return f->length;
+} \ No newline at end of file
diff --git a/src/user/knob/format.c b/src/user/knob/format.c
new file mode 100644
index 0000000..f55e857
--- /dev/null
+++ b/src/user/knob/format.c
@@ -0,0 +1,13 @@
+#include <stdbool.h>
+#include <stdint.h>
+
+bool try_sntoi(const char *s, uint32_t n, uint32_t *out) {
+ uint32_t calc = 0;
+ for (uint32_t i = 0; i < n; ++i) {
+ if ((s[i] < '0') || (s[i] > '9'))
+ return false;
+ calc = calc * 10 + s[i] - '0';
+ }
+ *out = calc;
+ return true;
+} \ No newline at end of file
diff --git a/src/user/knob/heap.c b/src/user/knob/heap.c
new file mode 100644
index 0000000..ec21129
--- /dev/null
+++ b/src/user/knob/heap.c
@@ -0,0 +1,112 @@
+#include <pland/syscall.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+//structure appears at the start of each block
+struct block_header {
+ struct block_header *next;
+ struct block_header *prev;
+
+ //does not include header
+ uint32_t length;
+ bool allocated;
+};
+
+static struct block_header *first_block = 0;
+
+static void add_header(struct block_header *bh) {
+ bh->next = first_block;
+ bh->prev = 0;
+ if (first_block)
+ first_block->prev = bh;
+ first_block = bh;
+}
+
+__attribute__ ((malloc))
+void *get_block(uint32_t bytes) {
+ struct block_header *header = 0;
+
+ for (struct block_header *ptr = first_block; ptr; ptr = ptr->next) {
+ if (ptr->allocated)
+ continue;
+ if (ptr->length == bytes) {
+ header = ptr;
+ break;
+ }
+ if (ptr->length > bytes + sizeof(struct block_header)) {
+ struct block_header *bh = (void *)ptr + sizeof(struct block_header) + bytes;
+
+ bh->length = ptr->length - sizeof(struct block_header) - bytes;
+ bh->allocated = false;
+ add_header(bh);
+
+ ptr->length = bytes;
+ header = ptr;
+ break;
+ }
+ }
+
+ if (!header) {
+ uint32_t size_with_header = bytes + sizeof(struct block_header);
+ if (!(size_with_header % 4096)) {
+ header = _allocate_ram(size_with_header / 4096);
+ if (!header)
+ return 0;
+ header->length = bytes;
+ add_header(header);
+ }
+ else {
+ uint32_t pages = (bytes + sizeof(struct block_header) * 2) / 4096 + 1;
+ header = _allocate_ram(pages);
+ if (!header)
+ return 0;
+ header->length = bytes;
+ add_header(header);
+
+ struct block_header *bh = (void *)header + sizeof(struct block_header) + bytes;
+ bh->length = pages * 4096 - bytes - 2 * sizeof(struct block_header);
+ bh->allocated = false;
+ add_header(bh);
+ }
+ }
+
+ header->allocated = true;
+ return (void *)header + sizeof(struct block_header);
+}
+
+static void remove_header(struct block_header *bh) {
+ if (bh == first_block)
+ first_block = bh->next;
+ if (bh->prev)
+ bh->prev->next = bh->next;
+ if (bh->next)
+ bh->next->prev = bh->prev;
+}
+
+void free_block(void *block) {
+ struct block_header *header = block - sizeof(struct block_header);
+
+ header->allocated = false;
+
+ void *after = block + header->length;
+ for (struct block_header *ptr = first_block; ptr; ptr = ptr->next) {
+ if (ptr == after) {
+ if (!ptr->allocated) {
+ header->length += ptr->length + sizeof(struct block_header);
+ remove_header(ptr);
+ }
+ break;
+ }
+ }
+
+ //we traverse the linked list twice. it would probably be more efficient
+ //to do both finding the after and finding the before in the same loop.
+ for (struct block_header *ptr = first_block; ptr; ptr = ptr->next)
+ if ((void *)ptr + sizeof(struct block_header) + ptr->length == header) {
+ if (!ptr->allocated) {
+ ptr->length += sizeof(struct block_header) + header->length;
+ remove_header(header);
+ }
+ break;
+ }
+} \ No newline at end of file
diff --git a/src/user/knob/quit.c b/src/user/knob/quit.c
new file mode 100644
index 0000000..a2ef5aa
--- /dev/null
+++ b/src/user/knob/quit.c
@@ -0,0 +1,29 @@
+#include <pland/syscall.h>
+#include <knob/heap.h>
+
+struct quit_list_node {
+ struct quit_list_node *prev;
+ void (*f)();
+};
+
+static struct quit_list_node head = {
+ .f = &_exit_task
+};
+
+static struct quit_list_node *last = &head;
+
+void on_quit(void (*run_f)()) {
+ struct quit_list_node *new = get_block(sizeof(struct quit_list_node));
+ new->prev = last;
+ new->f = run_f;
+ last = new;
+}
+
+__attribute__ ((noreturn))
+void quit() {
+ struct quit_list_node *node = last;
+ while (1) {
+ node->f();
+ node = node->prev;
+ }
+} \ No newline at end of file
diff --git a/src/user/knob/user.c b/src/user/knob/user.c
new file mode 100644
index 0000000..fb39851
--- /dev/null
+++ b/src/user/knob/user.c
@@ -0,0 +1,134 @@
+#include <pland/syscall.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+static const uint8_t caps_and_shift[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x22,
+ 0x28, 0x29, 0x2a, 0x2b, 0x3c, 0x5f, 0x3e, 0x3f,
+ 0x29, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5e, 0x26,
+ 0x2a, 0x28, 0x3a, 0x3a, 0x3c, 0x2d, 0x3e, 0x3f,
+
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x57, 0x7c, 0x7d, 0x5e, 0x5f,
+ 0x7e, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x00
+
+ //TODO: higher
+};
+
+static const uint8_t caps_no_shift[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x00
+
+ //TODO: higher
+};
+
+static const uint8_t shifted[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x22,
+ 0x28, 0x29, 0x2a, 0x2b, 0x3c, 0x5f, 0x3e, 0x3f,
+ 0x29, 0x21, 0x40, 0x23, 0x24, 0x25, 0x5e, 0x26,
+ 0x2a, 0x28, 0x3a, 0x3a, 0x3c, 0x2d, 0x3e, 0x3f,
+
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x57, 0x7c, 0x7d, 0x5e, 0x5f,
+ 0x7e, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x00
+
+ //TODO: higher
+};
+
+static char get_key_char() {
+ _key_code_t key;
+ while (!(key = _get_key()))
+ _yield_task();
+ return (char)(
+ (key & (_KEY_ALT | _KEY_CTRL | _KEY_META))
+ ? 0
+ : (key & _KEY_CAPS)
+ ? (key & _KEY_SHIFT)
+ ? caps_and_shift[key & 0xff]
+ : caps_no_shift[key & 0xff]
+ : (key & _KEY_SHIFT)
+ ? shifted[key & 0xff]
+ : (key & 0x80)
+ ? 0
+ : (key & 0x7f)
+ );
+}
+
+void tell_user_sz(const char *sz) {
+ _log_string(sz);
+}
+
+void tell_user_n(uint32_t n) {
+ char buf[11];
+ char *buf_ptr = buf;
+ bool zero_yet = false;
+ for (uint32_t d = 1000000000U; d; d /= 10) {
+ uint8_t v = (n / d ) % 10;
+ if (v || zero_yet) {
+ zero_yet = true;
+ *buf_ptr++ = v | '0';
+ }
+ }
+ if (buf_ptr == buf)
+ *buf_ptr++ = '0';
+ *buf_ptr = '\0';
+ tell_user_sz(buf);
+}
+
+//return value and max_length don't include null terminator
+uint32_t ask_user_line_sz(char *sz, uint32_t max_length) {
+ char log_buf[2];
+ log_buf[1] = '\0';
+
+ uint32_t i;
+ for (i = 0; i != max_length; ++i) {
+ char key = get_key_char();
+ if (key) {
+ log_buf[0] = key;
+ _log_string(log_buf);
+
+ if (key == '\b')
+ i -= i ? 2 : 1;
+ else if (key == '\n')
+ break;
+ else
+ sz[i] = key;
+ }
+ }
+
+ sz[i] = '\0';
+ return i;
+} \ No newline at end of file
diff --git a/src/user/libcanyo/file.c b/src/user/libcanyo/file.c
deleted file mode 100644
index d314887..0000000
--- a/src/user/libcanyo/file.c
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <stdint.h>
-#include <pland.h>
-
-//max_length and return value include null-terminator
-uint32_t read_line(fs_handle handle, uint32_t max_length, void *buffer) {
- int index = 0;
-
- while (++index < max_length) {
- if (!fs_read(handle, 1, buffer + index - 1) || (*(uint8_t *)(buffer + index - 1) == '\n'))
- break;
- }
-
- *(uint8_t *)(buffer + index - 1) = '\0';
- return index;
-} \ No newline at end of file