Pridana verze v0.1
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
*.sh text eol=lf
|
||||
setup text eol=lf
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
build/
|
||||
tools/
|
||||
asterfs.img
|
||||
clean.sh
|
||||
CONTRIBUTING.md
|
||||
DOCS.md
|
||||
README.md
|
||||
ROADMAP.md
|
||||
setup
|
||||
setup.sh
|
||||
setup-runfromdisk.sh
|
||||
@@ -0,0 +1,84 @@
|
||||
# AsterOS build systém
|
||||
|
||||
ARCH := x86_64
|
||||
# Prefer dedicated cross toolchain, fallback to host toolchain when unavailable.
|
||||
CROSS ?= $(shell if command -v x86_64-elf-gcc >/dev/null 2>&1; then echo x86_64-elf-; else echo; fi)
|
||||
CC := $(CROSS)gcc
|
||||
LD := $(CROSS)ld
|
||||
OBJCOPY := $(CROSS)objcopy
|
||||
AS := nasm
|
||||
|
||||
CFLAGS := -std=gnu11 -ffreestanding -fno-stack-protector -fno-pic -m64 -Wall -Wextra -Iinclude
|
||||
LDFLAGS := -nostdlib -T linker.ld
|
||||
|
||||
BUILD := build
|
||||
IMG := $(BUILD)/aster.img
|
||||
DATA_DISK := asterfs.img
|
||||
BRIDGE_IF ?= br0
|
||||
|
||||
KERNEL_OBJS := \
|
||||
$(BUILD)/arch/x86_64/entry.o \
|
||||
$(BUILD)/arch/x86_64/gdt.o \
|
||||
$(BUILD)/arch/x86_64/idt.o \
|
||||
$(BUILD)/arch/x86_64/interrupts.o \
|
||||
$(BUILD)/arch/x86_64/cpu.o \
|
||||
$(BUILD)/kernel/main.o \
|
||||
$(BUILD)/kernel/panic.o \
|
||||
$(BUILD)/kernel/printk.o \
|
||||
$(BUILD)/kernel/memory.o \
|
||||
$(BUILD)/kernel/process.o \
|
||||
$(BUILD)/kernel/scheduler.o \
|
||||
$(BUILD)/kernel/syscall.o \
|
||||
$(BUILD)/kernel/aster_api.o \
|
||||
$(BUILD)/kernel/string.o \
|
||||
$(BUILD)/drivers/display.o \
|
||||
$(BUILD)/drivers/keyboard.o \
|
||||
$(BUILD)/drivers/timer.o \
|
||||
$(BUILD)/drivers/storage.o
|
||||
|
||||
all: $(IMG)
|
||||
|
||||
$(BUILD):
|
||||
mkdir -p $(BUILD)/boot $(BUILD)/kernel $(BUILD)/arch/x86_64 $(BUILD)/drivers
|
||||
|
||||
$(BUILD)/boot/boot.bin: boot/boot.asm | $(BUILD)
|
||||
$(AS) -f bin $< -o $@
|
||||
|
||||
$(BUILD)/boot/stage2.bin: boot/stage2.asm boot/disk.asm | $(BUILD)
|
||||
$(AS) -I boot/ -f bin $< -o $@
|
||||
|
||||
$(BUILD)/arch/x86_64/%.o: arch/x86_64/%.asm | $(BUILD)
|
||||
$(AS) -f elf64 $< -o $@
|
||||
|
||||
$(BUILD)/arch/x86_64/%.o: arch/x86_64/%.c | $(BUILD)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/kernel/%.o: kernel/%.c | $(BUILD)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/drivers/%.o: drivers/%.c | $(BUILD)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(BUILD)/kernel.elf: $(KERNEL_OBJS) linker.ld
|
||||
$(LD) $(LDFLAGS) -o $@ $(KERNEL_OBJS)
|
||||
|
||||
$(BUILD)/kernel.bin: $(BUILD)/kernel.elf
|
||||
$(OBJCOPY) -O binary $< $@
|
||||
|
||||
$(IMG): $(BUILD)/boot/boot.bin $(BUILD)/boot/stage2.bin $(BUILD)/kernel.bin
|
||||
dd if=/dev/zero of=$(IMG) bs=512 count=2880
|
||||
dd if=$(BUILD)/boot/boot.bin of=$(IMG) conv=notrunc
|
||||
dd if=$(BUILD)/boot/stage2.bin of=$(IMG) bs=512 seek=1 conv=notrunc
|
||||
dd if=$(BUILD)/kernel.bin of=$(IMG) bs=512 seek=17 conv=notrunc
|
||||
|
||||
$(DATA_DISK):
|
||||
if [ -f $(BUILD)/asterfs.img ] && [ ! -f $(DATA_DISK) ]; then cp $(BUILD)/asterfs.img $(DATA_DISK); fi
|
||||
test -f $(DATA_DISK) || dd if=/dev/zero of=$(DATA_DISK) bs=512 count=8192
|
||||
|
||||
run: $(IMG) $(DATA_DISK)
|
||||
qemu-system-x86_64 -no-reboot -no-shutdown -drive format=raw,file=$(IMG),if=ide,index=0 -drive format=raw,file=$(DATA_DISK),if=ide,index=1
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD)
|
||||
|
||||
.PHONY: all run clean
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalas
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Ukazkova C aplikace nad Aster API. Aplikace predvadi jednoduchou
|
||||
* kalkulacku a muze slouzit jako sablona pro dalsi user-space programy.
|
||||
*/
|
||||
|
||||
#include "aster_api.h"
|
||||
|
||||
static void append_number(char *out, int value) {
|
||||
char tmp[16];
|
||||
int i = 0;
|
||||
int j;
|
||||
int neg = 0;
|
||||
|
||||
if (value == 0) {
|
||||
out[0] = '0';
|
||||
out[1] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
neg = 1;
|
||||
value = -value;
|
||||
}
|
||||
|
||||
while (value > 0 && i < 15) {
|
||||
tmp[i++] = (char)('0' + (value % 10));
|
||||
value /= 10;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
if (neg) {
|
||||
out[j++] = '-';
|
||||
}
|
||||
|
||||
while (i > 0) {
|
||||
out[j++] = tmp[--i];
|
||||
}
|
||||
|
||||
out[j] = '\0';
|
||||
}
|
||||
|
||||
void app_calc_main(void) {
|
||||
char n1[16];
|
||||
char n2[16];
|
||||
char nr[16];
|
||||
char line[80];
|
||||
int a = 24;
|
||||
int b = 18;
|
||||
int r = a + b;
|
||||
|
||||
append_number(n1, a);
|
||||
append_number(n2, b);
|
||||
append_number(nr, r);
|
||||
|
||||
line[0] = '\0';
|
||||
aster_api_print("[calc_app] start\n");
|
||||
aster_api_print("[calc_app] ");
|
||||
aster_api_print(n1);
|
||||
aster_api_print(" + ");
|
||||
aster_api_print(n2);
|
||||
aster_api_print(" = ");
|
||||
aster_api_print(nr);
|
||||
aster_api_print("\n");
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento architekturalni modul inicializuje GDT, IDT a remapuje PIC.
|
||||
* Obsahuje centralni vstup pro obsluhu vyjimek, hardwarovych preruseni
|
||||
* i syscall vektoru, vcetne predani rizeni scheduleru pri timer IRQ.
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "panic.h"
|
||||
#include "printk.h"
|
||||
#include "scheduler.h"
|
||||
#include "syscall.h"
|
||||
#include "timer.h"
|
||||
#include "types.h"
|
||||
|
||||
extern void arch_load_gdt(void *ptr);
|
||||
extern void arch_reload_segments(void);
|
||||
extern void arch_load_idt(void *ptr);
|
||||
extern void *isr_stub_table[];
|
||||
|
||||
#define IDT_ENTRIES 256
|
||||
|
||||
static inline void outb(u16 port, u8 value) {
|
||||
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline void io_wait(void) {
|
||||
__asm__ volatile ("outb %%al, $0x80" : : "a"(0));
|
||||
}
|
||||
|
||||
struct __attribute__((packed)) gdt_ptr {
|
||||
u16 limit;
|
||||
u64 base;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) idt_entry {
|
||||
u16 offset_low;
|
||||
u16 selector;
|
||||
u8 ist;
|
||||
u8 type_attr;
|
||||
u16 offset_mid;
|
||||
u32 offset_high;
|
||||
u32 zero;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) idt_ptr {
|
||||
u16 limit;
|
||||
u64 base;
|
||||
};
|
||||
|
||||
static u64 gdt_table[3];
|
||||
static struct idt_entry idt[IDT_ENTRIES];
|
||||
|
||||
static void set_idt_gate(u8 vec, void *isr, u8 flags) {
|
||||
u64 addr = (u64)isr;
|
||||
idt[vec].offset_low = (u16)(addr & 0xFFFF);
|
||||
idt[vec].selector = 0x08;
|
||||
idt[vec].ist = 0;
|
||||
idt[vec].type_attr = flags;
|
||||
idt[vec].offset_mid = (u16)((addr >> 16) & 0xFFFF);
|
||||
idt[vec].offset_high = (u32)((addr >> 32) & 0xFFFFFFFF);
|
||||
idt[vec].zero = 0;
|
||||
}
|
||||
|
||||
static void pic_remap(void) {
|
||||
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, 0x00);
|
||||
outb(0xA1, 0x00);
|
||||
}
|
||||
|
||||
void cpu_init(void) {
|
||||
struct gdt_ptr gp;
|
||||
|
||||
gdt_table[0] = 0x0000000000000000ULL;
|
||||
gdt_table[1] = 0x00AF9A000000FFFFULL;
|
||||
gdt_table[2] = 0x00CF92000000FFFFULL;
|
||||
|
||||
gp.limit = sizeof(gdt_table) - 1;
|
||||
gp.base = (u64)&gdt_table[0];
|
||||
|
||||
arch_load_gdt(&gp);
|
||||
arch_reload_segments();
|
||||
}
|
||||
|
||||
void interrupts_init(void) {
|
||||
struct idt_ptr ip;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < IDT_ENTRIES; ++i) {
|
||||
set_idt_gate((u8)i, isr_stub_table[0], 0x8E);
|
||||
}
|
||||
|
||||
for (i = 0; i < 34; ++i) {
|
||||
set_idt_gate((u8)i, isr_stub_table[i], 0x8E);
|
||||
}
|
||||
|
||||
set_idt_gate(128, isr_stub_table[34], 0xEE);
|
||||
|
||||
ip.limit = sizeof(idt) - 1;
|
||||
ip.base = (u64)&idt[0];
|
||||
|
||||
arch_load_idt(&ip);
|
||||
pic_remap();
|
||||
|
||||
__asm__ volatile ("sti");
|
||||
}
|
||||
|
||||
void interrupt_dispatch(interrupt_frame_t *frame) {
|
||||
if (frame->vector < 32) {
|
||||
printk("[EXC] vector=%d error=%x\n", (int)frame->vector, (u32)frame->error);
|
||||
panic("CPU exception");
|
||||
}
|
||||
|
||||
if (frame->vector == 32) {
|
||||
scheduler_tick();
|
||||
outb(0x20, 0x20);
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame->vector == 33) {
|
||||
outb(0x20, 0x20);
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame->vector == 128) {
|
||||
frame->rax = syscall_dispatch(frame->rax, frame->rbx, frame->rcx, frame->rdx, frame->rsi);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_halt(void) {
|
||||
__asm__ volatile ("cli");
|
||||
for (;;) {
|
||||
__asm__ volatile ("hlt");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
; /*
|
||||
; * AsterOS Kernel
|
||||
; * Autor: Pavel Kalaš
|
||||
; * Rok: 2026
|
||||
; *
|
||||
; * Vytvořeno jen tak z nudy a z chuti zkoušet nové věci.
|
||||
; */
|
||||
|
||||
[bits 64]
|
||||
|
||||
section .text.boot
|
||||
global _start
|
||||
extern kmain
|
||||
|
||||
_start:
|
||||
cli
|
||||
mov word [0xB800A], 0x0F4B
|
||||
mov rsp, stack_top
|
||||
call kmain
|
||||
|
||||
.halt:
|
||||
hlt
|
||||
jmp .halt
|
||||
|
||||
section .bss
|
||||
align 16
|
||||
stack_bottom:
|
||||
resb 16384
|
||||
stack_top:
|
||||
@@ -0,0 +1,31 @@
|
||||
; /*
|
||||
; * AsterOS Kernel
|
||||
; * Autor: Pavel Kalaš
|
||||
; * Rok: 2026
|
||||
; *
|
||||
; * Vytvořeno jen tak z nudy a z chuti zkoušet nové věci.
|
||||
; */
|
||||
|
||||
[bits 64]
|
||||
section .text
|
||||
global arch_load_gdt
|
||||
global arch_reload_segments
|
||||
|
||||
arch_load_gdt:
|
||||
lgdt [rdi]
|
||||
ret
|
||||
|
||||
arch_reload_segments:
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
push qword 0x08
|
||||
lea rax, [rel .flush]
|
||||
push rax
|
||||
retfq
|
||||
.flush:
|
||||
ret
|
||||
@@ -0,0 +1,15 @@
|
||||
; /*
|
||||
; * AsterOS Kernel
|
||||
; * Autor: Pavel Kalaš
|
||||
; * Rok: 2026
|
||||
; *
|
||||
; * Vytvořeno jen tak z nudy a z chuti zkoušet nové věci.
|
||||
; */
|
||||
|
||||
[bits 64]
|
||||
section .text
|
||||
global arch_load_idt
|
||||
|
||||
arch_load_idt:
|
||||
lidt [rdi]
|
||||
ret
|
||||
@@ -0,0 +1,143 @@
|
||||
; /*
|
||||
; * AsterOS Kernel
|
||||
; * Autor: Pavel Kalaš
|
||||
; * Rok: 2026
|
||||
; *
|
||||
; * Vytvořeno jen tak z nudy a z chuti zkoušet nové věci.
|
||||
; */
|
||||
|
||||
[bits 64]
|
||||
section .text
|
||||
|
||||
extern interrupt_dispatch
|
||||
|
||||
global isr_stub_table
|
||||
|
||||
%macro ISR_NOERR 1
|
||||
global isr_%1
|
||||
isr_%1:
|
||||
push qword 0
|
||||
push qword %1
|
||||
jmp isr_common
|
||||
%endmacro
|
||||
|
||||
%macro ISR_ERR 1
|
||||
global isr_%1
|
||||
isr_%1:
|
||||
push qword %1
|
||||
jmp isr_common
|
||||
%endmacro
|
||||
|
||||
ISR_NOERR 0
|
||||
ISR_NOERR 1
|
||||
ISR_NOERR 2
|
||||
ISR_NOERR 3
|
||||
ISR_NOERR 4
|
||||
ISR_NOERR 5
|
||||
ISR_NOERR 6
|
||||
ISR_NOERR 7
|
||||
ISR_ERR 8
|
||||
ISR_NOERR 9
|
||||
ISR_ERR 10
|
||||
ISR_ERR 11
|
||||
ISR_ERR 12
|
||||
ISR_ERR 13
|
||||
ISR_ERR 14
|
||||
ISR_NOERR 15
|
||||
ISR_NOERR 16
|
||||
ISR_ERR 17
|
||||
ISR_NOERR 18
|
||||
ISR_NOERR 19
|
||||
ISR_NOERR 20
|
||||
ISR_ERR 21
|
||||
ISR_NOERR 22
|
||||
ISR_NOERR 23
|
||||
ISR_NOERR 24
|
||||
ISR_NOERR 25
|
||||
ISR_NOERR 26
|
||||
ISR_NOERR 27
|
||||
ISR_NOERR 28
|
||||
ISR_NOERR 29
|
||||
ISR_ERR 30
|
||||
ISR_NOERR 31
|
||||
ISR_NOERR 32
|
||||
ISR_NOERR 33
|
||||
ISR_NOERR 128
|
||||
|
||||
isr_common:
|
||||
cld
|
||||
push rax
|
||||
push rbx
|
||||
push rcx
|
||||
push rdx
|
||||
push rbp
|
||||
push rdi
|
||||
push rsi
|
||||
push r8
|
||||
push r9
|
||||
push r10
|
||||
push r11
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
push r15
|
||||
|
||||
mov rdi, rsp
|
||||
call interrupt_dispatch
|
||||
|
||||
pop r15
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
pop r11
|
||||
pop r10
|
||||
pop r9
|
||||
pop r8
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rbp
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop rbx
|
||||
pop rax
|
||||
|
||||
add rsp, 16
|
||||
iretq
|
||||
|
||||
section .rodata
|
||||
isr_stub_table:
|
||||
dq isr_0
|
||||
dq isr_1
|
||||
dq isr_2
|
||||
dq isr_3
|
||||
dq isr_4
|
||||
dq isr_5
|
||||
dq isr_6
|
||||
dq isr_7
|
||||
dq isr_8
|
||||
dq isr_9
|
||||
dq isr_10
|
||||
dq isr_11
|
||||
dq isr_12
|
||||
dq isr_13
|
||||
dq isr_14
|
||||
dq isr_15
|
||||
dq isr_16
|
||||
dq isr_17
|
||||
dq isr_18
|
||||
dq isr_19
|
||||
dq isr_20
|
||||
dq isr_21
|
||||
dq isr_22
|
||||
dq isr_23
|
||||
dq isr_24
|
||||
dq isr_25
|
||||
dq isr_26
|
||||
dq isr_27
|
||||
dq isr_28
|
||||
dq isr_29
|
||||
dq isr_30
|
||||
dq isr_31
|
||||
dq isr_32
|
||||
dq isr_33
|
||||
dq isr_128
|
||||
@@ -0,0 +1,74 @@
|
||||
; /*
|
||||
; * AsterOS Kernel
|
||||
; * Autor: Pavel Kalaš
|
||||
; * Rok: 2026
|
||||
; *
|
||||
; */
|
||||
|
||||
[org 0x7C00]
|
||||
[bits 16]
|
||||
|
||||
STAGE2_SECTORS equ 16
|
||||
STAGE2_LBA equ 1
|
||||
|
||||
start:
|
||||
cli
|
||||
xor ax, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov ss, ax
|
||||
mov sp, 0x7C00
|
||||
sti
|
||||
|
||||
mov [boot_drive], dl
|
||||
|
||||
mov si, boot_msg
|
||||
call print_string
|
||||
|
||||
mov word [dap + 2], STAGE2_SECTORS
|
||||
mov word [dap + 4], 0x8000
|
||||
mov word [dap + 6], 0x0000
|
||||
mov dword [dap + 8], STAGE2_LBA
|
||||
mov dword [dap + 12], 0
|
||||
|
||||
mov si, dap
|
||||
mov ah, 0x42
|
||||
mov dl, [boot_drive]
|
||||
int 0x13
|
||||
jc disk_error
|
||||
|
||||
jmp 0x0000:0x8000
|
||||
|
||||
disk_error:
|
||||
mov si, disk_msg
|
||||
call print_string
|
||||
jmp $
|
||||
|
||||
print_string:
|
||||
lodsb
|
||||
test al, al
|
||||
jz .done
|
||||
mov ah, 0x0E
|
||||
mov bh, 0x00
|
||||
mov bl, 0x0F
|
||||
int 0x10
|
||||
jmp print_string
|
||||
.done:
|
||||
ret
|
||||
|
||||
boot_drive: db 0
|
||||
boot_msg: db "Aster Bootloader stage1", 13, 10, 0
|
||||
disk_msg: db "Disk read error", 13, 10, 0
|
||||
|
||||
align 8
|
||||
dap:
|
||||
db 0x10
|
||||
db 0
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dd 0
|
||||
dd 0
|
||||
|
||||
times 510 - ($ - $$) db 0
|
||||
dw 0xAA55
|
||||
@@ -0,0 +1,18 @@
|
||||
; /*
|
||||
; * AsterOS Kernel
|
||||
; * Autor: Pavel Kalaš
|
||||
; * Rok: 2026
|
||||
; *
|
||||
; */
|
||||
|
||||
[bits 16]
|
||||
|
||||
global disk_read_lba
|
||||
|
||||
; DS:SI -> adresa DAP struktury, DL -> číslo disku
|
||||
disk_read_lba:
|
||||
push ax
|
||||
mov ah, 0x42
|
||||
int 0x13
|
||||
pop ax
|
||||
ret
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
; /*
|
||||
; * AsterOS Kernel
|
||||
; * Autor: Pavel Kalaš
|
||||
; * Rok: 2026
|
||||
; *
|
||||
; */
|
||||
|
||||
[org 0x8000]
|
||||
[bits 16]
|
||||
|
||||
jmp start2
|
||||
|
||||
%include "disk.asm"
|
||||
|
||||
KERNEL_SECTORS equ 256
|
||||
KERNEL_START_LBA equ 17
|
||||
KERNEL_LOAD_SEG equ 0x1000
|
||||
PML4_ADDR equ 0x00070000
|
||||
PDPT_ADDR equ 0x00071000
|
||||
PD_ADDR equ 0x00072000
|
||||
|
||||
start2:
|
||||
cli
|
||||
xor ax, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov ss, ax
|
||||
mov sp, 0x9000
|
||||
sti
|
||||
|
||||
mov [boot_drive], dl
|
||||
|
||||
mov si, msg_stage2
|
||||
call print_string
|
||||
|
||||
call enable_a20
|
||||
call load_kernel
|
||||
jc load_error
|
||||
|
||||
mov si, msg_kernel_loaded
|
||||
call print_string
|
||||
|
||||
cli
|
||||
lgdt [gdt_descriptor]
|
||||
mov eax, cr0
|
||||
or eax, 0x1
|
||||
mov cr0, eax
|
||||
jmp 0x08:protected_entry
|
||||
|
||||
load_error:
|
||||
mov si, msg_load_error
|
||||
call print_string
|
||||
jmp $
|
||||
|
||||
enable_a20:
|
||||
in al, 0x92
|
||||
or al, 0x02
|
||||
out 0x92, al
|
||||
ret
|
||||
|
||||
load_kernel:
|
||||
mov word [remaining_sectors], KERNEL_SECTORS
|
||||
mov word [current_seg], KERNEL_LOAD_SEG
|
||||
mov dword [current_lba], KERNEL_START_LBA
|
||||
|
||||
.next_chunk:
|
||||
mov ax, [remaining_sectors]
|
||||
test ax, ax
|
||||
jz .done
|
||||
|
||||
cmp ax, 127
|
||||
jbe .use_rest
|
||||
mov ax, 127
|
||||
|
||||
.use_rest:
|
||||
mov [chunk_sectors], ax
|
||||
|
||||
mov word [dap + 2], ax
|
||||
mov word [dap + 4], 0x0000
|
||||
mov ax, [current_seg]
|
||||
mov word [dap + 6], ax
|
||||
mov eax, [current_lba]
|
||||
mov dword [dap + 8], eax
|
||||
mov dword [dap + 12], 0
|
||||
|
||||
mov dl, [boot_drive]
|
||||
mov si, dap
|
||||
call disk_read_lba
|
||||
jc .error
|
||||
|
||||
mov ax, [chunk_sectors]
|
||||
shl ax, 5
|
||||
add [current_seg], ax
|
||||
|
||||
mov eax, [current_lba]
|
||||
movzx ebx, word [chunk_sectors]
|
||||
add eax, ebx
|
||||
mov [current_lba], eax
|
||||
|
||||
mov ax, [remaining_sectors]
|
||||
sub ax, [chunk_sectors]
|
||||
mov [remaining_sectors], ax
|
||||
jmp .next_chunk
|
||||
|
||||
.error:
|
||||
stc
|
||||
ret
|
||||
|
||||
.done:
|
||||
clc
|
||||
ret
|
||||
|
||||
print_string:
|
||||
lodsb
|
||||
test al, al
|
||||
jz .done
|
||||
mov ah, 0x0E
|
||||
mov bh, 0x00
|
||||
mov bl, 0x0F
|
||||
int 0x10
|
||||
jmp print_string
|
||||
.done:
|
||||
ret
|
||||
|
||||
boot_drive: db 0
|
||||
msg_stage2: db "Aster Bootloader stage2", 13, 10, 0
|
||||
msg_kernel_loaded: db "Kernel read OK", 13, 10, 0
|
||||
msg_load_error: db "Kernel load error", 13, 10, 0
|
||||
|
||||
align 8
|
||||
dap:
|
||||
db 0x10
|
||||
db 0x00
|
||||
dw 0
|
||||
dw 0
|
||||
dw 0
|
||||
dd 0
|
||||
|
||||
remaining_sectors: dw 0
|
||||
chunk_sectors: dw 0
|
||||
current_seg: dw 0
|
||||
current_lba: dd 0
|
||||
dd 0
|
||||
|
||||
align 8
|
||||
gdt_start:
|
||||
dq 0x0000000000000000
|
||||
dq 0x00CF9A000000FFFF
|
||||
dq 0x00CF92000000FFFF
|
||||
dq 0x00AF9A000000FFFF
|
||||
dq 0x00CF92000000FFFF
|
||||
gdt_end:
|
||||
|
||||
gdt_descriptor:
|
||||
dw gdt_end - gdt_start - 1
|
||||
dd gdt_start
|
||||
|
||||
[bits 32]
|
||||
protected_entry:
|
||||
mov word [0xB8000], 0x0F50
|
||||
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
mov esp, 0x9FC00
|
||||
|
||||
mov esi, 0x00010000
|
||||
mov edi, 0x00100000
|
||||
mov ecx, (KERNEL_SECTORS * 512) / 4
|
||||
rep movsd
|
||||
|
||||
call setup_paging
|
||||
|
||||
mov word [0xB8002], 0x0F47
|
||||
|
||||
mov eax, cr4
|
||||
or eax, (1 << 5)
|
||||
mov cr4, eax
|
||||
|
||||
mov ecx, 0xC0000080
|
||||
rdmsr
|
||||
or eax, (1 << 8)
|
||||
wrmsr
|
||||
|
||||
mov eax, PML4_ADDR
|
||||
mov cr3, eax
|
||||
|
||||
mov eax, cr0
|
||||
or eax, 0x80000001
|
||||
mov cr0, eax
|
||||
|
||||
mov word [0xB8004], 0x0F4C
|
||||
|
||||
jmp 0x18:long_mode_entry
|
||||
|
||||
setup_paging:
|
||||
mov edi, PML4_ADDR
|
||||
mov ecx, (4096 * 3) / 4
|
||||
xor eax, eax
|
||||
rep stosd
|
||||
|
||||
mov dword [PML4_ADDR], PDPT_ADDR | 0x03
|
||||
mov dword [PML4_ADDR + 4], 0
|
||||
|
||||
mov dword [PDPT_ADDR], PD_ADDR | 0x03
|
||||
mov dword [PDPT_ADDR + 4], 0
|
||||
|
||||
mov edi, PD_ADDR
|
||||
xor ebx, ebx
|
||||
mov ecx, 512
|
||||
|
||||
.map_pd:
|
||||
mov eax, ebx
|
||||
or eax, 0x83
|
||||
mov dword [edi], eax
|
||||
mov dword [edi + 4], 0
|
||||
add ebx, 0x200000
|
||||
add edi, 8
|
||||
loop .map_pd
|
||||
|
||||
ret
|
||||
|
||||
[bits 64]
|
||||
long_mode_entry:
|
||||
mov word [0xB8006], 0x0F4D
|
||||
|
||||
mov ax, 0x20
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
mov rsp, 0x200000
|
||||
|
||||
mov word [0xB8008], 0x0F4A
|
||||
|
||||
mov rax, 0x00100000
|
||||
jmp rax
|
||||
|
||||
halt64:
|
||||
hlt
|
||||
jmp halt64
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento ovladac realizuje textovou konzoli nad VGA pameti na adrese 0xB8000.
|
||||
* Ridi pozici kurzoru, barvy znaku, zpracovani ridicich znaku
|
||||
* a automaticky scroll, kdyz vystup presahne posledni radek obrazovky.
|
||||
*/
|
||||
|
||||
#include "display.h"
|
||||
|
||||
#define VGA_WIDTH 80
|
||||
#define VGA_HEIGHT 25
|
||||
#define VGA_CONTENT_HEIGHT (VGA_HEIGHT - 1)
|
||||
|
||||
static volatile u16 *const vga = (u16 *)0xB8000;
|
||||
static u8 color = 0x0F;
|
||||
static usize row = 0;
|
||||
static usize col = 0;
|
||||
|
||||
static inline void outb(u16 port, u8 value) {
|
||||
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static void update_hw_cursor(void) {
|
||||
u16 pos = (u16)(row * VGA_WIDTH + col);
|
||||
|
||||
outb(0x3D4, 0x0F);
|
||||
outb(0x3D5, (u8)(pos & 0xFF));
|
||||
outb(0x3D4, 0x0E);
|
||||
outb(0x3D5, (u8)((pos >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
static inline u16 vga_entry(char c, u8 clr) {
|
||||
return (u16)c | ((u16)clr << 8);
|
||||
}
|
||||
|
||||
static void scroll_if_needed(void) {
|
||||
usize r;
|
||||
usize c;
|
||||
|
||||
if (row < VGA_CONTENT_HEIGHT) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (r = 1; r < VGA_CONTENT_HEIGHT; ++r) {
|
||||
for (c = 0; c < VGA_WIDTH; ++c) {
|
||||
vga[(r - 1) * VGA_WIDTH + c] = vga[r * VGA_WIDTH + c];
|
||||
}
|
||||
}
|
||||
|
||||
for (c = 0; c < VGA_WIDTH; ++c) {
|
||||
vga[(VGA_CONTENT_HEIGHT - 1) * VGA_WIDTH + c] = vga_entry(' ', color);
|
||||
}
|
||||
|
||||
row = VGA_CONTENT_HEIGHT - 1;
|
||||
}
|
||||
|
||||
void display_set_color(u8 fg, u8 bg) {
|
||||
color = (u8)((bg << 4) | (fg & 0x0F));
|
||||
}
|
||||
|
||||
void display_clear(void) {
|
||||
usize i;
|
||||
|
||||
for (i = 0; i < VGA_WIDTH * VGA_HEIGHT; ++i) {
|
||||
vga[i] = vga_entry(' ', color);
|
||||
}
|
||||
|
||||
row = 0;
|
||||
col = 0;
|
||||
update_hw_cursor();
|
||||
}
|
||||
|
||||
void display_init(void) {
|
||||
display_set_color(0x0F, 0x00);
|
||||
display_clear();
|
||||
}
|
||||
|
||||
void display_putc(char c) {
|
||||
if (c == '\n') {
|
||||
col = 0;
|
||||
++row;
|
||||
scroll_if_needed();
|
||||
update_hw_cursor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '\r') {
|
||||
col = 0;
|
||||
update_hw_cursor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '\b') {
|
||||
if (col > 0) {
|
||||
--col;
|
||||
} else if (row > 0) {
|
||||
--row;
|
||||
col = VGA_WIDTH - 1;
|
||||
}
|
||||
|
||||
vga[row * VGA_WIDTH + col] = vga_entry(' ', color);
|
||||
update_hw_cursor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '\t') {
|
||||
col = (col + 4) & ~3ULL;
|
||||
if (col >= VGA_WIDTH) {
|
||||
col = 0;
|
||||
++row;
|
||||
scroll_if_needed();
|
||||
}
|
||||
update_hw_cursor();
|
||||
return;
|
||||
}
|
||||
|
||||
vga[row * VGA_WIDTH + col] = vga_entry(c, color);
|
||||
++col;
|
||||
|
||||
if (col >= VGA_WIDTH) {
|
||||
col = 0;
|
||||
++row;
|
||||
scroll_if_needed();
|
||||
}
|
||||
|
||||
update_hw_cursor();
|
||||
}
|
||||
|
||||
void display_write(const char *text) {
|
||||
while (*text) {
|
||||
display_putc(*text++);
|
||||
}
|
||||
}
|
||||
|
||||
void display_get_cursor(usize *out_row, usize *out_col) {
|
||||
if (out_row) {
|
||||
*out_row = row;
|
||||
}
|
||||
if (out_col) {
|
||||
*out_col = col;
|
||||
}
|
||||
}
|
||||
|
||||
void display_set_cursor(usize new_row, usize new_col) {
|
||||
if (new_row >= VGA_HEIGHT) {
|
||||
new_row = VGA_HEIGHT - 1;
|
||||
}
|
||||
if (new_col >= VGA_WIDTH) {
|
||||
new_col = VGA_WIDTH - 1;
|
||||
}
|
||||
|
||||
row = new_row;
|
||||
col = new_col;
|
||||
update_hw_cursor();
|
||||
}
|
||||
|
||||
void display_fill_row(usize target_row, char ch, u8 fg, u8 bg) {
|
||||
usize c;
|
||||
u8 clr;
|
||||
|
||||
if (target_row >= VGA_HEIGHT) {
|
||||
return;
|
||||
}
|
||||
|
||||
clr = (u8)((bg << 4) | (fg & 0x0F));
|
||||
for (c = 0; c < VGA_WIDTH; ++c) {
|
||||
vga[target_row * VGA_WIDTH + c] = vga_entry(ch, clr);
|
||||
}
|
||||
}
|
||||
|
||||
void display_write_at(usize target_row, usize target_col, const char *text, u8 fg, u8 bg) {
|
||||
usize c = target_col;
|
||||
u8 clr;
|
||||
|
||||
if (!text || target_row >= VGA_HEIGHT || target_col >= VGA_WIDTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
clr = (u8)((bg << 4) | (fg & 0x0F));
|
||||
while (*text && c < VGA_WIDTH) {
|
||||
vga[target_row * VGA_WIDTH + c] = vga_entry(*text++, clr);
|
||||
++c;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor implementuje zakladni klavesnicovy vstup pres PS/2 porty.
|
||||
* Preklada scancode na ASCII znaky a poskytuje funkci pro nacitani radku,
|
||||
* kterou pouziva shell pro prijem prikazu od uzivatele.
|
||||
*/
|
||||
|
||||
#include "display.h"
|
||||
#include "drivers.h"
|
||||
|
||||
#define KBD_HISTORY_MAX 32
|
||||
#define KBD_HISTORY_LINE_MAX 128
|
||||
|
||||
static char g_history[KBD_HISTORY_MAX][KBD_HISTORY_LINE_MAX];
|
||||
static int g_history_count = 0;
|
||||
static int g_history_head = 0;
|
||||
static int g_ctrl_down = 0;
|
||||
|
||||
static inline unsigned char inb(unsigned short port) {
|
||||
unsigned char ret;
|
||||
__asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char keymap[128] = {
|
||||
0, 27, '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, ' ',
|
||||
};
|
||||
|
||||
void keyboard_init(void) {
|
||||
}
|
||||
|
||||
int keyboard_try_read_key(void) {
|
||||
unsigned char sc;
|
||||
int out = -1;
|
||||
|
||||
if ((inb(0x64) & 1) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sc = inb(0x60);
|
||||
|
||||
if (sc == 0xE0) {
|
||||
unsigned char ext;
|
||||
|
||||
if ((inb(0x64) & 1) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ext = inb(0x60);
|
||||
if (ext == 0x1D) {
|
||||
g_ctrl_down = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ext == 0x9D) {
|
||||
g_ctrl_down = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ext & 0x80) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ext == 0x48) {
|
||||
out = ASTER_KEY_UP;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (ext == 0x50) {
|
||||
out = ASTER_KEY_DOWN;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (ext == 0x4B) {
|
||||
out = ASTER_KEY_LEFT;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (ext == 0x4D) {
|
||||
out = ASTER_KEY_RIGHT;
|
||||
return out;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sc == 0x1D) {
|
||||
g_ctrl_down = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sc == 0x9D) {
|
||||
g_ctrl_down = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sc & 0x80) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sc == 0x3B) {
|
||||
out = ASTER_KEY_F1;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (sc == 0x3C) {
|
||||
out = ASTER_KEY_F2;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (sc == 0x1F && g_ctrl_down) {
|
||||
out = 19; /* Ctrl+S */
|
||||
return out;
|
||||
}
|
||||
|
||||
if (sc < 128 && keymap[sc]) {
|
||||
out = keymap[sc];
|
||||
return out;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int keyboard_read_key(void) {
|
||||
for (;;) {
|
||||
int k = keyboard_try_read_key();
|
||||
if (k != -1) {
|
||||
return k;
|
||||
}
|
||||
|
||||
__asm__ volatile ("pause");
|
||||
}
|
||||
}
|
||||
|
||||
static void clear_current_line(int len) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < len; ++j) {
|
||||
display_putc('\b');
|
||||
}
|
||||
}
|
||||
|
||||
static void set_line_from_history(char *buffer, int *len, int max_len, const char *src) {
|
||||
int j = 0;
|
||||
int limit = max_len - 1;
|
||||
|
||||
clear_current_line(*len);
|
||||
while (src[j] && j < limit) {
|
||||
buffer[j] = src[j];
|
||||
display_putc(src[j]);
|
||||
++j;
|
||||
}
|
||||
|
||||
buffer[j] = '\0';
|
||||
*len = j;
|
||||
}
|
||||
|
||||
static void history_push(const char *line) {
|
||||
int i;
|
||||
int slot;
|
||||
|
||||
if (!line || !line[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_history_count > 0) {
|
||||
int last = (g_history_head + KBD_HISTORY_MAX - 1) % KBD_HISTORY_MAX;
|
||||
if (g_history[last][0] != '\0') {
|
||||
i = 0;
|
||||
while (line[i] && g_history[last][i] && line[i] == g_history[last][i]) {
|
||||
++i;
|
||||
}
|
||||
if (line[i] == '\0' && g_history[last][i] == '\0') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slot = g_history_head;
|
||||
for (i = 0; i < KBD_HISTORY_LINE_MAX - 1 && line[i]; ++i) {
|
||||
g_history[slot][i] = line[i];
|
||||
}
|
||||
g_history[slot][i] = '\0';
|
||||
|
||||
g_history_head = (g_history_head + 1) % KBD_HISTORY_MAX;
|
||||
if (g_history_count < KBD_HISTORY_MAX) {
|
||||
++g_history_count;
|
||||
}
|
||||
}
|
||||
|
||||
int keyboard_readline(char *buffer, int max_len) {
|
||||
int i = 0;
|
||||
int history_nav = -1;
|
||||
|
||||
if (max_len <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int c = keyboard_read_key();
|
||||
|
||||
if (c == ASTER_KEY_F1 || c == ASTER_KEY_F2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ASTER_KEY_UP) {
|
||||
if (g_history_count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (history_nav < g_history_count - 1) {
|
||||
++history_nav;
|
||||
}
|
||||
|
||||
{
|
||||
int idx = (g_history_head + KBD_HISTORY_MAX - 1 - history_nav) % KBD_HISTORY_MAX;
|
||||
set_line_from_history(buffer, &i, max_len, g_history[idx]);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ASTER_KEY_DOWN) {
|
||||
if (g_history_count == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (history_nav > 0) {
|
||||
--history_nav;
|
||||
{
|
||||
int idx = (g_history_head + KBD_HISTORY_MAX - 1 - history_nav) % KBD_HISTORY_MAX;
|
||||
set_line_from_history(buffer, &i, max_len, g_history[idx]);
|
||||
}
|
||||
} else if (history_nav == 0) {
|
||||
history_nav = -1;
|
||||
clear_current_line(i);
|
||||
i = 0;
|
||||
buffer[0] = '\0';
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\n') {
|
||||
display_putc('\n');
|
||||
buffer[i] = '\0';
|
||||
history_push(buffer);
|
||||
return i;
|
||||
}
|
||||
|
||||
if (c == '\b') {
|
||||
if (i > 0) {
|
||||
--i;
|
||||
display_putc('\b');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i < max_len - 1) {
|
||||
buffer[i++] = (char)c;
|
||||
display_putc((char)c);
|
||||
buffer[i] = '\0';
|
||||
history_nav = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,669 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalas
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento modul implementuje AsterFS nad realnym ATA diskem v QEMU.
|
||||
* Data se nahravaji pri startu a po kazde zmene se synchronizuji
|
||||
* do sektoru na druhem IDE disku, aby pretrvala mezi restarty.
|
||||
*/
|
||||
|
||||
#include "storage.h"
|
||||
#include "string.h"
|
||||
|
||||
#define ATA_IO_BASE 0x1F0
|
||||
#define ATA_CTRL_BASE 0x3F6
|
||||
#define ATA_DRIVE_SLAVE 0xF0
|
||||
|
||||
#define ATA_REG_DATA 0
|
||||
#define ATA_REG_SECCOUNT0 2
|
||||
#define ATA_REG_LBA0 3
|
||||
#define ATA_REG_LBA1 4
|
||||
#define ATA_REG_LBA2 5
|
||||
#define ATA_REG_HDDEVSEL 6
|
||||
#define ATA_REG_COMMAND 7
|
||||
#define ATA_REG_STATUS 7
|
||||
|
||||
#define ATA_CMD_READ_PIO 0x20
|
||||
#define ATA_CMD_WRITE_PIO 0x30
|
||||
#define ATA_CMD_CACHE_FLUSH 0xE7
|
||||
|
||||
#define ATA_SR_BSY 0x80
|
||||
#define ATA_SR_DRQ 0x08
|
||||
#define ATA_SR_ERR 0x01
|
||||
|
||||
#define ASTERFS_MAGIC "ASTERFS1"
|
||||
#define ASTERFS_VERSION 1U
|
||||
|
||||
#define ASTERFS_NODE_BYTES (ASTERFS_NAME_LEN + 1 + 2 + 1 + ASTERFS_DATA_LEN)
|
||||
#define ASTERFS_NODE_SECTORS (((ASTERFS_MAX_FILES * ASTERFS_NODE_BYTES) + 511) / 512)
|
||||
#define ASTERFS_DISK_START_LBA 1U
|
||||
|
||||
typedef struct {
|
||||
char magic[8];
|
||||
u32 version;
|
||||
u32 nodes_used;
|
||||
u32 reserved;
|
||||
u8 pad[512 - 8 - 4 - 4 - 4];
|
||||
} asterfs_superblock_t;
|
||||
|
||||
typedef struct {
|
||||
char name[ASTERFS_NAME_LEN];
|
||||
u8 is_dir;
|
||||
u16 size;
|
||||
u8 reserved;
|
||||
u8 data[ASTERFS_DATA_LEN];
|
||||
} asterfs_disk_node_t;
|
||||
|
||||
static asterfs_node_t nodes[ASTERFS_MAX_FILES];
|
||||
static int nodes_used = 0;
|
||||
static int disk_ready = 0;
|
||||
static u8 g_sector_buffer[512];
|
||||
static u8 g_nodes_blob[ASTERFS_MAX_FILES * ASTERFS_NODE_BYTES];
|
||||
|
||||
static inline void outb(unsigned short port, unsigned char value) {
|
||||
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline unsigned char inb(unsigned short port) {
|
||||
unsigned char ret;
|
||||
__asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void outw(unsigned short port, unsigned short value) {
|
||||
__asm__ volatile ("outw %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
static inline unsigned short inw(unsigned short port) {
|
||||
unsigned short ret;
|
||||
__asm__ volatile ("inw %1, %0" : "=a"(ret) : "Nd"(port));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ata_delay_400ns(void) {
|
||||
(void)inb(ATA_CTRL_BASE);
|
||||
(void)inb(ATA_CTRL_BASE);
|
||||
(void)inb(ATA_CTRL_BASE);
|
||||
(void)inb(ATA_CTRL_BASE);
|
||||
}
|
||||
|
||||
static int ata_wait_not_busy(void) {
|
||||
unsigned int timeout = 200000U;
|
||||
while (timeout-- > 0) {
|
||||
unsigned char s = inb(ATA_IO_BASE + ATA_REG_STATUS);
|
||||
if (s == 0x00 || s == 0xFF) {
|
||||
return -1;
|
||||
}
|
||||
if ((s & ATA_SR_BSY) == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ata_wait_drq(void) {
|
||||
unsigned int timeout = 200000U;
|
||||
while (timeout-- > 0) {
|
||||
unsigned char s = inb(ATA_IO_BASE + ATA_REG_STATUS);
|
||||
if (s == 0x00 || s == 0xFF) {
|
||||
return -1;
|
||||
}
|
||||
if (s & ATA_SR_ERR) {
|
||||
return -1;
|
||||
}
|
||||
if ((s & ATA_SR_BSY) == 0 && (s & ATA_SR_DRQ) != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ata_select_drive_lba(u32 lba) {
|
||||
if (ata_wait_not_busy() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
outb(ATA_IO_BASE + ATA_REG_HDDEVSEL, (unsigned char)(ATA_DRIVE_SLAVE | ((lba >> 24) & 0x0F)));
|
||||
ata_delay_400ns();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ata_probe_slave(void) {
|
||||
unsigned char s;
|
||||
|
||||
outb(ATA_IO_BASE + ATA_REG_HDDEVSEL, ATA_DRIVE_SLAVE);
|
||||
ata_delay_400ns();
|
||||
|
||||
s = inb(ATA_IO_BASE + ATA_REG_STATUS);
|
||||
if (s == 0x00 || s == 0xFF) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ata_wait_not_busy();
|
||||
}
|
||||
|
||||
static int ata_read_sector(u32 lba, u8 *buf512) {
|
||||
int i;
|
||||
|
||||
if (!buf512) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ata_select_drive_lba(lba) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
outb(ATA_IO_BASE + ATA_REG_SECCOUNT0, 1);
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA0, (unsigned char)(lba & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA1, (unsigned char)((lba >> 8) & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA2, (unsigned char)((lba >> 16) & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
|
||||
|
||||
if (ata_wait_drq() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; ++i) {
|
||||
unsigned short w = inw(ATA_IO_BASE + ATA_REG_DATA);
|
||||
buf512[i * 2] = (u8)(w & 0xFF);
|
||||
buf512[i * 2 + 1] = (u8)((w >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ata_write_sector(u32 lba, const u8 *buf512) {
|
||||
int i;
|
||||
|
||||
if (!buf512) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ata_select_drive_lba(lba) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
outb(ATA_IO_BASE + ATA_REG_SECCOUNT0, 1);
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA0, (unsigned char)(lba & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA1, (unsigned char)((lba >> 8) & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_LBA2, (unsigned char)((lba >> 16) & 0xFF));
|
||||
outb(ATA_IO_BASE + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
|
||||
|
||||
if (ata_wait_drq() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 256; ++i) {
|
||||
unsigned short w = (unsigned short)buf512[i * 2] | ((unsigned short)buf512[i * 2 + 1] << 8);
|
||||
outw(ATA_IO_BASE + ATA_REG_DATA, w);
|
||||
}
|
||||
|
||||
outb(ATA_IO_BASE + ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
|
||||
if (ata_wait_not_busy() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fs_reset_memory(void) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ASTERFS_MAX_FILES; ++i) {
|
||||
nodes[i].name[0] = '\0';
|
||||
nodes[i].is_dir = 0;
|
||||
nodes[i].size = 0;
|
||||
}
|
||||
|
||||
nodes_used = 0;
|
||||
}
|
||||
|
||||
static void fs_encode_nodes(u8 *out) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ASTERFS_MAX_FILES; ++i) {
|
||||
asterfs_disk_node_t d;
|
||||
usize off = (usize)i * ASTERFS_NODE_BYTES;
|
||||
|
||||
aster_memset(&d, 0, sizeof(d));
|
||||
aster_memcpy(d.name, nodes[i].name, ASTERFS_NAME_LEN);
|
||||
d.is_dir = nodes[i].is_dir;
|
||||
d.size = nodes[i].size;
|
||||
aster_memcpy(d.data, nodes[i].data, ASTERFS_DATA_LEN);
|
||||
|
||||
aster_memcpy(out + off, &d, ASTERFS_NODE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
static void fs_decode_nodes(const u8 *in) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ASTERFS_MAX_FILES; ++i) {
|
||||
asterfs_disk_node_t d;
|
||||
usize off = (usize)i * ASTERFS_NODE_BYTES;
|
||||
|
||||
aster_memcpy(&d, in + off, ASTERFS_NODE_BYTES);
|
||||
|
||||
aster_memset(nodes[i].name, 0, ASTERFS_NAME_LEN);
|
||||
aster_memcpy(nodes[i].name, d.name, ASTERFS_NAME_LEN);
|
||||
nodes[i].is_dir = d.is_dir;
|
||||
nodes[i].size = d.size;
|
||||
if (nodes[i].size > ASTERFS_DATA_LEN) {
|
||||
nodes[i].size = ASTERFS_DATA_LEN;
|
||||
}
|
||||
aster_memcpy(nodes[i].data, d.data, ASTERFS_DATA_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
static int fs_flush_disk(void) {
|
||||
asterfs_superblock_t super;
|
||||
unsigned int sector;
|
||||
|
||||
if (!disk_ready) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aster_memset(&super, 0, sizeof(super));
|
||||
aster_memcpy(super.magic, ASTERFS_MAGIC, 8);
|
||||
super.version = ASTERFS_VERSION;
|
||||
super.nodes_used = (u32)nodes_used;
|
||||
|
||||
aster_memset(g_sector_buffer, 0, sizeof(g_sector_buffer));
|
||||
aster_memcpy(g_sector_buffer, &super, sizeof(super));
|
||||
if (ata_write_sector(ASTERFS_DISK_START_LBA, g_sector_buffer) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fs_encode_nodes(g_nodes_blob);
|
||||
for (sector = 0; sector < ASTERFS_NODE_SECTORS; ++sector) {
|
||||
usize off = (usize)sector * 512;
|
||||
aster_memset(g_sector_buffer, 0, sizeof(g_sector_buffer));
|
||||
if (off < sizeof(g_nodes_blob)) {
|
||||
usize left = sizeof(g_nodes_blob) - off;
|
||||
usize chunk = left < 512 ? left : 512;
|
||||
aster_memcpy(g_sector_buffer, g_nodes_blob + off, chunk);
|
||||
}
|
||||
|
||||
if (ata_write_sector(ASTERFS_DISK_START_LBA + 1U + sector, g_sector_buffer) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fs_load_disk(void) {
|
||||
asterfs_superblock_t super;
|
||||
unsigned int sector;
|
||||
|
||||
if (!disk_ready) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ata_read_sector(ASTERFS_DISK_START_LBA, g_sector_buffer) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aster_memcpy(&super, g_sector_buffer, sizeof(super));
|
||||
|
||||
if (aster_strncmp(super.magic, ASTERFS_MAGIC, 8) != 0 || super.version != ASTERFS_VERSION) {
|
||||
fs_reset_memory();
|
||||
return fs_flush_disk();
|
||||
}
|
||||
|
||||
for (sector = 0; sector < ASTERFS_NODE_SECTORS; ++sector) {
|
||||
usize off = (usize)sector * 512;
|
||||
if (ata_read_sector(ASTERFS_DISK_START_LBA + 1U + sector, g_sector_buffer) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (off < sizeof(g_nodes_blob)) {
|
||||
usize left = sizeof(g_nodes_blob) - off;
|
||||
usize chunk = left < 512 ? left : 512;
|
||||
aster_memcpy(g_nodes_blob + off, g_sector_buffer, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
fs_decode_nodes(g_nodes_blob);
|
||||
if (super.nodes_used > ASTERFS_MAX_FILES) {
|
||||
nodes_used = ASTERFS_MAX_FILES;
|
||||
} else {
|
||||
nodes_used = (int)super.nodes_used;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_root(const char *path) {
|
||||
return path && path[0] == '/' && path[1] == '\0';
|
||||
}
|
||||
|
||||
static int path_parent_exists(const char *path) {
|
||||
int i;
|
||||
int last = -1;
|
||||
char parent[ASTERFS_NAME_LEN];
|
||||
|
||||
if (!path || path[0] != '/') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; path[i] != '\0'; ++i) {
|
||||
if (path[i] == '/') {
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (last <= 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((usize)last >= ASTERFS_NAME_LEN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aster_memcpy(parent, path, (usize)last);
|
||||
parent[last] = '\0';
|
||||
|
||||
for (i = 0; i < nodes_used; ++i) {
|
||||
if (nodes[i].is_dir && aster_strcmp(nodes[i].name, parent) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int path_is_child(const char *parent, const char *path, const char **leaf_start) {
|
||||
usize parent_len;
|
||||
const char *rest;
|
||||
const char *p;
|
||||
|
||||
if (!parent || !path || path[0] != '/') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_root(parent)) {
|
||||
if (path[0] != '/' || path[1] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rest = path + 1;
|
||||
p = rest;
|
||||
while (*p && *p != '/') {
|
||||
++p;
|
||||
}
|
||||
|
||||
if (*p != '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*leaf_start = rest;
|
||||
return 1;
|
||||
}
|
||||
|
||||
parent_len = aster_strlen(parent);
|
||||
if (aster_strncmp(path, parent, parent_len) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (path[parent_len] != '/') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rest = path + parent_len + 1;
|
||||
if (*rest == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = rest;
|
||||
while (*p && *p != '/') {
|
||||
++p;
|
||||
}
|
||||
|
||||
if (*p != '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*leaf_start = rest;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void storage_init(void) {
|
||||
asterfs_init();
|
||||
}
|
||||
|
||||
void asterfs_init(void) {
|
||||
fs_reset_memory();
|
||||
|
||||
disk_ready = (ata_probe_slave() == 0);
|
||||
if (!disk_ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fs_load_disk() != 0) {
|
||||
fs_reset_memory();
|
||||
(void)fs_flush_disk();
|
||||
}
|
||||
}
|
||||
|
||||
static int find_node(const char *name) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nodes_used; ++i) {
|
||||
if (aster_strcmp(nodes[i].name, name) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int asterfs_create_file(const char *name) {
|
||||
usize len;
|
||||
|
||||
if (!name || name[0] != '/' || nodes_used >= ASTERFS_MAX_FILES) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!path_parent_exists(name)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (find_node(name) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = aster_strlen(name);
|
||||
if (len >= ASTERFS_NAME_LEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aster_memset(nodes[nodes_used].name, 0, ASTERFS_NAME_LEN);
|
||||
aster_memcpy(nodes[nodes_used].name, name, len);
|
||||
nodes[nodes_used].size = 0;
|
||||
nodes[nodes_used].is_dir = 0;
|
||||
++nodes_used;
|
||||
(void)fs_flush_disk();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asterfs_create_dir(const char *name) {
|
||||
usize len;
|
||||
|
||||
if (!name || name[0] != '/' || is_root(name) || nodes_used >= ASTERFS_MAX_FILES) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!path_parent_exists(name)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (find_node(name) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = aster_strlen(name);
|
||||
if (len >= ASTERFS_NAME_LEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
aster_memset(nodes[nodes_used].name, 0, ASTERFS_NAME_LEN);
|
||||
aster_memcpy(nodes[nodes_used].name, name, len);
|
||||
nodes[nodes_used].size = 0;
|
||||
nodes[nodes_used].is_dir = 1;
|
||||
++nodes_used;
|
||||
(void)fs_flush_disk();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asterfs_remove_file(const char *name) {
|
||||
int idx;
|
||||
int i;
|
||||
|
||||
idx = find_node(name);
|
||||
if (idx < 0 || nodes[idx].is_dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = idx; i < nodes_used - 1; ++i) {
|
||||
nodes[i] = nodes[i + 1];
|
||||
}
|
||||
|
||||
--nodes_used;
|
||||
(void)fs_flush_disk();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asterfs_remove_dir(const char *name) {
|
||||
int idx;
|
||||
int i;
|
||||
const char *leaf;
|
||||
|
||||
if (is_root(name)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
idx = find_node(name);
|
||||
if (idx < 0 || !nodes[idx].is_dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nodes_used; ++i) {
|
||||
if (i == idx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path_is_child(name, nodes[i].name, &leaf)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = idx; i < nodes_used - 1; ++i) {
|
||||
nodes[i] = nodes[i + 1];
|
||||
}
|
||||
|
||||
--nodes_used;
|
||||
(void)fs_flush_disk();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asterfs_write_file(const char *name, const u8 *data, u16 len) {
|
||||
int idx;
|
||||
|
||||
if (!name || !data || name[0] != '/') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
idx = find_node(name);
|
||||
if (idx < 0) {
|
||||
if (asterfs_create_file(name) != 0) {
|
||||
return -1;
|
||||
}
|
||||
idx = find_node(name);
|
||||
}
|
||||
|
||||
if (idx < 0 || nodes[idx].is_dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len > ASTERFS_DATA_LEN) {
|
||||
len = ASTERFS_DATA_LEN;
|
||||
}
|
||||
|
||||
aster_memset(nodes[idx].data, 0, ASTERFS_DATA_LEN);
|
||||
aster_memcpy(nodes[idx].data, data, len);
|
||||
nodes[idx].size = len;
|
||||
(void)fs_flush_disk();
|
||||
return len;
|
||||
}
|
||||
|
||||
int asterfs_read_file(const char *name, u8 *out, u16 max_len) {
|
||||
int idx;
|
||||
u16 len;
|
||||
|
||||
if (!name || !out || name[0] != '/') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
idx = find_node(name);
|
||||
if (idx < 0 || nodes[idx].is_dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = nodes[idx].size;
|
||||
if (len > max_len) {
|
||||
len = max_len;
|
||||
}
|
||||
|
||||
aster_memcpy(out, nodes[idx].data, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
void asterfs_list(void (*cb)(const char *name, u8 is_dir, u16 size)) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nodes_used; ++i) {
|
||||
cb(nodes[i].name, nodes[i].is_dir, nodes[i].size);
|
||||
}
|
||||
}
|
||||
|
||||
int asterfs_get_type(const char *name) {
|
||||
int idx;
|
||||
|
||||
if (is_root(name)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
idx = find_node(name);
|
||||
if (idx < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nodes[idx].is_dir ? 1 : 0;
|
||||
}
|
||||
|
||||
void asterfs_list_dir(const char *path, void (*cb)(const char *name, u8 is_dir, u16 size)) {
|
||||
int i;
|
||||
const char *leaf = 0;
|
||||
char temp[ASTERFS_NAME_LEN];
|
||||
usize n;
|
||||
|
||||
for (i = 0; i < nodes_used; ++i) {
|
||||
if (!path_is_child(path, nodes[i].name, &leaf)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
n = aster_strlen(leaf);
|
||||
if (n >= ASTERFS_NAME_LEN) {
|
||||
n = ASTERFS_NAME_LEN - 1;
|
||||
}
|
||||
|
||||
aster_memcpy(temp, leaf, n);
|
||||
temp[n] = '\0';
|
||||
cb(temp, nodes[i].is_dir, nodes[i].size);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor implementuje ovladac PIT casovace.
|
||||
* Nastavuje frekvenci hardwaroveho casovace, drzi globalni citac tiku
|
||||
* a poskytuje blokujici uspani jadra na zadany pocet milisekund.
|
||||
*/
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
static volatile unsigned long g_ticks = 0;
|
||||
|
||||
static inline unsigned long long read_tsc(void) {
|
||||
unsigned int lo;
|
||||
unsigned int hi;
|
||||
__asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi));
|
||||
return ((unsigned long long)hi << 32) | (unsigned long long)lo;
|
||||
}
|
||||
|
||||
static int interrupts_enabled(void) {
|
||||
unsigned long long rflags;
|
||||
__asm__ volatile ("pushfq; popq %0" : "=r"(rflags));
|
||||
return (rflags & (1ULL << 9)) != 0;
|
||||
}
|
||||
|
||||
static void busy_delay_ms(unsigned long ms) {
|
||||
unsigned long long start = read_tsc();
|
||||
unsigned long long wait_cycles = (unsigned long long)ms * 1000000ULL;
|
||||
unsigned long long end = start + wait_cycles;
|
||||
|
||||
while (read_tsc() < end) {
|
||||
__asm__ volatile ("pause");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void outb(unsigned short port, unsigned char value) {
|
||||
__asm__ volatile ("outb %0, %1" : : "a"(value), "Nd"(port));
|
||||
}
|
||||
|
||||
void timer_init(unsigned int hz) {
|
||||
unsigned int divisor;
|
||||
|
||||
if (hz == 0) {
|
||||
hz = 100;
|
||||
}
|
||||
|
||||
divisor = 1193180U / hz;
|
||||
|
||||
outb(0x43, 0x36);
|
||||
outb(0x40, (unsigned char)(divisor & 0xFF));
|
||||
outb(0x40, (unsigned char)((divisor >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
unsigned long timer_ticks(void) {
|
||||
return g_ticks;
|
||||
}
|
||||
|
||||
void timer_tick_advance(void) {
|
||||
++g_ticks;
|
||||
}
|
||||
|
||||
void timer_sleep_ms(unsigned long ms) {
|
||||
if (!interrupts_enabled()) {
|
||||
busy_delay_ms(ms);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned long start = timer_ticks();
|
||||
unsigned long wait_ticks = (ms + 9UL) / 10UL;
|
||||
|
||||
if (wait_ticks == 0) {
|
||||
wait_ticks = 1;
|
||||
}
|
||||
|
||||
while ((timer_ticks() - start) < wait_ticks) {
|
||||
__asm__ volatile ("hlt");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalas
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento header definuje experimentalni API vrstvu pro C aplikace.
|
||||
* API je navrzene pro budouci user-space runtime, ale lze ho uz ted
|
||||
* pouzit pro jednotne volani kernel sluzeb v internich aplikacich.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_API_H
|
||||
#define ASTER_API_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define ASTER_APP_API_VERSION 1
|
||||
|
||||
typedef struct {
|
||||
u32 api_version;
|
||||
u32 reserved;
|
||||
} aster_app_context_t;
|
||||
|
||||
int aster_api_print(const char *text);
|
||||
int aster_api_clear(void);
|
||||
int aster_api_ticks(u64 *out_ticks);
|
||||
void *aster_api_alloc(usize size);
|
||||
int aster_api_file_create(const char *path);
|
||||
int aster_api_file_read(const char *path, u8 *out, u16 max_len);
|
||||
int aster_api_file_write(const char *path, const u8 *data, u16 len);
|
||||
int aster_api_process_spawn(void (*entry)(void), const char *name, u8 priority);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento header definuje datovy tvar CPU kontextu ulozeneho pri preruseni.
|
||||
* Obsahuje strukturu interrupt_frame_t a rozhrani pro inicializaci CPU,
|
||||
* nastaveni tabulek preruseni a centralni dispatch obsluhy preruseni.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_CPU_H
|
||||
#define ASTER_CPU_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
typedef struct {
|
||||
u64 r15;
|
||||
u64 r14;
|
||||
u64 r13;
|
||||
u64 r12;
|
||||
u64 r11;
|
||||
u64 r10;
|
||||
u64 r9;
|
||||
u64 r8;
|
||||
u64 rsi;
|
||||
u64 rdi;
|
||||
u64 rbp;
|
||||
u64 rdx;
|
||||
u64 rcx;
|
||||
u64 rbx;
|
||||
u64 rax;
|
||||
u64 vector;
|
||||
u64 error;
|
||||
u64 rip;
|
||||
u64 cs;
|
||||
u64 rflags;
|
||||
u64 rsp;
|
||||
u64 ss;
|
||||
} interrupt_frame_t;
|
||||
|
||||
void cpu_init(void);
|
||||
void interrupts_init(void);
|
||||
void interrupt_dispatch(interrupt_frame_t *frame);
|
||||
void cpu_halt(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento header vystavuje jednotne API pro textovy vystup v VGA rezimu.
|
||||
* Definuje operace pro inicializaci, cisteni obrazovky, zapis znaku,
|
||||
* zapis retezce a zmenu barevne palety textove konzole.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_DISPLAY_H
|
||||
#define ASTER_DISPLAY_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void display_init(void);
|
||||
void display_clear(void);
|
||||
void display_putc(char c);
|
||||
void display_write(const char *text);
|
||||
void display_set_color(u8 fg, u8 bg);
|
||||
void display_get_cursor(usize *out_row, usize *out_col);
|
||||
void display_set_cursor(usize new_row, usize new_col);
|
||||
void display_fill_row(usize target_row, char ch, u8 fg, u8 bg);
|
||||
void display_write_at(usize target_row, usize target_col, const char *text, u8 fg, u8 bg);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor slouzi jako centralni rozcestnik pro verejne ovladace jadra.
|
||||
* Drzi pohromade deklarace klavesnice, casovace a storage vrstvy,
|
||||
* aby je kernel mohl jednoduse inicializovat z jednoho mista.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_DRIVERS_H
|
||||
#define ASTER_DRIVERS_H
|
||||
|
||||
#define ASTER_KEY_F1 0x101
|
||||
#define ASTER_KEY_F2 0x102
|
||||
#define ASTER_KEY_UP 0x103
|
||||
#define ASTER_KEY_DOWN 0x104
|
||||
#define ASTER_KEY_LEFT 0x105
|
||||
#define ASTER_KEY_RIGHT 0x106
|
||||
|
||||
void keyboard_init(void);
|
||||
int keyboard_read_key(void);
|
||||
int keyboard_try_read_key(void);
|
||||
int keyboard_readline(char *buffer, int max_len);
|
||||
|
||||
void timer_init(unsigned int hz);
|
||||
unsigned long timer_ticks(void);
|
||||
|
||||
void storage_init(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento header definuje rozhrani pametoveho subsystému jadra.
|
||||
* Obsahuje API pro allocator fyzickych stranek, jednoduche heap alokace
|
||||
* a funkce pro cteni zakladnich statistik o celkove a volne pameti.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_MEMORY_H
|
||||
#define ASTER_MEMORY_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
void memory_init(void);
|
||||
void *page_alloc(void);
|
||||
void page_free(void *page);
|
||||
void *kmalloc(usize size);
|
||||
void kfree(void *ptr);
|
||||
usize memory_total_pages(void);
|
||||
usize memory_free_pages(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento header deklaruje fatalni panic cestu jadra.
|
||||
* Funkce panic je urcena pro situace, kdy nelze bezpecne pokracovat,
|
||||
* proto vypise duvod chyby a prevede system do zastaveneho stavu.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_PANIC_H
|
||||
#define ASTER_PANIC_H
|
||||
|
||||
void panic(const char *reason);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor definuje rozhrani pro kernelovy diagnosticky vystup.
|
||||
* Poskytuje jednoduche API pro vypis retezce i formatovany log,
|
||||
* ktery slouzi pro ladeni bootu, preruseni i behu kernel sluzeb.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_PRINTK_H
|
||||
#define ASTER_PRINTK_H
|
||||
|
||||
void aster_print(const char *text);
|
||||
void printk(const char *fmt, ...);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento header popisuje procesovy model jadra.
|
||||
* Obsahuje stavy procesu, kontext CPU registru a strukturu process_t,
|
||||
* plus API pro vytvareni procesu, ukonceni a pristup k procesove tabulce.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_PROCESS_H
|
||||
#define ASTER_PROCESS_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define ASTER_MAX_PROCESSES 16
|
||||
|
||||
typedef enum {
|
||||
PROCESS_UNUSED = 0,
|
||||
PROCESS_READY,
|
||||
PROCESS_RUNNING,
|
||||
PROCESS_BLOCKED,
|
||||
PROCESS_EXITED
|
||||
} process_state_t;
|
||||
|
||||
typedef struct {
|
||||
u64 r15;
|
||||
u64 r14;
|
||||
u64 r13;
|
||||
u64 r12;
|
||||
u64 rbx;
|
||||
u64 rbp;
|
||||
u64 rip;
|
||||
} context_t;
|
||||
|
||||
typedef struct {
|
||||
u32 pid;
|
||||
process_state_t state;
|
||||
u8 priority;
|
||||
u8 *stack_base;
|
||||
u64 stack_top;
|
||||
context_t context;
|
||||
const char *name;
|
||||
} process_t;
|
||||
|
||||
void process_init(void);
|
||||
process_t *process_create(const char *name, void (*entry)(void), u8 priority);
|
||||
void process_exit(void);
|
||||
process_t *process_current(void);
|
||||
void process_set_current(process_t *p);
|
||||
process_t *process_table(void);
|
||||
usize process_count(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor deklaruje planovac, ktery rozhoduje o prideleni CPU casu.
|
||||
* Popisuje vstupni body pro inicializaci, periodicky tick a prepnuti,
|
||||
* a funkce pro vyber dalsiho kandidata k vykonu.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_SCHEDULER_H
|
||||
#define ASTER_SCHEDULER_H
|
||||
|
||||
#include "process.h"
|
||||
|
||||
void scheduler_init(void);
|
||||
void scheduler_tick(void);
|
||||
void scheduler_run_once(void);
|
||||
void scheduler_yield(void);
|
||||
process_t *scheduler_pick_next(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento header definuje verejne rozhrani jednoducheho AsterFS.
|
||||
* Popisuje format uzlu souboru nebo adresare a deklaruje operace
|
||||
* pro inicializaci, vytvareni, cteni, zapis a vypis obsahu.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_STORAGE_H
|
||||
#define ASTER_STORAGE_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define ASTERFS_MAX_FILES 64
|
||||
#define ASTERFS_NAME_LEN 64
|
||||
#define ASTERFS_DATA_LEN 512
|
||||
|
||||
typedef struct {
|
||||
char name[ASTERFS_NAME_LEN];
|
||||
u8 is_dir;
|
||||
u16 size;
|
||||
u8 data[ASTERFS_DATA_LEN];
|
||||
} asterfs_node_t;
|
||||
|
||||
void asterfs_init(void);
|
||||
int asterfs_create_file(const char *name);
|
||||
int asterfs_create_dir(const char *name);
|
||||
int asterfs_remove_file(const char *name);
|
||||
int asterfs_remove_dir(const char *name);
|
||||
int asterfs_write_file(const char *name, const u8 *data, u16 len);
|
||||
int asterfs_read_file(const char *name, u8 *out, u16 max_len);
|
||||
int asterfs_get_type(const char *name);
|
||||
void asterfs_list(void (*cb)(const char *name, u8 is_dir, u16 size));
|
||||
void asterfs_list_dir(const char *path, void (*cb)(const char *name, u8 is_dir, u16 size));
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor deklaruje minimalisticke utility bez zavislosti na libc.
|
||||
* Obsahuje zakladni operace nad retezci a pameti, ktere kernel potrebuje
|
||||
* uz v ranych fazich bootu, kdy standardni knihovny nejsou dostupne.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_STRING_H
|
||||
#define ASTER_STRING_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
usize aster_strlen(const char *s);
|
||||
int aster_strcmp(const char *a, const char *b);
|
||||
int aster_strncmp(const char *a, const char *b, usize n);
|
||||
void *aster_memcpy(void *dst, const void *src, usize n);
|
||||
void *aster_memset(void *dst, int v, usize n);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento header popisuje syscall ABI mezi user casti a jadrem.
|
||||
* Definuje cisla volani, centralni dispatcher a obalove funkce,
|
||||
* ktere mapuji uzivatelske pozadavky na kernelove sluzby.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_SYSCALL_H
|
||||
#define ASTER_SYSCALL_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define SYSCALL_WRITE 0
|
||||
#define SYSCALL_ALLOC 1
|
||||
#define SYSCALL_PROCESS_CREATE 2
|
||||
|
||||
void syscall_init(void);
|
||||
u64 syscall_dispatch(u64 id, u64 a1, u64 a2, u64 a3, u64 a4);
|
||||
|
||||
u64 aster_write(const char *text);
|
||||
void *aster_alloc(usize size);
|
||||
i64 aster_process_create(void (*entry)(void), const char *name, u8 priority);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor deklaruje casovaci vrstvu nad PIT hardwarem.
|
||||
* Zahrnuje inicializaci frekvence, cteni tiku, inkrement z ISR
|
||||
* a blokujici uspani jadra na zadany pocet milisekund.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_TIMER_H
|
||||
#define ASTER_TIMER_H
|
||||
|
||||
void timer_init(unsigned int hz);
|
||||
unsigned long timer_ticks(void);
|
||||
void timer_tick_advance(void);
|
||||
void timer_sleep_ms(unsigned long ms);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento header sjednocuje zakladni celoctiselne typy jadra.
|
||||
* Cilem je mit stabilni velikosti typu nezavisle na kompilatoru
|
||||
* a pouzivat je konzistentne ve vsech kernel subsystémech.
|
||||
*/
|
||||
|
||||
#ifndef ASTER_TYPES_H
|
||||
#define ASTER_TYPES_H
|
||||
|
||||
typedef unsigned long long u64;
|
||||
typedef signed long long i64;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int i32;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short i16;
|
||||
typedef unsigned char u8;
|
||||
typedef signed char i8;
|
||||
typedef unsigned long usize;
|
||||
typedef long isize;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalas
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor implementuje bridge mezi API pro aplikace a jadrem.
|
||||
* Dnes funguje jako tenka vrstva nad kernel sluzbami a syscall API,
|
||||
* aby bylo mozne na stejnem rozhrani pozdeji stavet user-space runtime.
|
||||
*/
|
||||
|
||||
#include "aster_api.h"
|
||||
#include "display.h"
|
||||
#include "storage.h"
|
||||
#include "syscall.h"
|
||||
#include "timer.h"
|
||||
|
||||
int aster_api_print(const char *text) {
|
||||
return (int)aster_write(text);
|
||||
}
|
||||
|
||||
int aster_api_clear(void) {
|
||||
display_clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aster_api_ticks(u64 *out_ticks) {
|
||||
if (!out_ticks) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out_ticks = (u64)timer_ticks();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *aster_api_alloc(usize size) {
|
||||
return aster_alloc(size);
|
||||
}
|
||||
|
||||
int aster_api_file_create(const char *path) {
|
||||
return asterfs_create_file(path);
|
||||
}
|
||||
|
||||
int aster_api_file_read(const char *path, u8 *out, u16 max_len) {
|
||||
return asterfs_read_file(path, out, max_len);
|
||||
}
|
||||
|
||||
int aster_api_file_write(const char *path, const u8 *data, u16 len) {
|
||||
return asterfs_write_file(path, data, len);
|
||||
}
|
||||
|
||||
int aster_api_process_spawn(void (*entry)(void), const char *name, u8 priority) {
|
||||
return (int)aster_process_create(entry, name, priority);
|
||||
}
|
||||
+2959
File diff suppressed because it is too large
Load Diff
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento modul realizuje dve vrstvy spravy pameti jadra.
|
||||
* Prvni cast je bitmap allocator fyzickych stranek pro page_alloc/page_free,
|
||||
* druha cast je jednoducha linearni heap alokace pro kernelove objekty.
|
||||
*/
|
||||
|
||||
#include "memory.h"
|
||||
#include "string.h"
|
||||
|
||||
#define PMM_MAX_MEMORY (64ULL * 1024ULL * 1024ULL)
|
||||
#define PMM_MAX_PAGES (PMM_MAX_MEMORY / PAGE_SIZE)
|
||||
#define PMM_BITMAP_SIZE (PMM_MAX_PAGES / 8)
|
||||
|
||||
static u8 page_bitmap[PMM_BITMAP_SIZE];
|
||||
static usize free_pages = PMM_MAX_PAGES;
|
||||
|
||||
#define KHEAP_SIZE (1024 * 1024)
|
||||
static u8 kernel_heap[KHEAP_SIZE];
|
||||
static usize heap_offset = 0;
|
||||
|
||||
static void bitmap_set(usize idx) {
|
||||
page_bitmap[idx / 8] |= (u8)(1u << (idx % 8));
|
||||
}
|
||||
|
||||
static void bitmap_clear(usize idx) {
|
||||
page_bitmap[idx / 8] &= (u8)~(1u << (idx % 8));
|
||||
}
|
||||
|
||||
static int bitmap_test(usize idx) {
|
||||
return (page_bitmap[idx / 8] & (u8)(1u << (idx % 8))) != 0;
|
||||
}
|
||||
|
||||
void memory_init(void) {
|
||||
aster_memset(page_bitmap, 0, PMM_BITMAP_SIZE);
|
||||
free_pages = PMM_MAX_PAGES;
|
||||
heap_offset = 0;
|
||||
|
||||
for (usize i = 0; i < 256; ++i) {
|
||||
bitmap_set(i);
|
||||
--free_pages;
|
||||
}
|
||||
}
|
||||
|
||||
void *page_alloc(void) {
|
||||
usize i;
|
||||
|
||||
for (i = 0; i < PMM_MAX_PAGES; ++i) {
|
||||
if (!bitmap_test(i)) {
|
||||
bitmap_set(i);
|
||||
--free_pages;
|
||||
return (void *)(i * PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void page_free(void *page) {
|
||||
usize idx;
|
||||
|
||||
if (!page) {
|
||||
return;
|
||||
}
|
||||
|
||||
idx = ((usize)page) / PAGE_SIZE;
|
||||
if (idx < PMM_MAX_PAGES && bitmap_test(idx)) {
|
||||
bitmap_clear(idx);
|
||||
++free_pages;
|
||||
}
|
||||
}
|
||||
|
||||
void *kmalloc(usize size) {
|
||||
usize aligned;
|
||||
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aligned = (size + 15) & ~((usize)15);
|
||||
if (heap_offset + aligned > KHEAP_SIZE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ptr = &kernel_heap[heap_offset];
|
||||
heap_offset += aligned;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void kfree(void *ptr) {
|
||||
(void)ptr;
|
||||
}
|
||||
|
||||
usize memory_total_pages(void) {
|
||||
return PMM_MAX_PAGES;
|
||||
}
|
||||
|
||||
usize memory_free_pages(void) {
|
||||
return free_pages;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor drzi nouzovou cestu pro neobnovitelne chyby jadra.
|
||||
* Pri volani panic prepne vizualni styl vystupu, vypise duvod selhani
|
||||
* a bezpecne zastavi CPU, aby se predeslo dalsimu poskozeni stavu.
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "display.h"
|
||||
#include "panic.h"
|
||||
#include "printk.h"
|
||||
|
||||
void panic(const char *reason) {
|
||||
display_set_color(0x0F, 0x04);
|
||||
printk("\n[KERNEL PANIC] %s\n", reason ? reason : "unknown");
|
||||
cpu_halt();
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento modul implementuje tiskovou vrstvu kernelu nad VGA vystupem.
|
||||
* Obsahuje podporu zakladnich formatovacich specifikatoru pro ladeni
|
||||
* a jednotne API, ktere pouzivaji ostatni casti jadra pri logovani.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "printk.h"
|
||||
|
||||
static void print_u64(unsigned long long value, unsigned int base) {
|
||||
char buf[32];
|
||||
const char *digits = "0123456789abcdef";
|
||||
int i = 0;
|
||||
|
||||
if (value == 0) {
|
||||
display_putc('0');
|
||||
return;
|
||||
}
|
||||
|
||||
while (value > 0 && i < (int)sizeof(buf)) {
|
||||
buf[i++] = digits[value % base];
|
||||
value /= base;
|
||||
}
|
||||
|
||||
while (i > 0) {
|
||||
display_putc(buf[--i]);
|
||||
}
|
||||
}
|
||||
|
||||
void aster_print(const char *text) {
|
||||
display_write(text);
|
||||
}
|
||||
|
||||
void printk(const char *fmt, ...) {
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
while (*fmt) {
|
||||
if (*fmt != '%') {
|
||||
display_putc(*fmt++);
|
||||
continue;
|
||||
}
|
||||
|
||||
++fmt;
|
||||
switch (*fmt) {
|
||||
case '%':
|
||||
display_putc('%');
|
||||
break;
|
||||
case 'c': {
|
||||
int c = va_arg(args, int);
|
||||
display_putc((char)c);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
const char *s = va_arg(args, const char *);
|
||||
if (!s) {
|
||||
s = "(null)";
|
||||
}
|
||||
display_write(s);
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
long v = va_arg(args, int);
|
||||
if (v < 0) {
|
||||
display_putc('-');
|
||||
print_u64((unsigned long long)(-v), 10);
|
||||
} else {
|
||||
print_u64((unsigned long long)v, 10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
unsigned int v = va_arg(args, unsigned int);
|
||||
print_u64(v, 10);
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
unsigned int v = va_arg(args, unsigned int);
|
||||
print_u64(v, 16);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
unsigned long long v = (unsigned long long)va_arg(args, void *);
|
||||
display_write("0x");
|
||||
print_u64(v, 16);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
display_putc('?');
|
||||
break;
|
||||
}
|
||||
++fmt;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor spravuje zivotni cyklus kernel procesu.
|
||||
* Implementuje statickou procesovou tabulku, prirazovani PID,
|
||||
* pripravu stacku a zakladni operace pro vytvoreni a ukonceni procesu.
|
||||
*/
|
||||
|
||||
#include "memory.h"
|
||||
#include "process.h"
|
||||
#include "string.h"
|
||||
|
||||
#define PROCESS_STACK_SIZE (16 * 1024)
|
||||
|
||||
static process_t g_processes[ASTER_MAX_PROCESSES];
|
||||
static usize g_process_count = 0;
|
||||
static u32 g_next_pid = 1;
|
||||
static process_t *g_current = 0;
|
||||
|
||||
void process_init(void) {
|
||||
usize i;
|
||||
|
||||
for (i = 0; i < ASTER_MAX_PROCESSES; ++i) {
|
||||
g_processes[i].pid = 0;
|
||||
g_processes[i].state = PROCESS_UNUSED;
|
||||
g_processes[i].priority = 0;
|
||||
g_processes[i].stack_base = 0;
|
||||
g_processes[i].stack_top = 0;
|
||||
g_processes[i].name = 0;
|
||||
g_processes[i].context.rip = 0;
|
||||
}
|
||||
|
||||
g_process_count = 0;
|
||||
g_current = 0;
|
||||
}
|
||||
|
||||
process_t *process_create(const char *name, void (*entry)(void), u8 priority) {
|
||||
process_t *p;
|
||||
void *stack;
|
||||
|
||||
if (g_process_count >= ASTER_MAX_PROCESSES || !entry) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = &g_processes[g_process_count];
|
||||
stack = kmalloc(PROCESS_STACK_SIZE);
|
||||
if (!stack) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p->pid = g_next_pid++;
|
||||
p->state = PROCESS_READY;
|
||||
p->priority = priority;
|
||||
p->stack_base = (u8 *)stack;
|
||||
p->stack_top = (u64)(p->stack_base + PROCESS_STACK_SIZE - 16);
|
||||
p->name = name;
|
||||
|
||||
aster_memset(&p->context, 0, sizeof(context_t));
|
||||
p->context.rip = (u64)entry;
|
||||
p->context.rbp = p->stack_top;
|
||||
|
||||
++g_process_count;
|
||||
return p;
|
||||
}
|
||||
|
||||
void process_exit(void) {
|
||||
if (g_current) {
|
||||
g_current->state = PROCESS_EXITED;
|
||||
}
|
||||
}
|
||||
|
||||
process_t *process_current(void) {
|
||||
return g_current;
|
||||
}
|
||||
|
||||
void process_set_current(process_t *p) {
|
||||
g_current = p;
|
||||
}
|
||||
|
||||
process_t *process_table(void) {
|
||||
return &g_processes[0];
|
||||
}
|
||||
|
||||
usize process_count(void) {
|
||||
return g_process_count;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento modul implementuje jednoduchy planovac typu round-robin.
|
||||
* Reaguje na tiky casovace, vybira dalsi bezici proces
|
||||
* a ridi prepinani mezi stavy READY, RUNNING a EXITED.
|
||||
*/
|
||||
|
||||
#include "process.h"
|
||||
#include "scheduler.h"
|
||||
#include "timer.h"
|
||||
|
||||
static usize g_current_index = 0;
|
||||
static unsigned long g_quantum_ticks = 0;
|
||||
|
||||
void scheduler_init(void) {
|
||||
g_current_index = 0;
|
||||
g_quantum_ticks = 0;
|
||||
}
|
||||
|
||||
process_t *scheduler_pick_next(void) {
|
||||
process_t *table = process_table();
|
||||
usize count = process_count();
|
||||
usize i;
|
||||
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
usize idx = (g_current_index + i + 1) % count;
|
||||
if (table[idx].state == PROCESS_READY || table[idx].state == PROCESS_RUNNING) {
|
||||
g_current_index = idx;
|
||||
return &table[idx];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void scheduler_run_once(void) {
|
||||
process_t *next = scheduler_pick_next();
|
||||
|
||||
if (!next) {
|
||||
return;
|
||||
}
|
||||
|
||||
process_set_current(next);
|
||||
if (next->state == PROCESS_READY) {
|
||||
void (*entry)(void) = (void (*)(void))next->context.rip;
|
||||
next->state = PROCESS_RUNNING;
|
||||
entry();
|
||||
if (next->state == PROCESS_RUNNING) {
|
||||
next->state = PROCESS_EXITED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scheduler_yield(void) {
|
||||
process_t *current = process_current();
|
||||
|
||||
if (current && current->state == PROCESS_RUNNING) {
|
||||
current->state = PROCESS_READY;
|
||||
}
|
||||
|
||||
scheduler_run_once();
|
||||
}
|
||||
|
||||
void scheduler_tick(void) {
|
||||
timer_tick_advance();
|
||||
++g_quantum_ticks;
|
||||
|
||||
if ((g_quantum_ticks % 5) == 0) {
|
||||
scheduler_run_once();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento soubor obsahuje vlastni implementace zakladnich utility funkci.
|
||||
* Kernel je pouziva misto standardni knihovny pro strlen, strcmp,
|
||||
* memcpy a memset, aby zustal freestanding a plne pod kontrolou jadra.
|
||||
*/
|
||||
|
||||
#include "string.h"
|
||||
|
||||
usize aster_strlen(const char *s) {
|
||||
usize n = 0;
|
||||
while (s && s[n]) {
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int aster_strcmp(const char *a, const char *b) {
|
||||
while (*a && (*a == *b)) {
|
||||
++a;
|
||||
++b;
|
||||
}
|
||||
return (unsigned char)*a - (unsigned char)*b;
|
||||
}
|
||||
|
||||
int aster_strncmp(const char *a, const char *b, usize n) {
|
||||
usize i;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (a[i] != b[i] || a[i] == '\0' || b[i] == '\0') {
|
||||
return (unsigned char)a[i] - (unsigned char)b[i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *aster_memcpy(void *dst, const void *src, usize n) {
|
||||
usize i;
|
||||
u8 *d = (u8 *)dst;
|
||||
const u8 *s = (const u8 *)src;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
d[i] = s[i];
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void *aster_memset(void *dst, int v, usize n) {
|
||||
usize i;
|
||||
u8 *d = (u8 *)dst;
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
d[i] = (u8)v;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Tento modul je vstupni brana pro systemova volani.
|
||||
* Obsahuje dispatcher, ktery podle ID vola konkretni kernelovou sluzbu,
|
||||
* a implementuje zakladni syscall operace pro zapis, alokaci a procesy.
|
||||
*/
|
||||
|
||||
#include "memory.h"
|
||||
#include "printk.h"
|
||||
#include "process.h"
|
||||
#include "syscall.h"
|
||||
|
||||
void syscall_init(void) {
|
||||
}
|
||||
|
||||
u64 syscall_dispatch(u64 id, u64 a1, u64 a2, u64 a3, u64 a4) {
|
||||
(void)a3;
|
||||
(void)a4;
|
||||
|
||||
switch (id) {
|
||||
case SYSCALL_WRITE:
|
||||
return aster_write((const char *)a1);
|
||||
case SYSCALL_ALLOC:
|
||||
return (u64)aster_alloc((usize)a1);
|
||||
case SYSCALL_PROCESS_CREATE:
|
||||
return (u64)aster_process_create((void (*)(void))a1, (const char *)a2, (u8)a3);
|
||||
default:
|
||||
return (u64)-1;
|
||||
}
|
||||
}
|
||||
|
||||
u64 aster_write(const char *text) {
|
||||
if (!text) {
|
||||
return (u64)-1;
|
||||
}
|
||||
|
||||
printk("%s", text);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *aster_alloc(usize size) {
|
||||
return kmalloc(size);
|
||||
}
|
||||
|
||||
i64 aster_process_create(void (*entry)(void), const char *name, u8 priority) {
|
||||
process_t *p = process_create(name, entry, priority);
|
||||
if (!p) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return p->pid;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* AsterOS Kernel
|
||||
* Autor: Pavel Kalaš
|
||||
* Rok: 2026
|
||||
*
|
||||
* Vytvořeno jen tak z nudy a z chuti zkoušet nové věci.
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT(elf64-x86-64)
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x00100000;
|
||||
|
||||
.text :
|
||||
{
|
||||
*(.text.boot)
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata*)
|
||||
}
|
||||
|
||||
.data :
|
||||
{
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
.bss :
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user