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