Compare commits

..

54 commits

Author SHA1 Message Date
59d3db9738
readme warning 2023-08-22 00:29:21 +02:00
77fd0ce4cf
towards 0.10 2023-08-22 00:27:27 +02:00
08d447115d
towards 0.10.1 2023-08-06 15:38:23 +02:00
e392799b91 wip: std.mem.allocator 2020-02-07 15:28:22 +01:00
50ac72e24b modify build.zig for 0.6.0 2020-02-05 21:20:25 +01:00
22f05324fa zig: 0.5.0 -> 0.5.0+e6a812c82 2020-02-02 18:24:48 +01:00
8380a657ac ide: driver works, ready to start ext2 2020-02-01 15:07:09 +01:00
635134b1e3 ide: refactor 2020-01-31 23:42:19 +01:00
bc46a138ff ide: cleanup, still PF at rep insw 2020-01-13 23:07:05 +01:00
2db4f9899a driver: looks like zig UB, going to upgrade to master 2020-01-12 00:11:25 +01:00
7483995316 driver: separate subtree 2020-01-11 12:43:21 +01:00
b1c76c405b console: refactor to print available commands 2020-01-07 17:47:56 +01:00
c3dcb449cb closes #2 2020-01-07 16:49:39 +01:00
e1dbcf80da task: fix potential mem leak 2020-01-06 21:48:40 +01:00
8f3aea077a readme: cleanup 2020-01-06 21:19:17 +01:00
b7fe260871 main: cleanup, terminate boot task when done 2020-01-06 21:15:11 +01:00
276e73a198 ide: polling, read_sectors 2020-01-06 21:09:39 +01:00
5718710584 readme: add screenshot 2020-01-06 21:09:08 +01:00
0269925bf4 qemu.sh: fix suicide 2020-01-06 19:38:33 +01:00
b9ff019c39 readme: add reference to zen kernel 2020-01-06 17:27:12 +01:00
7bb65b0395
Merge pull request #1 from nuetoban/master
Add Shebang
2020-01-06 17:25:27 +01:00
Viktor
bf78080782 Add Shebang 2020-01-06 17:30:17 +03:00
9887fba6a9 readme: update 2020-01-06 15:09:36 +01:00
88279f3f0d ide: first commit 2020-01-05 20:35:21 +01:00
0307afe365 task auto termination when done 2020-01-02 17:57:06 +01:00
a01e9a5f2a add utilisation tracker 2020-01-01 22:59:01 +01:00
4148314d84 ring buffer for the keyboard 2020-01-01 21:59:05 +01:00
c64c6cbcfe Step 12: task termination 2019-12-27 16:43:26 +01:00
ad329b5f81 Step 11: Preemptive scheduler 2019-12-27 11:02:29 +01:00
2a37ac201e Step 8: for real this time 2019-12-18 19:39:32 +01:00
78f7197cb9 Step 6: for real this time 2019-12-18 18:56:27 +01:00
a8c68611ce step 10: idle mode 2019-12-15 21:36:49 +01:00
e30f016977 some cleanup before the rest of the tutorial 2019-12-15 19:38:42 +01:00
9ec23055bc Step 7+8+9: usleep(), DeltaQueue 2019-12-15 19:38:37 +01:00
a527695202 Step 6: block/unblock tasks 2019-12-15 13:35:58 +01:00
f2d2ab867e Step 5: lock/unlock scheduler 2019-12-15 13:35:18 +01:00
b3d8b7abc7 Step 4: task states 2019-12-15 11:46:47 +01:00
27e6f2684b Step 3: time_used 2019-12-15 11:14:10 +01:00
ec7ee599a1 Step 2: schedule() 2019-12-15 01:13:00 +01:00
6af31b5b89 Brendan's Tutorial Step 1: done 2019-12-14 22:46:48 +01:00
8d7e7591e9 cleaned up some task details 2019-12-01 22:10:20 +01:00
ed5b97a87b commit before starting Brendan's multitasking tutorial 2019-11-29 21:24:32 +01:00
5880d5296e cleaning up before kmalloc 2019-11-23 20:40:38 +01:00
902aa136c6 PIT configured, preparing for scheduling 2019-09-14 15:49:57 +02:00
bd12f0495f changes imports again... 2019-08-23 22:30:41 +02:00
bdc3b2939b compiles now, new import architecture 2019-08-22 21:59:23 +02:00
888b51282d running edge compiler, addPackagePath broken 2019-08-20 23:48:59 +02:00
9fd40142fa pci ascii header 2019-08-20 20:39:36 +02:00
28175d9336 refactor namespacing for readability 2019-08-18 23:47:37 +02:00
d051e1b0c8 added virtio, refactoring untill compiler is fixed 2019-08-18 23:16:52 +02:00
4c9f4b54d8 refactoring pci 2019-08-17 22:37:37 +02:00
07089c2ea1 paging done 2019-08-15 21:15:10 +02:00
97aa541b34 keyboard works 2019-08-13 23:28:24 +02:00
e832dede16 paging almost done, endianess problem for now 2019-08-13 22:37:05 +02:00
45 changed files with 2281 additions and 667 deletions

5
.gdbinit Normal file
View file

@ -0,0 +1,5 @@
file build/kernel
target remote localhost:4242
set arch i386
set step-mode on
directory src

1
.gitignore vendored
View file

@ -1 +1,2 @@
zig-cache zig-cache
disk.img

View file

