Initial commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
Language: Cpp
|
||||||
|
BasedOnStyle: LLVM
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
IndentWidth: 2
|
||||||
|
TabWidth: 2
|
||||||
|
UseTab: Never
|
||||||
|
ColumnLimit: 140
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
target remote localhost:1234
|
||||||
|
set architecture i386:x86-64
|
||||||
|
set disassembly-flavor intel
|
||||||
|
display/i ($cs * 16 + $rip)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
.vscode
|
||||||
|
build
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
export CC=clang
|
||||||
|
|
||||||
|
meson setup build --reconfigure
|
||||||
|
meson compile -v -C build
|
||||||
|
|
||||||
|
sector_size=512
|
||||||
|
partition_offset=2048
|
||||||
|
|
||||||
|
dd if=/dev/zero of=build/os.img bs="${sector_size}" count="$((partition_offset + 20480))"
|
||||||
|
dd if=build/mbr.bin of=build/os.img bs="${sector_size}" count=1 conv=notrunc
|
||||||
|
dd if=build/bootloader.bin of=build/os.img bs="${sector_size}" seek=1 count="$((partition_offset - 1))" conv=notrunc
|
||||||
|
mkfs.fat -F 16 --offset "${partition_offset}" build/os.img
|
||||||
|
mcopy -i build/os.img@@"$((partition_offset * sector_size))" src ::src
|
||||||
|
mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/kernel.bin ::kernel.bin
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
project(
|
||||||
|
'freywaros',
|
||||||
|
'c',
|
||||||
|
default_options: [
|
||||||
|
'warning_level=2',
|
||||||
|
'c_std=c11',
|
||||||
|
],
|
||||||
|
version: '0.1.0',
|
||||||
|
)
|
||||||
|
|
||||||
|
run_target(
|
||||||
|
'compdb',
|
||||||
|
command: ['ninja', '-C', meson.project_build_root(), '-t', 'compdb'],
|
||||||
|
)
|
||||||
|
|
||||||
|
nasm = find_program('nasm')
|
||||||
|
|
||||||
|
custom_target(
|
||||||
|
'boot',
|
||||||
|
input: 'src/mbr.asm',
|
||||||
|
output: 'mbr.bin',
|
||||||
|
command: [nasm, '-f', 'bin', '-l', 'mbr.lst', '@INPUT@', '-o', '@OUTPUT@'],
|
||||||
|
build_by_default: true,
|
||||||
|
)
|
||||||
|
|
||||||
|
custom_target(
|
||||||
|
'bootloader',
|
||||||
|
input: 'src/bootloader.asm',
|
||||||
|
output: 'bootloader.bin',
|
||||||
|
command: [nasm, '-f', 'bin', '-l', 'bootloader.lst', '@INPUT@', '-o', '@OUTPUT@'],
|
||||||
|
build_by_default: true,
|
||||||
|
)
|
||||||
|
|
||||||
|
kernel_entry_o = custom_target(
|
||||||
|
'kernel_entry',
|
||||||
|
input: 'src/kernel_entry.asm',
|
||||||
|
output: 'kernel_entry.o',
|
||||||
|
command: [nasm, '-f', 'elf64', '@INPUT@', '-o', '@OUTPUT@'],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc = meson.get_compiler('c')
|
||||||
|
|
||||||
|
kernel_sources = files(
|
||||||
|
'src/ata.c',
|
||||||
|
'src/fat16.c',
|
||||||
|
'src/fs.c',
|
||||||
|
'src/idt.c',
|
||||||
|
'src/kernel.c',
|
||||||
|
'src/keyboard.c',
|
||||||
|
'src/memory.c',
|
||||||
|
'src/panic.c',
|
||||||
|
'src/pic.c',
|
||||||
|
'src/string.c',
|
||||||
|
'src/terminal.c',
|
||||||
|
'src/vga.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
kernel_elf = executable(
|
||||||
|
'kernel.elf',
|
||||||
|
sources: [kernel_entry_o, kernel_sources],
|
||||||
|
c_args: [
|
||||||
|
'-ffreestanding',
|
||||||
|
'-nostdlib',
|
||||||
|
'-nostartfiles',
|
||||||
|
'-mno-red-zone',
|
||||||
|
'-mgeneral-regs-only',
|
||||||
|
'-Wno-unused-command-line-argument',
|
||||||
|
'-Wconversion',
|
||||||
|
'-DVERSION="' + meson.project_version() + '"',
|
||||||
|
],
|
||||||
|
link_args: [
|
||||||
|
'-T', meson.project_source_root() / 'src/linker.ld',
|
||||||
|
'-nostdlib',
|
||||||
|
],
|
||||||
|
link_depends: 'src/linker.ld',
|
||||||
|
)
|
||||||
|
|
||||||
|
custom_target(
|
||||||
|
'kernel_bin',
|
||||||
|
input: kernel_elf,
|
||||||
|
output: 'kernel.bin',
|
||||||
|
command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'],
|
||||||
|
build_by_default: true,
|
||||||
|
)
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#include "src/ata.h"
|
||||||
|
#include "src/panic.h"
|
||||||
|
#include "src/util.h"
|
||||||
|
|
||||||
|
#define BSY 0b10000000
|
||||||
|
#define DF 0b00100000
|
||||||
|
#define DRQ 0b00001000
|
||||||
|
#define ERR 0b00000001
|
||||||
|
|
||||||
|
#define READ_SECTORS 0x20
|
||||||
|
#define WRITE_SECTORS 0x30
|
||||||
|
#define FLUSH_CACHE 0xE7
|
||||||
|
|
||||||
|
#define SECTOR_WORDS 256
|
||||||
|
|
||||||
|
void ata_read_sectors(uint32_t index, uint8_t count, void *to) {
|
||||||
|
while (inb(0x1F7) & BSY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(0x1F2, count);
|
||||||
|
outb(0x1F3, index & 0xFF);
|
||||||
|
index = index >> 8;
|
||||||
|
outb(0x1F4, index & 0xFF);
|
||||||
|
index = index >> 8;
|
||||||
|
outb(0x1F5, index & 0xFF);
|
||||||
|
index = index >> 8;
|
||||||
|
outb(0x1F6, 0b11100000 | (index & 0b00001111));
|
||||||
|
|
||||||
|
outb(0x1F7, READ_SECTORS);
|
||||||
|
|
||||||
|
while (count--) {
|
||||||
|
while (!(inb(0x1F7) & DRQ)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
insw(0x1F0, to, SECTOR_WORDS);
|
||||||
|
to = (uint16_t *)to + SECTOR_WORDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(!(inb(0x1F7) & (ERR | DF)), "ata_read_sectors: read error");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ata_write_sectors(uint32_t index, uint8_t count, const void *from) {
|
||||||
|
while (inb(0x1F7) & BSY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(0x1F2, count);
|
||||||
|
outb(0x1F3, index & 0xFF);
|
||||||
|
index = index >> 8;
|
||||||
|
outb(0x1F4, index & 0xFF);
|
||||||
|
index = index >> 8;
|
||||||
|
outb(0x1F5, index & 0xFF);
|
||||||
|
index = index >> 8;
|
||||||
|
outb(0x1F6, 0b11100000 | (index & 0b00001111));
|
||||||
|
|
||||||
|
outb(0x1F7, WRITE_SECTORS);
|
||||||
|
|
||||||
|
while (count--) {
|
||||||
|
while (!(inb(0x1F7) & DRQ)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
outsw(0x1F0, from, SECTOR_WORDS);
|
||||||
|
from = (const uint16_t *)from + SECTOR_WORDS;
|
||||||
|
}
|
||||||
|
|
||||||
|
outb(0x1F7, FLUSH_CACHE);
|
||||||
|
while (inb(0x1F7) & BSY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(!(inb(0x1F7) & (ERR | DF)), "ata_write_sectors: write error");
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void ata_read_sectors(uint32_t index, uint8_t count, void *to);
|
||||||
|
|
||||||
|
void ata_write_sectors(uint32_t index, uint8_t count, const void *from);
|
||||||
@@ -0,0 +1,337 @@
|
|||||||
|
bits 16
|
||||||
|
org 0x8000
|
||||||
|
|
||||||
|
xor ax, ax
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov ss, ax
|
||||||
|
mov sp, 0x7000
|
||||||
|
|
||||||
|
FIRST_PARTITION_SECTOR equ 2048 ; TODO Read MBR.
|
||||||
|
STAGE_BUFFER_SEGMENT equ 0x0900
|
||||||
|
STAGE_BUFFER_OFFSET equ 0x0000
|
||||||
|
KERNEL_BUFFER_SEGMENT equ 0x2000
|
||||||
|
|
||||||
|
jmp prepare_kernel
|
||||||
|
|
||||||
|
dap:
|
||||||
|
.dap_size: db 16
|
||||||
|
.reserved: db 0
|
||||||
|
.src_sectors: dw 1
|
||||||
|
.dst_offset: dw 0
|
||||||
|
.dst_segment: dw 0
|
||||||
|
.src_lba: dq 0
|
||||||
|
dap_end:
|
||||||
|
|
||||||
|
bpb:
|
||||||
|
.bytes_per_sector: dw 0
|
||||||
|
.sectors_per_cluster: db 0
|
||||||
|
.reserved_sectors: dw 0
|
||||||
|
.fats_count: db 0
|
||||||
|
.root_entry_count: dw 0
|
||||||
|
.total_sectors_16: dw 0
|
||||||
|
.media_type: db 0
|
||||||
|
.sectors_per_fat: dw 0
|
||||||
|
bpb_end:
|
||||||
|
|
||||||
|
fat_start: dw 0
|
||||||
|
root_start: dw 0
|
||||||
|
root_size: dw 0
|
||||||
|
data_start: dw 0
|
||||||
|
|
||||||
|
kernel_filename: db 'KERNEL BIN'
|
||||||
|
kernel_cluster: dw 0
|
||||||
|
kernel_buffer_offset: dw 0
|
||||||
|
|
||||||
|
read_sectors:
|
||||||
|
xor bx, bx
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
mov [dap.src_lba], eax
|
||||||
|
mov [dap.src_sectors], cx
|
||||||
|
mov [dap.dst_segment], es
|
||||||
|
mov [dap.dst_offset], di
|
||||||
|
mov ah, 0x42
|
||||||
|
mov dl, 0x80
|
||||||
|
mov si, dap
|
||||||
|
int 0x13
|
||||||
|
jc disk_error
|
||||||
|
ret
|
||||||
|
|
||||||
|
disk_error:
|
||||||
|
mov ah, 0x0E
|
||||||
|
mov al, 'E'
|
||||||
|
int 0x10
|
||||||
|
jmp $
|
||||||
|
|
||||||
|
prepare_kernel:
|
||||||
|
load_bpb:
|
||||||
|
xor bx, bx
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
mov eax, FIRST_PARTITION_SECTOR
|
||||||
|
mov cx, 1
|
||||||
|
mov bx, STAGE_BUFFER_SEGMENT
|
||||||
|
mov es, bx
|
||||||
|
mov di, STAGE_BUFFER_OFFSET
|
||||||
|
call read_sectors
|
||||||
|
|
||||||
|
mov bx, STAGE_BUFFER_SEGMENT
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
mov si, STAGE_BUFFER_OFFSET + 11
|
||||||
|
xor bx, bx
|
||||||
|
mov es, bx
|
||||||
|
mov di, bpb
|
||||||
|
mov cx, bpb_end - bpb
|
||||||
|
rep movsb
|
||||||
|
|
||||||
|
find_data_start:
|
||||||
|
xor bx, bx
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
mov ax, FIRST_PARTITION_SECTOR
|
||||||
|
add ax, [bpb.reserved_sectors]
|
||||||
|
mov [fat_start], ax
|
||||||
|
|
||||||
|
xor ax, ax
|
||||||
|
mov al, [bpb.fats_count]
|
||||||
|
mul word [bpb.sectors_per_fat]
|
||||||
|
add ax, [fat_start]
|
||||||
|
mov [root_start], ax ; dx should be zero for sane FAT16 values
|
||||||
|
|
||||||
|
mov ax, [bpb.root_entry_count]
|
||||||
|
shl ax, 5 ; * 32
|
||||||
|
shr ax, 9 ; / 512 ; there should be at least 512 entries, so safe
|
||||||
|
mov [root_size], ax
|
||||||
|
|
||||||
|
mov ax, [root_start]
|
||||||
|
add ax, [root_size]
|
||||||
|
mov [data_start], ax
|
||||||
|
|
||||||
|
find_kernel:
|
||||||
|
xor bx, bx
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
mov ax, [root_start]
|
||||||
|
mov cx, [root_size]
|
||||||
|
mov bx, STAGE_BUFFER_SEGMENT
|
||||||
|
mov es, bx
|
||||||
|
mov di, STAGE_BUFFER_OFFSET
|
||||||
|
call read_sectors
|
||||||
|
|
||||||
|
mov cx, [bpb.root_entry_count]
|
||||||
|
mov bx, STAGE_BUFFER_SEGMENT
|
||||||
|
mov ds, bx
|
||||||
|
mov si, STAGE_BUFFER_OFFSET
|
||||||
|
|
||||||
|
.check_entry:
|
||||||
|
mov bx, STAGE_BUFFER_SEGMENT
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
cmp byte [si], 0x00
|
||||||
|
je .no_more_entries
|
||||||
|
cmp byte [si], 0xE5
|
||||||
|
je .continue
|
||||||
|
|
||||||
|
push si
|
||||||
|
push cx
|
||||||
|
xor bx, bx
|
||||||
|
mov es, bx
|
||||||
|
mov di, kernel_filename
|
||||||
|
mov cx, 11
|
||||||
|
rep cmpsb
|
||||||
|
pop cx
|
||||||
|
pop si
|
||||||
|
je .found
|
||||||
|
|
||||||
|
.continue:
|
||||||
|
add si, 32
|
||||||
|
loop .check_entry
|
||||||
|
|
||||||
|
.no_more_entries:
|
||||||
|
mov ah, 0x0E
|
||||||
|
mov al, 'K'
|
||||||
|
int 0x10
|
||||||
|
jmp $
|
||||||
|
|
||||||
|
.found:
|
||||||
|
mov bx, STAGE_BUFFER_SEGMENT
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
mov ax, [si + 26]
|
||||||
|
|
||||||
|
xor bx, bx
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
mov [kernel_cluster], ax
|
||||||
|
|
||||||
|
load_fat:
|
||||||
|
xor bx, bx
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
mov ax, [fat_start]
|
||||||
|
mov cx, [bpb.sectors_per_fat]
|
||||||
|
mov bx, STAGE_BUFFER_SEGMENT
|
||||||
|
mov es, bx
|
||||||
|
mov di, STAGE_BUFFER_OFFSET
|
||||||
|
call read_sectors
|
||||||
|
|
||||||
|
load_kernel_cluster:
|
||||||
|
xor bx, bx
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
mov ax, [kernel_cluster]
|
||||||
|
sub ax, 2
|
||||||
|
xor bx, bx
|
||||||
|
mov bl, [bpb.sectors_per_cluster]
|
||||||
|
mul bx
|
||||||
|
add ax, [data_start]
|
||||||
|
xor cx, cx
|
||||||
|
mov cl, [bpb.sectors_per_cluster]
|
||||||
|
mov bx, KERNEL_BUFFER_SEGMENT
|
||||||
|
mov es, bx
|
||||||
|
mov di, [kernel_buffer_offset]
|
||||||
|
call read_sectors
|
||||||
|
|
||||||
|
mov ax, [bpb.sectors_per_cluster]
|
||||||
|
shl ax, 9 ; * 512
|
||||||
|
add ax, [kernel_buffer_offset]
|
||||||
|
mov [kernel_buffer_offset], ax
|
||||||
|
|
||||||
|
find_next_kernel_cluster:
|
||||||
|
xor bx, bx
|
||||||
|
mov ds, bx
|
||||||
|
|
||||||
|
mov si, [kernel_cluster]
|
||||||
|
shl si, 1 ; * 2
|
||||||
|
add si, STAGE_BUFFER_OFFSET
|
||||||
|
mov bx, STAGE_BUFFER_SEGMENT
|
||||||
|
mov ds, bx
|
||||||
|
mov ax, [si]
|
||||||
|
xor bx, bx
|
||||||
|
mov ds, bx
|
||||||
|
mov [kernel_cluster], ax
|
||||||
|
cmp ax, 0xFFF8
|
||||||
|
jb load_kernel_cluster
|
||||||
|
|
||||||
|
; ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
jmp load_gdt
|
||||||
|
|
||||||
|
gdt_start:
|
||||||
|
gdt_0: dq 0x0000000000000000
|
||||||
|
gdt_1:
|
||||||
|
.limit_low: dw 0xFFFF
|
||||||
|
.base_low: dw 0x0000
|
||||||
|
.base_middle: db 0x00
|
||||||
|
.access: db 10011010b ; present, ring 0, code, executable, readable
|
||||||
|
.flags: db 11001111b ; 32-bit, 4KB granularity
|
||||||
|
.base_high: db 0x00
|
||||||
|
gdt_1_end:
|
||||||
|
gdt_2:
|
||||||
|
.limit_low: dw 0xFFFF
|
||||||
|
.base_low: dw 0x0000
|
||||||
|
.base_middle: db 0x00
|
||||||
|
.access: db 10011010b ; present, ring 0, code, executable, readable
|
||||||
|
.flags: db 10101111b ; long mode (L bit)
|
||||||
|
.base_high: db 0x00
|
||||||
|
gdt_2_end:
|
||||||
|
gdt_3:
|
||||||
|
.limit_low: dw 0xFFFF
|
||||||
|
.base_low: dw 0x0000
|
||||||
|
.base_middle: db 0x00
|
||||||
|
.access: db 10010010b ; present, ring 0, data, writable
|
||||||
|
.flags: db 00000000b
|
||||||
|
.base_high: db 0x00
|
||||||
|
gdt_3_end:
|
||||||
|
gdt_end:
|
||||||
|
|
||||||
|
gdt_descriptor:
|
||||||
|
.size: dw gdt_end - gdt_start - 1
|
||||||
|
.offset: dd gdt_start
|
||||||
|
gdt_descriptor_end:
|
||||||
|
|
||||||
|
load_gdt:
|
||||||
|
cli
|
||||||
|
|
||||||
|
lgdt [gdt_descriptor]
|
||||||
|
|
||||||
|
mov eax, cr0
|
||||||
|
or eax, 0x00000001
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
jmp 0x0008:protected_mode
|
||||||
|
|
||||||
|
; ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bits 32
|
||||||
|
protected_mode:
|
||||||
|
mov ax, 0x0018 ; data segment is now third entry = 0x18
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov ss, ax
|
||||||
|
mov esp, 0x00090000
|
||||||
|
|
||||||
|
; zero page table memory at 0x00010000 (3 pages = 768 dwords)
|
||||||
|
mov edi, 0x00010000
|
||||||
|
mov ecx, 768
|
||||||
|
xor eax, eax
|
||||||
|
rep stosd
|
||||||
|
|
||||||
|
; PML4[0] -> PDPT at 0x00011000
|
||||||
|
mov dword [0x00010000], 0x00011003
|
||||||
|
; PDPT[0] -> PD at 0x00012000
|
||||||
|
mov dword [0x00011000], 0x00012003
|
||||||
|
; PD: fill 64 entries, each mapping 2MB
|
||||||
|
mov edi, 0x00012000
|
||||||
|
mov eax, 0x00000083 ; present, writable, huge page, base 0
|
||||||
|
mov ecx, 64
|
||||||
|
|
||||||
|
.fill_pd:
|
||||||
|
mov dword [edi], eax
|
||||||
|
add edi, 8
|
||||||
|
add eax, 0x00200000 ; next 2MB
|
||||||
|
loop .fill_pd
|
||||||
|
|
||||||
|
; load PML4 into CR3
|
||||||
|
mov eax, 0x00010000
|
||||||
|
mov cr3, eax
|
||||||
|
|
||||||
|
; enable PAE
|
||||||
|
mov eax, cr4
|
||||||
|
or eax, 0x00000020
|
||||||
|
mov cr4, eax
|
||||||
|
|
||||||
|
; enable long mode in EFER MSR
|
||||||
|
mov ecx, 0xC0000080
|
||||||
|
rdmsr
|
||||||
|
or eax, 0x00000100
|
||||||
|
wrmsr
|
||||||
|
|
||||||
|
; enable paging (and keep protection enabled)
|
||||||
|
mov eax, cr0
|
||||||
|
or eax, 0x80000000
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
jmp 0x0010:long_mode ; 0x10 = second entry = 64-bit code segment
|
||||||
|
|
||||||
|
; ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bits 64
|
||||||
|
long_mode:
|
||||||
|
mov ax, 0x0018 ; data segment
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov ss, ax
|
||||||
|
mov rsp, 0x0000000000090000
|
||||||
|
|
||||||
|
xor rax, rax
|
||||||
|
xor rbx, rbx
|
||||||
|
xor rcx, rcx
|
||||||
|
xor rdx, rdx
|
||||||
|
|
||||||
|
jmp 0x00020000
|
||||||
+259
@@ -0,0 +1,259 @@
|
|||||||
|
#include "src/fat16.h"
|
||||||
|
#include "src/ata.h"
|
||||||
|
#include "src/memory.h"
|
||||||
|
#include "src/string.h"
|
||||||
|
#include "src/util.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define SECTOR_SIZE 512
|
||||||
|
#define FIRST_PARTITION_SECTOR 2048
|
||||||
|
#define ATTRIBUTE_SUBDIRECTORY 0x10
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
uint16_t bytes_per_sector;
|
||||||
|
uint8_t sectors_per_cluster;
|
||||||
|
uint16_t reserved_sectors;
|
||||||
|
uint8_t fats_count;
|
||||||
|
uint16_t root_entry_count;
|
||||||
|
uint16_t total_sectors_16;
|
||||||
|
uint8_t media_type;
|
||||||
|
uint16_t sectors_per_fat;
|
||||||
|
} fat16_bpb_t;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
char name[8];
|
||||||
|
char ext[3];
|
||||||
|
uint8_t attributes;
|
||||||
|
uint8_t reserved[10];
|
||||||
|
uint16_t modified_time;
|
||||||
|
uint16_t modified_date;
|
||||||
|
uint16_t first_cluster;
|
||||||
|
uint32_t size;
|
||||||
|
} fat16_dir_entry_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
fs_node_t base;
|
||||||
|
fat16_dir_entry_t entry;
|
||||||
|
} fat16_node_t;
|
||||||
|
|
||||||
|
static fat16_bpb_t bpb; // Assuming one partition.
|
||||||
|
static fs_node_t *fs = NUL;
|
||||||
|
static uint16_t *fat = NUL;
|
||||||
|
|
||||||
|
static void to_8_3(const char *name, char *output) {
|
||||||
|
memory_set(' ', 11, output);
|
||||||
|
output[11] = '\0';
|
||||||
|
const char *c = name;
|
||||||
|
uint64_t i = 0;
|
||||||
|
uint8_t ext = 0;
|
||||||
|
while (*c) {
|
||||||
|
if (*c == '.') {
|
||||||
|
i = 8;
|
||||||
|
ext = 1;
|
||||||
|
} else if (i < (!ext ? 8 : 11)) {
|
||||||
|
output[i++] = *c >= 'a' && *c <= 'z' ? *c - 32 : *c;
|
||||||
|
}
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void from_8_3(const char *name, const char *extension, char *output) {
|
||||||
|
memory_set(0, 13, output);
|
||||||
|
|
||||||
|
uint16_t ni = 0, ei = 0, oi = 0;
|
||||||
|
while (ni < 8 && name[ni] != ' ') {
|
||||||
|
output[oi++] = name[ni++];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extension[ei] != ' ') {
|
||||||
|
output[oi++] = '.';
|
||||||
|
while (ei < 3 && extension[ei] != ' ') {
|
||||||
|
output[oi++] = extension[ei++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_node_t *fat16_mount() { // Assuming one partition.
|
||||||
|
uint8_t sector[512];
|
||||||
|
ata_read_sectors(FIRST_PARTITION_SECTOR, 1, §or);
|
||||||
|
memory_copy(sector + 11, sizeof(fat16_bpb_t), &bpb);
|
||||||
|
fat16_node_t *node = memory_allocate(sizeof(fat16_node_t));
|
||||||
|
node->base.type = FAT16;
|
||||||
|
node->base.name[0] = node->entry.name[0] = '/';
|
||||||
|
node->base.size = node->entry.size = sizeof(fat16_dir_entry_t) * bpb.root_entry_count;
|
||||||
|
node->base.is_dir = 1;
|
||||||
|
return fs = (fs_node_t *)node;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ensure_fat() {
|
||||||
|
ASSERT(bpb.sectors_per_fat<256, "ensure_fat: big FAT not implemented")
|
||||||
|
if (!fat) {
|
||||||
|
fat = memory_allocate(bpb.sectors_per_fat * SECTOR_SIZE);
|
||||||
|
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors, (uint8_t)bpb.sectors_per_fat, fat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fat16_dir_entry_t *load_directory(fat16_node_t *directory) {
|
||||||
|
fat16_dir_entry_t *entries;
|
||||||
|
|
||||||
|
if (!directory->entry.first_cluster) {
|
||||||
|
uint8_t sectors = (uint8_t)((directory->base.size + SECTOR_SIZE - 1) / SECTOR_SIZE);
|
||||||
|
entries = memory_allocate(sectors * SECTOR_SIZE + sizeof(fat16_dir_entry_t)); // One extra as null terminator.
|
||||||
|
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.fats_count * bpb.sectors_per_fat, sectors, entries);
|
||||||
|
} else {
|
||||||
|
ensure_fat();
|
||||||
|
|
||||||
|
uint16_t next_cluster = directory->entry.first_cluster;
|
||||||
|
uint32_t cluster_count = 0;
|
||||||
|
while (next_cluster < 0xFFF8) {
|
||||||
|
cluster_count++;
|
||||||
|
next_cluster = fat[next_cluster];
|
||||||
|
}
|
||||||
|
entries =
|
||||||
|
memory_allocate(cluster_count * bpb.sectors_per_cluster * SECTOR_SIZE + sizeof(fat16_dir_entry_t)); // One extra as null terminator.
|
||||||
|
|
||||||
|
uint8_t *chunk = (uint8_t *)entries;
|
||||||
|
next_cluster = directory->entry.first_cluster;
|
||||||
|
while (next_cluster < 0xFFF8) {
|
||||||
|
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
|
||||||
|
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
|
||||||
|
bpb.sectors_per_cluster * (next_cluster - 2),
|
||||||
|
bpb.sectors_per_cluster, chunk);
|
||||||
|
chunk += bpb.sectors_per_cluster * SECTOR_SIZE;
|
||||||
|
next_cluster = fat[next_cluster];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fs_node_t *open_entry(const char *name, const fat16_dir_entry_t *entry) {
|
||||||
|
if (!entry->name[0]) {
|
||||||
|
return NUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fat16_node_t *result = memory_allocate(sizeof(fat16_node_t));
|
||||||
|
memory_copy((char *)name, string_length(name) + 1, &(result->base.name));
|
||||||
|
result->base.type = FAT16;
|
||||||
|
result->base.size = entry->size;
|
||||||
|
result->base.is_dir = entry->attributes & ATTRIBUTE_SUBDIRECTORY;
|
||||||
|
memory_copy((fat16_dir_entry_t *)entry, sizeof(fat16_dir_entry_t), (uint8_t *)result + sizeof(fs_node_t));
|
||||||
|
|
||||||
|
return (fs_node_t *)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name) {
|
||||||
|
ASSERT(directory->type == FAT16, "fat16_open_by: directory is not FAT16");
|
||||||
|
ASSERT(directory->is_dir, "fat16_open_by: directory is not a directory");
|
||||||
|
|
||||||
|
char name_8_3[12];
|
||||||
|
to_8_3(name, name_8_3);
|
||||||
|
|
||||||
|
fat16_dir_entry_t *entries = load_directory((fat16_node_t *)directory);
|
||||||
|
|
||||||
|
fat16_dir_entry_t *entry = entries;
|
||||||
|
while (entry->name[0]) {
|
||||||
|
if ((uint8_t)entry->name[0] != 0xE5 && (uint8_t)entry->attributes != 0x0F && bytes_equal(name_8_3, (char *)entry, 11)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entry++;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_node_t *result = open_entry(name, entry);
|
||||||
|
memory_free(entries);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index) {
|
||||||
|
ASSERT(directory->type == FAT16, "fat16_open_at: directory is not FAT16");
|
||||||
|
ASSERT(directory->is_dir, "fat16_open_at: directory is not a directory");
|
||||||
|
|
||||||
|
fat16_dir_entry_t *entries = load_directory((fat16_node_t *)directory);
|
||||||
|
|
||||||
|
uint64_t ei = 0, vi = 0;
|
||||||
|
while (entries[ei].name[0]) {
|
||||||
|
if ((uint8_t)entries[ei].name[0] != 0xE5 && (uint8_t)entries[ei].attributes != 0x0F) {
|
||||||
|
if (vi == index) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vi++;
|
||||||
|
}
|
||||||
|
ei++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entries[ei].name[0]) {
|
||||||
|
return NUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char name[13];
|
||||||
|
from_8_3(entries[ei].name, entries[ei].ext, name);
|
||||||
|
|
||||||
|
fs_node_t *result = open_entry(name, entries + ei);
|
||||||
|
memory_free(entries);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fat16_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to) {
|
||||||
|
ASSERT(file->type == FAT16, "fat16_read: file is not FAT16");
|
||||||
|
ASSERT(!file->is_dir, "fat16_read: can not read directory");
|
||||||
|
ASSERT(file->size >= offset + size, "fat16_read: offset/size are out of bounds");
|
||||||
|
|
||||||
|
ensure_fat();
|
||||||
|
|
||||||
|
fat16_node_t *fat_file = (fat16_node_t *)file;
|
||||||
|
|
||||||
|
uint32_t cluster_size = bpb.sectors_per_cluster * SECTOR_SIZE;
|
||||||
|
uint32_t next_cluster = fat_file->entry.first_cluster;
|
||||||
|
|
||||||
|
// Assuming filesystem is correct. TODO Check for real.
|
||||||
|
|
||||||
|
while (offset >= cluster_size) {
|
||||||
|
next_cluster = fat[next_cluster];
|
||||||
|
offset -= cluster_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *cursor = to;
|
||||||
|
|
||||||
|
uint8_t *tmp = memory_allocate(cluster_size);
|
||||||
|
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
|
||||||
|
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
|
||||||
|
bpb.sectors_per_cluster * (next_cluster - 2),
|
||||||
|
bpb.sectors_per_cluster, tmp);
|
||||||
|
uint64_t prefix_size = size <= cluster_size - offset ? size : cluster_size - offset;
|
||||||
|
memory_copy(tmp + offset, prefix_size, cursor);
|
||||||
|
size -= prefix_size;
|
||||||
|
cursor += prefix_size;
|
||||||
|
next_cluster = fat[next_cluster];
|
||||||
|
|
||||||
|
while (size >= cluster_size) {
|
||||||
|
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
|
||||||
|
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
|
||||||
|
bpb.sectors_per_cluster * (next_cluster - 2),
|
||||||
|
bpb.sectors_per_cluster, cursor);
|
||||||
|
size -= cluster_size;
|
||||||
|
cursor += cluster_size;
|
||||||
|
next_cluster = fat[next_cluster];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size) {
|
||||||
|
ata_read_sectors(FIRST_PARTITION_SECTOR + bpb.reserved_sectors + bpb.sectors_per_fat * bpb.fats_count +
|
||||||
|
(bpb.root_entry_count * sizeof(fat16_dir_entry_t) + SECTOR_SIZE - 1) / SECTOR_SIZE +
|
||||||
|
bpb.sectors_per_cluster * (next_cluster - 2),
|
||||||
|
bpb.sectors_per_cluster, tmp);
|
||||||
|
memory_copy(tmp, size, cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fat16_close(fs_node_t *node) {
|
||||||
|
ASSERT(node->type == FAT16, "fat16_close: node is not FAT16");
|
||||||
|
ASSERT(node != fs, "fat16_close: can not unmount FS");
|
||||||
|
memory_free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fat16_unmount(fs_node_t *node) {
|
||||||
|
ASSERT(node->type == FAT16, "fat16_unmount: node is not FAT16");
|
||||||
|
ASSERT(node == fs, "fat16_unmount: node is not filesystem");
|
||||||
|
ASSERT(0, "fat16_unmount: not implemented");
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/fs.h"
|
||||||
|
|
||||||
|
#define FAT16 1
|
||||||
|
|
||||||
|
fs_node_t *fat16_mount();
|
||||||
|
|
||||||
|
fs_node_t *fat16_open_by(const fs_node_t *directory, const char *name);
|
||||||
|
|
||||||
|
fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index);
|
||||||
|
|
||||||
|
void fat16_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to);
|
||||||
|
|
||||||
|
void fat16_close(fs_node_t *node);
|
||||||
|
|
||||||
|
void fat16_unmount(fs_node_t *fs);
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#include "src/fs.h"
|
||||||
|
#include "src/fat16.h"
|
||||||
|
|
||||||
|
fs_node_t *fs_mount() {
|
||||||
|
return fat16_mount();
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_node_t *fs_open_by(const fs_node_t *directory, const char *name) {
|
||||||
|
return fat16_open_by(directory, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_node_t *fs_open_at(const fs_node_t *directory, uint64_t index) {
|
||||||
|
return fat16_open_at(directory, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to) {
|
||||||
|
fat16_read(file, offset, size, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs_close(fs_node_t *node) {
|
||||||
|
fat16_close(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fs_unmount(fs_node_t *fs) {
|
||||||
|
fat16_unmount(fs);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define FILENAME_SIZE_LIMIT 255
|
||||||
|
|
||||||
|
typedef struct fs_node {
|
||||||
|
char name[FILENAME_SIZE_LIMIT + 1];
|
||||||
|
uint32_t size;
|
||||||
|
uint8_t is_dir;
|
||||||
|
uint8_t type;
|
||||||
|
} fs_node_t;
|
||||||
|
|
||||||
|
|
||||||
|
fs_node_t *fs_mount();
|
||||||
|
|
||||||
|
fs_node_t *fs_open_by(const fs_node_t *directory, const char *name);
|
||||||
|
|
||||||
|
fs_node_t *fs_open_at(const fs_node_t *directory, uint64_t index);
|
||||||
|
|
||||||
|
void fs_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to);
|
||||||
|
|
||||||
|
void fs_close(fs_node_t *node);
|
||||||
|
|
||||||
|
void fs_unmount(fs_node_t *fs);
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "src/idt.h"
|
||||||
|
|
||||||
|
struct idt_entry {
|
||||||
|
uint16_t offset_low;
|
||||||
|
uint16_t selector;
|
||||||
|
uint8_t ist;
|
||||||
|
uint8_t flags;
|
||||||
|
uint16_t offset_mid;
|
||||||
|
uint32_t offset_high;
|
||||||
|
uint32_t zero;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct idt_descriptor {
|
||||||
|
uint16_t size;
|
||||||
|
uint64_t offset;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
static struct idt_entry idt[256];
|
||||||
|
static struct idt_descriptor idt_desc;
|
||||||
|
|
||||||
|
static void idt_load() {
|
||||||
|
idt_desc.size = sizeof(idt) - 1;
|
||||||
|
idt_desc.offset = (uint64_t)&idt;
|
||||||
|
__asm__ volatile("lidt %0" : : "m"(idt_desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void idt_init() {
|
||||||
|
idt_load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void idt_set_entry(int vector, void (*handler)(struct interrupt_frame *), uint8_t flags) {
|
||||||
|
uint64_t addr = (uint64_t)handler;
|
||||||
|
idt[vector].offset_low = addr & 0xFFFF;
|
||||||
|
idt[vector].selector = 0x0010;
|
||||||
|
idt[vector].ist = 0;
|
||||||
|
idt[vector].flags = flags;
|
||||||
|
idt[vector].offset_mid = (addr >> 16) & 0xFFFF;
|
||||||
|
idt[vector].offset_high = (addr >> 32) & 0xFFFFFFFF;
|
||||||
|
idt[vector].zero = 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct interrupt_frame {
|
||||||
|
uint64_t ip;
|
||||||
|
uint64_t cs;
|
||||||
|
uint64_t flags;
|
||||||
|
uint64_t sp;
|
||||||
|
uint64_t ss;
|
||||||
|
};
|
||||||
|
|
||||||
|
void idt_init();
|
||||||
|
|
||||||
|
void idt_set_entry(int vector, void (*handler)(struct interrupt_frame *), uint8_t flags);
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#include "src/idt.h"
|
||||||
|
#include "src/keyboard.h"
|
||||||
|
#include "src/memory.h"
|
||||||
|
#include "src/pic.h"
|
||||||
|
#include "src/terminal.h"
|
||||||
|
#include "src/util.h"
|
||||||
|
#include "src/vga.h"
|
||||||
|
|
||||||
|
__attribute__((interrupt)) void isr_divide_by_zero([[maybe_unused]] struct interrupt_frame *frame) {
|
||||||
|
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: divide by zero", 0x4F);
|
||||||
|
while (1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((interrupt)) void isr_page_fault([[maybe_unused]] struct interrupt_frame *frame) {
|
||||||
|
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: page fault", 0x4F);
|
||||||
|
while (1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((interrupt)) void isr_general_violation([[maybe_unused]] struct interrupt_frame *frame) {
|
||||||
|
vga_set_string(VGA_HEIGHT - 1, 0, "EXCEPTION: general violation", 0x4F);
|
||||||
|
while (1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((interrupt)) void isr_ata_primary([[maybe_unused]] struct interrupt_frame *frame) {
|
||||||
|
outb(0x20, 0x20);
|
||||||
|
outb(0xA0, 0x20);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernel_main() {
|
||||||
|
pic_init();
|
||||||
|
idt_init();
|
||||||
|
outb(0x21, inb(0x21) | 0x01); // mask out timer interrupt
|
||||||
|
idt_set_entry(0, isr_divide_by_zero, 0x8E);
|
||||||
|
idt_set_entry(0x0E, isr_page_fault, 0x8E);
|
||||||
|
idt_set_entry(0x0D, isr_general_violation, 0x8E);
|
||||||
|
idt_set_entry(46, isr_ata_primary, 0x8E);
|
||||||
|
__asm__ volatile("sti");
|
||||||
|
|
||||||
|
memory_init();
|
||||||
|
keyboard_init();
|
||||||
|
terminal_init();
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
bits 64
|
||||||
|
extern kernel_main
|
||||||
|
jmp kernel_main
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#include "src/keyboard.h"
|
||||||
|
#include "src/idt.h"
|
||||||
|
#include "src/util.h"
|
||||||
|
|
||||||
|
static keyboard_handler_t current_handler = 0;
|
||||||
|
|
||||||
|
__attribute__((interrupt)) static void isr_keyboard([[maybe_unused]] struct interrupt_frame *frame) {
|
||||||
|
uint8_t scancode = inb(0x60);
|
||||||
|
outb(0x20, 0x20);
|
||||||
|
if (current_handler) {
|
||||||
|
current_handler(scancode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyboard_init() {
|
||||||
|
outb(0x21, inb(0x21) & ~0x02);
|
||||||
|
idt_set_entry(33, isr_keyboard, 0x8E);
|
||||||
|
}
|
||||||
|
|
||||||
|
void keyboard_set_handler(keyboard_handler_t handler) {
|
||||||
|
current_handler = handler;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void keyboard_init();
|
||||||
|
|
||||||
|
typedef void (*keyboard_handler_t)(uint8_t scancode);
|
||||||
|
|
||||||
|
void keyboard_set_handler(keyboard_handler_t handler);
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
ENTRY(kernel_main)
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
. = 0x0000000000020000;
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
*(.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
*(.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
.bss : {
|
||||||
|
*(.bss)
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
bits 16
|
||||||
|
org 0x7C00
|
||||||
|
|
||||||
|
setup:
|
||||||
|
xor ax, ax
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov ss, ax
|
||||||
|
mov sp, 0x7C00
|
||||||
|
|
||||||
|
load_bootloader:
|
||||||
|
mov ah, 0x02 ; BIOS read sectors function
|
||||||
|
mov al, 63 ; number of sectors to read
|
||||||
|
mov bx, 0x8000 ; to memory address
|
||||||
|
mov ch, 0 ; from cylinder 0
|
||||||
|
mov cl, 2 ; from sector 2
|
||||||
|
mov dh, 0 ; from head 0
|
||||||
|
int 0x13
|
||||||
|
|
||||||
|
jmp 0x8000
|
||||||
|
|
||||||
|
times 446 - ($ - $$) db 0
|
||||||
|
|
||||||
|
partition_table:
|
||||||
|
partition_1:
|
||||||
|
.bootable: db 0x80
|
||||||
|
.chs_start: db 0x20, 0x21, 0x00
|
||||||
|
.type: db 0x06
|
||||||
|
.chs_end: db 0xFF, 0xFF, 0xFF
|
||||||
|
.lba_start: dd 0x00000800
|
||||||
|
.lba_size dd 0x00005000
|
||||||
|
partition_1_end:
|
||||||
|
partition_table_end:
|
||||||
|
|
||||||
|
times 510 - ($ - $$) db 0
|
||||||
|
|
||||||
|
boot_signature:
|
||||||
|
dw 0xAA55
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
#include "src/memory.h"
|
||||||
|
#include "src/panic.h"
|
||||||
|
#include "src/util.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define MEMORY_SIZE 134217728
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#define PAGE_COUNT (MEMORY_SIZE / PAGE_SIZE)
|
||||||
|
#define MAP_UNIT 64
|
||||||
|
#define FULL_UNIT 0xFFFFFFFFFFFFFFFF
|
||||||
|
|
||||||
|
// Claim 2MB for bootloader, kernel, and stuff.
|
||||||
|
static uint16_t free_page = MAP_UNIT * 8;
|
||||||
|
static uint64_t allocation[PAGE_COUNT / MAP_UNIT] = {FULL_UNIT, FULL_UNIT, FULL_UNIT, FULL_UNIT,
|
||||||
|
FULL_UNIT, FULL_UNIT, FULL_UNIT, FULL_UNIT};
|
||||||
|
|
||||||
|
static uint8_t get_allocated(uint16_t page) {
|
||||||
|
return !!allocation[page / MAP_UNIT] & ((uint64_t)1 << (page % MAP_UNIT));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_allocated(uint16_t page, uint8_t allocated) {
|
||||||
|
if (allocated) {
|
||||||
|
allocation[page / MAP_UNIT] |= ((uint64_t)1 << (page % MAP_UNIT));
|
||||||
|
} else {
|
||||||
|
allocation[page / MAP_UNIT] &= ~((uint64_t)1 << (page % MAP_UNIT));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *memory_page_allocate() {
|
||||||
|
ASSERT(free_page < PAGE_COUNT, "memory_page_allocate: out of memory");
|
||||||
|
const uint16_t page = free_page;
|
||||||
|
set_allocated(page, 1);
|
||||||
|
for (uint16_t unit = free_page / MAP_UNIT; unit < PAGE_COUNT / MAP_UNIT; unit++) {
|
||||||
|
if (allocation[unit] != FULL_UNIT) {
|
||||||
|
uint16_t bit = (uint16_t)__builtin_ctzll(~allocation[unit]);
|
||||||
|
free_page = unit * MAP_UNIT + bit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (void *)((uint64_t)page * PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] static void memory_page_free(void *address) {
|
||||||
|
uint16_t page = (uint16_t)((uint64_t)address / PAGE_SIZE);
|
||||||
|
ASSERT(get_allocated(page), "memory_page_free: page not allocated")
|
||||||
|
set_allocated(page, 0);
|
||||||
|
if (page < free_page) {
|
||||||
|
free_page = page;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HEAP_PAGE_COUNT 256
|
||||||
|
|
||||||
|
typedef struct free_chunk {
|
||||||
|
uint64_t size; // header + data
|
||||||
|
struct free_chunk *next;
|
||||||
|
} free_chunk_t;
|
||||||
|
|
||||||
|
static free_chunk_t *free_list;
|
||||||
|
|
||||||
|
void memory_init() {
|
||||||
|
free_list = memory_page_allocate();
|
||||||
|
for (uint64_t i = 1; i < HEAP_PAGE_COUNT; i++) {
|
||||||
|
memory_page_allocate(); // TODO Map the pages.
|
||||||
|
}
|
||||||
|
free_list->size = HEAP_PAGE_COUNT * PAGE_SIZE;
|
||||||
|
free_list->next = NUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memory_allocate(uint64_t size) {
|
||||||
|
size = sizeof(free_chunk_t) + (size + 7) & (uint64_t)~7;
|
||||||
|
|
||||||
|
free_chunk_t *prev = NUL;
|
||||||
|
free_chunk_t *curr = free_list;
|
||||||
|
|
||||||
|
while (curr) {
|
||||||
|
if (curr->size >= size + (sizeof(free_chunk_t) + 8)) {
|
||||||
|
free_chunk_t *remainder = (free_chunk_t *)((uint8_t *)curr + size);
|
||||||
|
remainder->size = curr->size - size;
|
||||||
|
remainder->next = curr->next;
|
||||||
|
curr->size = size;
|
||||||
|
curr->next = remainder;
|
||||||
|
}
|
||||||
|
if (curr->size >= size) {
|
||||||
|
if (prev) {
|
||||||
|
prev->next = curr->next;
|
||||||
|
} else {
|
||||||
|
free_list = curr->next;
|
||||||
|
}
|
||||||
|
void *result = (void *)((uint8_t *)curr + sizeof(free_chunk_t));
|
||||||
|
memory_set(0, size - sizeof(free_chunk_t), result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
prev = curr;
|
||||||
|
curr = curr->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(0, "memory_heap_allocate: out of heap memory");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_free(void *pointer) {
|
||||||
|
free_chunk_t *chunk = (free_chunk_t *)((uint8_t *)pointer - sizeof(free_chunk_t));
|
||||||
|
chunk->next = free_list;
|
||||||
|
free_list = chunk;
|
||||||
|
// TODO Merge adjacent free chunks.
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/panic.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void memory_init();
|
||||||
|
|
||||||
|
void *memory_allocate(uint64_t size);
|
||||||
|
|
||||||
|
void memory_free(void *pointer);
|
||||||
|
|
||||||
|
static inline void memory_set(uint8_t value, uint64_t bytes, void *to) {
|
||||||
|
__asm__ volatile("rep stosb" : "=D"(to), "=c"(bytes) : "D"(to), "a"(value), "c"(bytes) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void memory_move(void *from, uint64_t bytes, void *to) {
|
||||||
|
if (to == from) {
|
||||||
|
return;
|
||||||
|
} else if (to < from) {
|
||||||
|
__asm__ volatile("rep movsb" : "=D"(to), "=S"(from), "=c"(bytes) : "D"(to), "S"(from), "c"(bytes) : "memory");
|
||||||
|
} else {
|
||||||
|
ASSERT(0, "memory_move: forward overlapping move not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void memory_copy(const void *from, uint64_t bytes, void *to) {
|
||||||
|
ASSERT((uint64_t)to + bytes <= (uint64_t)from || (uint64_t)to >= (uint64_t)from + bytes, "memory_copy: overlapping backward copy");
|
||||||
|
|
||||||
|
__asm__ volatile("rep movsb" : "=D"(to), "=S"(from), "=c"(bytes) : "D"(to), "S"(from), "c"(bytes) : "memory");
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#include "src/panic.h"
|
||||||
|
#include "src/vga.h"
|
||||||
|
|
||||||
|
void kernel_panic(const char *msg, [[maybe_unused]] const char *file, [[maybe_unused]] int line) {
|
||||||
|
vga_set_string(0, VGA_HEIGHT - 1, msg, 0x28);
|
||||||
|
while (1)
|
||||||
|
;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define ASSERT(cond, msg) \
|
||||||
|
if (!(cond)) { \
|
||||||
|
kernel_panic(msg " (assertion: " #cond ")", __FILE__, __LINE__); \
|
||||||
|
}
|
||||||
|
|
||||||
|
void kernel_panic(const char *msg, const char *file, int line);
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#include "src/pic.h"
|
||||||
|
#include "src/util.h"
|
||||||
|
|
||||||
|
static void pic_remap() {
|
||||||
|
uint8_t mask1 = inb(0x21);
|
||||||
|
uint8_t mask2 = inb(0xA1);
|
||||||
|
|
||||||
|
outb(0x20, 0x11);
|
||||||
|
io_wait();
|
||||||
|
outb(0xA0, 0x11);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
outb(0x21, 0x20);
|
||||||
|
io_wait();
|
||||||
|
outb(0xA1, 0x28);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
outb(0x21, 0x04);
|
||||||
|
io_wait();
|
||||||
|
outb(0xA1, 0x02);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
outb(0x21, 0x01);
|
||||||
|
io_wait();
|
||||||
|
outb(0xA1, 0x01);
|
||||||
|
io_wait();
|
||||||
|
|
||||||
|
outb(0x21, mask1);
|
||||||
|
outb(0xA1, mask2);
|
||||||
|
io_wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void pic_init() {
|
||||||
|
pic_remap();
|
||||||
|
}
|
||||||
+230
@@ -0,0 +1,230 @@
|
|||||||
|
#include "src/string.h"
|
||||||
|
#include "src/memory.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint8_t bytes_equal(const char *l, const char *r, uint64_t count) {
|
||||||
|
while (count--) {
|
||||||
|
if (*l++ != *r++) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t string_empty(const char *s) {
|
||||||
|
return *s == '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t string_length(const char *s) {
|
||||||
|
const char *e = s;
|
||||||
|
while (*e) {
|
||||||
|
e++;
|
||||||
|
}
|
||||||
|
return (uint64_t)(e - s);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t string_equal(const char *l, const char *r) {
|
||||||
|
while (*l && *r) {
|
||||||
|
if (*l != *r) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
l++;
|
||||||
|
r++;
|
||||||
|
}
|
||||||
|
return *l == *r;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t string_split(char *string, char separator, uint64_t max, char **result) {
|
||||||
|
uint64_t count = 1;
|
||||||
|
*result = string;
|
||||||
|
|
||||||
|
while (*string && count < max) {
|
||||||
|
if (*string == separator) {
|
||||||
|
*string = '\0';
|
||||||
|
result++;
|
||||||
|
*result = string + 1;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
string++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result) {
|
||||||
|
if (max == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max == 1) {
|
||||||
|
result[0] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max == 2) {
|
||||||
|
result[0] = '0';
|
||||||
|
result[1] = '\0';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char tmp[3] = "00";
|
||||||
|
|
||||||
|
uint8_t i = 0;
|
||||||
|
while (value) {
|
||||||
|
uint8_t nibble = value & 0xF;
|
||||||
|
tmp[i++] = nibble < 10 ? '0' + nibble : 'a' + nibble - 10;
|
||||||
|
value >>= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[0] = '0';
|
||||||
|
result[1] = 'x';
|
||||||
|
|
||||||
|
i = 2;
|
||||||
|
while (i < 4 && i < max - 1) {
|
||||||
|
result[i] = tmp[3 - i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
result[i] = '\0';
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result) {
|
||||||
|
if (max == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char tmp[20];
|
||||||
|
uint8_t len = 0;
|
||||||
|
|
||||||
|
if (value == 0) {
|
||||||
|
tmp[len++] = '0';
|
||||||
|
} else {
|
||||||
|
while (value && len < 20) {
|
||||||
|
tmp[len++] = '0' + (value % 10);
|
||||||
|
value /= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t i = 0;
|
||||||
|
while (i < len && i < max - 1) {
|
||||||
|
result[i] = tmp[len - 1 - i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
result[i] = '\0';
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result) {
|
||||||
|
if (max == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value >= 0) {
|
||||||
|
return string_uint_to_decimal((uint64_t)value, max, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
result[0] = '-';
|
||||||
|
return 1 + string_uint_to_decimal((uint64_t)(-value), max - 1, result + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t string_uint_to_hex(uint64_t value, uint64_t max, char *result) {
|
||||||
|
if (max == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max == 1) {
|
||||||
|
result[0] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max == 2) {
|
||||||
|
result[0] = '0';
|
||||||
|
result[1] = '\0';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char tmp[17] = "0000000000000000";
|
||||||
|
|
||||||
|
uint8_t i = 0;
|
||||||
|
while (value) {
|
||||||
|
uint8_t nibble = value & 0xF;
|
||||||
|
tmp[i++] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10;
|
||||||
|
value >>= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[0] = '0';
|
||||||
|
result[1] = 'x';
|
||||||
|
|
||||||
|
i = 2;
|
||||||
|
while (i < 18 && i < max - 1) {
|
||||||
|
result[i] = tmp[17 - i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
result[i] = '\0';
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *string_format(const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
|
||||||
|
uint64_t limit = 1023;
|
||||||
|
|
||||||
|
char *result = memory_allocate(limit + 1);
|
||||||
|
|
||||||
|
uint64_t fi = 0, ri = 0;
|
||||||
|
while (format[fi] && ri < limit) {
|
||||||
|
if (format[fi] != '%') {
|
||||||
|
result[ri++] = format[fi++];
|
||||||
|
} else {
|
||||||
|
switch (format[fi + 1]) {
|
||||||
|
case 'c':
|
||||||
|
result[ri++] = (char)va_arg(args, int);
|
||||||
|
fi += 2;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
string_int_to_decimal(va_arg(args, int64_t), limit - ri, result + ri);
|
||||||
|
while (result[ri]) {
|
||||||
|
ri++;
|
||||||
|
}
|
||||||
|
fi += 2;
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
string_uint_to_decimal(va_arg(args, uint64_t), limit - ri, result + ri);
|
||||||
|
while (result[ri]) {
|
||||||
|
ri++;
|
||||||
|
}
|
||||||
|
fi += 2;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
string_uint_to_hex(va_arg(args, uint64_t), limit - ri, result + ri);
|
||||||
|
while (result[ri]) {
|
||||||
|
ri++;
|
||||||
|
}
|
||||||
|
fi += 2;
|
||||||
|
break;
|
||||||
|
case 's': {
|
||||||
|
char *s = va_arg(args, char *);
|
||||||
|
while (*s && ri < limit) {
|
||||||
|
result[ri++] = *s;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
fi += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '%': {
|
||||||
|
result[ri++] = '%';
|
||||||
|
fi += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
result[ri++] = format[fi++];
|
||||||
|
result[ri++] = format[fi++];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result[ri] = '\0';
|
||||||
|
va_end(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint8_t bytes_equal(const char *l, const char *r, uint64_t count);
|
||||||
|
|
||||||
|
uint8_t string_empty(const char *s);
|
||||||
|
|
||||||
|
uint64_t string_length(const char *s);
|
||||||
|
|
||||||
|
uint8_t string_equal(const char *l, const char *r);
|
||||||
|
|
||||||
|
uint64_t string_split(char *string, char separator, uint64_t max, char **result);
|
||||||
|
|
||||||
|
uint64_t string_byte_to_hex(uint8_t value, uint64_t max, char *result);
|
||||||
|
|
||||||
|
uint64_t string_uint_to_decimal(uint64_t value, uint64_t max, char *result);
|
||||||
|
|
||||||
|
uint64_t string_int_to_decimal(int64_t value, uint64_t max, char *result);
|
||||||
|
|
||||||
|
uint64_t string_uint_to_hex(uint64_t value, uint64_t max, char *result);
|
||||||
|
|
||||||
|
char *string_format(const char *format, ...);
|
||||||
+397
@@ -0,0 +1,397 @@
|
|||||||
|
#include "src/terminal.h"
|
||||||
|
#include "src/fs.h"
|
||||||
|
#include "src/keyboard.h"
|
||||||
|
#include "src/memory.h"
|
||||||
|
#include "src/string.h"
|
||||||
|
#include "src/util.h"
|
||||||
|
#include "src/vga.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define KEYBOARD_STATE_LSHIFT 0b00000001
|
||||||
|
#define KEYBOARD_STATE_RSHIFT 0b00000010
|
||||||
|
#define KEYBOARD_STATE_LCTRL 0b00000100
|
||||||
|
#define KEYBOARD_STATE_RCTRL 0b00001000
|
||||||
|
#define KEYBOARD_STATE_LALT 0b00010000
|
||||||
|
#define KEYBOARD_STATE_RALT 0b00100000
|
||||||
|
#define KEYBOARD_STATE_SEQ 0b01000000
|
||||||
|
|
||||||
|
static uint8_t keyboard_state = 0;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
static const char scancode_normal[128] = {
|
||||||
|
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u',
|
||||||
|
'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x',
|
||||||
|
'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char scancode_shifted[128] = {
|
||||||
|
0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U',
|
||||||
|
'I', 'O', 'P', '{', '}', '\n', 0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X',
|
||||||
|
'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#define PROMPT_PREFIX 2
|
||||||
|
#define PROMPT_LENGTH 255
|
||||||
|
|
||||||
|
fs_node_t *root;
|
||||||
|
|
||||||
|
static char prompt[PROMPT_LENGTH + 1];
|
||||||
|
static char prompt_swap[PROMPT_LENGTH + 1];
|
||||||
|
static uint8_t prompt_row = 0;
|
||||||
|
static uint8_t prompt_length = 0;
|
||||||
|
static uint8_t prompt_offset = 0;
|
||||||
|
static char prompt_buf[VGA_WIDTH + 1] = "$ ";
|
||||||
|
|
||||||
|
static void render_prompt() {
|
||||||
|
uint8_t offset = PROMPT_PREFIX + prompt_offset < VGA_WIDTH ? 0 : PROMPT_PREFIX + prompt_offset - VGA_WIDTH + 1;
|
||||||
|
uint8_t length = PROMPT_PREFIX + prompt_length < VGA_WIDTH ? prompt_length : VGA_WIDTH - PROMPT_PREFIX;
|
||||||
|
prompt_buf[1] = PROMPT_PREFIX + prompt_offset < VGA_WIDTH ? ' ' : '<';
|
||||||
|
memory_copy(prompt + offset, length, prompt_buf + PROMPT_PREFIX);
|
||||||
|
if (PROMPT_PREFIX + prompt_length > VGA_WIDTH && prompt_offset < prompt_length - 1) {
|
||||||
|
prompt_buf[VGA_WIDTH - 1] = '>';
|
||||||
|
}
|
||||||
|
prompt_buf[PROMPT_PREFIX + length] = '\0';
|
||||||
|
|
||||||
|
vga_clear(prompt_row, 1);
|
||||||
|
vga_set_string(prompt_row, 0, prompt_buf, 0x0F);
|
||||||
|
vga_set_cursor(prompt_row, PROMPT_PREFIX + prompt_offset - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_home_pressed() {
|
||||||
|
if (keyboard_state || !prompt_offset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_offset = 0;
|
||||||
|
render_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_left_pressed() {
|
||||||
|
if (keyboard_state || !prompt_offset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_offset--;
|
||||||
|
render_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_right_pressed() {
|
||||||
|
if (keyboard_state || prompt_offset == prompt_length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_offset++;
|
||||||
|
render_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_end_pressed() {
|
||||||
|
if (keyboard_state || prompt_offset == prompt_length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_offset = prompt_length;
|
||||||
|
render_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void advance_row(uint8_t rows) {
|
||||||
|
while (rows--) {
|
||||||
|
if (prompt_row == VGA_HEIGHT - 1) {
|
||||||
|
vga_copy(1, VGA_HEIGHT - 1, 0);
|
||||||
|
} else {
|
||||||
|
prompt_row++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_line(const char *string) {
|
||||||
|
advance_row(1);
|
||||||
|
vga_clear(prompt_row - 1, 1);
|
||||||
|
vga_set_string(prompt_row - 1, 0, string, 0x0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help(uint8_t argc, [[maybe_unused]] char **argv) {
|
||||||
|
if (argc > 1) {
|
||||||
|
print_line("help: accepts no arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
advance_row(4);
|
||||||
|
vga_set_string(prompt_row - 4, 0, "Available commands:", 0x0F);
|
||||||
|
vga_set_string(prompt_row - 3, 0, "help", 0x0F);
|
||||||
|
vga_set_string(prompt_row - 2, 0, "ls DIR", 0x0F);
|
||||||
|
vga_set_string(prompt_row - 1, 0, "cat FILE", 0x0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ls(uint8_t argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
print_line("ls: requires a single path");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *path_components[16];
|
||||||
|
uint64_t path_length = string_split(argv[1], '/', 16, path_components);
|
||||||
|
if (!string_empty(path_components[0])) {
|
||||||
|
print_line("ls: relative paths not supported");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_node_t *prev = NUL;
|
||||||
|
fs_node_t *curr = root;
|
||||||
|
uint8_t i = 1;
|
||||||
|
while (i < path_length) {
|
||||||
|
if (string_empty(path_components[i])) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!curr->is_dir) {
|
||||||
|
print_line("ls: can not navigate into a file");
|
||||||
|
fs_close(curr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = curr;
|
||||||
|
curr = fs_open_by(curr, path_components[i]);
|
||||||
|
if (prev != root) {
|
||||||
|
fs_close(prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!curr) {
|
||||||
|
print_line("ls: invalid path");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!curr->is_dir) {
|
||||||
|
print_line("ls: can not list file");
|
||||||
|
fs_close(curr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t j = 0;
|
||||||
|
fs_node_t *item = fs_open_at(curr, j++);
|
||||||
|
while (item) {
|
||||||
|
print_line(item->name);
|
||||||
|
fs_close(item);
|
||||||
|
item = fs_open_at(curr, j++);
|
||||||
|
}
|
||||||
|
if (curr != root) {
|
||||||
|
fs_close(curr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cat(uint8_t argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
print_line("cat: requires a single path");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *path_components[16];
|
||||||
|
uint64_t path_length = string_split(argv[1], '/', 16, path_components);
|
||||||
|
if (!string_empty(path_components[0])) {
|
||||||
|
print_line("cat: relative paths not supported");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_node_t *prev = NUL;
|
||||||
|
fs_node_t *curr = root;
|
||||||
|
uint8_t i = 1;
|
||||||
|
while (i < path_length) {
|
||||||
|
if (string_empty(path_components[i])) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!curr->is_dir) {
|
||||||
|
print_line("cat: can not navigate into a file");
|
||||||
|
fs_close(curr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = curr;
|
||||||
|
curr = fs_open_by(curr, path_components[i]);
|
||||||
|
if (prev != root) {
|
||||||
|
fs_close(prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!curr) {
|
||||||
|
print_line("cat: invalid path");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curr->is_dir) {
|
||||||
|
print_line("cat: can not print a directory");
|
||||||
|
if (curr != root) {
|
||||||
|
fs_close(curr);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *content = memory_allocate(curr->size + 1);
|
||||||
|
fs_read(curr, 0, curr->size, content);
|
||||||
|
content[curr->size] = '\0';
|
||||||
|
|
||||||
|
char **lines = memory_allocate(curr->size * sizeof(char *));
|
||||||
|
uint64_t lines_count = string_split(content, '\n', curr->size, lines);
|
||||||
|
|
||||||
|
for (uint64_t i = 0; i < lines_count; i++) {
|
||||||
|
print_line(lines[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_free(lines);
|
||||||
|
memory_free(content);
|
||||||
|
fs_close(curr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_enter_pressed() {
|
||||||
|
prompt[prompt_length] = '\0';
|
||||||
|
|
||||||
|
char *argv[16];
|
||||||
|
uint8_t argc = (uint8_t)string_split(prompt, ' ', 16, argv);
|
||||||
|
|
||||||
|
advance_row(1);
|
||||||
|
|
||||||
|
if (string_equal(argv[0], "help")) {
|
||||||
|
help(argc, argv);
|
||||||
|
} else if (string_equal(argv[0], "ls")) {
|
||||||
|
ls(argc, argv);
|
||||||
|
} else if (string_equal(argv[0], "cat")) {
|
||||||
|
cat(argc, argv);
|
||||||
|
} else {
|
||||||
|
print_line("Unknown command");
|
||||||
|
}
|
||||||
|
prompt_length = prompt_offset = 0;
|
||||||
|
render_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_cancel_pressed() {
|
||||||
|
advance_row(1);
|
||||||
|
prompt_length = prompt_offset = 0;
|
||||||
|
render_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_character_pressed(uint8_t code) {
|
||||||
|
if (keyboard_state && (keyboard_state & (KEYBOARD_STATE_LCTRL | KEYBOARD_STATE_RCTRL)) == keyboard_state) {
|
||||||
|
switch (code) {
|
||||||
|
case 0x2E: // C
|
||||||
|
on_cancel_pressed();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyboard_state & ~(KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) || prompt_length >= PROMPT_LENGTH) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prompt_offset == prompt_length) {
|
||||||
|
prompt[prompt_offset + 1] = '\0';
|
||||||
|
} else {
|
||||||
|
memory_copy(prompt, prompt_length + 1, prompt_swap);
|
||||||
|
memory_copy(prompt_swap + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset + 1);
|
||||||
|
}
|
||||||
|
prompt[prompt_offset] = (keyboard_state & (KEYBOARD_STATE_LSHIFT | KEYBOARD_STATE_RSHIFT) ? scancode_shifted : scancode_normal)[code];
|
||||||
|
prompt_offset++;
|
||||||
|
prompt_length++;
|
||||||
|
render_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_backspace_pressed() {
|
||||||
|
if (keyboard_state || !prompt_offset) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prompt_offset == prompt_length) {
|
||||||
|
prompt[prompt_offset - 1] = '\0';
|
||||||
|
} else {
|
||||||
|
memory_copy(prompt, prompt_length + 1, prompt_swap);
|
||||||
|
memory_copy(prompt_swap + prompt_offset, prompt_length - prompt_offset, prompt + prompt_offset - 1);
|
||||||
|
}
|
||||||
|
prompt_offset--;
|
||||||
|
prompt_length--;
|
||||||
|
render_prompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_delete_pressed() {
|
||||||
|
if (keyboard_state || prompt_offset == prompt_length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_offset++;
|
||||||
|
on_backspace_pressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void terminal_on_key(uint8_t scancode) {
|
||||||
|
const uint8_t pressed = !(scancode & 0x80);
|
||||||
|
const uint8_t code = scancode & ~0x80;
|
||||||
|
|
||||||
|
if (!(keyboard_state & KEYBOARD_STATE_SEQ)) {
|
||||||
|
switch (code) {
|
||||||
|
case 0x60:
|
||||||
|
keyboard_state = keyboard_state | KEYBOARD_STATE_SEQ;
|
||||||
|
break;
|
||||||
|
case 0x2A:
|
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LSHIFT : keyboard_state & ~KEYBOARD_STATE_LSHIFT;
|
||||||
|
break;
|
||||||
|
case 0x36:
|
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RSHIFT : keyboard_state & ~KEYBOARD_STATE_RSHIFT;
|
||||||
|
break;
|
||||||
|
case 0x38:
|
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LALT : keyboard_state & ~KEYBOARD_STATE_LALT;
|
||||||
|
break;
|
||||||
|
case 0x1D:
|
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_LCTRL : keyboard_state & ~KEYBOARD_STATE_LCTRL;
|
||||||
|
break;
|
||||||
|
case 0x0E:
|
||||||
|
pressed ? on_backspace_pressed() : 0;
|
||||||
|
break;
|
||||||
|
case 0x1C:
|
||||||
|
pressed ? on_enter_pressed() : 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pressed &&scancode_normal[code] ? on_character_pressed(code) : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keyboard_state = keyboard_state & ~KEYBOARD_STATE_SEQ;
|
||||||
|
switch (code) {
|
||||||
|
case 0x38:
|
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RALT : keyboard_state & ~KEYBOARD_STATE_RALT;
|
||||||
|
break;
|
||||||
|
case 0x1D:
|
||||||
|
keyboard_state = pressed ? keyboard_state | KEYBOARD_STATE_RCTRL : keyboard_state & ~KEYBOARD_STATE_RCTRL;
|
||||||
|
break;
|
||||||
|
case 0x47:
|
||||||
|
pressed ? on_home_pressed() : 0;
|
||||||
|
break;
|
||||||
|
case 0x4B:
|
||||||
|
pressed ? on_left_pressed() : 0;
|
||||||
|
break;
|
||||||
|
case 0x4D:
|
||||||
|
pressed ? on_right_pressed() : 0;
|
||||||
|
break;
|
||||||
|
case 0x4F:
|
||||||
|
pressed ? on_end_pressed() : 0;
|
||||||
|
break;
|
||||||
|
case 0x53:
|
||||||
|
pressed ? on_delete_pressed() : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminal_init() {
|
||||||
|
root = fs_mount();
|
||||||
|
|
||||||
|
keyboard_set_handler(terminal_on_key);
|
||||||
|
vga_clear(0, VGA_HEIGHT);
|
||||||
|
char *greeting = string_format("Welcome to FreywarOS v%s!", VERSION);
|
||||||
|
vga_set_string(0, 0, greeting, 0x0F);
|
||||||
|
memory_free(greeting);
|
||||||
|
prompt_row += 2;
|
||||||
|
render_prompt();
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void terminal_init();
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define NUL 0 // TODO Fix VSCode thinking `NULL` conflicts with some other definition.
|
||||||
|
|
||||||
|
static inline uint8_t inb(uint16_t port) {
|
||||||
|
uint8_t value;
|
||||||
|
__asm__ volatile("inb %1, %0" : "=a"(value) : "Nd"(port));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void outb(uint16_t port, uint8_t value) {
|
||||||
|
__asm__ volatile("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t inw(uint16_t port) {
|
||||||
|
uint16_t value;
|
||||||
|
__asm__ volatile("inw %1, %0" : "=a"(value) : "Nd"(port));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void outw(uint16_t port, uint16_t value) {
|
||||||
|
__asm__ volatile("outw %0, %1" : : "a"(value), "Nd"(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void insw(uint16_t port, void *to, uint32_t count) {
|
||||||
|
__asm__ volatile("rep insw" : "=D"(to), "=c"(count) : "d"(port), "D"(to), "c"(count) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void outsw(uint16_t port, const void *buffer, uint32_t count) {
|
||||||
|
__asm__ volatile("rep outsw" : "=S"(buffer), "=c"(count) : "d"(port), "S"(buffer), "c"(count) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void io_wait() {
|
||||||
|
outb(0x80, 0x00);
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#include "src/vga.h"
|
||||||
|
#include "src/memory.h"
|
||||||
|
#include "src/panic.h"
|
||||||
|
#include "src/util.h"
|
||||||
|
|
||||||
|
static uint16_t *vga = (uint16_t *)0x000B8000;
|
||||||
|
|
||||||
|
void vga_set_cursor(uint8_t row, uint8_t col) {
|
||||||
|
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_cursor: invalid coordinates")
|
||||||
|
uint16_t pos = row * VGA_WIDTH + col;
|
||||||
|
outb(0x3D4, 0x0F);
|
||||||
|
outb(0x3D5, pos & 0xFF);
|
||||||
|
outb(0x3D4, 0x0E);
|
||||||
|
outb(0x3D5, (pos >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vga_set_char(uint8_t row, uint8_t col, char c, uint8_t color) {
|
||||||
|
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_char: invalid coordinates");
|
||||||
|
|
||||||
|
vga[row * VGA_WIDTH + col] = (uint16_t)(((uint16_t)color << 8) | (uint16_t)c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color) {
|
||||||
|
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates")
|
||||||
|
while (*str) {
|
||||||
|
vga_set_char(row, col++, *str++, color);
|
||||||
|
if (col >= VGA_WIDTH) {
|
||||||
|
col = 0;
|
||||||
|
row++;
|
||||||
|
ASSERT(row < VGA_HEIGHT && col < VGA_WIDTH, "vga_set_string: invalid coordinates")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void vga_copy(uint8_t src_row, uint8_t rows, uint8_t dst_row) {
|
||||||
|
ASSERT(src_row < VGA_HEIGHT && dst_row < VGA_HEIGHT && src_row + rows <= VGA_HEIGHT && dst_row + rows <= VGA_HEIGHT,
|
||||||
|
"vga_copy: invalid copy dimensions");
|
||||||
|
memory_move(vga + src_row * VGA_WIDTH, rows * VGA_WIDTH * 2, vga + dst_row * VGA_WIDTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vga_clear(uint8_t row, uint8_t rows) {
|
||||||
|
ASSERT(row < VGA_HEIGHT && rows <= VGA_HEIGHT && row + rows <= VGA_HEIGHT, "vga_clear: invalid clear dimensions");
|
||||||
|
|
||||||
|
uint16_t *dst = vga + row * VGA_WIDTH;
|
||||||
|
uint16_t fill = 0x0F20;
|
||||||
|
uint32_t count = VGA_WIDTH * rows;
|
||||||
|
__asm__ volatile("rep stosw" : "=D"(dst), "=c"(count) : "D"(dst), "a"(fill), "c"(count) : "memory");
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define VGA_WIDTH 80
|
||||||
|
#define VGA_HEIGHT 25
|
||||||
|
|
||||||
|
void vga_set_cursor(uint8_t row, uint8_t col);
|
||||||
|
|
||||||
|
void vga_set_char(uint8_t row, uint8_t col, char c, uint8_t color);
|
||||||
|
|
||||||
|
void vga_set_string(uint8_t row, uint8_t col, const char *str, uint8_t color);
|
||||||
|
|
||||||
|
void vga_copy(uint8_t src_row, uint8_t rows, uint8_t dst_row);
|
||||||
|
|
||||||
|
void vga_clear(uint8_t row, uint8_t rows);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
debug_flags=()
|
||||||
|
if [[ ${1:-} == '--debug' ]] || [[ ${1:-} == '-d' ]]; then
|
||||||
|
debug_flags+=(-s -S)
|
||||||
|
fi
|
||||||
|
|
||||||
|
qemu-system-x86_64 \
|
||||||
|
"${debug_flags[@]}" \
|
||||||
|
-monitor stdio \
|
||||||
|
-drive file="build/os.img",format=raw,if=ide \
|
||||||
|
-no-reboot \
|
||||||
|
-d int
|
||||||
Reference in New Issue
Block a user