program loading, others

big kernel additions: paging, elf loading, separate kernel and user page allocation
it now properly loads and runs sd0:bin/init.elf
still need to determine which disk was booted from, and start the init on that disk
This commit is contained in:
Benji Dial 2020-09-06 00:48:07 -04:00
parent 7ff724fe8f
commit e8c6577617
63 changed files with 1713 additions and 563 deletions

View file

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

View file

@ -1,5 +0,0 @@
0x08: 0x0000.0000 - 0x0003.7fff (code)
0x10: 0x0000.0000 - 0x1fff.ffff (data)
0x18: task
0x20: user code
0x28: user data

View file

@ -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
0x1000.0000 - 0x1fff.ffff (256M): dynamic memory
0x000a.0000 - 0x000f.ffff (384k): VGA and BIOS memory
0x0040.0000 - 0x007f.ffff (4M): kernel page tables
0x00f0.0000 - 0x00ff.ffff (1M): ISA memory
0x0400.0000 - 0x07ff.ffff (64M): kernel heap, bss pages
0x0800.0000 - 0xffff.ffff (rest): user pages, hardware memory

View file

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

View file

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

42
doc/keys.txt Normal file
View file

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

View file

@ -1 +1 @@
/bin/hello.ple
/bin/shell.ple

View file

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

View file

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

View file

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

View file

@ -4,7 +4,8 @@
#include <stdint.h>
enum {
BIS_PCI = 0x80
BIS_PCI = 0x80,
BIS_PAE = 0x40
};
enum {

View file

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

View file

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

View file

@ -13,9 +13,9 @@ SECTIONS {
.data : {
*(.data)
}
. = 0x10000000;
. = 0x04000000;
.bss : {
*(.bss)
kernel_bss_end = .;
_kernel_bss_end = .;
}
}

116
src/kernel/elf.c Normal file
View file

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

9
src/kernel/elf.h Normal file
View file

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

View file

@ -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();

View file

@ -2,7 +2,6 @@
#define FAT_H
#include <stdbool.h>
#include <stdint.h>
#include "drive.h"
void init_fat();

View file

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

116
src/kernel/idt.c Normal file
View file

@ -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");
}

9
src/kernel/idt.h Normal file
View file

@ -0,0 +1,9 @@
#ifndef IDT_H
#define IDT_H
#include <stdint.h>
void init_idt();
void enable_idt();
#endif

94
src/kernel/isrs.asm Normal file
View file

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

View file

@ -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 dynamic memory free.\n\n");
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 user memory free.\n");
logsz("sd0 root:\n");
tree(&drives[0]);
//while (1)
// asm ("hlt");
while (1)
asm ("hlt");
init_idt();
_start_user_mode();
}

View file

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

View file

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

View file

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

130
src/kernel/paging.c Normal file
View file

@ -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));
}

17
src/kernel/paging.h Normal file
View file

@ -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();

View file

@ -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);
}

View file

@ -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);
}

View file

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

127
src/kernel/pmap.c Normal file
View file

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

17
src/kernel/pmap.h Normal file
View file

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

View file

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

View file

@ -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");
}
segment_id mirror_segment(bool is_code, segment_id other) {
panic("TODO: make new segment with same base and limit");
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.");
}
task_handle new_task(segment_id cs, segment_id ds, uint32_t eip, uint32_t esp) {
panic("TODO: add task to scheduler");
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);
}
void delete_task(struct task_state *state) {
free_task_pd(state->page_directory);
state->page_directory = 0;
}

View file

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

View file

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

View file

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

View file

@ -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();
}

View file

@ -5,7 +5,6 @@
void vga_set_color(uint8_t color);
void vga_blank();
void vga_scroll();
void vga_printch(char ch);
#endif

View file

@ -1,17 +0,0 @@
OUTPUT_FORMAT(elf32-i386)
OUTPUT_ARCH(i386)
SECTIONS {
.text : {
*(.text)
}
.rodata : {
*(.rodata)
}
.data : {
*(.data)
}
.bss : {
*(.bss)
}
}

View file

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

8
src/user/elf.ld Normal file
View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,9 @@
#ifndef KNOB_ENV_H
#define KNOB_ENV_H
#include <stdint.h>
//not implemented yet
extern uint32_t current_drive;
#endif

View file

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

View file

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

View file

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

View file

@ -0,0 +1,7 @@
#ifndef KNOB_QUIT_H
#define KNOB_QUIT_H
void on_quit(void (*run_f)());
void quit() __attribute__ ((noreturn));
#endif

View file

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

View file

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

View file

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

View file

@ -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");
fs_close(f);
char *buf = get_block(size + 1);
read_from_file(f, size, buf);
buf[size] = '\0';
close_file(f);
tell_user_sz(buf);
tell_user_sz("\n\nGoodbye!\n");
}

23
src/user/knob/entry.asm Normal file
View file

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

3
src/user/knob/env.c Normal file
View file

@ -0,0 +1,3 @@
#include <stdint.h>
uint32_t current_drive;

82
src/user/knob/file.c Normal file
View file

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

13
src/user/knob/format.c Normal file
View file

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

112
src/user/knob/heap.c Normal file
View file

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

29
src/user/knob/quit.c Normal file
View file

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

134
src/user/knob/user.c Normal file
View file

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

View file

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