@ -1,29 +1,49 @@
## hobby kernel in zig # hobby kernel in zig
slowly porting from rust. # WARNING: this project was written for zig 0.5, it doesn't compile for future versions
![screenshot](screenshot.png)
### features ### features
- vga frame buffer - 80x25 frame buffer
- ps2 keyboard driver - ps2 keyboard driver
- interrupts
- terminal console - terminal console
- lspci - lspci
- x86
- MMU
- interrupts
- pit timer
- scheduler
- time slice preemption with round robin
- sleep()
- block()/unblock()
- Storage
- IDE ATA driver (in progress)
### dependencies ### dependencies
`zig` compiler - [ziglang](https://github.com/ziglang/zig) 0.5.0
### compile ### compiling
`zig build` compiles and links the multiboot kernel, without a bootloader. `zig build` compiles and links the multiboot kernel (without a bootloader)
### test ### running
`./qemu.sh start` - `./qemu.sh start`
`./qemu.sh monitor` - `./qemu.sh monitor`
`./qemu.sh gdb` - `./qemu.sh quit`
- `gdb` (see provided `.gdbinit`)
### todo # Notes
- recycling allocator that wraps the bump allocator ## interrupt call chain
`interrupt` -> `idt[n]` -> `isrN` -> `isrDispatch` -> `handlers[n]` (default `unhandled()`)
## References
- zig microkernel: https://github.com/AndreaOrru/zen
- scheduling tutorial: https://wiki.osdev.org/Brendan%27s_Multi-tasking_Tutorial
- booting/paging/interrupts: https://os.phil-opp.com/

View file

@ -1,18 +1,45 @@
const Builder = @import("std").build.Builder; const Builder = @import("std").build.Builder;
const Target = @import("std").Target;
const CrossTarget = @import("std").zig.CrossTarget;
const Feature = @import("std").Target.Cpu.Feature;
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("std");
pub fn build(b: *Builder) void { pub fn build(b: *Builder) void {
const kernel = b.addExecutable("kernel", "src/arch/x86/main.zig"); const kernel = b.addExecutable("kernel", "src/main.zig");
kernel.addPackagePath("kernel", "src/index.zig"); kernel.addPackagePath("kernel", "src/index.zig");
kernel.addPackagePath("arch", "src/arch/x86/lib/index.zig"); kernel.addPackagePath("x86", "src/arch/x86/index.zig");
kernel.setOutputDir("build"); kernel.setOutputDir("build");
kernel.addAssemblyFile("src/arch/x86/_start.s"); kernel.addAssemblyFile("src/arch/x86/start.s");
kernel.addAssemblyFile("src/arch/x86/gdt.s"); kernel.addAssemblyFile("src/arch/x86/gdt.s");
kernel.addAssemblyFile("src/arch/x86/isr.s"); kernel.addAssemblyFile("src/arch/x86/isr.s");
kernel.addAssemblyFile("src/arch/x86/paging.s");
kernel.addAssemblyFile("src/arch/x86/switch_tasks.s");
// const features = Target.x86.Feature;
// var disabled_features = Feature.Set.empty;
// var enabled_features = Feature.Set.empty;
// disabled_features.addFeature(@enumToInt(features.mmx));
// disabled_features.addFeature(@enumToInt(features.sse));
// disabled_features.addFeature(@enumToInt(features.sse2));
// disabled_features.addFeature(@enumToInt(features.avx));
// disabled_features.addFeature(@enumToInt(features.avx2));
// enabled_features.addFeature(@enumToInt(features.soft_float));
const target = CrossTarget{
.cpu_arch = Target.Cpu.Arch.i386,
.os_tag = Target.Os.Tag.freestanding,
.abi = Target.Abi.none,
// .cpu_features_sub = disabled_features,
// .cpu_features_add = enabled_features
};
kernel.setTarget(target);
kernel.setBuildMode(b.standardReleaseOptions()); kernel.setBuildMode(b.standardReleaseOptions());
kernel.setTarget(builtin.Arch.i386, builtin.Os.freestanding, builtin.Abi.none); kernel.setLinkerScriptPath(.{ .path = "src/arch/x86/linker.ld" });
kernel.setLinkerScriptPath("src/arch/x86/linker.ld");
b.default_step.dependOn(&kernel.step); b.default_step.dependOn(&kernel.step);
} }

20
grub.sh
View file

@ -1,20 +0,0 @@
#!/bin/bash
exit_missing() {
printf "$_ must be installed\n" ; exit 1;
}
which xorriso || exit_missing
which grub-mkrescue || exit_missing
mkdir -p build/iso/boot/grub
cp build/kernel build/iso/boot
>build/iso/boot/grub/grub.cfg <<EOF
set timeout=0
set default=0
menuentry "OS" {
multiboot2 /boot/kernel
boot
}
EOF
grub-mkrescue -o build/kernel.iso build/iso

9
mkiso.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/bash
exit_missing() { printf "$_ must be installed\n" ; exit 1; }
which xorriso || exit_missing
which grub-mkrescue || exit_missing
mkdir -p build/iso/boot
cp build/bzImage build/iso/boot
grub-mkrescue -o build/kernel.iso build/iso

46
qemu.sh
View file

@ -1,41 +1,35 @@
#!/bin/bash
# usage: ./qemu.sh start
# ./qemu.sh quit
QEMU_SOCKET=/tmp/qemu.sock QEMU_SOCKET=/tmp/qemu.sock
QEMU_MONITOR="socat - UNIX-CONNECT:${QEMU_SOCKET}" QEMU_MONITOR="sudo socat - UNIX-CONNECT:${QEMU_SOCKET}"
QEMU_GDB_PORT=4242 QEMU_GDB_PORT=4242
KERNEL=build/kernel KERNEL=build/kernel
start() { qemu_start() {
touch disk.img
# sudo pkill -9 qemu-system-i386
sudo qemu-system-i386 \ sudo qemu-system-i386 \
-gdb tcp::${QEMU_GDB_PORT} \ -gdb tcp::${QEMU_GDB_PORT} \
-monitor unix:${QEMU_SOCKET},server,nowait \ -monitor unix:${QEMU_SOCKET},server,nowait \
-enable-kvm \ -enable-kvm \
-m 50M \
-serial mon:stdio \
-curses \ -curses \
-m 1341M \
-hda disk.img \
-kernel ${KERNEL} -kernel ${KERNEL}
# -cdrom ${KERNEL}.iso # -drive file=disk.img,if=virtio\
# -append "Hello" \ # -no-reboot \
# -S
# -device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no \ # -device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no \
# -S \
# build/kernel.iso # build/kernel.iso
"$@"
# this allows to monitor with ^a-c, but doesn't
# play nice with irqs apparently...
# -serial mon:stdio \
} }
monitor() { qemu_quit() { echo quit | sudo ${QEMU_MONITOR} >/dev/null; }
[ "$1" == "" ] && sudo ${QEMU_MONITOR} || echo "$1" | sudo ${QEMU_MONITOR} >/dev/null qemu_monitor() { sudo ${QEMU_MONITOR}; }
}
reload() { qemu_"$@"
monitor stop
# monitor "change ide1-cd0 ${KERNEL}"
monitor system_reset
monitor cont
}
gdb() {
gdb -q \
-symbols "${KERNEL}" \
-ex "target remote :${QEMU_GDB_PORT}" \
-ex "set arch i386"
}
"$@"

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View file

@ -1,5 +1,5 @@
// const tty = @import("tty.zig"); // const tty = @import("tty.zig");
const x86 = @import("lib/index.zig"); const x86 = @import("x86");
// GDT segment selectors. // GDT segment selectors.
pub const KERNEL_CODE = 0x08; pub const KERNEL_CODE = 0x08;
@ -72,7 +72,7 @@ fn makeEntry(base: usize, limit: usize, access: u8, flags: u4) GDTEntry {
} }
// Fill in the GDT. // Fill in the GDT.
var gdt align(4) = []GDTEntry{ var gdt align(4) = [_]GDTEntry{
makeEntry(0, 0, 0, 0), makeEntry(0, 0, 0, 0),
makeEntry(0, 0xFFFFF, KERNEL | CODE, PROTECTED | BLOCKS_4K), makeEntry(0, 0xFFFFF, KERNEL | CODE, PROTECTED | BLOCKS_4K),
makeEntry(0, 0xFFFFF, KERNEL | DATA, PROTECTED | BLOCKS_4K), makeEntry(0, 0xFFFFF, KERNEL | DATA, PROTECTED | BLOCKS_4K),
@ -83,7 +83,7 @@ var gdt align(4) = []GDTEntry{
// GDT descriptor register pointing at the GDT. // GDT descriptor register pointing at the GDT.
var gdtr = GDTRegister{ var gdtr = GDTRegister{
.limit = u16(@sizeOf(@typeOf(gdt))), .limit = @as(u16, @sizeOf(@TypeOf(gdt))),
.base = &gdt[0], .base = &gdt[0],
}; };
@ -92,7 +92,7 @@ var tss = TSS{
.unused1 = 0, .unused1 = 0,
.esp0 = undefined, .esp0 = undefined,
.ss0 = KERNEL_DATA, .ss0 = KERNEL_DATA,
.unused2 = []u32{0} ** 22, .unused2 = [_]u32{0} ** 22,
.unused3 = 0, .unused3 = 0,
.iomap_base = @sizeOf(TSS), .iomap_base = @sizeOf(TSS),
}; };
@ -107,27 +107,16 @@ pub fn setKernelStack(esp0: usize) void {
tss.esp0 = esp0; tss.esp0 = esp0;
} }
////
// Load the GDT into the system registers (defined in assembly).
//
// Arguments:
// gdtr: Pointer to the GDTR.
//
extern fn loadGDT(gdtr: *const GDTRegister) void; extern fn loadGDT(gdtr: *const GDTRegister) void;
////
// Initialize the Global Descriptor Table.
//
pub fn initialize() void { pub fn initialize() void {
// tty.step("Setting up the Global Descriptor Table");
// Initialize GDT. // Initialize GDT.
loadGDT(&gdtr); loadGDT(&gdtr); //asm routine
// Initialize TSS. // Initialize TSS.
const tss_entry = makeEntry(@ptrToInt(&tss), @sizeOf(TSS) - 1, TSS_ACCESS, PROTECTED); // const tss_entry = makeEntry(@ptrToInt(&tss), @sizeOf(TSS) - 1, TSS_ACCESS, PROTECTED);
gdt[TSS_DESC / @sizeOf(GDTEntry)] = tss_entry; // gdt[TSS_DESC / @sizeOf(GDTEntry)] = tss_entry;
x86.ltr(TSS_DESC); // x86.ltr(TSS_DESC);
// tty.stepOK(); // tty.stepOK();
} }

View file

@ -1,12 +1,21 @@
// https://wiki.osdev.org/IDT // https://wiki.osdev.org/IDT
const x86 = @import("lib/index.zig");
const interrupt = @import("interrupt.zig"); const kernel = @import("kernel");
const gdt = @import("gdt.zig"); const x86 = @import("x86");
// Types of gates. // Types of gates.
pub const INTERRUPT_GATE = 0x8E; pub const INTERRUPT_GATE = 0x8E;
pub const SYSCALL_GATE = 0xEE; pub const SYSCALL_GATE = 0xEE;
// Interrupt Descriptor Table.
var idt_table: [256]IDTEntry = undefined;
// IDT descriptor register pointing at the IDT.
const idtr = IDTRegister{
.limit = @as(u16, @sizeOf(@TypeOf(idt_table))),
.base = &idt_table,
};
// Structure representing an entry in the IDT. // Structure representing an entry in the IDT.
const IDTEntry = packed struct { const IDTEntry = packed struct {
offset_low: u16, offset_low: u16,
@ -22,16 +31,6 @@ const IDTRegister = packed struct {
base: *[256]IDTEntry, base: *[256]IDTEntry,
}; };
// Interrupt Descriptor Table.
var idt: [256]IDTEntry = undefined;
// IDT descriptor register pointing at the IDT.
const idtr = IDTRegister{
.limit = u16(@sizeOf(@typeOf(idt))),
.base = &idt,
};
////
// Setup an IDT entry. // Setup an IDT entry.
// //
// Arguments: // Arguments:
@ -39,20 +38,52 @@ const idtr = IDTRegister{
// flags: Type and attributes. // flags: Type and attributes.
// offset: Address of the ISR. // offset: Address of the ISR.
// //
pub fn setGate(n: u8, flags: u8, offset: extern fn () void) void { pub fn setGate(n: u8, flags: u8, offset: anytype) void {
const intOffset = @ptrToInt(offset); const intOffset = @ptrToInt(&offset);
// const intOffset = offset;
idt[n].offset_low = @truncate(u16, intOffset); idt_table[n].offset_low = @truncate(u16, intOffset);
idt[n].offset_high = @truncate(u16, intOffset >> 16); idt_table[n].offset_high = @truncate(u16, intOffset >> 16);
idt[n].flags = flags; idt_table[n].flags = flags;
idt[n].zero = 0; idt_table[n].zero = 0;
idt[n].selector = gdt.KERNEL_CODE; idt_table[n].selector = x86.gdt.KERNEL_CODE;
} }
////
// Initialize the Interrupt Descriptor Table. // Initialize the Interrupt Descriptor Table.
//
pub fn initialize() void { pub fn initialize() void {
interrupt.initialize(); // configure PIC
x86.lidt(@ptrToInt(&idtr)); x86.interrupt.remapPIC();
x86.interrupt.configPIT();
// install ISRs
x86.isr.install_exceptions();
x86.isr.install_irqs();
x86.isr.install_syscalls();
// x86.interrupt.registerIRQ(0, kernel.time.increment);
// x86.interrupt.registerIRQ(1, kernel.ps2.keyboard_handler);
// x86.interrupt.register(1, debug_trap);
// x86.interrupt.register(13, general_protection_fault);
// x86.interrupt.register(14, page_fault);
// load IDT
x86.instr.lidt(@ptrToInt(&idtr));
}
fn general_protection_fault() void {
kernel.vga.println("general protection fault", .{});
x86.instr.hang();
}
fn debug_trap() void {
kernel.vga.println("debug fault/trap", .{});
kernel.vga.println("dr7: 0b{b}", .{x86.instr.dr7()});
}
fn page_fault() void {
const vaddr = x86.instr.cr2();
kernel.vga.println("cr2: 0x{x}", .{vaddr});
kernel.vga.println("phy: 0x{x}", .{x86.paging.translate(vaddr)});
kernel.vga.println("pde: 0x{x} ({})", .{ x86.paging.pde(vaddr), vaddr >> 22 });
kernel.vga.println("pte: 0x{x} ({})", .{ x86.paging.pte(vaddr), vaddr >> 12 });
// paging.format();
x86.instr.hang();
} }

16
src/arch/x86/index.zig Normal file
View file

@ -0,0 +1,16 @@
pub usingnamespace @import("../../common.zig");
// from core kernel
pub const kernel = @import("../../index.zig");
// x86 namespace
pub const PAGE_SIZE: usize = 4096;
pub const io = @import("lib/io.zig");
pub const instr = @import("lib/instructions.zig");
pub usingnamespace @import("main.zig");
pub const pmem = @import("pmem.zig");
pub const paging = @import("paging.zig");
pub const idt = @import("idt.zig");
pub const isr = @import("isr.zig");
pub const gdt = @import("gdt.zig");
pub const interrupt = @import("interrupt.zig");

View file

@ -1,5 +1,6 @@
const x86 = @import("lib/index.zig"); const std = @import("std");
const isr = @import("isr.zig"); const kernel = @import("kernel");
const x86 = @import("x86");
// PIC ports. // PIC ports.
const PIC1_CMD = 0x20; const PIC1_CMD = 0x20;
@ -15,6 +16,11 @@ const ICW1_ICW4 = 0x01;
const ICW4_8086 = 0x01; const ICW4_8086 = 0x01;
// write 0 to wait // write 0 to wait
const WAIT_PORT = 0x80; const WAIT_PORT = 0x80;
// PIT Channels
const PIT_CHAN0 = 0x40;
const PIT_CHAN1 = 0x41;
const PIT_CHAN2 = 0x42;
const PIT_CMD = 0x43;
// Interrupt Vector offsets of exceptions. // Interrupt Vector offsets of exceptions.
const EXCEPTION_0 = 0; const EXCEPTION_0 = 0;
const EXCEPTION_31 = EXCEPTION_0 + 31; const EXCEPTION_31 = EXCEPTION_0 + 31;
@ -24,35 +30,38 @@ const IRQ_15 = IRQ_0 + 15;
// Interrupt Vector offsets of syscalls. // Interrupt Vector offsets of syscalls.
const SYSCALL = 128; const SYSCALL = 128;
// Registered interrupt handlers. (see isr.s) // Registered interrupt handlers. (see x86.isr.s)
var handlers = [_]fn () void{unhandled} ** 48; const handlers = [_]fn () void{unhandled} ** 48;
// Registered IRQ subscribers. (see isr.s) // Registered IRQ subscribers. (see x86.isr.s)
// var irq_subscribers = []MailboxId{MailboxId.Kernel} ** 16; // var irq_subscribers = []MailboxId{MailboxId.Kernel} ** 16;
fn unhandled() noreturn { fn unhandled() noreturn {
const n = isr.context.interrupt_n; const n = x86.isr.context.interrupt_n;
// if (n >= IRQ_0) { kernel.vga.print("unhandled interrupt number {d}", .{n});
// tty.panic("unhandled IRQ number {d}", n - IRQ_0); if (n < IRQ_0) kernel.vga.println(" (exception)", .{});
// } else { if (n >= IRQ_0) kernel.vga.println(" (IRQ number {d})", .{n - IRQ_0});
// tty.panic("unhandled exception number {d}", n); x86.instr.hang();
// }
x86.hang();
} }
inline fn picwait() void { inline fn picwait() void {
x86.outb(WAIT_PORT, 0); x86.io.outb(WAIT_PORT, 0);
} }
//// ////
// Call the correct handler based on the interrupt number. // Call the correct handler based on the interrupt number.
// //
export fn interruptDispatch() void { export fn interruptDispatch() void {
const n = @intCast(u8, isr.context.interrupt_n); const n = @intCast(u8, x86.isr.context.interrupt_n);
switch (n) { switch (n) {
// Exceptions. // Exceptions.
EXCEPTION_0...EXCEPTION_31 => { EXCEPTION_0...EXCEPTION_31 => {
handlers[n](); kernel.vga.println("", .{});
kernel.vga.println("num: {}", .{n});
kernel.vga.println("err: {}", .{@truncate(u8, x86.isr.context.error_code)});
kernel.vga.println("ip: 0x{x}", .{@truncate(u16, x86.isr.context.eip)});
kernel.vga.println("ip: 0x{x}", .{@truncate(u16, x86.isr.context.eip >> 16)});
return handlers[n]();
}, },
// IRQs. // IRQs.
@ -67,7 +76,7 @@ export fn interruptDispatch() void {
// Syscalls. // Syscalls.
// SYSCALL => { // SYSCALL => {
// const syscall_n = isr.context.registers.eax; // const syscall_n = x86.isr.context.registers.eax;
// if (syscall_n < syscall.handlers.len) { // if (syscall_n < syscall.handlers.len) {
// syscall.handlers[syscall_n](); // syscall.handlers[syscall_n]();
// } else { // } else {
@ -80,8 +89,8 @@ export fn interruptDispatch() void {
// If no user thread is ready to run, halt here and wait for interrupts. // If no user thread is ready to run, halt here and wait for interrupts.
// if (scheduler.current() == null) { // if (scheduler.current() == null) {
// x86.sti(); // sti();
// x86.hlt(); // hlt();
// } // }
} }
@ -91,8 +100,8 @@ inline fn spuriousIRQ(irq: u8) bool {
// TODO: handle spurious IRQ15. // TODO: handle spurious IRQ15.
// Read the value of the In-Service Register. // Read the value of the In-Service Register.
x86.outb(PIC1_CMD, ISR_READ); x86.io.outb(PIC1_CMD, ISR_READ);
const in_service = x86.inb(PIC1_CMD); const in_service = x86.io.inb(PIC1_CMD);
// Verify whether IRQ7 is set in the ISR. // Verify whether IRQ7 is set in the ISR.
return (in_service & (1 << 7)) == 0; return (in_service & (1 << 7)) == 0;
@ -102,11 +111,11 @@ inline fn startOfInterrupt(irq: u8) void {
// mask the irq and then ACK // mask the irq and then ACK
if (irq >= 8) { if (irq >= 8) {
maskIRQ(irq, true); maskIRQ(irq, true);
x86.outb(PIC1_CMD, ACK); x86.io.outb(PIC1_CMD, ACK);
x86.outb(PIC2_CMD, ACK); x86.io.outb(PIC2_CMD, ACK);
} else { } else {
maskIRQ(irq, true); maskIRQ(irq, true);
x86.outb(PIC1_CMD, ACK); x86.io.outb(PIC1_CMD, ACK);
} }
} }
@ -114,75 +123,80 @@ inline fn endOfInterrupt(irq: u8) void {
// unmask the irq and then ACK // unmask the irq and then ACK
if (irq >= 8) { if (irq >= 8) {
maskIRQ(irq, false); maskIRQ(irq, false);
x86.outb(PIC2_CMD, ACK); x86.io.outb(PIC2_CMD, ACK);
} else { } else {
maskIRQ(irq, false); maskIRQ(irq, false);
x86.outb(PIC1_CMD, ACK); x86.io.outb(PIC1_CMD, ACK);
} }
} }
pub fn register(n: u8, handler: fn () void) void { pub fn register(n: u8, comptime handler: fn () void) void {
handlers[n] = handler; handlers[n] = handler;
} }
pub fn registerIRQ(irq: u8, handler: fn () void) void { pub fn registerIRQ(irq: u8, comptime handler: fn () void) void {
register(IRQ_0 + irq, handler); register(IRQ_0 + irq, handler);
maskIRQ(irq, false); // Unmask the IRQ. maskIRQ(irq, false); // Unmask the IRQ.
} }
fn remapPIC() void { pub fn remapPIC() void {
// ICW1: start initialization sequence. // ICW1: start initialization sequence.
x86.outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4); x86.io.outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
picwait(); picwait();
x86.outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4); x86.io.outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4);
picwait(); picwait();
// ICW2: Interrupt Vector offsets of IRQs. // ICW2: Interrupt Vector offsets of IRQs.
x86.outb(PIC1_DATA, IRQ_0); // IRQ 0..7 -> Interrupt 32..39 x86.io.outb(PIC1_DATA, IRQ_0); // IRQ 0..7 -> Interrupt 32..39
picwait(); picwait();
x86.outb(PIC2_DATA, IRQ_0 + 8); // IRQ 8..15 -> Interrupt 40..47 x86.io.outb(PIC2_DATA, IRQ_0 + 8); // IRQ 8..15 -> Interrupt 40..47
picwait(); picwait();
// ICW3: IRQ line 2 to connect master to slave PIC. // ICW3: IRQ line 2 to connect master to slave PIC.
x86.outb(PIC1_DATA, 1 << 2); x86.io.outb(PIC1_DATA, 1 << 2);
picwait(); picwait();
x86.outb(PIC2_DATA, 2); x86.io.outb(PIC2_DATA, 2);
picwait(); picwait();
// ICW4: 80x86 mode. // ICW4: 80x86 mode.
x86.outb(PIC1_DATA, ICW4_8086); x86.io.outb(PIC1_DATA, ICW4_8086);
picwait(); picwait();
x86.outb(PIC2_DATA, ICW4_8086); x86.io.outb(PIC2_DATA, ICW4_8086);
picwait(); picwait();
// Mask all IRQs. // Mask all IRQs.
x86.outb(PIC1_DATA, 0xFF); x86.io.outb(PIC1_DATA, 0xFF);
picwait(); picwait();
x86.outb(PIC2_DATA, 0xFF); x86.io.outb(PIC2_DATA, 0xFF);
picwait(); picwait();
} }
pub fn maskIRQ(irq: u8, mask: bool) void { pub fn maskIRQ(irq: u8, comptime mask: bool) void {
if (irq > 15) { if (irq > 15) return;
return;
}
// Figure out if master or slave PIC owns the IRQ. // Figure out if master or slave PIC owns the IRQ.
const port = if (irq < 8) u16(PIC1_DATA) else u16(PIC2_DATA); const port = @as(u16, if (irq < 8) PIC1_DATA else PIC2_DATA);
const old = x86.inb(port); // Retrieve the current mask. const old = x86.io.inb(port); // Retrieve the current mask.
// Mask or unmask the interrupt. // Mask or unmask the interrupt.
const shift = @intCast(u3, irq % 8); // TODO: waiting for Andy to fix this. const shift = @truncate(u3, irq % 8);
if (mask) { // const shift = @truncate(u3, if (irq < 8) irq else irq - 8);
x86.outb(port, old | (u8(1) << shift)); const bit = @as(u8, 1) << shift;
} else { if (mask) x86.io.outb(port, old | bit);
x86.outb(port, old & ~(u8(1) << shift)); if (!mask) x86.io.outb(port, old & ~bit);
} // TODO uncomment
// const new = x86.io.inb(port); // Retrieve the current mask.
} }
//// // configures the chan0 with a rate generator, which will trigger irq0
// Initialize interrupts. pub const divisor = 2685;
// pub const tick = 2251; // f = 1.193182 MHz, TODO: turn into a function
pub fn initialize() void { pub fn configPIT() void {
remapPIC(); const chanNum = 0;
isr.install(); // const chan = PIT_CHAN0;
const LOHI = 0b11; // bit4 | bit5
const PITMODE_RATE_GEN = 0x2;
x86.io.outb(PIT_CMD, chanNum << 6 | LOHI << 4 | PITMODE_RATE_GEN << 1);
x86.io.outb(PIT_CHAN0, divisor & 0xff);
x86.io.outb(PIT_CHAN0, divisor >> 8);
} }

View file

@ -1,5 +1,5 @@
// Kernel stack for interrupt handling. // Kernel stack for interrupt handling.
KERNEL_STACK = 0x80000 // KERNEL_STACK = 0x10000
// GDT selectors. // GDT selectors.
KERNEL_DS = 0x10 KERNEL_DS = 0x10
@ -27,24 +27,23 @@ isrCommon:
pusha // Save the registers state. pusha // Save the registers state.
// Setup kernel data segment. // Setup kernel data segment.
mov $KERNEL_DS, %ax // mov $KERNEL_DS, %ax
mov %ax, %ds // mov %ax, %ds
mov %ax, %es // mov %ax, %es
// Save the pointer to the current context and switch to the kernel stack. // Save the pointer to the current context and switch to the kernel stack.
mov %esp, context mov %esp, context
mov $KERNEL_STACK, %esp // mov $KERNEL_STACK, %esp
call interruptDispatch // Handle the interrupt event. call interruptDispatch // Handle the interrupt event.
// Restore the pointer to the context (of a different thread, potentially). // Restore the pointer to the context (of a different thread, potentially).
mov context, %esp // mov context, %esp
// Setup user data segment. // Setup user data segment.
mov $USER_DS, %ax // mov $USER_DS, %ax
mov %ax, %ds // mov %ax, %ds
mov %ax, %es // mov %ax, %es
popa // Restore the registers state. popa // Restore the registers state.
add $8, %esp // Remove interrupt number and error code from stack. add $8, %esp // Remove interrupt number and error code from stack.

View file

@ -65,9 +65,9 @@ pub const Context = packed struct {
esp: u32, esp: u32,
ss: u32, ss: u32,
pub inline fn setReturnValue(self: *volatile Context, value: var) void { // pub inline fn setReturnValue(self: *volatile Context, value: var) void {
self.registers.eax = if (@typeOf(value) == bool) @boolToInt(value) else @intCast(u32, value); // self.registers.eax = if (@TypeOf(value) == bool) @boolToInt(value) else @intCast(u32, value);
} // }
}; };
// Structure holding general purpose registers as saved by PUSHA. // Structure holding general purpose registers as saved by PUSHA.
@ -80,19 +80,6 @@ pub const Registers = packed struct {
edx: u32, edx: u32,
ecx: u32, ecx: u32,
eax: u32, eax: u32,
pub fn init() Registers {
return Registers{
.edi = 0,
.esi = 0,
.ebp = 0,
.esp = 0,
.ebx = 0,
.edx = 0,
.ecx = 0,
.eax = 0,
};
}
}; };
// Pointer to the current saved context. // Pointer to the current saved context.
@ -100,9 +87,8 @@ pub export var context: *volatile Context = undefined;
//// ////
// Install the Interrupt Service Routines in the IDT. // Install the Interrupt Service Routines in the IDT.
// //
pub fn install() void { pub fn install_exceptions() void {
// Exceptions. // Exceptions.
idt.setGate(0, idt.INTERRUPT_GATE, isr0);
idt.setGate(1, idt.INTERRUPT_GATE, isr1); idt.setGate(1, idt.INTERRUPT_GATE, isr1);
idt.setGate(2, idt.INTERRUPT_GATE, isr2); idt.setGate(2, idt.INTERRUPT_GATE, isr2);
idt.setGate(3, idt.INTERRUPT_GATE, isr3); idt.setGate(3, idt.INTERRUPT_GATE, isr3);
@ -134,25 +120,29 @@ pub fn install() void {
idt.setGate(29, idt.INTERRUPT_GATE, isr29); idt.setGate(29, idt.INTERRUPT_GATE, isr29);
idt.setGate(30, idt.INTERRUPT_GATE, isr30); idt.setGate(30, idt.INTERRUPT_GATE, isr30);
idt.setGate(31, idt.INTERRUPT_GATE, isr31); idt.setGate(31, idt.INTERRUPT_GATE, isr31);
}
// // IRQs. // IRQs.
pub fn install_irqs() void {
idt.setGate(32, idt.INTERRUPT_GATE, isr32); idt.setGate(32, idt.INTERRUPT_GATE, isr32);
idt.setGate(33, idt.INTERRUPT_GATE, isr33); idt.setGate(33, idt.INTERRUPT_GATE, isr33);
// idt.setGate(34, idt.INTERRUPT_GATE, isr34); idt.setGate(34, idt.INTERRUPT_GATE, isr34);
// idt.setGate(35, idt.INTERRUPT_GATE, isr35); idt.setGate(35, idt.INTERRUPT_GATE, isr35);
// idt.setGate(36, idt.INTERRUPT_GATE, isr36); idt.setGate(36, idt.INTERRUPT_GATE, isr36);
// idt.setGate(37, idt.INTERRUPT_GATE, isr37); idt.setGate(37, idt.INTERRUPT_GATE, isr37);
// idt.setGate(38, idt.INTERRUPT_GATE, isr38); idt.setGate(38, idt.INTERRUPT_GATE, isr38);
// idt.setGate(39, idt.INTERRUPT_GATE, isr39); idt.setGate(39, idt.INTERRUPT_GATE, isr39);
// idt.setGate(40, idt.INTERRUPT_GATE, isr40); idt.setGate(40, idt.INTERRUPT_GATE, isr40);
// idt.setGate(41, idt.INTERRUPT_GATE, isr41); idt.setGate(41, idt.INTERRUPT_GATE, isr41);
// idt.setGate(42, idt.INTERRUPT_GATE, isr42); idt.setGate(42, idt.INTERRUPT_GATE, isr42);
// idt.setGate(43, idt.INTERRUPT_GATE, isr43); idt.setGate(43, idt.INTERRUPT_GATE, isr43);
// idt.setGate(44, idt.INTERRUPT_GATE, isr44); idt.setGate(44, idt.INTERRUPT_GATE, isr44);
// idt.setGate(45, idt.INTERRUPT_GATE, isr45); idt.setGate(45, idt.INTERRUPT_GATE, isr45);
// idt.setGate(46, idt.INTERRUPT_GATE, isr46); idt.setGate(46, idt.INTERRUPT_GATE, isr46);
// idt.setGate(47, idt.INTERRUPT_GATE, isr47); idt.setGate(47, idt.INTERRUPT_GATE, isr47);
}
// // Syscalls.
// idt.setGate(128, idt.SYSCALL_GATE, isr128); // Syscalls.
pub fn install_syscalls() void {
idt.setGate(128, idt.SYSCALL_GATE, isr128);
} }

View file

@ -1,2 +0,0 @@
use @import("io.zig");
use @import("instructions.zig");

View file

@ -1,39 +1,47 @@
////
// Load a new Task Register.
//
// Arguments:
// desc: Segment selector of the TSS.
//
pub inline fn ltr(desc: u16) void { pub inline fn ltr(desc: u16) void {
asm volatile ("ltr %[desc]" asm volatile ("ltr %[desc]"
: :
: [desc] "r" (desc) : [desc] "r" (desc),
); );
} }
////
// Completely stop the computer.
//
pub inline fn hang() noreturn { pub inline fn hang() noreturn {
asm volatile ("cli"); cli();
while (true) { while (true) asm volatile ("hlt");
asm volatile ("hlt");
}
} }
pub inline fn sti() void { //TODO: inline this
pub fn cli() void {
asm volatile ("cli");
}
//TODO: inline this
pub fn sti() void {
asm volatile ("sti"); asm volatile ("sti");
} }
pub inline fn hlt() void {
asm volatile ("hlt");
}
pub inline fn int3() void {
asm volatile ("int3");
}
////
// Load a new Interrupt Descriptor Table.
//
// Arguments:
// idtr: Address of the IDTR register.
//
pub inline fn lidt(idtr: usize) void { pub inline fn lidt(idtr: usize) void {
asm volatile ("lidt (%[idtr])" asm volatile ("lidt (%[idtr])"
: :
: [idtr] "r" (idtr) : [idtr] "r" (idtr),
);
}
pub fn cr2() usize {
return asm volatile ("movl %%cr2, %[result]"
: [result] "=r" (-> usize),
);
}
pub fn dr7() usize {
return asm volatile ("movl %%dr7, %[result]"
: [result] "=r" (-> usize),
); );
} }

View file

@ -1,19 +1,34 @@
usingnamespace @import("../index.zig");
pub inline fn inb(port: u16) u8 { pub inline fn inb(port: u16) u8 {
return asm volatile ("inb %[port], %[result]" return asm volatile ("inb %[port], %[result]"
: [result] "={al}" (-> u8) : [result] "={al}" (-> u8),
: [port] "N{dx}" (port) : [port] "N{dx}" (port),
); );
} }
pub inline fn inw(port: u16) u16 { pub inline fn inw(port: u16) u16 {
return asm volatile ("inw %[port], %[result]" return asm volatile ("inw %[port], %[result]"
: [result] "={ax}" (-> u16) : [result] "={ax}" (-> u16),
: [port] "N{dx}" (port) : [port] "N{dx}" (port),
); );
} }
pub inline fn inl(port: u16) u32 { pub inline fn inl(port: u16) u32 {
return asm volatile ("inl %[port], %[result]" return asm volatile ("inl %[port], %[result]"
: [result] "={eax}" (-> u32) : [result] "={eax}" (-> u32),
: [port] "N{dx}" (port) : [port] "N{dx}" (port),
);
}
pub inline fn insl(port: u16, addr: anytype, cnt: usize) void {
asm volatile ("cld; repne; insl;"
: [addr] "={edi}" (addr),
[cnt] "={ecx}" (cnt),
: [port] "{dx}" (port),
[addr] "0" (addr),
[cnt] "1" (cnt),
: "memory", "cc"
); );
} }
@ -21,20 +36,22 @@ pub inline fn outb(port: u16, value: u8) void {
asm volatile ("outb %[value], %[port]" asm volatile ("outb %[value], %[port]"
: :
: [value] "{al}" (value), : [value] "{al}" (value),
[port] "N{dx}" (port) [port] "N{dx}" (port),
); );
} }
pub inline fn outw(port: u16, value: u16) void { pub inline fn outw(port: u16, value: u16) void {
asm volatile ("outw %[value], %[port]" asm volatile ("outw %[value], %[port]"
: :
: [value] "{ax}" (value), : [value] "{ax}" (value),
[port] "N{dx}" (port) [port] "N{dx}" (port),
); );
} }
pub inline fn outl(port: u16, value: u32) void { pub inline fn outl(port: u16, value: u32) void {
asm volatile ("outl %[value], %[port]" asm volatile ("outl %[value], %[port]"
: :
: [value] "{eax}" (value), : [value] "{eax}" (value),
[port] "N{dx}" (port) [port] "N{dx}" (port),
); );
} }

View file

@ -8,7 +8,8 @@ SECTIONS {
. = 0xb8000; . = 0xb8000;
. += 80 * 25 * 2; . += 80 * 25 * 2;
. = 0x100000; /* lower half kernel <1MiB */
. = 0x10000;
/* ensure that the multiboot header is at the beginning */ /* ensure that the multiboot header is at the beginning */
.multiboot : .multiboot :
@ -36,47 +37,15 @@ SECTIONS {
. = ALIGN(4K); . = ALIGN(4K);
} }
/* NOT A GOOD IDEA TO GROUP debug_* SYMBOLS ! */
/* .debug : */
/* { */
/* /1* KEEP(*(.debug_*)) *1/ */
/* *(.debug_*) */
/* . = ALIGN(4K); */
/* } */
.gdt : .gdt :
{ {
*(.gdt) *(.gdt)
. = ALIGN(4K); . = ALIGN(4K);
} }
.got :
{
*(.got)
. = ALIGN(4K);
}
.got.plt :
{
*(.got.plt)
. = ALIGN(4K);
}
.bss : .bss :
{ {
*(.bss .bss.*) *(.bss .bss.*)
. = ALIGN(4K); . = ALIGN(4K);
} }
/* .stab : */
/* { */
/* KEEP(*(.stab)) */
/* . = ALIGN(4K); */
/* } */
/* .stabstr : */
/* { */
/* KEEP(*(.stabstr)) */
/* . = ALIGN(4K); */
/* } */
} }

