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:
parent
7ff724fe8f
commit
e8c6577617
63 changed files with 1713 additions and 563 deletions
|
@ -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.
|
|
@ -1,5 +0,0 @@
|
|||
0x08: 0x0000.0000 - 0x0003.7fff (code)
|
||||
0x10: 0x0000.0000 - 0x1fff.ffff (data)
|
||||
0x18: task
|
||||
0x20: user code
|
||||
0x28: user data
|
|
@ -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
|
|
@ -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.
|
52
doc/ints.txt
52
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
|
||||
|
||||
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
42
doc/keys.txt
Normal 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.
|
|
@ -1 +1 @@
|
|||
/bin/hello.ple
|
||||
/bin/shell.ple
|
51
makefile
51
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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
49
src/boot.asm
49
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
|
|
@ -4,7 +4,8 @@
|
|||
#include <stdint.h>
|
||||
|
||||
enum {
|
||||
BIS_PCI = 0x80
|
||||
BIS_PCI = 0x80,
|
||||
BIS_PAE = 0x40
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -13,9 +13,9 @@ SECTIONS {
|
|||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
. = 0x10000000;
|
||||
. = 0x04000000;
|
||||
.bss : {
|
||||
*(.bss)
|
||||
kernel_bss_end = .;
|
||||
_kernel_bss_end = .;
|
||||
}
|
||||
}
|
116
src/kernel/elf.c
Normal file
116
src/kernel/elf.c
Normal 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
9
src/kernel/elf.h
Normal 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
|
|
@ -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();
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define FAT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "drive.h"
|
||||
|
||||
void init_fat();
|
||||
|
|
|
@ -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
116
src/kernel/idt.c
Normal 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
9
src/kernel/idt.h
Normal 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
94
src/kernel/isrs.asm
Normal 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
|
|
@ -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();
|
||||
}
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
130
src/kernel/paging.c
Normal 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
17
src/kernel/paging.h
Normal 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();
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
127
src/kernel/pmap.c
Normal 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
17
src/kernel/pmap.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
void vga_set_color(uint8_t color);
|
||||
void vga_blank();
|
||||
void vga_scroll();
|
||||
void vga_printch(char ch);
|
||||
|
||||
#endif
|
17
src/lib.ld
17
src/lib.ld
|
@ -1,17 +0,0 @@
|
|||
OUTPUT_FORMAT(elf32-i386)
|
||||
OUTPUT_ARCH(i386)
|
||||
|
||||
SECTIONS {
|
||||
.text : {
|
||||
*(.text)
|
||||
}
|
||||
.rodata : {
|
||||
*(.rodata)
|
||||
}
|
||||
.data : {
|
||||
*(.data)
|
||||
}
|
||||
.bss : {
|
||||
*(.bss)
|
||||
}
|
||||
}
|
|
@ -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
8
src/user/elf.ld
Normal 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
|
||||
}
|
|
@ -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
|
|
@ -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
|
8
src/user/include/knob/block.h
Normal file
8
src/user/include/knob/block.h
Normal 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
|
9
src/user/include/knob/env.h
Normal file
9
src/user/include/knob/env.h
Normal 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
|
17
src/user/include/knob/file.h
Normal file
17
src/user/include/knob/file.h
Normal 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
|
9
src/user/include/knob/format.h
Normal file
9
src/user/include/knob/format.h
Normal 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
|
9
src/user/include/knob/heap.h
Normal file
9
src/user/include/knob/heap.h
Normal 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
|
7
src/user/include/knob/quit.h
Normal file
7
src/user/include/knob/quit.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#ifndef KNOB_QUIT_H
|
||||
#define KNOB_QUIT_H
|
||||
|
||||
void on_quit(void (*run_f)());
|
||||
void quit() __attribute__ ((noreturn));
|
||||
|
||||
#endif
|
12
src/user/include/knob/user.h
Normal file
12
src/user/include/knob/user.h
Normal 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
|
|
@ -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
|
139
src/user/include/pland/syscall.h
Normal file
139
src/user/include/pland/syscall.h
Normal 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
|
|
@ -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
23
src/user/knob/entry.asm
Normal 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
3
src/user/knob/env.c
Normal file
|
@ -0,0 +1,3 @@
|
|||
#include <stdint.h>
|
||||
|
||||
uint32_t current_drive;
|
82
src/user/knob/file.c
Normal file
82
src/user/knob/file.c
Normal 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
13
src/user/knob/format.c
Normal 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
112
src/user/knob/heap.c
Normal 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
29
src/user/knob/quit.c
Normal 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
134
src/user/knob/user.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Reference in a new issue