View file

@ -1,18 +1,12 @@
usingnamespace @import("kernel").main; const std = @import("std");
usingnamespace @import("kernel").multiboot; const kernel = @import("kernel");
const idt = @import("idt.zig"); const x86 = @import("x86");
const mem = @import("mem.zig");
const gdt = @import("gdt.zig");
const x86 = @import("lib/index.zig");
const console = @import("../console.zig");
const printf = @import("../../vga.zig").printf;
/// x86 specific intialization /// x86 specific intialization
/// first entry point (see linker.ld) pub fn x86_main(info: *const kernel.multiboot.MultibootInfo) void {
pub fn x86_main(info: *const multibootInfo) void { x86.gdt.initialize();
var allocator = mem.initialize(info); x86.idt.initialize();
x86.pmem.initialize(info);
// gdt.initialize(); x86.paging.initialize();
idt.initialize(); x86.instr.sti();
x86.sti();
} }

View file

@ -1,142 +0,0 @@
usingnamespace @import("kernel").multiboot;
const assert = @import("std").debug.assert;
const vga = @import("../../vga.zig");
const std = @import("std");
const println = @import("../../vga.zig").println;
pub fn initialize(info: *const multibootInfo) void {
// assert((info.flags & MULTIBOOT_INFO_MEMORY) != 0);
// assert((info.flags & MULTIBOOT_INFO_MEM_MAP) != 0);
format_multibootinfo(info);
// return bumpAllocator.new(info);
}
pub fn format_multibootentry(entry: *multibootMMapEntry) void {
if (entry.type == MULTIBOOT_MEMORY_AVAILABLE) {
vga.printf("AVAILABLE: ");
} else {
vga.printf("NOT AVAILABLE: ");
}
vga.printf("{x} ", entry.addr);
if (entry.len / (1024 * 1024) > 0) {
vga.printf("({} MB)\n", entry.len / (1024 * 1024));
} else {
vga.printf("({} kB)\n", entry.len / (1024));
}
}
pub fn format_multibootinfo(info: *const multibootInfo) void {
println("-- multiboot2 info --");
// println("start: {}, end {}, size {}", info.start_address(), info.end_address(), info.total_size());
}
// returns each available physical frame one by one in order
// pub const bumpAllocator = struct {
// next_free_frame: physFrame,
// current_area: ?*multibootMMapEntry,
// info: *const multibootInfo,
// pub fn new(info: *const multibootInfo) bumpAllocator {
// const first_area = @intToPtr(*MultibootMMapEntry, info.mmap_addr);
// var allocator = bumpAllocator{
// .current_area = first_area,
// .next_free_frame = physFrame.from_addr(first_area.addr),
// .info = info,
// };
// // allocator.choose_next_area();
// return allocator;
// }
// pub fn allocate(self: var, count: u64) ?physFrame {
// if (count == 0) {
// return null;
// }
// if (self.current_area == null) {
// return null;
// }
// if (self.current_area.?.type != MULTIBOOT_MEMORY_AVAILABLE) {
// self.choose_next_area();
// return self.allocate(count);
// }
// const start_frame = self.next_free_frame;
// const end_frame = self.next_free_frame.add(count - 1);
// const current_area_last_frame = physFrame.from_addr(self.current_area.?.addr + self.current_area.?.len);
// if (end_frame.number > current_area_last_frame.number) {
// self.choose_next_area();
// return self.allocate(count);
// }
// self.next_free_frame.number += count;
// return start_frame;
// }
// pub fn choose_next_area(self: var) void {
// printf("choosing next area\n");
// const current = self.current_area.?;
// var next_area = @ptrToInt(current) + current.size + @sizeOf(@typeOf(current));
// if (next_area >= self.info.mmap_addr + self.info.mmap_length) {
// self.current_area = null;
// } else {
// format_multibootentry(self.current_area.?);
// self.current_area = @intToPtr(*MultibootMMapEntry, next_area);
// format_multibootentry(self.current_area.?);
// self.next_free_frame = physFrame.from_addr(self.current_area.?.addr);
// }
// }
// };
// pub const PAGE_SIZE = 4096;
// pub const physFrame = struct {
// number: u64,
// pub fn from_addr(addr: u64) physFrame {
// return physFrame{ .number = @divTrunc(addr, PAGE_SIZE) };
// }
// pub fn add(self: physFrame, count: u64) physFrame {
// return physFrame{ .number = self.number + count };
// }
// pub fn start_addr(self: physFrame) u64 {
// return (self.number * PAGE_SIZE);
// }
// };
// const PageDirectory = packed struct {
// entries: [1024]PageDirectoryEntry,
// };
// const PageDirectoryEntry = packed struct {
// address: u21,
// available: u2,
// ignored: u1,
// size: u1,
// zero: u1,
// accessed: u1,
// cache_disabled: u1,
// write_through: u1,
// user: u1,
// writeable: u1,
// present: u1,
// };
// const PageTable = packed struct {
// entries: [1024]PageTableEntry,
// };
// const PageTableEntry = packed struct {
// address: u21,
// available: u2,
// global: u1,
// zero: u1,
// dirty: u1,
// accessed: u1,
// cache_disabled: u1,
// write_through: u1,
// user: u1,
// writeable: u1,
// present: u1,
// };
// // assert(@sizeOf(PageTableEntry) == 32);

26
src/arch/x86/paging.s Normal file
View file

@ -0,0 +1,26 @@
.type setupPaging, @function
.global setupPaging
////
// Enable the paging system.
//
// Arguments:
// phys_pd: Physical pointer to the page directory.
//
setupPaging:
mov +4(%esp), %eax // Fetch the phys_pd parameter.
mov %eax, %cr3 // Point CR3 to the page directory.
// Enable Page Size Extension
mov %cr4, %eax
or $0x10, %eax
mov %eax, %cr4
// Enable Paging.
mov %cr0, %eax
mov $1, %eax
or $(1 << 0), %eax
or $(1 << 31), %eax
mov %eax, %cr0
ret

80
src/arch/x86/paging.zig Normal file
View file

@ -0,0 +1,80 @@
const std = @import("std");
const kernel = @import("kernel");
const x86 = @import("x86");
extern fn setupPaging(phys_pd: usize) void;
const PageEntry = usize;
pub const PT = @intToPtr([*]PageEntry, 0xFFC00000);
pub const PD = @intToPtr([*]PageEntry, 0xFFFFF000);
const PRESENT = 0x1;
const WRITE = 0x2;
const USER = 0x4;
const WRITE_THRU = 0x8;
const NOCACHE = 0x10;
const ACCESSED = 0x20;
const HUGE = 0x80;
pub var pageDirectory: [1024]PageEntry align(4096) linksection(".bss") = [_]PageEntry{0} ** 1024;
// TODO: inline these
fn pageBase(virt: usize) usize {
return virt & (~x86.PAGE_SIZE +% 1);
}
pub fn pde(virt: usize) *PageEntry {
return &PD[virt >> 22]; //relies on recursive mapping
}
pub fn pte(virt: usize) *PageEntry {
return &PT[virt >> 12]; //relies on recursive mapping
}
// virtual to physical
pub fn translate(virt: usize) ?usize {
if (pde(virt).* == 0) return null;
return pageBase(pte(virt).*);
}
pub fn unmap(virt: usize) void {
if (translate(virt)) |phys| {
x86.pmem.free(phys);
} else {
kernel.vga.println("can't unmap 0x{x} because it is not mapped.", .{virt});
}
}
pub fn mmap(virt: usize, phys: ?usize) !void {
//TODO: support hugepages
// allocate a page directory if there is none
if (pde(virt).* == 0) pde(virt).* = (try x86.pmem.allocate()) | WRITE | PRESENT;
// allocate a frame if phys isn't specified
pte(virt).* = (if (phys) |p| p else try x86.pmem.allocate()) | PRESENT;
}
pub fn initialize() void {
var p2 = pageDirectory[0..];
// identity map 0 -> 4MB
p2[0] = 0x000000 | PRESENT | WRITE | HUGE;
// recursive mapping
p2[1023] = @ptrToInt(&p2[0]) | PRESENT | WRITE;
// TODO: verify is this a hack?
std.debug.assert(x86.pmem.stack_end < kernel.layout.IDENTITY);
setupPaging(@ptrToInt(&pageDirectory[0])); //asm routine
}
pub fn format() void {
var i: usize = 1;
i = 0;
while (i < 1024) : (i += 1) {
if (PD[i] == 0) continue;
kernel.vga.println("p2[{}] -> 0x{x}", .{ i, PD[i] });
if (PD[i] & HUGE != 0) continue;
var j: usize = 0;
while (j < 1024) : (j += 1) {
var entry: PageEntry = PT[i * 1024 + j];
if (entry != 0) kernel.vga.println("p2[{}]p1[{}] -> 0x{x}", .{ i, j, entry });
}
}
}

95
src/arch/x86/pmem.zig Normal file
View file

@ -0,0 +1,95 @@
const std = @import("std");
const kernel = @import("kernel");
const x86 = @import("x86");
var stack: [*]usize = undefined; // Stack of free physical page.
var stack_index: usize = 0; // Index into the stack.
// Boundaries of the frame stack.
pub var stack_size: usize = undefined;
pub var stack_end: usize = undefined;
pub inline fn pageAlign(address: u32) u32 {
// 4095 -> 4096
// 4096 -> 4096
// 4097 -> 8192
return (address + x86.PAGE_SIZE - 1) & (~x86.PAGE_SIZE +% 1);
}
// Return the amount of variable elements (in bytes).
//
pub fn available() usize {
return stack_index * x86.PAGE_SIZE;
}
pub inline fn available_MiB() usize {
return available() / (1024 * 1024);
}
// Request a free physical page and return its address.
//
pub fn allocate() !usize {
if (available() == 0) {
kernel.println("out of memory", .{});
return error.OutOfMemory;
}
stack_index -= 1;
return stack[stack_index];
}
// Free a previously allocated physical page.
//
// Arguments:
// address: Address of the page to be freed.
//
pub fn free(address: usize) void {
stack[stack_index] = address;
stack_index += 1;
}
// Scan the memory map to index all available memory.
//
// Arguments:
// info: Information structure from bootloader.
//
pub fn initialize(info: *const kernel.multiboot.MultibootInfo) void {
// Ensure the bootloader has given us the memory map.
std.debug.assert((info.flags & kernel.multiboot.MULTIBOOT_INFO_MEMORY) != 0);
std.debug.assert((info.flags & kernel.multiboot.MULTIBOOT_INFO_MEM_MAP) != 0);
// TODO: WHAT WHY WHAAAAT, must check back here later
// Place stack at 0x200000 so that in the future I trigger a
// random bug and I won't ever know where it came from, great.
stack = @intToPtr([*]usize, 0x200000); // 2 MiB
// Place the stack of free pages after the last Multiboot module.
// stack = @intToPtr([*]usize, pageAlign(info.mods_addr));
// Calculate the approximate size of the stack based on the amount of total upper memory.
stack_size = ((info.mem_upper * 1024) / x86.PAGE_SIZE) * @sizeOf(usize);
stack_end = pageAlign(@ptrToInt(stack) + stack_size);
var map: usize = info.mmap_addr;
while (map < info.mmap_addr + info.mmap_length) {
var entry = @intToPtr(*kernel.multiboot.MultibootMMapEntry, map);
// Calculate the start and end of this memory area.
var start = @truncate(usize, entry.addr);
var end = @truncate(usize, start + entry.len);
// Anything that comes before the end of the stack of free pages is reserved.
start = if (start >= stack_end) start else stack_end;
// Flag all the pages in this memory area as free.
if (entry.type == kernel.multiboot.MULTIBOOT_MEMORY_AVAILABLE) while (start < end) : (start += x86.PAGE_SIZE)
free(start);
// Go to the next entry in the memory map.
map += entry.size + @sizeOf(@TypeOf(entry.size));
}
kernel.vga.println("available memory: {d} MiB ", .{available() / 1024 / 1024});
}
pub fn format() void {
kernel.vga.println("physframes left: {d} ({d} MiB)", .{ stack_index, available_MiB() });
}

View file

@ -8,6 +8,7 @@ __start:
push %ebx // Pass multiboot info structure. push %ebx // Pass multiboot info structure.
push %eax // Pass multiboot magic code. push %eax // Pass multiboot magic code.
call kmain // Call the kernel. call kmain // Call the kernel.
// Halt the CPU. // Halt the CPU.

View file

@ -0,0 +1,33 @@
//https://wiki.osdev.org/Multitasking_Systems
//C declaration:
// void switch_tasks(thread_control_block *next_thread)//
//
//WARNING: Caller is expected to disable IRQs before calling, and enable IRQs again after function returns
.type switch_tasks, @function
.global switch_tasks
switch_tasks:
push %ebp
mov %esp, %ebp
mov +12(%esp), %eax
mov %esp, (%eax) //save old esp
mov +8(%esp), %eax
mov %eax, %esp // move the forged stack to esp
pop %ebp // the top of the forged stack contains ebp
ret //the top of the forged stack contains eip to go to
// .type jmp_to_entrypoint, @function
// .global jmp_to_entrypoint
// jmp_to_entrypoint:
// mov %esp, %ebp
// mov +4(%esp), %eax
// jmp *%eax
// .type birthasm, @function
// .global birthasm
// birthasm:
// call unlock_scheduler
// mov %esp, %ebp
// mov +4(%esp), %eax
// jmp *%eax

10
src/bio.zig Normal file
View file

@ -0,0 +1,10 @@
// Block Device
// Glue code between Driver and FS
pub fn BlockDev(comptime sector_size: usize) type {
return struct {
const sector_size;
read: fn (u64, *[sector_size]u8) void, //TODO: inferred !void or var (issue 447)
write: ?fn (u64, *[sector_size]u8) void,
};
}

3
src/common.zig Normal file
View file

@ -0,0 +1,3 @@
pub const std = @import("std");
pub const assert = std.debug.assert;
pub const builtin = @import("builtin");

View file

@ -1,30 +1,60 @@
const interrupt = @import("arch/x86/interrupt.zig"); usingnamespace @import("index.zig");
const x86 = @import("arch/x86/lib/index.zig");
const ps2 = @import("ps2.zig"); var input_ring: Ring(u8) = undefined;
const pci = @import("pci.zig");
const std = @import("std");
const mem = std.mem;
usingnamespace @import("vga.zig");
var command: [10]u8 = undefined; var command: [10]u8 = undefined;
var command_len: usize = 0; var command_len: usize = 0;
fn execute(com: []u8) void { fn sleep2() void {
if (mem.eql(u8, com, "lspci")) pci.lspci(); task.usleep(2 * 1000 * 1000) catch unreachable;
}
fn t_sleep2() void {
_ = task.new(@ptrToInt(sleep2)) catch unreachable;
}
const Command = struct {
name: []const u8,
f: fn () void,
};
const commands = [_]Command{
Command{ .name = "clear", .f = clear },
Command{ .name = "paging", .f = x86.paging.format },
Command{ .name = "memory", .f = x86.pmem.format },
Command{ .name = "tasks", .f = task.format },
Command{ .name = "lspci", .f = pci.lspci },
Command{ .name = "sleep2", .f = sleep2 },
Command{ .name = "t-sleep2", .f = t_sleep2 },
Command{ .name = "uptime", .f = time.uptime },
};
fn execute(input: []u8) void {
for (commands) |c| if (std.mem.eql(u8, input, c.name)) return c.f();
println("{}: command not found, list of available commands:", .{input});
for (commands) |c| println("{}", .{c.name});
} }
pub fn keypress(char: u8) void { pub fn keypress(char: u8) void {
// this is a custom "readline" capped at 10 characters // this is a custom "readline" capped at 10 characters
switch (char) { switch (char) {
'\n' => { '\n' => {
vga.writeChar('\n'); print("\n", .{});
execute(command[0..command_len]); if (command_len > 0) execute(command[0..command_len]);
vga.writeString("> "); print("> ", .{});
command_len = 0; command_len = 0;
}, },
'\x00' => return,
'\x08' => {
// backspace
if (command_len == 0) return;
vga.writeChar(char);
command_len -= 1;
command[command_len] = '\x00';
},
else => { else => {
if (command_len == 10) // general case
return; if (command_len == 10) return;
vga.writeChar(char); vga.writeChar(char);
command[command_len] = char; command[command_len] = char;
command_len += 1; command_len += 1;
@ -32,8 +62,17 @@ pub fn keypress(char: u8) void {
} }
} }
pub fn initialize() void { pub fn keyboard_callback(char: u8) void {
vga.clear(); input_ring.write(char);
// vga.writeString("> "); }
interrupt.registerIRQ(1, ps2.keyboard_handler);
pub fn loop() void {
input_ring.init(vmem.allocator) catch unreachable;
input_ring.task = task.current_task;
ps2.keyboard_callback = keyboard_callback;
print("> ", .{});
while (true) {
while (input_ring.read()) |c| keypress(c);
task.block(.IOWait);
}
} }

1
src/constants.zig Normal file
View file

@ -0,0 +1 @@
pub const SMP: bool = false;

153
src/delta_queue.zig Normal file
View file

@ -0,0 +1,153 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
/// DeltaQueue is a singly-linked list where each
/// node has a counter. Each counter is relative
/// to the previous.
///
/// Inspired by https://wiki.osdev.org/Blocking_Process
/// Based on std.SinglyLinkedList
pub fn DeltaQueue(comptime T: type) type {
return struct {
const Self = @This();
/// Node inside the linked list wrapping the actual data.
pub const Node = struct {
next: ?*Node,
data: T,
counter: u64,
pub fn init(data: T, counter: u64) Node {
return Node{
.next = null,
.data = data,
.counter = counter,
};
}
/// Insert a new node after the current one.
///
/// Arguments:
/// new_node: Pointer to the new node to insert.
fn insertAfter(node: *Node, new_node: *Node) void {
if (node.next) |after| {
std.debug.assert(new_node.counter <= after.counter); //sanity check
after.counter -= new_node.counter;
}
new_node.next = node.next;
node.next = new_node;
}
};
len: usize = 0,
first: ?*Node,
/// Initialize a delta queue.
///
/// Returns:
/// An empty linked list.
pub fn init() Self {
return Self{
.first = null,
};
}
/// Insert a node in the list
///
/// Arguments:
/// node: Pointer to a node in the list.
/// new_node: Pointer to the new node to insert.
pub fn insert(list: *Self, node: *Node) void {
list.len += 1;
var target: ?*Node = null;
var next: ?*Node = list.first;
while (true) {
if (next == null or node.counter <= next.?.counter) {
if (target) |tg| return tg.insertAfter(node);
return list.prepend(node);
}
if (target) |tg| node.counter -= tg.counter;
target = next;
next = target.?.next;
}
}
/// worst case is O(n)
/// Could be better with a different data structure
/// Example: case of large list with all counters at 0,
/// we need to traverse the whole list.
pub fn decrement(list: *Self, count: u64) void {
var it = list.first;
var i = count;
while (it) |node| : (it = node.next) {
if (node.counter >= i) {
node.counter -= i;
return;
}
i -= node.counter;
node.counter = 0;
}
}
/// Insert a new node at the head.
///
/// Arguments:
/// new_node: Pointer to the new node to insert.
fn prepend(list: *Self, new_node: *Node) void {
if (list.first) |after| {
std.debug.assert(new_node.counter <= after.counter); //sanity check
after.counter -= new_node.counter;
}
new_node.next = list.first;
list.first = new_node;
}
/// Remove and return the first node in the list
/// if its counter is 0.
///
/// Returns:
/// A pointer to the first node in the list.
pub fn popZero(list: *Self) ?*Node {
const first = list.first orelse return null;
if (first.counter != 0) return null;
list.first = first.next;
list.len -= 1;
return first;
}
/// Allocate a new node.
///
/// Arguments:
/// allocator: Dynamic memory allocator.
///
/// Returns:
/// A pointer to the new node.
pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
return allocator.create(Node);
}
/// Deallocate a node.
///
/// Arguments:
/// node: Pointer to the node to deallocate.
/// allocator: Dynamic memory allocator.
pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
allocator.destroy(node);
}
/// Allocate and initialize a node and its data.
///
/// Arguments:
/// data: The data to put inside the node.
/// allocator: Dynamic memory allocator.
///
/// Returns:
/// A pointer to the new node.
pub fn createNode(list: *Self, data: T, counter: u64, allocator: *Allocator) !*Node {
var node = try list.allocateNode(allocator);
node.* = Node.init(data, counter);
return node;
}
};
}

425
src/driver/ide.zig Normal file
View file

@ -0,0 +1,425 @@
usingnamespace @import("index.zig");
const IDE_ATA = 0x00;
const IDE_ATAPI = 0x01;
const ATA_MASTER = 0x00;
const ATA_SLAVE = 0x01;
// Channels:
const ATA_PRIMARY = 0x00;
const ATA_SECONDARY = 0x01;
// Directions:
const ATA_READ = 0x00;
const ATA_WRITE = 0x01;
// Commands
const ATA_CMD_READ_PIO = 0x20;
const ATA_CMD_READ_PIO_EXT = 0x24;
const ATA_CMD_READ_DMA = 0xC8;
const ATA_CMD_READ_DMA_EXT = 0x25;
const ATA_CMD_WRITE_PIO = 0x30;
const ATA_CMD_WRITE_PIO_EXT = 0x34;
const ATA_CMD_WRITE_DMA = 0xCA;
const ATA_CMD_WRITE_DMA_EXT = 0x35;
const ATA_CMD_CACHE_FLUSH = 0xE7;
const ATA_CMD_CACHE_FLUSH_EXT = 0xEA;
const ATA_CMD_PACKET = 0xA0;
const ATA_CMD_IDENTIFY_PACKET = 0xA1;
const ATA_CMD_IDENTIFY = 0xEC;
// Status:
const ATA_SR_BUSY = 0x80;
const ATA_SR_DRDY = 0x40; // Drive ready
const ATA_SR_DF = 0x20; // Drive write fault
const ATA_SR_DSC = 0x10; // Drive seek complete
const ATA_SR_DRQ = 0x08; // Data request ready
const ATA_SR_CORR = 0x04; // Corrected data
const ATA_SR_IDX = 0x02;
const ATA_SR_ERR = 0x01;
// Registers:
const ATA_REG_DATA = 0x00;
const ATA_REG_ERROR = 0x01;
const ATA_REG_FEATURES = 0x01;
const ATA_REG_SECCOUNT0 = 0x02;
const ATA_REG_LBA0 = 0x03;
const ATA_REG_LBA1 = 0x04;
const ATA_REG_LBA2 = 0x05;
const ATA_REG_HDDEVSEL = 0x06;
const ATA_REG_COMMAND = 0x07;
const ATA_REG_STATUS = 0x07;
const ATA_REG_SECCOUNT1 = 0x08;
const ATA_REG_LBA3 = 0x09;
const ATA_REG_LBA4 = 0x0A;
const ATA_REG_LBA5 = 0x0B;
const ATA_REG_CONTROL = 0x0C;
const ATA_REG_ALTSTATUS = 0x0C;
const ATA_REG_DEVADDRESS = 0x0D;
// Identification space
const ATA_IDENT_DEVICETYPE = 0;
const ATA_IDENT_CYLINDERS = 2;
const ATA_IDENT_HEADS = 6;
const ATA_IDENT_SECTORS = 12;
const ATA_IDENT_SERIAL = 20;
const ATA_IDENT_MODEL = 54;
const ATA_IDENT_CAPABILITIES = 98;
const ATA_IDENT_FIELDVALID = 106;
const ATA_IDENT_MAX_LBA = 120;
const ATA_IDENT_COMMANDSETS = 164;
const ATA_IDENT_MAX_LBA_EXT = 200;
const atapi_packet: [12]u8 = [1]u8{0xA8} ++ [1]u8{0} ** 11;
var ide_buf: [2048]u8 = [1]u8{0} ** 2048;
const IDEDevice = struct {
reserved: u8, // 0 (Empty) or 1 (This Drive really exists).
channel: IDEChannelRegister,
drive: u8, // 0 (Master Drive) or 1 (Slave Drive).
idetype: u16, // 0: ATA, 1:ATAPI.
signature: u16, // Drive Signature
capabilities: u16, // Features.
commandsets: usize, // Command Sets Supported.
size: usize, // Size in Sectors.
model: [41]u8, // Model in string.
ide_irq_invoked: bool = false,
pub fn init(channel: IDEChannelRegister, drive: u8) !?*IDEDevice {
var idetype: u8 = IDE_ATA;
var err: u8 = 0;
var status: u8 = 0;
// TODO: make this nicer
var self = try kernel.vmem.allocator.create(IDEDevice);
errdefer kernel.vmem.allocator.destroy(self);
self.reserved = 1;
self.channel = channel;
self.drive = drive;
// (0) Turn off irqs
self.write(ATA_REG_CONTROL, 2);
// (I) Select Drive:
self.write(ATA_REG_HDDEVSEL, 0xA0 | (drive << 4)); // Select Drive.
try kernel.task.usleep(1000); // Wait 1ms for drive select to work.
// (II) Send ATA Identify Command:
self.write(ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
try kernel.task.usleep(1000);
if (self.read(ATA_REG_STATUS) == 0) return null; // If Status = 0, No Device.
while (true) {
status = self.read(ATA_REG_STATUS);
if (status & ATA_SR_ERR != 0) {
err = 1;
break;
} // If Err, Device is not ATA.
if ((status & ATA_SR_BUSY == 0) and (status & ATA_SR_DRQ != 0)) break; // Everything is right.
}
// (IV) Probe for ATAPI Devices:)
if (err != 0) {
// Device is not ATA
const cl = self.read(ATA_REG_LBA1);
const ch = self.read(ATA_REG_LBA2);
if (cl == 0x14 and ch == 0xEB) idetype = IDE_ATAPI;
if (cl == 0x69 and ch == 0x96) idetype = IDE_ATAPI;
if (idetype != IDE_ATAPI) {
return null; // Unknown Type (may not be a device).
}
self.write(ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
try kernel.task.usleep(1000);
}
self.idetype = idetype;
// (V) Read Identification Space of the Device:
self.read_buffer(ATA_REG_DATA, &ide_buf, 128);
self.signature = @ptrCast(*const u8, &ide_buf[ATA_IDENT_DEVICETYPE]).*;
self.capabilities = @ptrCast(*const u8, &ide_buf[ATA_IDENT_CAPABILITIES]).*;
self.commandsets = @ptrCast(*const usize, &ide_buf[ATA_IDENT_COMMANDSETS]).*;
// (VII) Get Size:
if (self.commandsets & (1 << 26) != 0) {
// Device uses 48-Bit Addressing:
self.size = @ptrCast(*const usize, &ide_buf[ATA_IDENT_MAX_LBA_EXT]).*;
} else {
// Device uses CHS or 28-bit Addressing:
self.size = @ptrCast(*const usize, &ide_buf[ATA_IDENT_MAX_LBA]).*;
}
// (VIII) String indicates model of device (like Western Digital HDD and SONY DVD-RW...):
var k: u16 = 0;
while (k < 40) : (k = k + 2) {
self.model[k] = ide_buf[ATA_IDENT_MODEL + k + 1];
self.model[k + 1] = ide_buf[ATA_IDENT_MODEL + k];
}
self.model[40] = 0; // Terminate String.
self.format();
return self;
}
inline fn poll(self: IDEDevice) void {
for ([_]u8{ 0, 1, 2, 3 }) |_| _ = self.read(ATA_REG_ALTSTATUS); // wait 100ns per call
while (self.read(ATA_REG_STATUS) & ATA_SR_BUSY != 0) {} // Wait for BSY to be zero.
}
inline fn poll_check(self: IDEDevice) !void {
// (I) Delay 400 nanosecond for BSY to be set:
self.poll();
const state = self.read(ATA_REG_STATUS); // Read Status Register.
if (state & ATA_SR_ERR != 0) return error.ATAStatusReg; // Error.
if (state & ATA_SR_DF != 0) return error.ATADeviceFault; // Device Fault.
if ((state & ATA_SR_DRQ) == 0) return error.ATANoDRQ; // DRQ should be set
}
pub inline fn read(self: IDEDevice, comptime reg: u8) u8 {
if (reg > 0x07 and reg < 0x0C) self.write(ATA_REG_CONTROL, 0x80 | self.channel.nIEN);
defer if (reg > 0x07 and reg < 0x0C) self.write(ATA_REG_CONTROL, self.channel.nIEN);
return switch (reg) {
0x0...0x7 => x86.inb(self.channel.base + reg - 0x0),
0x8...0xb => x86.inb(self.channel.base + reg - 0x6),
0xc...0xd => x86.inb(self.channel.ctrl + reg - 0xa),
0xe...0x16 => x86.inb(self.channel.bmide + reg - 0xe),
else => @compileError("bad IDE register."),
};
}
pub inline fn read_buffer(self: IDEDevice, comptime reg: u8, buf: var, cnt: usize) void {
if (reg > 0x07 and reg < 0x0C) self.write(ATA_REG_CONTROL, 0x80 | self.channel.nIEN);
defer if (reg > 0x07 and reg < 0x0C) self.write(ATA_REG_CONTROL, self.channel.nIEN);
switch (reg) {
0x0...0x7 => x86.insl(self.channel.base + reg - 0x0, buf, cnt),
0x8...0xb => x86.insl(self.channel.base + reg - 0x6, buf, cnt),
0xc...0xd => x86.insl(self.channel.ctrl + reg - 0xa, buf, cnt),
0xe...0x16 => x86.insl(self.channel.bmide + reg - 0xe, buf, cnt),
else => @compileError("bad IDE register."),
}
}
pub inline fn write(self: IDEDevice, comptime reg: u8, data: u8) void {
if (reg > 0x07 and reg < 0x0C) self.write(ATA_REG_CONTROL, 0x80 | self.channel.nIEN);
defer if (reg > 0x07 and reg < 0x0C) self.write(ATA_REG_CONTROL, self.channel.nIEN);
switch (reg) {
0x0...0x7 => x86.outb(self.channel.base + reg - 0x0, data),
0x8...0xb => x86.outb(self.channel.base + reg - 0x6, data),
0xc...0xd => x86.outb(self.channel.ctrl + reg - 0xa, data),
0xe...0x16 => x86.outb(self.channel.bmide + reg - 0xe, data),
else => @compileError("bad IDE register."),
}
}
pub fn read_sectors(self: *IDEDevice, numsects: u8, lba: u64, selector: u8, buf: usize) !void {
// 1: Check if the drive presents:
if (self.reserved == 0) {
return error.DriveNotFound; // Drive Not Found!
} else if (self.idetype == IDE_ATA and (lba + numsects) > self.size) {
// 2: Check if inputs are valid:
return error.InvalidSeek; // Seeking to invalid position.
} else {
// 3: Read in PIO Mode through Polling & IRQs:
if (self.idetype == IDE_ATA) {
try self.ata_access(ATA_READ, lba, numsects, selector, buf);
} else if (self.idetype == IDE_ATAPI) {
return error.ATAPINotImplemented;
// var i: u8 = 0;
// while (i < numsects) : (i = i + 1) {
// // err = ide_atapi_read(drive, lba + i, 1, selector, buf + (i * 2048));
// }
}
}
}
pub fn format(self: IDEDevice) void {
kernel.println("[ide] {} drive ({}MB) - {}", .{
if (self.idetype == 0) "ATA " else "ATAPI",
self.size * 512 / 1024 / 1024,
self.model,
});
}
fn ata_access(self: *IDEDevice, direction: u8, lba: u64, numsects: u8, selector: u16, buf: usize) !void {
var dma = false; // 0: No DMA, 1: DMA
var cmd: u8 = 0;
var lba_io = [1]u8{0} ** 8;
const bus: u16 = self.channel.base; // Bus Base, like 0x1F0 which is also data port.
const words: usize = 256; // Almost every ATA drive has a sector-size of 512-byte.
self.ide_irq_invoked = false;
self.write(ATA_REG_CONTROL, 2); // disable IRQa
var lba_mode: u8 = undefined;
var head: u8 = undefined;
// (I) Select one from LBA28, LBA48 or CHS;
if (lba >= 0x10000000) {
// Sure Drive should support LBA in this case, or you are giving a wrong LBA.
// LBA48:
lba_io = @bitCast([8]u8, lba);
head = 0; // Lower 4-bits of HDDEVSEL are not used here.
lba_mode = 2;
} else if (self.capabilities & 0x200 != 0) { // Drive supports LBA?
// LBA28:
lba_io = @bitCast([8]u8, lba);
assert(lba_io[3] == 0);
head = @intCast(u8, (lba & 0xF000000) >> 24);
lba_mode = 1;
} else {
// CHS:
const sect = @intCast(u8, (lba % 63) + 1);
const cyl = (lba + 1 - sect) / (16 * 63);
lba_io[0] = sect;
lba_io[1] = @intCast(u8, (cyl >> 0) & 0xFF);
lba_io[2] = @intCast(u8, (cyl >> 8) & 0xFF);
lba_io[3] = 0;
lba_io[4] = 0;
lba_io[5] = 0;
head = @intCast(u8, (lba + 1 - sect) % (16 * 63) / (63)); // Head number is written to HDDEVSEL lower 4-bits.
lba_mode = 0;
}
// (III) Wait if the drive is busy;
while (self.read(ATA_REG_STATUS) & ATA_SR_BUSY != 0) {} // Wait if busy.)
// (IV) Select Drive from the controller;
if (lba_mode == 0) self.write(ATA_REG_HDDEVSEL, 0xA0 | (self.drive << 4) | head); // Drive & CHS.
if (lba_mode != 0) self.write(ATA_REG_HDDEVSEL, 0xE0 | (self.drive << 4) | head); // Drive & LBA
// (V) Write Parameters;
if (lba_mode == 2) {
self.write(ATA_REG_SECCOUNT1, 0);
self.write(ATA_REG_LBA3, lba_io[3]);
self.write(ATA_REG_LBA4, lba_io[4]);
self.write(ATA_REG_LBA5, lba_io[5]);
}
self.write(ATA_REG_SECCOUNT0, numsects);
self.write(ATA_REG_LBA0, lba_io[0]);
self.write(ATA_REG_LBA1, lba_io[1]);
self.write(ATA_REG_LBA2, lba_io[2]);
// (VI) Select the command and send it;
if (lba_mode == 0 and direction == 0 and !dma) cmd = ATA_CMD_READ_PIO;
if (lba_mode == 1 and direction == 0 and !dma) cmd = ATA_CMD_READ_PIO;
if (lba_mode == 2 and direction == 0 and !dma) cmd = ATA_CMD_READ_PIO_EXT;
if (lba_mode == 0 and direction == 0 and dma) cmd = ATA_CMD_READ_DMA;
if (lba_mode == 1 and direction == 0 and dma) cmd = ATA_CMD_READ_DMA;
if (lba_mode == 2 and direction == 0 and dma) cmd = ATA_CMD_READ_DMA_EXT;
if (lba_mode == 0 and direction == 1 and !dma) cmd = ATA_CMD_WRITE_PIO;
if (lba_mode == 1 and direction == 1 and !dma) cmd = ATA_CMD_WRITE_PIO;
if (lba_mode == 2 and direction == 1 and !dma) cmd = ATA_CMD_WRITE_PIO_EXT;
if (lba_mode == 0 and direction == 1 and dma) cmd = ATA_CMD_WRITE_DMA;
if (lba_mode == 1 and direction == 1 and dma) cmd = ATA_CMD_WRITE_DMA;
if (lba_mode == 2 and direction == 1 and dma) cmd = ATA_CMD_WRITE_DMA_EXT;
self.write(ATA_REG_COMMAND, cmd); // Send the Command.
if (dma) {
//TODO: dma
// if (direction == 0);
// // DMA Read.
// else;
// // DMA Write.
}
if (!dma and direction == 0) {
// PIO Read.
var i: u8 = 0;
while (i < numsects) : (i = i + 1) {
var iedi = buf + i * (words * 2);
try self.poll_check(); // Polling, set error and exit if there is.
// TODO? use selectors for non flat layouts
// asm volatile ("pushw %%es");
// asm volatile ("mov %[a], %%es"
// :
// : [a] "{eax}" (selector)
// );
asm volatile ("rep insw"
: [iedi] "={edi}" (iedi),
[words] "={ecx}" (words)
: [bus] "{dx}" (bus),
[iedi] "0" (iedi),
[words] "1" (words)
: "memory", "cc"
);
// asm volatile ("popw %%es");
// x86.hang();
}
}
if (!dma and direction == 1) {
// PIO Write.
var i: u8 = 0;
while (i < numsects) : (i = i + 1) {
var iedi = buf + i * (words * 2);
self.poll(); // Polling.
asm volatile ("pushw %%ds");
asm volatile ("mov %%ax, %%ds"
: [selector] "={eax}" (selector)
);
asm volatile ("rep outsw"
:
: [words] "{ecx}" (words),
[bus] "{dx}" (bus),
[iedi] "{esi}" (iedi)
); // Send Data
asm volatile ("popw %%ds");
}
if (lba_mode == 2) self.write(ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH_EXT);
if (lba_mode != 2) self.write(ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
try self.poll_check(); // Polling.
}
}
};
var ide_device_0: ?*IDEDevice = null;
var ide_device_1: ?*IDEDevice = null;
var ide_device_2: ?*IDEDevice = null;
var ide_device_3: ?*IDEDevice = null;
const IDEChannelRegister = struct {
base: u16, // I/O Base.
ctrl: u16, // Control Base
bmide: u16, // Bus Master IDE
nIEN: u8, // nIEN (No Interrupt);
};
pub const first_ide_drive = kernel.bio.BlockDev(512){
.read = ide_block_read,
.write = null,
};
pub fn ide_block_read(lba: u64, buf: *[512]u8) void {
// read 1 sector on drive 0
return ide_device_0.?.read_sectors(1, lba, 0x10, @ptrToInt(buf)) catch unreachable;
}
pub fn init(dev: kernel.pci.PciDevice) void {
kernel.println("-- ide init --", .{});
kernel.print("[ide] ", .{});
dev.format();
assert(dev.header_type() == 0x0); // mass storage device
dev.config_write(@intCast(u8, 0xfe), 0x3c);
if (dev.intr_line() == 0xfe) {
kernel.println("[ide] detected ATA device", .{});
} else {
kernel.println("[ide] Potential SATA device. Not implemented. Hanging", .{});
x86.hang();
}
const BAR0 = @intCast(u16, dev.bar(0));
const BAR1 = @intCast(u16, dev.bar(1));
const BAR2 = @intCast(u16, dev.bar(2));
const BAR3 = @intCast(u16, dev.bar(3));
const BAR4 = @intCast(u16, dev.bar(4));
var channels: [2]IDEChannelRegister = undefined;
channels[ATA_PRIMARY].base = if (BAR0 == 0) 0x1f0 else BAR0;
channels[ATA_PRIMARY].ctrl = if (BAR1 == 0) 0x3F6 else BAR1;
channels[ATA_SECONDARY].base = if (BAR2 == 0) 0x170 else BAR2;
channels[ATA_SECONDARY].ctrl = if (BAR3 == 0) 0x376 else BAR3;
channels[ATA_PRIMARY].bmide = (BAR4 & 0xFFFC); // Bus Master IDE
channels[ATA_SECONDARY].bmide = (BAR4 & 0xFFFC) + 8; // Bus Master IDE
ide_device_0 = IDEDevice.init(channels[ATA_PRIMARY], 0) catch unreachable;
ide_device_1 = IDEDevice.init(channels[ATA_PRIMARY], 1) catch unreachable;
ide_device_2 = IDEDevice.init(channels[ATA_SECONDARY], 0) catch unreachable;
ide_device_3 = IDEDevice.init(channels[ATA_SECONDARY], 1) catch unreachable;
}

8
src/driver/index.zig Normal file
View file

@ -0,0 +1,8 @@
// pub usingnamespace @import("../common.zig");
// pub const kernel = @import("../index.zig");
pub const x86 = @import("../arch/x86/index.zig");
pub const ide = @import("ide.zig");
pub const virtio = @import("virtio.zig");

48
src/driver/virtio.zig Normal file
View file

@ -0,0 +1,48 @@
usingnamespace @import("index.zig");
pub fn init(dev: kernel.pci.PciDevice) void {
kernel.println("-- virtio-block init --", .{});
dev.format();
assert(dev.header_type() == 0x0); // mass storage device
assert(dev.subsystem() == 0x2); // virtio-block
const intr_line = dev.config_read(u8, 0x3c);
const intr_pin = dev.config_read(u8, 0x3d);
const min_grant = dev.config_read(u8, 0x3e);
const max_lat = dev.config_read(u8, 0x3f);
kernel.println("{x} {} {} {}", .{ intr_line, intr_pin, min_grant, max_lat });
// all virtio
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | dev features |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | guest features |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | queue address |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | queue size | queue notify |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | device status | isr status |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// println("dev feats =0x{x}", dev.config_read(u32, 0x10));
// println("guest feats =0x{x}", dev.config_read(u32, 0x14));
// println("queue addr =0x{x}", dev.config_read(u32, 0x18));
// println("queue size =0x{x}", dev.config_read(u16, 0x1c));
// println("queue select =0x{x}", dev.config_read(u16, 0x1e));
// println("queue notify =0x{x}", dev.config_read(u16, 0x20));
// println("device status=0x{x}", dev.config_read(u8, 0x22));
// println("isr status =0x{x}", dev.config_read(u8, 0x23));
// all virtio-block
// println("Total Sector Count={}", dev.config_read(u32, 0x24));
// println("Total Sector Count={}", dev.config_read(u32, 0x28));
// println("Maximum Seg Size ={}", dev.config_read(u16, 0x2c));
// println("Maximum Seg Count ={}", dev.config_read(u32, 0x30));
// println("Cylinder Count ={}", dev.config_read(u16, 0x34));
// println("Head Count ={}", dev.config_read(u8, 0x36));
// println("Sector Count ={}", dev.config_read(u8, 0x37));
// println("Block Length ={}", dev.config_read(u8, 0x38));
}

View file

@ -1,3 +1,25 @@
pub const dq = @import("delta_queue.zig");
// pub usingnamespace @import("common.zig");
// pub usingnamespace @import("ring_buffer.zig");
pub const vga = @import("vga.zig"); pub const vga = @import("vga.zig");
pub const main = @import("main.zig");
pub const multiboot = @import("multiboot2.zig"); //drivers
pub const driver = @import("driver/index.zig");
//arch
pub const x86 = @import("arch/x86/index.zig");
//core
pub const constants = @import("constants.zig");
pub const layout = @import("layout.zig");
pub const multiboot = @import("multiboot.zig");
pub const vmem = @import("vmem.zig");
pub const task = @import("task.zig");
pub const time = @import("time.zig");
//extra
// pub const console = @import("console.zig");
// pub const bio = @import("bio.zig");
pub const pci = @import("pci/pci.zig");
pub const ps2 = @import("ps2.zig");

17
src/layout.zig Normal file
View file

@ -0,0 +1,17 @@
//https://wiki.osdev.org/Memory_Map_(x86)
// virtual memory layout of the kernel
const kiB = 1024;
const MiB = 1024 * kiB;
const GiB = 1024 * MiB;
// zig fmt: off
pub const KSTACK = 0x80000; // todo: move to .bss
pub const KERNEL = 1 * MiB;
pub const IDENTITY = 4 * MiB; // 0->4MiB
pub const HEAP = 8 * MiB;
pub const HEAP_END = 0x01000000;
pub const USER_STACKS = 0x01000000;
pub const USER_STACKS_END = 0x02000000;
// zig fmt: on

View file

@ -1,38 +1,49 @@
usingnamespace @import("multiboot2.zig"); const std = @import("std");
usingnamespace @import("vga.zig"); const builtin = std.builtin;
const kernel = @import("index.zig");
const arch = @import("arch/x86/lib/index.zig"); // Place the header at the very beginning of the binary.
const x86 = @import("arch/x86/main.zig"); export const multiboot_header align(4) linksection(".multiboot") = multiboot: {
const multiboot = @import("multiboot2.zig"); const MAGIC = @as(u32, 0x1BADB002); // multiboot magic
const ALIGN = @as(u32, 1 << 0); // Align loaded modules at 4k
const MEMINFO = @as(u32, 1 << 1); // Receive a memory map from the bootloader.
// const ADDR = @as(u32, 1 << 16); // Load specific addr
const FLAGS = ALIGN | MEMINFO; // Combine the flags.
const assert = @import("std").debug.assert; break :multiboot kernel.multiboot.MultibootHeader{
const pci = @import("pci.zig"); .magic = MAGIC,
const console = @import("console.zig"); .flags = FLAGS,
.checksum = ~(MAGIC +% FLAGS) +% 1,
};
};
// arch independant initialization // arch independant initialization
export fn kmain(magic: u32, info_addr: u32) noreturn { export fn kmain(magic: u32, info: *const kernel.multiboot.MultibootInfo) noreturn {
println("--- hello x86_main ---"); std.debug.assert(magic == kernel.multiboot.MULTIBOOT_BOOTLOADER_MAGIC);
assert(magic == MULTIBOOT2_BOOTLOADER_MAGIC); kernel.vga.clear();
kernel.vga.println("--- x86 initialization ---", .{});
kernel.x86.x86_main(info);
kernel.vga.println("--- core initialization ---", .{});
kernel.vmem.init();
kernel.pci.scan();
kernel.vga.println("--- finished booting --- ", .{});
const info = multiboot.load(info_addr); // kernel.task.cleaner_task = kernel.task.new(@ptrToInt(kernel.task.cleaner_loop)) catch unreachable;
console.initialize(); // _ = kernel.task.new(@ptrToInt(kernel.vga.topbar)) catch unreachable;
// _ = kernel.task.new(@ptrToInt(kernel.console.loop)) catch unreachable;
println("--- hello x86_main ---"); // var buf = kernel.vmem.allocator.create([512]u8) catch unreachable;
// kernel.vga.println("buf at 0x{x}", .{@ptrToInt(buf)});
// kernel.driver.ide.first_ide_drive.read(2, buf);
x86.x86_main(&info); // const sig = buf[56..58];
// kernel.vga.println("sig: {x}", .{sig});
// pagefault_test(0xfeffc000); // kernel.task.terminate();
println("--- end ---");
while (true) {}
} }
fn pagefault_test(addr: u32) void { // pub fn panic(a: []const u8, b: ?*builtin.StackTrace) noreturn {
const ptr = @intToPtr(*volatile u8, addr); // kernel.vga.println("{}", .{a});
var a: u8 = ptr.*; // kernel.vga.println("{}", .{b});
printf("a = {}\n", a); // while (true) asm volatile ("hlt");
// }
ptr.* += 1;
printf("a = {}\n", ptr.*);
}

138
src/multiboot.zig Normal file
View file

@ -0,0 +1,138 @@
// MULTIBOOT1
// https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
// This should be in EAX.
pub const MULTIBOOT_BOOTLOADER_MAGIC = 0x2BADB002;
// Is there basic lower/upper memory information?
pub const MULTIBOOT_INFO_MEMORY = 0x00000001;
// Is there a full memory map?
pub const MULTIBOOT_INFO_MEM_MAP = 0x00000040;
// System information structure passed by the bootloader.
pub const MultibootInfo = packed struct {
// Multiboot info version number.
flags: u32,
// Available memory from BIOS.
// present if flags[0]
mem_lower: u32,
mem_upper: u32,
// present if flags[1]
boot_device: u32,
// present if flags[2]
cmdline: u32,
// Boot-Module list.
mods_count: u32,
mods_addr: u32,
syms: packed union {
// present if flags[4]
nlist: packed struct {
tabsize: u32,
strsize: u32,
addr: u32,
_reserved: u32,
},
// present if flags[5]
shdr: packed struct {
num: u32,
size: u32,
addr: u32,
shndx: u32,
},
},
// Memory Mapping buffer.
// present if flags[6]
mmap_length: u32,
mmap_addr: u32,
// Drive Info buffer.
drives_length: u32,
drives_addr: u32,
// ROM configuration table.
config_table: u32,
// Boot Loader Name.
boot_loader_name: u32,
// APM table.
apm_table: u32,
// Video.
vbe_control_info: u32,
vbe_mode_info: u32,
vbe_mode: u16,
vbe_interface_seg: u16,
vbe_interface_off: u16,
vbe_interface_len: u16,
////
// Return the ending address of the last module.
//
pub fn lastModuleEnd(self: *const MultibootInfo) usize {
if (self.mods_count > 0) {
const mods = @intToPtr([*]MultibootModule, self.mods_addr);
return mods[self.mods_count - 1].mod_end;
} else {
return self.mods_addr;
}
}
////
// Load all the modules passed by the bootloader.
//
// pub fn loadModules(self: *const MultibootInfo) void {
// const mods = @intToPtr([*]MultibootModule, self.mods_addr)[0..self.mods_count];
// for (mods) |mod| {
// const cmdline = cstr.toSlice(@intToPtr([*]u8, mod.cmdline));
// tty.step("Loading \"{}\"", cmdline);
// _ = Process.create(mod.mod_start, null);
// // TODO: deallocate the original memory.
// tty.stepOK();
// }
// }
};
// Types of memory map entries.
pub const MULTIBOOT_MEMORY_AVAILABLE = 1;
pub const MULTIBOOT_MEMORY_RESERVED = 2;
// Entries in the memory map.
pub const MultibootMMapEntry = packed struct {
size: u32,
addr: u64,
len: u64,
type: u32,
};
pub const MultibootModule = packed struct {
// The memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive.
mod_start: u32,
mod_end: u32,
cmdline: u32, // Module command line.
pad: u32, // Padding to take it to 16 bytes (must be zero).
};
// Multiboot structure to be read by the bootloader.
pub const MultibootHeader = extern struct {
magic: u32, // Must be equal to header magic number.
flags: u32, // Feature flags.
checksum: u32, // Above fields plus this one must equal 0 mod 2^32.
// following fields are used if flag bit 16 is specified
header_addr: u32 = 0,
load_addr: u32 = 0,
load_end_addr: u32 = 0,
bss_end_addr: u32 = 0,
entry_addr: u32 = 0,
};
// NOTE: this structure is incomplete.

View file

@ -1,97 +0,0 @@
const arch = @import("arch/x86/lib/index.zig");
const PCI_CONFIG_ADDRESS = 0xCF8;
const PCI_CONFIG_DATA = 0xCFC;
const vga = @import("vga.zig");
pub const PciAddress = packed struct {
offset: u8,
function: u3,
slot: u5,
bus: u8,
reserved: u7,
enable: u1,
};
pub const PciDevice = struct {
bus: u8,
slot: u5,
function: u3,
device: u16,
vendor: u16,
class: u8,
subclass: u8,
header_type: u8,
pub fn init(bus: u8, slot: u5, function: u3) ?PciDevice {
var pcidevice = PciDevice{
.bus = bus,
.slot = slot,
.function = function,
.device = undefined,
.class = undefined,
.subclass = undefined,
.header_type = undefined,
.vendor = undefined,
};
pcidevice.vendor = pci_config_read_word(pcidevice, 0);
if (pcidevice.vendor == 0xffff)
return null;
pcidevice.device = pci_config_read_word(pcidevice, 2);
pcidevice.subclass = pci_config_read_byte(pcidevice, 10);
pcidevice.class = pci_config_read_byte(pcidevice, 11);
pcidevice.header_type = pci_config_read_byte(pcidevice, 14);
return (pcidevice);
}
pub fn address(self: PciDevice, offset: u8) u32 {
var addr = PciAddress{
.enable = 1,
.reserved = 0,
.bus = self.bus,
.slot = self.slot,
.function = self.function,
.offset = offset,
};
return (@bitCast(u32, addr));
}
pub fn format(self: PciDevice) void {
vga.printf("{}:{}.{} {x},{x}: {x} {x}\n", self.bus, self.slot, self.function, self.class, self.subclass, self.vendor, self.device);
}
pub fn access(self: PciDevice, offset: u8) void {
arch.outl(PCI_CONFIG_ADDRESS, self.address(offset));
}
pub fn pci_config_read_byte(self: PciDevice, offset: u8) u8 {
self.access(offset);
return (arch.inb(PCI_CONFIG_DATA));
}
pub fn pci_config_read_word(self: PciDevice, offset: u8) u16 {
self.access(offset);
return (arch.inw(PCI_CONFIG_DATA));
}
pub fn pci_config_read_long(self: PciDevice, offset: u8) u32 {
self.access(offset);
return (arch.inl(PCI_CONFIG_DATA));
}
};
pub fn lspci() void {
var slot: u5 = 0;
while (true) {
if (PciDevice.init(0, slot, 0)) |device| {
device.format();
var function: u3 = 0;
while (true) {
if (PciDevice.init(0, slot, function)) |vf|
vf.format();
if (function == 7) break else function += 1;
}
}
if (slot == 31) break else slot += 1;
}
}

189
src/pci/pci.zig Normal file
View file

@ -0,0 +1,189 @@
pub const std = @import("std");
pub const kernel = @import("index.zig");
pub const x86 = @import("x86.zig");
pub const driver = @import("driver/index.zig");
const PCI_CONFIG_ADDRESS = 0xCF8;
const PCI_CONFIG_DATA = 0xCFC;
// https://wiki.osdev.org/Pci
pub const PciAddress = packed struct {
offset: u8,
function: u3,
slot: u5,
bus: u8,
reserved: u7,
enable: u1,
};
pub const PciDevice = struct {
bus: u8,
slot: u5,
function: u3,
vendor: u16 = undefined,
pub fn init(bus: u8, slot: u5, function: u3) ?PciDevice {
var dev = PciDevice{ .bus = bus, .slot = slot, .function = function };
dev.vendor = dev.vendor_id();
if (dev.vendor == 0xffff) return null;
return dev;
}
pub fn address(self: PciDevice, offset: u8) u32 {
var addr = PciAddress{
.enable = 1,
.reserved = 0,
.bus = self.bus,
.slot = self.slot,
.function = self.function,
.offset = offset,
};
return @bitCast(u32, addr);
}
pub fn format(self: PciDevice) void {
kernel.vga.print("{}:{}.{}", .{ self.bus, self.slot, self.function });
kernel.vga.print(" {x},{x:2}", .{ self.class(), self.subclass() });
kernel.vga.print(" 0x{x} 0x{x}", .{ self.vendor, self.device() });
kernel.vga.println(" {}", .{if (self.driver()) |d| d.name else " (none)"});
}
pub fn driver(self: PciDevice) ?Driver {
var i: usize = 0;
while (i < Drivers.len) : (i += 1) {
var drv = Drivers[i];
if (self.class() != drv.class or self.subclass() != drv.subclass)
continue;
if (drv.vendor) |v|
if (self.vendor != v)
continue;
if (drv.subsystem) |ss|
if (self.subsystem() != ss)
continue;
return drv;
}
return null;
}
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | vendor ID | device ID |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | command | status |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | revision ID | prog IF | subclass | class |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |cache line size| latency timer | header type | bist |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
pub fn vendor_id(self: PciDevice) u16 {
return self.config_read(u16, 0x0);
}
pub fn device(self: PciDevice) u16 {
return self.config_read(u16, 0x2);
}
pub fn subclass(self: PciDevice) u8 {
return self.config_read(u8, 0xa);
}
pub fn class(self: PciDevice) u8 {
return self.config_read(u8, 0xb);
}
pub fn header_type(self: PciDevice) u8 {
return self.config_read(u8, 0xe);
}
pub fn intr_line(self: PciDevice) u8 {
return self.config_read(u8, 0x3c);
}
pub fn bar(self: PciDevice, comptime n: usize) u32 {
std.debug.assert(n <= 5);
return self.config_read(u32, 0x10 + 4 * n);
}
// only for header_type == 0
pub fn subsystem(self: PciDevice) u16 {
return self.config_read(u8, 0x2e);
}
pub inline fn config_write(self: PciDevice, value: anytype, comptime offset: u8) void {
// ask for access before writing config
x86.outl(PCI_CONFIG_ADDRESS, self.address(offset));
switch (@TypeOf(value)) {
// read the correct size
u8 => return x86.outb(PCI_CONFIG_DATA, value),
u16 => return x86.outw(PCI_CONFIG_DATA, value),
u32 => return x86.outl(PCI_CONFIG_DATA, value),
else => @compileError("pci config space only supports writing u8, u16, u32."),
}
}
pub inline fn config_read(self: PciDevice, comptime size: type, comptime offset: u8) size {
// ask for access before reading config
x86.outl(PCI_CONFIG_ADDRESS, self.address(offset));
switch (size) {
// read the correct size
u8 => return x86.inb(PCI_CONFIG_DATA),
u16 => return x86.inw(PCI_CONFIG_DATA),
u32 => return x86.inl(PCI_CONFIG_DATA),
else => @compileError("pci config space only supports reading u8, u16, u32."),
}
}
};
const Driver = struct {
name: []const u8,
class: u8,
subclass: u8,
vendor: ?u16 = null,
subsystem: ?u16 = null,
init: fn (PciDevice) void,
};
const Drivers = [_]Driver{
Driver{
.name = "virtio-blk",
.class = 0x1,
.subclass = 0x0,
.vendor = 0x1af4,
.subsystem = 0x2,
.init = driver.virtio.init,
},
Driver{
.name = "ide-ata",
.class = 0x1,
.subclass = 0x1,
.init = driver.ide.init,
},
};
// TODO: factor 2 functions with a closure or a generator when released
pub fn scan() void {
var slot: u5 = 0;
// 0..31
while (slot < 31) : (slot += 1) {
if (PciDevice.init(0, slot, 0)) |dev| {
var function: u3 = 0;
// 0..7
while (function < 7) : (function += 1) {
if (PciDevice.init(0, slot, function)) |vf| {
if (vf.driver()) |d| d.init(vf);
}
}
}
}
}
pub fn lspci() void {
var slot: u5 = 0;
kernel.vga.println("b:s.f c, s vendor device driver", .{});
while (slot < 31) : (slot += 1) {
if (PciDevice.init(0, slot, 0)) |dev| {
var function: u3 = 0;
// 0..7
while (function < 7) : (function += 1) {
if (PciDevice.init(0, slot, function)) |vf| {
vf.format();
}
}
}
}
}

View file

@ -1,10 +1,9 @@
const x86 = @import("arch/x86/lib/index.zig"); usingnamespace @import("index.zig");
const console = @import("console.zig"); const x86 = @import("x86");
const PS2_DATA = 0x60; const PS2_DATA = 0x60;
const PS2_STATUS = 0x64; const PS2_STATUS = 0x64;
const KEYMAP_MAX = 59; const KEYMAP_US = [_][]const u8{
const KEYMAP_US = [_][2]u8{
"\x00\x00", "\x00\x00",
"\x00\x00", //escape "\x00\x00", //escape
"1!", "1!",
@ -19,7 +18,7 @@ const KEYMAP_US = [_][2]u8{
"0)", "0)",
"-_", "-_",
"=+", "=+",
"\x00\x00", //backspace "\x08\x08", //backspace
"\x00\x00", //tab "\x00\x00", //tab
"qQ", "qQ",
"wW", "wW",
@ -68,25 +67,23 @@ const KEYMAP_US = [_][2]u8{
fn ps2_scancode() u8 { fn ps2_scancode() u8 {
var scancode: u8 = 0; var scancode: u8 = 0;
while (true) { while (true) if (x86.io.inb(PS2_DATA) != scancode) {
if (x86.inb(PS2_DATA) != scancode) { scancode = x86.io.inb(PS2_DATA);
scancode = x86.inb(PS2_DATA); if (scancode > 0) return scancode;
if (scancode > 0) };
return scancode;
}
}
} }
fn key_isrelease(scancode: u8) bool { fn key_isrelease(scancode: u8) bool {
return (scancode & 1 << 7 != 0); return scancode & (1 << 7) != 0;
} }
pub var keyboard_callback: fn (u8) void = undefined;
pub fn keyboard_handler() void { pub fn keyboard_handler() void {
const scancode = ps2_scancode(); const scancode = ps2_scancode();
const isrelease = key_isrelease(scancode); if (scancode > KEYMAP_US.len) return;
if (isrelease) { if (key_isrelease(scancode)) return; // don't process releases
return; const shift = false; // don't know about modifiers yet
} const character = KEYMAP_US[scancode][if (shift) 1 else 0];
const shift = false; if (keyboard_callback != undefined) keyboard_callback(character);
console.keypress(KEYMAP_US[scancode][if (shift) 1 else 0]);
} }

32
src/ring_buffer.zig Normal file
View file

@ -0,0 +1,32 @@
usingnamespace @import("index.zig");
pub fn Ring(comptime T: type) type {
return struct {
const Self = @This();
const Size = u10; // 0-1023
const size = @import("std").math.maxInt(Size);
allocator: std.mem.Allocator = undefined,
buffer: *[size]T,
task: ?*task.TaskNode = null,
read_index: Size = 0,
write_index: Size = 0,
pub fn init(ring: *Self, alloc: std.mem.Allocator) !void {
ring.allocator = alloc;
ring.buffer = try ring.allocator.create(@TypeOf(ring.buffer.*));
}
pub fn write(ring: *Self, elem: T) void {
ring.buffer[ring.write_index] = elem;
ring.write_index +%= 1;
if (ring.task) |t| task.unblock(t);
}
pub fn read(ring: *Self) ?T {
if (ring.write_index == ring.read_index) return null;
const id = ring.read_index;
ring.read_index +%= 1; // add with overflow to loop the ring
return ring.buffer[id];
}
};
}

366
src/task.zig Normal file
View file

@ -0,0 +1,366 @@
const kernel = @import("kernel");
const std = @import("std");
const x86 = @import("x86");
var boot_task = Task{ .tid = 0, .esp = 0x47, .state = .Running, .born = true };
var tid_counter: u16 = 1;
pub const TaskNode = std.TailQueue(*Task).Node;
pub const SleepNode = kernel.dq.DeltaQueue(*TaskNode).Node;
pub var current_task: *TaskNode = &TaskNode.init(&boot_task);
pub var cleaner_task: *TaskNode = undefined;
pub var ready_tasks = std.TailQueue(*Task).init();
pub var blocked_tasks = std.TailQueue(*Task).init();
pub var terminated_tasks = std.TailQueue(*Task).init();
pub var sleeping_tasks = kernel.dq.DeltaQueue(*TaskNode).init();
const STACK_SIZE = x86.PAGE_SIZE; // Size of thread stacks.
var timer_last_count: u64 = 0;
pub fn update_time_used() void {
const current_count = kernel.time.offset_us;
const elapsed = current_count - timer_last_count;
// if (current_task.data.tid == 1) kernel.vga.println("{} adding {} time", current_task.data.tid, elapsed);
timer_last_count = current_count;
current_task.data.time_used += elapsed;
}
pub const TaskState = enum {
Running, // <=> current_task
ReadyToRun, // <=> inside of ready_tasks
IOWait, // waiting to be woken up by IO
Paused, // unpaused arbitrarily by another process
Sleeping, // woken up by timer
Terminated, // <=> inside of terminated_tasks, waiting to be destroyed
};
pub const Task = struct {
stack: *[STACK_SIZE]u8 = undefined,
esp: usize,
tid: u16,
time_used: u64 = 0,
born: bool = false,
state: TaskState,
//context: isr.Context,
//cr3: usize,
pub fn create(entrypoint: usize) !*Task {
// Allocate and initialize the thread structure.
var t = try kernel.vmem.allocator.create(Task);
errdefer kernel.vmem.allocator.destroy(t);
t.time_used = 0;
t.state = .ReadyToRun;
t.tid = tid_counter;
tid_counter +%= 1;
std.debug.assert(tid_counter != 0); //overflow
// allocate a new stack
t.stack = try kernel.vmem.allocator.create([STACK_SIZE]u8);
t.esp = @ptrToInt(t.stack) + STACK_SIZE;
// if the tasks rets from its main function, it will go to terminate
// NOTE: if terminate is called this way it has an incorrect ebp!
t.esp -= 4;
@intToPtr(*usize, t.esp).* = @ptrToInt(terminate);
// this will be what ret goes to
t.esp -= 4;
@intToPtr(*usize, t.esp).* = entrypoint;
// this will be popped into ebp
t.esp -= 4;
@intToPtr(*usize, t.esp).* = t.esp + 8;
return t;
}
pub fn destroy(self: *Task) void {
kernel.vmem.allocator.destroy(self.stack);
kernel.vmem.allocator.destroy(self);
}
};
///ASM
extern fn switch_tasks(new_esp: usize, old_esp_addr: usize) void;
pub fn new(entrypoint: usize) !*TaskNode {
kernel.task.lock_scheduler();
defer kernel.task.unlock_scheduler();
const node = try kernel.vmem.allocator.create(TaskNode);
errdefer kernel.vmem.allocator.destroy(node);
node.data = try Task.create(entrypoint);
ready_tasks.prepend(node);
return node;
}
pub fn wakeup_tick(tick: usize) bool {
kernel.task.lock_scheduler();
defer kernel.task.unlock_scheduler();
kernel.task.sleeping_tasks.decrement(tick);
var popped = false;
while (kernel.task.sleeping_tasks.popZero()) |sleepnode| {
const tasknode = sleepnode.data;
tasknode.data.state = .ReadyToRun;
kernel.vmem.allocator.destroy(sleepnode);
ready_tasks.prepend(tasknode);
popped = true;
}
return popped;
}
// TODO: make a sleep without malloc
pub fn usleep(usec: u64) !void {
std.debug.assert(current_task.data.state == .Running);
update_time_used();
const node = try kernel.vmem.allocator.create(SleepNode);
lock_scheduler();
defer unlock_scheduler();
current_task.data.state = .Sleeping;
node.data = current_task;
node.counter = usec;
sleeping_tasks.insert(node);
schedule();
}
pub fn block(state: TaskState) void {
std.debug.assert(current_task.data.state == .Running);
std.debug.assert(state != .Running);
std.debug.assert(state != .ReadyToRun);
update_time_used();
// println("blocking {} as {}", current_task.data.tid, state);
lock_scheduler();
defer unlock_scheduler();
current_task.data.state = state;
blocked_tasks.append(current_task);
schedule();
}
pub fn unblock(node: *TaskNode) void {
if (node.data.state != .Paused and node.data.state != .IOWait) return;
lock_scheduler();
defer unlock_scheduler();
node.data.state = .ReadyToRun;
blocked_tasks.remove(node);
// TODO: find a way to fastpath here, hard because of unblock inside of interrupts
// if (current_task.data.state != .Running and ready_tasks.first == null) {
// // Only one task was running before, fastpath
// switch_to(node);
// } else {
// // There's at least one task on the "ready to run" queue already, so don't pre-empt
// ready_tasks.append(node);
// }
ready_tasks.append(node);
}
pub fn terminate() noreturn {
std.debug.assert(current_task.data.state == .Running);
lock_scheduler();
current_task.data.state = .Terminated;
terminated_tasks.append(current_task);
unblock(cleaner_task);
unlock_scheduler();
preempt();
unreachable;
}
pub fn cleaner_loop() noreturn {
while (true) {
if (terminated_tasks.popFirst()) |n| {
notify("DESTROYING {}", .{n.data.tid});
n.data.destroy();
kernel.vmem.allocator.destroy(n);
} else {
notify("NOTHING TO CLEAN", .{});
block(.Paused);
}
}
}
pub var IRQ_disable_counter: usize = 0;
pub var postpone_task_switches_counter: isize = 0; // this counter can go negative when we are scheduling after a postpone
pub var postpone_task_switches_flag: bool = false;
fn lock_scheduler() void {
if (kernel.constants.SMP == false) {
x86.instr.cli();
IRQ_disable_counter += 1;
postpone_task_switches_counter += 1;
}
}
fn unlock_scheduler() void {
if (kernel.constants.SMP == false) {
std.debug.assert(IRQ_disable_counter > 0);
postpone_task_switches_counter -= 1;
if (postpone_task_switches_flag == true and postpone_task_switches_counter == 0) {
postpone_task_switches_flag = false;
notify("AFTER POSTPONE", .{});
schedule();
}
IRQ_disable_counter -= 1;
// must be the last instruction because we do interrupts inside interrupts
if (IRQ_disable_counter == 0) x86.instr.sti();
}
}
pub fn preempt() void {
if (current_task.data.state != .Running and current_task.data.state != .Terminated) return;
update_time_used();
if (ready_tasks.first == null) {
notify("NO PREEMPT SINGLE TASK", .{});
kernel.time.task_slice_remaining = 0;
return;
}
lock_scheduler();
schedule();
unlock_scheduler();
}
// expects:
// - chosen is .ReadyToRun
// - chosen is not in any scheduler lists
// - current_task has been moved to a queue
// - scheduler is locked
// - the tasks being switched to will unlock_scheduler()
pub fn switch_to(chosen: *TaskNode) void {
std.debug.assert(chosen.data.state == .ReadyToRun);
std.debug.assert(current_task.data.state != .Running);
// save old stack
const old_task_esp_addr = &current_task.data.esp;
// switch states
chosen.data.state = .Running;
current_task = chosen;
if (ready_tasks.first == null) kernel.time.task_slice_remaining = 0;
if (ready_tasks.first != null) kernel.time.task_slice_remaining = kernel.time.TASK_SLICE;
// we don't have any startup code for tasks, so i do it here
if (current_task.data.born == false) {
current_task.data.born = true;
unlock_scheduler();
}
// don't inline the asm function, it needs to ret
@call(
.{ .modifier = .never_inline },
switch_tasks,
.{ chosen.data.esp, @ptrToInt(old_task_esp_addr) },
);
}
pub var CPU_idle_time: u64 = 0;
pub var CPU_idle_start_time: u64 = 0;
// expects:
// lock_scheduler should be called before
// unlock_scheduler should be called after
// current_task is blocked or running (preemption)
pub fn schedule() void {
std.debug.assert(IRQ_disable_counter > 0);
std.debug.assert(current_task.data.state != .ReadyToRun);
// postponed
if (postpone_task_switches_counter != 0 and current_task.data.state == .Running) {
postpone_task_switches_flag = true;
notify("POSTPONING SCHEDULE", .{});
return;
}
// next task
if (ready_tasks.popFirst()) |t| {
t.prev = null;
t.next = null;
// notify("SWITCHING TO 0x{x}", t.data.esp);
if (current_task.data.state == .Running) {
current_task.data.state = .ReadyToRun;
ready_tasks.append(current_task);
}
return switch_to(t);
}
// single task
if (current_task.data.state == .Running) {
notify("SINGLE TASK", .{});
kernel.time.task_slice_remaining = 0;
return;
}
// no tasks
idle_mode();
}
fn idle_mode() void {
std.debug.assert(ready_tasks.first == null);
std.debug.assert(current_task.data.state != .Running);
std.debug.assert(current_task.data.state != .ReadyToRun);
notify("IDLE", .{});
// borrow the current task
const borrow = current_task;
CPU_idle_start_time = kernel.time.offset_us; //for power management
while (true) { // idle loop
if (ready_tasks.popFirst()) |t| { // found a new task
CPU_idle_time += kernel.time.offset_us - CPU_idle_start_time; // count time as idle
timer_last_count = kernel.time.offset_us; // don't count time as used
// println("went into idle mode for {}usecs", time.offset_us - CPU_idle_start_time);
if (t == borrow) {
t.data.state = .Running;
return; //no need to ctx_switch we are already running this
}
return switch_to(t);
} else { // no tasks ready, let the timer fire
x86.instr.sti(); // enable interrupts to allow the timer to fire
x86.instr.hlt(); // halt and wait for the timer to fire
x86.instr.cli(); // disable interrupts again to see if there is something to do
}
}
}
pub fn notify(comptime message: []const u8, args: anytype) void {
const bg = kernel.vga.background;
const fg = kernel.vga.foreground;
const cursor = kernel.vga.cursor;
kernel.vga.background = fg;
kernel.vga.foreground = bg;
kernel.vga.cursor = 80 - message.len;
kernel.vga.cursor_enabled = false;
kernel.vga.print(message, args);
kernel.vga.cursor_enabled = true;
kernel.vga.cursor = cursor;
kernel.vga.background = bg;
kernel.vga.foreground = fg;
}
pub fn format_short() void {
kernel.vga.print("{}R {}B {}S", .{ ready_tasks.len, blocked_tasks.len, sleeping_tasks.len });
}
pub fn format() void {
update_time_used();
kernel.vga.println("{}", .{current_task.data});
var it = ready_tasks.first;
while (it) |node| : (it = node.next) kernel.vga.println("{}", .{node.data});
it = blocked_tasks.first;
while (it) |node| : (it = node.next) kernel.vga.println("{}", .{node.data});
var sit = sleeping_tasks.first;
while (sit) |node| : (sit = node.next) kernel.vga.println("{} {}", .{ node.data.data, node.counter });
}

32
src/time.zig Normal file
View file

@ -0,0 +1,32 @@
const kernel = @import("index.zig");
const x86 = @import("x86");
pub var offset_us: u64 = 0;
pub var task_slice_remaining: u64 = 0;
pub var TASK_SLICE: u64 = 50 * 1000;
pub fn increment() void {
const tick = x86.interrupt.tick; //us
offset_us += tick; //global time counter
var should_preempt = kernel.task.wakeup_tick(tick);
if (task_slice_remaining != 0) {
// There is a time slice length
if (task_slice_remaining <= tick) should_preempt = true;
if (task_slice_remaining > tick) task_slice_remaining -= tick;
}
if (should_preempt) kernel.task.preempt();
}
pub fn uptime() void {
var offset_ms: u64 = offset_us / 1000;
const offset_s: u64 = offset_ms / 1000;
offset_ms = @mod(offset_ms / 100, 10);
kernel.vga.print("{}.{:.3}", .{ offset_s, offset_ms });
}
pub fn utilisation() void {
kernel.vga.print("{}%", .{100 * (offset_us - kernel.task.CPU_idle_time) / offset_us});
}

View file

@ -1,16 +1,16 @@
const builtin = @import("builtin");
const mem = @import("std").mem;
const arch = @import("arch/x86/lib/index.zig");
const std = @import("std"); const std = @import("std");
const kernel = @import("index.zig");
// VRAM buffer address in physical memory.
pub const VRAM_ADDR = 0xB8000;
pub const VRAM_SIZE = 0x8000;
// Screen size. // Screen size.
pub const VGA_WIDTH = 80; pub const VGA_WIDTH = 80;
pub const VGA_HEIGHT = 25; pub const VGA_HEIGHT = 25;
pub const VGA_SIZE = VGA_WIDTH * VGA_HEIGHT; pub const VGA_SIZE = VGA_WIDTH * VGA_HEIGHT;
pub var vga = VGA.init(VRAM_ADDR); pub var vga = VGA{
.vram = @intToPtr([*]VGAEntry, 0xb8000)[0..0x4000],
.cursor = 80 * 2,
.foreground = Color.Black,
.background = Color.White,
};
// Color codes. // Color codes.
pub const Color = enum(u4) { pub const Color = enum(u4) {
@ -39,66 +39,76 @@ pub const VGAEntry = packed struct {
background: Color, background: Color,
}; };
////
// Enable hardware cursor. // Enable hardware cursor.
//
pub fn enableCursor() void { pub fn enableCursor() void {
outb(0x3D4, 0x0A); kernel.x86.io.outb(0x3D4, 0x0A);
outb(0x3D5, 0x00); kernel.x86.io.outb(0x3D5, 0x00);
} }
////
// Disable hardware cursor. // Disable hardware cursor.
//
pub fn disableCursor() void { pub fn disableCursor() void {
outb(0x3D4, 0x0A); kernel.x86.io.outb(0x3D4, 0x0A);
outb(0x3D5, 1 << 5); kernel.x86.io.outb(0x3D5, 1 << 5);
} }
const Errors = error{}; const Errors = error{};
pub fn printf(comptime format: []const u8, args: ...) void { pub fn print(comptime format: []const u8, args: anytype) void {
var a = std.fmt.format({}, Errors, printCallback, format, args); try std.fmt.format(.{ .writeAll = printCallback }, format, args);
}
pub fn println(comptime format: []const u8, args: ...) void {
var a = std.fmt.format({}, Errors, printCallback, format ++ "\n", args);
} }
fn printCallback(context: void, string: []const u8) Errors!void { pub fn println(comptime format: []const u8, args: anytype) void {
print(format ++ "\n", args);
}
// const time = @import("time.zig");
pub fn clear() void {
vga.clear();
}
pub fn topbar() void {
const bg = vga.background;
const fg = vga.foreground;
// println("topbar1");
while (true) {
const cursor = vga.cursor;
vga.background = Color.Black;
vga.foreground = Color.White;
vga.cursor = 0;
vga.cursor_enabled = false;
kernel.time.uptime();
print(" | ", .{});
kernel.time.utilisation();
print(" | ", .{});
kernel.task.format_short();
println("", .{});
vga.cursor_enabled = true;
vga.cursor = cursor;
vga.background = bg;
vga.foreground = fg;
kernel.task.usleep(50 * 1000) catch unreachable; // 60ms
}
}
fn printCallback(string: []const u8) anyerror!void {
vga.writeString(string); vga.writeString(string);
} }
// VGA status. // VGA status.
pub const VGA = struct { const VGA = struct {
vram: []VGAEntry, vram: []VGAEntry,
cursor: usize, cursor: usize,
cursor_enabled: bool = true,
foreground: Color, foreground: Color,
background: Color, background: Color,
////
// Initialize the VGA status.
//
// Arguments:
// vram: The address of the VRAM buffer.
//
// Returns:
// A structure holding the VGA status.
//
pub fn init(vram: usize) VGA {
return VGA{
.vram = @intToPtr([*]VGAEntry, vram)[0..0x4000],
.cursor = 0,
.foreground = Color.Black,
.background = Color.Brown,
};
}
//// ////
// Clear the screen. // Clear the screen.
//
pub fn clear(self: *VGA) void { pub fn clear(self: *VGA) void {
mem.set(VGAEntry, self.vram[0..VGA_SIZE], self.entry(' ')); std.mem.set(VGAEntry, self.vram[0..VGA_SIZE], self.entry(' '));
self.cursor = 0; self.cursor = 80; // skip 1 line for topbar
self.updateCursor(); self.updateCursor();
} }
@ -127,12 +137,9 @@ pub const VGA = struct {
self.writeChar(' '); self.writeChar(' ');
}, },
// Backspace. // Backspace.
// FIXME: hardcoded 8 here is horrible. '\x08' => {
8 => {
self.cursor -= 1; self.cursor -= 1;
// self.writeChar(' ');
self.vram[self.cursor] = self.entry(' '); self.vram[self.cursor] = self.entry(' ');
// self.cursor -= 1;
}, },
// Any other character. // Any other character.
else => { else => {
@ -140,7 +147,7 @@ pub const VGA = struct {
self.cursor += 1; self.cursor += 1;
}, },
} }
self.updateCursor(); if (self.cursor_enabled) self.updateCursor();
} }
//// ////
@ -150,11 +157,8 @@ pub const VGA = struct {
// string: String to be printed. // string: String to be printed.
// //
pub fn writeString(self: *VGA, string: []const u8) void { pub fn writeString(self: *VGA, string: []const u8) void {
for (string) |char| { for (string) |char| self.writeChar(char);
self.writeChar(char); if (self.cursor_enabled) self.updateCursor();
}
self.updateCursor();
} }
//// ////
@ -162,12 +166,14 @@ pub const VGA = struct {
// //
fn scrollDown(self: *VGA) void { fn scrollDown(self: *VGA) void {
const first = VGA_WIDTH; // Index of first line. const first = VGA_WIDTH; // Index of first line.
const second = 2 * VGA_WIDTH; // Index of first line.
const last = VGA_SIZE - VGA_WIDTH; // Index of last line. const last = VGA_SIZE - VGA_WIDTH; // Index of last line.
// Copy all the screen (apart from the first line) up one line. // Copy all the screen (apart from the first line) up one line.
mem.copy(VGAEntry, self.vram[0..last], self.vram[first..VGA_SIZE]); // std.mem.copy(VGAEntry, self.vram[0..last], self.vram[first .. VGA_SIZE]); // whole screen
std.mem.copy(VGAEntry, self.vram[first..last], self.vram[second..VGA_SIZE]); // skip topbar
// Clean the last line. // Clean the last line.
mem.set(VGAEntry, self.vram[last..VGA_SIZE], self.entry(' ')); std.mem.set(VGAEntry, self.vram[last..VGA_SIZE], self.entry(' '));
// Bring the cursor back to the beginning of the last line. // Bring the cursor back to the beginning of the last line.
self.cursor -= VGA_WIDTH; self.cursor -= VGA_WIDTH;
@ -178,10 +184,10 @@ pub const VGA = struct {
// Use the software cursor as the source of truth. // Use the software cursor as the source of truth.
// //
pub fn updateCursor(self: *const VGA) void { pub fn updateCursor(self: *const VGA) void {
arch.outb(0x3D4, 0x0F); kernel.x86.io.outb(0x3D4, 0x0F);
arch.outb(0x3D5, @truncate(u8, self.cursor)); kernel.x86.io.outb(0x3D5, @truncate(u8, self.cursor));
arch.outb(0x3D4, 0x0E); kernel.x86.io.outb(0x3D4, 0x0E);
arch.outb(0x3D5, @truncate(u8, self.cursor >> 8)); kernel.x86.io.outb(0x3D5, @truncate(u8, self.cursor >> 8));
} }
//// ////
@ -191,11 +197,11 @@ pub const VGA = struct {
pub fn fetchCursor(self: *VGA) void { pub fn fetchCursor(self: *VGA) void {
var cursor: usize = 0; var cursor: usize = 0;
arch.outb(0x3D4, 0x0E); kernel.x86.io.outb(0x3D4, 0x0E);
cursor |= usize(arch.inb(0x3D5)) << 8; cursor |= usize(kernel.x86.io.inb(0x3D5)) << 8;
arch.outb(0x3D4, 0x0F); kernel.x86.outb(0x3D4, 0x0F);
cursor |= arch.inb(0x3D5); cursor |= kernel.x86.io.inb(0x3D5);
self.cursor = cursor; self.cursor = cursor;
} }

60
src/vmem.zig Normal file
View file

@ -0,0 +1,60 @@
const std = @import("std");
const kernel = @import("kernel");
const x86 = @import("x86");
pub var allocator: std.mem.Allocator = undefined;
// TODO: make a better memory allocator
// stupid simple virtual memory allocator
// - does 1:1 virtual/physical mapping
// - no defragmentation
// - no allocation bigger than a page
const stack_size: usize = (kernel.layout.HEAP_END - kernel.layout.HEAP) / kernel.x86.PAGE_SIZE;
var stack_index: usize = 0; // Index into the stack.
var stack: [stack_size]usize = undefined; // Stack of free virtual addresses
pub fn available() usize {
return stack_index * x86.PAGE_SIZE;
}
fn free(addr: usize) void {
x86.paging.unmap(addr);
stack[stack_index] = addr;
stack_index += 1;
}
// const Error = error{OutOfMemory};
fn alloc(
ctx: *anyopaque,
len: usize,
ptr_align: u8,
ret_addr: usize,
) ?[*]u8 {
// new allocation
std.debug.assert(len < x86.PAGE_SIZE); // this allocator only support 1:1 mapping
if (available() == 0) return error.OutOfMemory;
stack_index -= 1;
var vaddr: usize = stack[stack_index];
try x86.paging.mmap(vaddr, null);
return @intToPtr([*]u8, vaddr)[0..len];
}
pub fn init() void {
allocator = std.mem.Allocator{
.ptr = undefined,
.vtable = std.mem.Allocator.VTable{
.alloc = alloc,
.resize = undefined,
.free = free,
}
// .reallocFn = realloc,
// .shrinkFn = shrink,
};
var addr: usize = kernel.layout.HEAP;
while (addr < kernel.layout.HEAP_END) : (addr += x86.PAGE_SIZE) {
stack[stack_index] = addr;
stack_index += 1;
}
}