Compare commits
10 commits
7483995316
...
59d3db9738
| Author | SHA1 | Date | |
|---|---|---|---|
| 59d3db9738 | |||
| 77fd0ce4cf | |||
| 08d447115d | |||
| e392799b91 | |||
| 50ac72e24b | |||
| 22f05324fa | |||
| 8380a657ac | |||
| 635134b1e3 | |||
| bc46a138ff | |||
| 2db4f9899a |
29 changed files with 736 additions and 682 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
# hobby kernel in zig
|
# hobby kernel in zig
|
||||||
|
|
||||||
|
# WARNING: this project was written for zig 0.5, it doesn't compile for future versions
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### features
|
### features
|
||||||
|
|
|
||||||
29
build.zig
29
build.zig
|
|
@ -1,10 +1,15 @@
|
||||||
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/main.zig");
|
const kernel = b.addExecutable("kernel", "src/main.zig");
|
||||||
kernel.addPackagePath("kernel", "src/index.zig");
|
kernel.addPackagePath("kernel", "src/index.zig");
|
||||||
kernel.addPackagePath("x86", "src/arch/x86/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");
|
||||||
|
|
@ -13,8 +18,28 @@ pub fn build(b: *Builder) void {
|
||||||
kernel.addAssemblyFile("src/arch/x86/paging.s");
|
kernel.addAssemblyFile("src/arch/x86/paging.s");
|
||||||
kernel.addAssemblyFile("src/arch/x86/switch_tasks.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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
23
qemu.sh
23
qemu.sh
|
|
@ -1,19 +1,21 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# usage: ./qemu.sh start
|
||||||
|
# ./qemu.sh quit
|
||||||
|
|
||||||
QEMU_SOCKET=/tmp/qemu.sock
|
QEMU_SOCKET=/tmp/qemu.sock
|
||||||
QEMU_MONITOR="sudo 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
|
touch disk.img
|
||||||
sudo pkill -9 qemu-system-i386
|
# 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 1341M \
|
|
||||||
-curses \
|
-curses \
|
||||||
|
-m 1341M \
|
||||||
-hda disk.img \
|
-hda disk.img \
|
||||||
-kernel ${KERNEL}
|
-kernel ${KERNEL}
|
||||||
# -drive file=disk.img,if=virtio\
|
# -drive file=disk.img,if=virtio\
|
||||||
|
|
@ -27,16 +29,7 @@ start() {
|
||||||
# -serial mon:stdio \
|
# -serial mon:stdio \
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor() { sudo ${QEMU_MONITOR}; }
|
qemu_quit() { echo quit | sudo ${QEMU_MONITOR} >/dev/null; }
|
||||||
monitor-exec() { echo "$1" | sudo ${QEMU_MONITOR} >/dev/null; }
|
qemu_monitor() { sudo ${QEMU_MONITOR}; }
|
||||||
|
|
||||||
quit() { monitor-exec quit; }
|
qemu_"$@"
|
||||||
|
|
||||||
reload() {
|
|
||||||
monitor-exec stop
|
|
||||||
# monitor "change ide1-cd0 ${KERNEL}"
|
|
||||||
monitor-exec system_reset
|
|
||||||
monitor-exec cont
|
|
||||||
}
|
|
||||||
|
|
||||||
"$@"
|
|
||||||
|
|
|
||||||
|
|
@ -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],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// https://wiki.osdev.org/IDT
|
// https://wiki.osdev.org/IDT
|
||||||
// usingnamespace @import("kernel");
|
|
||||||
// usingnamespace @import("x86");
|
const kernel = @import("kernel");
|
||||||
usingnamespace @import("index.zig");
|
const x86 = @import("x86");
|
||||||
|
|
||||||
// Types of gates.
|
// Types of gates.
|
||||||
pub const INTERRUPT_GATE = 0x8E;
|
pub const INTERRUPT_GATE = 0x8E;
|
||||||
|
|
@ -12,7 +12,7 @@ var idt_table: [256]IDTEntry = undefined;
|
||||||
|
|
||||||
// IDT descriptor register pointing at the IDT.
|
// IDT descriptor register pointing at the IDT.
|
||||||
const idtr = IDTRegister{
|
const idtr = IDTRegister{
|
||||||
.limit = u16(@sizeOf(@typeOf(idt_table))),
|
.limit = @as(u16, @sizeOf(@TypeOf(idt_table))),
|
||||||
.base = &idt_table,
|
.base = &idt_table,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -38,50 +38,52 @@ const IDTRegister = packed struct {
|
||||||
// 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_table[n].offset_low = @truncate(u16, intOffset);
|
idt_table[n].offset_low = @truncate(u16, intOffset);
|
||||||
idt_table[n].offset_high = @truncate(u16, intOffset >> 16);
|
idt_table[n].offset_high = @truncate(u16, intOffset >> 16);
|
||||||
idt_table[n].flags = flags;
|
idt_table[n].flags = flags;
|
||||||
idt_table[n].zero = 0;
|
idt_table[n].zero = 0;
|
||||||
idt_table[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 {
|
||||||
// configure PIC
|
// configure PIC
|
||||||
interrupt.remapPIC();
|
x86.interrupt.remapPIC();
|
||||||
interrupt.configPIT();
|
x86.interrupt.configPIT();
|
||||||
// install ISRs
|
// install ISRs
|
||||||
isr.install_exceptions();
|
x86.isr.install_exceptions();
|
||||||
isr.install_irqs();
|
x86.isr.install_irqs();
|
||||||
isr.install_syscalls();
|
x86.isr.install_syscalls();
|
||||||
interrupt.registerIRQ(0, kernel.time.increment);
|
// x86.interrupt.registerIRQ(0, kernel.time.increment);
|
||||||
interrupt.registerIRQ(1, kernel.ps2.keyboard_handler);
|
// x86.interrupt.registerIRQ(1, kernel.ps2.keyboard_handler);
|
||||||
interrupt.register(1, debug_trap);
|
// x86.interrupt.register(1, debug_trap);
|
||||||
interrupt.register(13, general_protection_fault);
|
// x86.interrupt.register(13, general_protection_fault);
|
||||||
interrupt.register(14, page_fault);
|
// x86.interrupt.register(14, page_fault);
|
||||||
|
|
||||||
// load IDT
|
// load IDT
|
||||||
lidt(@ptrToInt(&idtr));
|
x86.instr.lidt(@ptrToInt(&idtr));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn general_protection_fault() void {
|
fn general_protection_fault() void {
|
||||||
kernel.println("general protection fault");
|
kernel.vga.println("general protection fault", .{});
|
||||||
|
x86.instr.hang();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_trap() void {
|
fn debug_trap() void {
|
||||||
kernel.println("debug fault/trap");
|
kernel.vga.println("debug fault/trap", .{});
|
||||||
kernel.println("dr7: 0b{b}", dr7());
|
kernel.vga.println("dr7: 0b{b}", .{x86.instr.dr7()});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn page_fault() void {
|
fn page_fault() void {
|
||||||
const vaddr = cr2();
|
const vaddr = x86.instr.cr2();
|
||||||
kernel.println("cr2: 0x{x}", vaddr);
|
kernel.vga.println("cr2: 0x{x}", .{vaddr});
|
||||||
kernel.println("phy: 0x{x}", paging.translate(vaddr));
|
kernel.vga.println("phy: 0x{x}", .{x86.paging.translate(vaddr)});
|
||||||
kernel.println("pde: 0x{x} ({})", paging.pde(vaddr), vaddr >> 22);
|
kernel.vga.println("pde: 0x{x} ({})", .{ x86.paging.pde(vaddr), vaddr >> 22 });
|
||||||
kernel.println("pte: 0x{x} ({})", paging.pte(vaddr), vaddr >> 12);
|
kernel.vga.println("pte: 0x{x} ({})", .{ x86.paging.pte(vaddr), vaddr >> 12 });
|
||||||
// paging.format();
|
// paging.format();
|
||||||
while (true) asm volatile ("hlt");
|
x86.instr.hang();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ pub const kernel = @import("../../index.zig");
|
||||||
|
|
||||||
// x86 namespace
|
// x86 namespace
|
||||||
pub const PAGE_SIZE: usize = 4096;
|
pub const PAGE_SIZE: usize = 4096;
|
||||||
pub usingnamespace @import("lib/io.zig");
|
pub const io = @import("lib/io.zig");
|
||||||
pub usingnamespace @import("lib/instructions.zig");
|
pub const instr = @import("lib/instructions.zig");
|
||||||
pub usingnamespace @import("main.zig");
|
pub usingnamespace @import("main.zig");
|
||||||
pub const pmem = @import("pmem.zig");
|
pub const pmem = @import("pmem.zig");
|
||||||
pub const paging = @import("paging.zig");
|
pub const paging = @import("paging.zig");
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
usingnamespace @import("index.zig");
|
const std = @import("std");
|
||||||
|
const kernel = @import("kernel");
|
||||||
|
const x86 = @import("x86");
|
||||||
|
|
||||||
// PIC ports.
|
// PIC ports.
|
||||||
const PIC1_CMD = 0x20;
|
const PIC1_CMD = 0x20;
|
||||||
|
|
@ -28,39 +30,37 @@ 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;
|
||||||
kernel.print("unhandled interrupt number {d}", n);
|
kernel.vga.print("unhandled interrupt number {d}", .{n});
|
||||||
if (n < IRQ_0) {
|
if (n < IRQ_0) kernel.vga.println(" (exception)", .{});
|
||||||
kernel.println(" (exception)");
|
if (n >= IRQ_0) kernel.vga.println(" (IRQ number {d})", .{n - IRQ_0});
|
||||||
} else {
|
x86.instr.hang();
|
||||||
kernel.println(" (IRQ number {d})", n - IRQ_0);
|
|
||||||
}
|
|
||||||
hang();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn picwait() void {
|
inline fn picwait() void {
|
||||||
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 => {
|
||||||
kernel.println("");
|
kernel.vga.println("", .{});
|
||||||
kernel.println("num: {}", isr.context.interrupt_n);
|
kernel.vga.println("num: {}", .{n});
|
||||||
kernel.println("err: {}", isr.context.error_code);
|
kernel.vga.println("err: {}", .{@truncate(u8, x86.isr.context.error_code)});
|
||||||
kernel.println("ip: 0x{x}", isr.context.eip);
|
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]();
|
return handlers[n]();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -76,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 {
|
||||||
|
|
@ -100,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.
|
||||||
outb(PIC1_CMD, ISR_READ);
|
x86.io.outb(PIC1_CMD, ISR_READ);
|
||||||
const in_service = 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;
|
||||||
|
|
@ -111,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);
|
||||||
outb(PIC1_CMD, ACK);
|
x86.io.outb(PIC1_CMD, ACK);
|
||||||
outb(PIC2_CMD, ACK);
|
x86.io.outb(PIC2_CMD, ACK);
|
||||||
} else {
|
} else {
|
||||||
maskIRQ(irq, true);
|
maskIRQ(irq, true);
|
||||||
outb(PIC1_CMD, ACK);
|
x86.io.outb(PIC1_CMD, ACK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,65 +123,68 @@ 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);
|
||||||
outb(PIC2_CMD, ACK);
|
x86.io.outb(PIC2_CMD, ACK);
|
||||||
} else {
|
} else {
|
||||||
maskIRQ(irq, false);
|
maskIRQ(irq, false);
|
||||||
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.
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remapPIC() void {
|
pub fn remapPIC() void {
|
||||||
// ICW1: start initialization sequence.
|
// ICW1: start initialization sequence.
|
||||||
outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
|
x86.io.outb(PIC1_CMD, ICW1_INIT | ICW1_ICW4);
|
||||||
picwait();
|
picwait();
|
||||||
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.
|
||||||
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();
|
||||||
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.
|
||||||
outb(PIC1_DATA, 1 << 2);
|
x86.io.outb(PIC1_DATA, 1 << 2);
|
||||||
picwait();
|
picwait();
|
||||||
outb(PIC2_DATA, 2);
|
x86.io.outb(PIC2_DATA, 2);
|
||||||
picwait();
|
picwait();
|
||||||
|
|
||||||
// ICW4: 80x86 mode.
|
// ICW4: 80x86 mode.
|
||||||
outb(PIC1_DATA, ICW4_8086);
|
x86.io.outb(PIC1_DATA, ICW4_8086);
|
||||||
picwait();
|
picwait();
|
||||||
outb(PIC2_DATA, ICW4_8086);
|
x86.io.outb(PIC2_DATA, ICW4_8086);
|
||||||
picwait();
|
picwait();
|
||||||
|
|
||||||
// Mask all IRQs.
|
// Mask all IRQs.
|
||||||
outb(PIC1_DATA, 0xFF);
|
x86.io.outb(PIC1_DATA, 0xFF);
|
||||||
picwait();
|
picwait();
|
||||||
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) return;
|
if (irq > 15) 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 = 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);
|
const shift = @truncate(u3, irq % 8);
|
||||||
if (mask) outb(port, old | (u8(1) << shift));
|
// const shift = @truncate(u3, if (irq < 8) irq else irq - 8);
|
||||||
if (!mask) outb(port, old & ~(u8(1) << shift));
|
const bit = @as(u8, 1) << shift;
|
||||||
const new = inb(port); // Retrieve the current mask.
|
if (mask) x86.io.outb(port, old | bit);
|
||||||
|
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
|
// configures the chan0 with a rate generator, which will trigger irq0
|
||||||
|
|
@ -189,10 +192,11 @@ pub const divisor = 2685;
|
||||||
pub const tick = 2251; // f = 1.193182 MHz, TODO: turn into a function
|
pub const tick = 2251; // f = 1.193182 MHz, TODO: turn into a function
|
||||||
pub fn configPIT() void {
|
pub fn configPIT() void {
|
||||||
const chanNum = 0;
|
const chanNum = 0;
|
||||||
const chan = PIT_CHAN0;
|
// const chan = PIT_CHAN0;
|
||||||
const LOHI = 0b11; // bit4 | bit5
|
const LOHI = 0b11; // bit4 | bit5
|
||||||
const PITMODE_RATE_GEN = 0x2;
|
const PITMODE_RATE_GEN = 0x2;
|
||||||
outb(PIT_CMD, chanNum << 6 | LOHI << 4 | PITMODE_RATE_GEN << 1);
|
|
||||||
outb(PIT_CHAN0, divisor & 0xff);
|
x86.io.outb(PIT_CMD, chanNum << 6 | LOHI << 4 | PITMODE_RATE_GEN << 1);
|
||||||
outb(PIT_CHAN0, divisor >> 8);
|
x86.io.outb(PIT_CHAN0, divisor & 0xff);
|
||||||
|
x86.io.outb(PIT_CHAN0, divisor >> 8);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
usingnamespace @import("index.zig");
|
const idt = @import("idt.zig");
|
||||||
|
|
||||||
// Interrupt Service Routines defined externally in assembly.
|
// Interrupt Service Routines defined externally in assembly.
|
||||||
extern fn isr0() void;
|
extern fn isr0() void;
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,18 +30,18 @@ pub inline fn int3() void {
|
||||||
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 {
|
pub fn cr2() usize {
|
||||||
return asm volatile ("movl %%cr2, %[result]"
|
return asm volatile ("movl %%cr2, %[result]"
|
||||||
: [result] "=r" (-> usize)
|
: [result] "=r" (-> usize),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dr7() usize {
|
pub fn dr7() usize {
|
||||||
return asm volatile ("movl %%dr7, %[result]"
|
return asm volatile ("movl %%dr7, %[result]"
|
||||||
: [result] "=r" (-> usize)
|
: [result] "=r" (-> usize),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,33 @@
|
||||||
|
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: var, cnt: usize) void {
|
pub inline fn insl(port: u16, addr: anytype, cnt: usize) void {
|
||||||
asm volatile ("cld; repne; insl;"
|
asm volatile ("cld; repne; insl;"
|
||||||
: [addr] "={edi}" (addr),
|
: [addr] "={edi}" (addr),
|
||||||
[cnt] "={ecx}" (cnt)
|
[cnt] "={ecx}" (cnt),
|
||||||
: [port] "{dx}" (port),
|
: [port] "{dx}" (port),
|
||||||
[addr] "0" (addr),
|
[addr] "0" (addr),
|
||||||
[cnt] "1" (cnt)
|
[cnt] "1" (cnt),
|
||||||
: "memory", "cc"
|
: "memory", "cc"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +36,7 @@ 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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,7 +44,7 @@ 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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,6 +52,6 @@ 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),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
// usingnamespace @import("kernel");
|
const std = @import("std");
|
||||||
usingnamespace @import("index.zig");
|
const kernel = @import("kernel");
|
||||||
|
const x86 = @import("x86");
|
||||||
|
|
||||||
/// x86 specific intialization
|
/// x86 specific intialization
|
||||||
pub fn x86_main(info: *const kernel.multiboot.MultibootInfo) void {
|
pub fn x86_main(info: *const kernel.multiboot.MultibootInfo) void {
|
||||||
gdt.initialize();
|
x86.gdt.initialize();
|
||||||
idt.initialize();
|
x86.idt.initialize();
|
||||||
pmem.initialize(info);
|
x86.pmem.initialize(info);
|
||||||
paging.initialize();
|
x86.paging.initialize();
|
||||||
// enable interrupts
|
x86.instr.sti();
|
||||||
sti();
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
usingnamespace @import("index.zig");
|
const std = @import("std");
|
||||||
|
const kernel = @import("kernel");
|
||||||
|
const x86 = @import("x86");
|
||||||
|
|
||||||
extern fn setupPaging(phys_pd: usize) void;
|
extern fn setupPaging(phys_pd: usize) void;
|
||||||
|
|
||||||
|
|
@ -17,7 +19,7 @@ pub var pageDirectory: [1024]PageEntry align(4096) linksection(".bss") = [_]Page
|
||||||
|
|
||||||
// TODO: inline these
|
// TODO: inline these
|
||||||
fn pageBase(virt: usize) usize {
|
fn pageBase(virt: usize) usize {
|
||||||
return virt & (~PAGE_SIZE +% 1);
|
return virt & (~x86.PAGE_SIZE +% 1);
|
||||||
}
|
}
|
||||||
pub fn pde(virt: usize) *PageEntry {
|
pub fn pde(virt: usize) *PageEntry {
|
||||||
return &PD[virt >> 22]; //relies on recursive mapping
|
return &PD[virt >> 22]; //relies on recursive mapping
|
||||||
|
|
@ -34,18 +36,18 @@ pub fn translate(virt: usize) ?usize {
|
||||||
|
|
||||||
pub fn unmap(virt: usize) void {
|
pub fn unmap(virt: usize) void {
|
||||||
if (translate(virt)) |phys| {
|
if (translate(virt)) |phys| {
|
||||||
pmem.free(phys);
|
x86.pmem.free(phys);
|
||||||
} else {
|
} else {
|
||||||
kernel.println("can't unmap 0x{x} because it is not mapped.", virt);
|
kernel.vga.println("can't unmap 0x{x} because it is not mapped.", .{virt});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mmap(virt: usize, phys: ?usize) !void {
|
pub fn mmap(virt: usize, phys: ?usize) !void {
|
||||||
//TODO: support hugepages
|
//TODO: support hugepages
|
||||||
// allocate a page directory if there is none
|
// allocate a page directory if there is none
|
||||||
if (pde(virt).* == 0) pde(virt).* = (try pmem.allocate()) | WRITE | PRESENT;
|
if (pde(virt).* == 0) pde(virt).* = (try x86.pmem.allocate()) | WRITE | PRESENT;
|
||||||
// allocate a frame if phys isn't specified
|
// allocate a frame if phys isn't specified
|
||||||
pte(virt).* = (if (phys) |p| p else try pmem.allocate()) | PRESENT;
|
pte(virt).* = (if (phys) |p| p else try x86.pmem.allocate()) | PRESENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize() void {
|
pub fn initialize() void {
|
||||||
|
|
@ -57,7 +59,7 @@ pub fn initialize() void {
|
||||||
p2[1023] = @ptrToInt(&p2[0]) | PRESENT | WRITE;
|
p2[1023] = @ptrToInt(&p2[0]) | PRESENT | WRITE;
|
||||||
|
|
||||||
// TODO: verify is this a hack?
|
// TODO: verify is this a hack?
|
||||||
assert(pmem.stack_end < kernel.layout.IDENTITY);
|
std.debug.assert(x86.pmem.stack_end < kernel.layout.IDENTITY);
|
||||||
|
|
||||||
setupPaging(@ptrToInt(&pageDirectory[0])); //asm routine
|
setupPaging(@ptrToInt(&pageDirectory[0])); //asm routine
|
||||||
}
|
}
|
||||||
|
|
@ -67,12 +69,12 @@ pub fn format() void {
|
||||||
i = 0;
|
i = 0;
|
||||||
while (i < 1024) : (i += 1) {
|
while (i < 1024) : (i += 1) {
|
||||||
if (PD[i] == 0) continue;
|
if (PD[i] == 0) continue;
|
||||||
kernel.println("p2[{}] -> 0x{x}", i, PD[i]);
|
kernel.vga.println("p2[{}] -> 0x{x}", .{ i, PD[i] });
|
||||||
if (PD[i] & HUGE != 0) continue;
|
if (PD[i] & HUGE != 0) continue;
|
||||||
var j: usize = 0;
|
var j: usize = 0;
|
||||||
while (j < 1024) : (j += 1) {
|
while (j < 1024) : (j += 1) {
|
||||||
var entry: PageEntry = PT[i * 1024 + j];
|
var entry: PageEntry = PT[i * 1024 + j];
|
||||||
if (entry != 0) kernel.println("p2[{}]p1[{}] -> 0x{x}", i, j, entry);
|
if (entry != 0) kernel.vga.println("p2[{}]p1[{}] -> 0x{x}", .{ i, j, entry });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
usingnamespace @import("index.zig");
|
const std = @import("std");
|
||||||
|
const kernel = @import("kernel");
|
||||||
|
const x86 = @import("x86");
|
||||||
|
|
||||||
var stack: [*]usize = undefined; // Stack of free physical page.
|
var stack: [*]usize = undefined; // Stack of free physical page.
|
||||||
var stack_index: usize = 0; // Index into the stack.
|
var stack_index: usize = 0; // Index into the stack.
|
||||||
|
|
@ -11,13 +13,13 @@ pub inline fn pageAlign(address: u32) u32 {
|
||||||
// 4095 -> 4096
|
// 4095 -> 4096
|
||||||
// 4096 -> 4096
|
// 4096 -> 4096
|
||||||
// 4097 -> 8192
|
// 4097 -> 8192
|
||||||
return (address + PAGE_SIZE - 1) & (~PAGE_SIZE +% 1);
|
return (address + x86.PAGE_SIZE - 1) & (~x86.PAGE_SIZE +% 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the amount of variable elements (in bytes).
|
// Return the amount of variable elements (in bytes).
|
||||||
//
|
//
|
||||||
pub fn available() usize {
|
pub fn available() usize {
|
||||||
return stack_index * PAGE_SIZE;
|
return stack_index * x86.PAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn available_MiB() usize {
|
pub inline fn available_MiB() usize {
|
||||||
|
|
@ -28,7 +30,7 @@ pub inline fn available_MiB() usize {
|
||||||
//
|
//
|
||||||
pub fn allocate() !usize {
|
pub fn allocate() !usize {
|
||||||
if (available() == 0) {
|
if (available() == 0) {
|
||||||
kernel.println("out of memory");
|
kernel.println("out of memory", .{});
|
||||||
return error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
}
|
}
|
||||||
stack_index -= 1;
|
stack_index -= 1;
|
||||||
|
|
@ -52,8 +54,8 @@ pub fn free(address: usize) void {
|
||||||
//
|
//
|
||||||
pub fn initialize(info: *const kernel.multiboot.MultibootInfo) void {
|
pub fn initialize(info: *const kernel.multiboot.MultibootInfo) void {
|
||||||
// Ensure the bootloader has given us the memory map.
|
// Ensure the bootloader has given us the memory map.
|
||||||
assert((info.flags & kernel.multiboot.MULTIBOOT_INFO_MEMORY) != 0);
|
std.debug.assert((info.flags & kernel.multiboot.MULTIBOOT_INFO_MEMORY) != 0);
|
||||||
assert((info.flags & kernel.multiboot.MULTIBOOT_INFO_MEM_MAP) != 0);
|
std.debug.assert((info.flags & kernel.multiboot.MULTIBOOT_INFO_MEM_MAP) != 0);
|
||||||
|
|
||||||
// TODO: WHAT WHY WHAAAAT, must check back here later
|
// TODO: WHAT WHY WHAAAAT, must check back here later
|
||||||
// Place stack at 0x200000 so that in the future I trigger a
|
// Place stack at 0x200000 so that in the future I trigger a
|
||||||
|
|
@ -64,7 +66,7 @@ pub fn initialize(info: *const kernel.multiboot.MultibootInfo) void {
|
||||||
// stack = @intToPtr([*]usize, pageAlign(info.mods_addr));
|
// stack = @intToPtr([*]usize, pageAlign(info.mods_addr));
|
||||||
|
|
||||||
// Calculate the approximate size of the stack based on the amount of total upper memory.
|
// Calculate the approximate size of the stack based on the amount of total upper memory.
|
||||||
stack_size = ((info.mem_upper * 1024) / PAGE_SIZE) * @sizeOf(usize);
|
stack_size = ((info.mem_upper * 1024) / x86.PAGE_SIZE) * @sizeOf(usize);
|
||||||
stack_end = pageAlign(@ptrToInt(stack) + stack_size);
|
stack_end = pageAlign(@ptrToInt(stack) + stack_size);
|
||||||
|
|
||||||
var map: usize = info.mmap_addr;
|
var map: usize = info.mmap_addr;
|
||||||
|
|
@ -78,16 +80,16 @@ pub fn initialize(info: *const kernel.multiboot.MultibootInfo) void {
|
||||||
start = if (start >= stack_end) start else stack_end;
|
start = if (start >= stack_end) start else stack_end;
|
||||||
|
|
||||||
// Flag all the pages in this memory area as free.
|
// Flag all the pages in this memory area as free.
|
||||||
if (entry.type == kernel.multiboot.MULTIBOOT_MEMORY_AVAILABLE) while (start < end) : (start += PAGE_SIZE)
|
if (entry.type == kernel.multiboot.MULTIBOOT_MEMORY_AVAILABLE) while (start < end) : (start += x86.PAGE_SIZE)
|
||||||
free(start);
|
free(start);
|
||||||
|
|
||||||
// Go to the next entry in the memory map.
|
// Go to the next entry in the memory map.
|
||||||
map += entry.size + @sizeOf(@typeOf(entry.size));
|
map += entry.size + @sizeOf(@TypeOf(entry.size));
|
||||||
}
|
}
|
||||||
|
|
||||||
kernel.println("available memory: {d} MiB ", available() / 1024 / 1024);
|
kernel.vga.println("available memory: {d} MiB ", .{available() / 1024 / 1024});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format() void {
|
pub fn format() void {
|
||||||
kernel.println("physframes left: {d} ({d} MiB)", stack_index, available_MiB());
|
kernel.vga.println("physframes left: {d} ({d} MiB)", .{ stack_index, available_MiB() });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/bio.zig
10
src/bio.zig
|
|
@ -1,6 +1,10 @@
|
||||||
// Block Device
|
// Block Device
|
||||||
// Glue code between Driver and FS
|
// Glue code between Driver and FS
|
||||||
|
|
||||||
pub const BlockDev = struct {
|
pub fn BlockDev(comptime sector_size: usize) type {
|
||||||
read: fn (u64) void,
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,17 +31,17 @@ const commands = [_]Command{
|
||||||
|
|
||||||
fn execute(input: []u8) void {
|
fn execute(input: []u8) void {
|
||||||
for (commands) |c| if (std.mem.eql(u8, input, c.name)) return c.f();
|
for (commands) |c| if (std.mem.eql(u8, input, c.name)) return c.f();
|
||||||
println("{}: command not found, list of available commands:", input);
|
println("{}: command not found, list of available commands:", .{input});
|
||||||
for (commands) |c| println("{}", c.name);
|
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' => {
|
||||||
print("\n");
|
print("\n", .{});
|
||||||
if (command_len > 0) execute(command[0..command_len]);
|
if (command_len > 0) execute(command[0..command_len]);
|
||||||
print("> ");
|
print("> ", .{});
|
||||||
command_len = 0;
|
command_len = 0;
|
||||||
},
|
},
|
||||||
'\x00' => return,
|
'\x00' => return,
|
||||||
|
|
@ -67,10 +67,10 @@ pub fn keyboard_callback(char: u8) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn loop() void {
|
pub fn loop() void {
|
||||||
input_ring.init() catch unreachable;
|
input_ring.init(vmem.allocator) catch unreachable;
|
||||||
input_ring.task = task.current_task;
|
input_ring.task = task.current_task;
|
||||||
ps2.keyboard_callback = keyboard_callback;
|
ps2.keyboard_callback = keyboard_callback;
|
||||||
print("> ");
|
print("> ", .{});
|
||||||
while (true) {
|
while (true) {
|
||||||
while (input_ring.read()) |c| keypress(c);
|
while (input_ring.read()) |c| keypress(c);
|
||||||
task.block(.IOWait);
|
task.block(.IOWait);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
/// DeltaQueue is a singly-linked list where each
|
/// DeltaQueue is a singly-linked list where each
|
||||||
/// node has a counter. Each counter is relative
|
/// node has a counter. Each counter is relative
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,14 @@ const ATA_CMD_IDENTIFY_PACKET = 0xA1;
|
||||||
const ATA_CMD_IDENTIFY = 0xEC;
|
const ATA_CMD_IDENTIFY = 0xEC;
|
||||||
|
|
||||||
// Status:
|
// Status:
|
||||||
const ATA_SR_BSY = 0x80; // Busy
|
const ATA_SR_BUSY = 0x80;
|
||||||
const ATA_SR_DRDY = 0x40; // Drive ready
|
const ATA_SR_DRDY = 0x40; // Drive ready
|
||||||
const ATA_SR_DF = 0x20; // Drive write fault
|
const ATA_SR_DF = 0x20; // Drive write fault
|
||||||
const ATA_SR_DSC = 0x10; // Drive seek complete
|
const ATA_SR_DSC = 0x10; // Drive seek complete
|
||||||
const ATA_SR_DRQ = 0x08; // Data request ready
|
const ATA_SR_DRQ = 0x08; // Data request ready
|
||||||
const ATA_SR_CORR = 0x04; // Corrected data
|
const ATA_SR_CORR = 0x04; // Corrected data
|
||||||
const ATA_SR_IDX = 0x02; // Index
|
const ATA_SR_IDX = 0x02;
|
||||||
const ATA_SR_ERR = 0x01; // Error
|
const ATA_SR_ERR = 0x01;
|
||||||
|
|
||||||
// Registers:
|
// Registers:
|
||||||
const ATA_REG_DATA = 0x00;
|
const ATA_REG_DATA = 0x00;
|
||||||
|
|
@ -71,13 +71,12 @@ const ATA_IDENT_MAX_LBA = 120;
|
||||||
const ATA_IDENT_COMMANDSETS = 164;
|
const ATA_IDENT_COMMANDSETS = 164;
|
||||||
const ATA_IDENT_MAX_LBA_EXT = 200;
|
const ATA_IDENT_MAX_LBA_EXT = 200;
|
||||||
|
|
||||||
const ide_buf: [2048]u8 = [1]u8{0} ** 2048;
|
|
||||||
const atapi_packet: [12]u8 = [1]u8{0xA8} ++ [1]u8{0} ** 11;
|
const atapi_packet: [12]u8 = [1]u8{0xA8} ++ [1]u8{0} ** 11;
|
||||||
var ide_irq_invoked = false;
|
var ide_buf: [2048]u8 = [1]u8{0} ** 2048;
|
||||||
|
|
||||||
const IDEDevice = struct {
|
const IDEDevice = struct {
|
||||||
reserved: u8, // 0 (Empty) or 1 (This Drive really exists).
|
reserved: u8, // 0 (Empty) or 1 (This Drive really exists).
|
||||||
channel: u8, // 0 (Primary Channel) or 1 (Secondary Channel).
|
channel: IDEChannelRegister,
|
||||||
drive: u8, // 0 (Master Drive) or 1 (Slave Drive).
|
drive: u8, // 0 (Master Drive) or 1 (Slave Drive).
|
||||||
idetype: u16, // 0: ATA, 1:ATAPI.
|
idetype: u16, // 0: ATA, 1:ATAPI.
|
||||||
signature: u16, // Drive Signature
|
signature: u16, // Drive Signature
|
||||||
|
|
@ -85,9 +84,295 @@ const IDEDevice = struct {
|
||||||
commandsets: usize, // Command Sets Supported.
|
commandsets: usize, // Command Sets Supported.
|
||||||
size: usize, // Size in Sectors.
|
size: usize, // Size in Sectors.
|
||||||
model: [41]u8, // Model in string.
|
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_devices: [4]IDEDevice = undefined;
|
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 {
|
const IDEChannelRegister = struct {
|
||||||
base: u16, // I/O Base.
|
base: u16, // I/O Base.
|
||||||
|
|
@ -96,230 +381,27 @@ const IDEChannelRegister = struct {
|
||||||
nIEN: u8, // nIEN (No Interrupt);
|
nIEN: u8, // nIEN (No Interrupt);
|
||||||
};
|
};
|
||||||
|
|
||||||
var channels: [2]IDEChannelRegister = undefined;
|
pub const first_ide_drive = kernel.bio.BlockDev(512){
|
||||||
|
.read = ide_block_read,
|
||||||
|
.write = null,
|
||||||
|
};
|
||||||
|
|
||||||
pub inline fn ide_read(channel: u8, comptime reg: u8) u8 {
|
pub fn ide_block_read(lba: u64, buf: *[512]u8) void {
|
||||||
if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
|
// read 1 sector on drive 0
|
||||||
defer if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
|
return ide_device_0.?.read_sectors(1, lba, 0x10, @ptrToInt(buf)) catch unreachable;
|
||||||
return switch (reg) {
|
|
||||||
0x0...0x7 => x86.inb(channels[channel].base + reg - 0x0),
|
|
||||||
0x8...0xb => x86.inb(channels[channel].base + reg - 0x6),
|
|
||||||
0xc...0xd => x86.inb(channels[channel].ctrl + reg - 0xa),
|
|
||||||
0xe...0x16 => x86.inb(channels[channel].bmide + reg - 0xe),
|
|
||||||
else => @compileError("bad IDE register."),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn ide_read_buffer(channel: u8, comptime reg: u8, buf: var, cnt: usize) void {
|
|
||||||
if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
|
|
||||||
defer if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
|
|
||||||
switch (reg) {
|
|
||||||
0x0...0x7 => x86.insl(channels[channel].base + reg - 0x0, buf, cnt),
|
|
||||||
0x8...0xb => x86.insl(channels[channel].base + reg - 0x6, buf, cnt),
|
|
||||||
0xc...0xd => x86.insl(channels[channel].ctrl + reg - 0xa, buf, cnt),
|
|
||||||
0xe...0x16 => x86.insl(channels[channel].bmide + reg - 0xe, buf, cnt),
|
|
||||||
else => @compileError("bad IDE register."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn ide_write(channel: u8, comptime reg: u8, data: u8) void {
|
|
||||||
if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, 0x80 | channels[channel].nIEN);
|
|
||||||
defer if (reg > 0x07 and reg < 0x0C) ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
|
|
||||||
switch (reg) {
|
|
||||||
0x0...0x7 => x86.outb(channels[channel].base + reg - 0x0, data),
|
|
||||||
0x8...0xb => x86.outb(channels[channel].base + reg - 0x6, data),
|
|
||||||
0xc...0xd => x86.outb(channels[channel].ctrl + reg - 0xa, data),
|
|
||||||
0xe...0x16 => x86.outb(channels[channel].bmide + reg - 0xe, data),
|
|
||||||
else => @compileError("bad IDE register."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn ide_polling(channel: u8, comptime advanced_check: bool) ?u8 {
|
|
||||||
// (I) Delay 400 nanosecond for BSY to be set:
|
|
||||||
for ([_]u8{ 0, 1, 2, 3 }) |_| _ = ide_read(channel, ATA_REG_ALTSTATUS); // wate 100ns per call
|
|
||||||
while (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY != 0) {} // Wait for BSY to be zero.
|
|
||||||
if (advanced_check) {
|
|
||||||
const state = ide_read(channel, ATA_REG_STATUS); // Read Status Register.
|
|
||||||
if (state & ATA_SR_ERR != 0) return u8(2); // Error.
|
|
||||||
if (state & ATA_SR_DF != 0) return 1; // Device Fault.
|
|
||||||
if ((state & ATA_SR_DRQ) == 0) return 3; // DRQ should be set
|
|
||||||
}
|
|
||||||
return null; // No Error.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ide_ata_access(direction: u8, drive: u8, lba: u64, numsects: u8, selector: u16, edi: usize) u8 {
|
|
||||||
var dma = false; // 0: No DMA, 1: DMA
|
|
||||||
var cmd: u8 = 0;
|
|
||||||
var lba_io = [1]u8{0} ** 8;
|
|
||||||
const channel = ide_devices[drive].channel; // Read the Channel.
|
|
||||||
const slavebit = ide_devices[drive].drive; // Read the Drive [Master/Slave]
|
|
||||||
const bus: usize = channels[channel].base; // Bus Base, like 0x1F0 which is also data port.
|
|
||||||
var words: usize = 256; // Almost every ATA drive has a sector-size of 512-byte.
|
|
||||||
|
|
||||||
ide_irq_invoked = false;
|
|
||||||
channels[channel].nIEN = 0x02;
|
|
||||||
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
|
|
||||||
|
|
||||||
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 (ide_devices[drive].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 (ide_read(channel, ATA_REG_STATUS) & ATA_SR_BSY != 0) {} // Wait if busy.)
|
|
||||||
|
|
||||||
// (IV) Select Drive from the controller;
|
|
||||||
if (lba_mode == 0) ide_write(channel, ATA_REG_HDDEVSEL, 0xA0 | (slavebit << 4) | head); // Drive & CHS.
|
|
||||||
if (lba_mode != 0) ide_write(channel, ATA_REG_HDDEVSEL, 0xE0 | (slavebit << 4) | head); // Drive & LBA
|
|
||||||
|
|
||||||
// (V) Write Parameters;
|
|
||||||
if (lba_mode == 2) {
|
|
||||||
ide_write(channel, ATA_REG_SECCOUNT1, 0);
|
|
||||||
ide_write(channel, ATA_REG_LBA3, lba_io[3]);
|
|
||||||
ide_write(channel, ATA_REG_LBA4, lba_io[4]);
|
|
||||||
ide_write(channel, ATA_REG_LBA5, lba_io[5]);
|
|
||||||
}
|
|
||||||
ide_write(channel, ATA_REG_SECCOUNT0, numsects);
|
|
||||||
ide_write(channel, ATA_REG_LBA0, lba_io[0]);
|
|
||||||
ide_write(channel, ATA_REG_LBA1, lba_io[1]);
|
|
||||||
ide_write(channel, 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;
|
|
||||||
ide_write(channel, 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;
|
|
||||||
var iedi = edi;
|
|
||||||
while (i < numsects) : (i = i + 1) {
|
|
||||||
iedi = edi + i * (words * 2);
|
|
||||||
if (ide_polling(channel, true)) |err| return err; // Polling, set error and exit if there is.
|
|
||||||
asm volatile ("pushw %%es");
|
|
||||||
asm volatile ("mov %[a], %%es"
|
|
||||||
:
|
|
||||||
: [a] "{eax}" (selector)
|
|
||||||
);
|
|
||||||
asm volatile ("rep insw"
|
|
||||||
:
|
|
||||||
: [words] "{ecx}" (words),
|
|
||||||
[bus] "{dx}" (bus),
|
|
||||||
[iedi] "{edi}" (iedi)
|
|
||||||
); // Receive Data.
|
|
||||||
asm volatile ("popw %%es");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!dma and direction == 1) {
|
|
||||||
// PIO Write.
|
|
||||||
var i: u8 = 0;
|
|
||||||
var iedi = edi;
|
|
||||||
while (i < numsects) : (i = i + 1) {
|
|
||||||
iedi = edi + i * (words * 2);
|
|
||||||
_ = ide_polling(channel, false); // 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) ide_write(channel, ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH_EXT);
|
|
||||||
if (lba_mode != 2) ide_write(channel, ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH);
|
|
||||||
_ = ide_polling(channel, true); // Polling.
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const blockdev = kernel.bio.BlockDev{ .read = ide_block_read };
|
|
||||||
pub const sectorbuffer = [1]u8{0} ** 512;
|
|
||||||
pub fn ide_block_read(lba: u64) void {
|
|
||||||
_ = ide_read_sectors(0, 1, lba, 0x8, @ptrToInt(§orbuffer[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ide_read_sectors(drive: u8, numsects: u8, lba: u64, es: u8, edi: usize) u8 {
|
|
||||||
// 1: Check if the drive presents:
|
|
||||||
if (drive > 3 or ide_devices[drive].reserved == 0) {
|
|
||||||
return 0x1; // Drive Not Found!
|
|
||||||
} else if (((lba + numsects) > ide_devices[drive].size) and (ide_devices[drive].idetype == IDE_ATA)) {
|
|
||||||
// 2: Check if inputs are valid:
|
|
||||||
return 0x2; // Seeking to invalid position.
|
|
||||||
} else {
|
|
||||||
// 3: Read in PIO Mode through Polling & IRQs:
|
|
||||||
var err: u8 = 0;
|
|
||||||
if (ide_devices[drive].idetype == IDE_ATA) {
|
|
||||||
err = ide_ata_access(ATA_READ, drive, lba, numsects, es, edi);
|
|
||||||
} else if (ide_devices[drive].idetype == IDE_ATAPI) {
|
|
||||||
var i: u8 = 0;
|
|
||||||
while (i < numsects) : (i = i + 1) {
|
|
||||||
// err = ide_atapi_read(drive, lba + i, 1, es, edi + (i * 2048));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ide_print_error(drive, err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(dev: kernel.pci.PciDevice) void {
|
pub fn init(dev: kernel.pci.PciDevice) void {
|
||||||
kernel.println("-- ide init --");
|
kernel.println("-- ide init --", .{});
|
||||||
kernel.print("[ide] ");
|
kernel.print("[ide] ", .{});
|
||||||
dev.format();
|
dev.format();
|
||||||
assert(dev.header_type() == 0x0); // mass storage device
|
assert(dev.header_type() == 0x0); // mass storage device
|
||||||
|
|
||||||
dev.config_write(@intCast(u8, 0xfe), 0x3c);
|
dev.config_write(@intCast(u8, 0xfe), 0x3c);
|
||||||
if (dev.intr_line() == 0xfe) {
|
if (dev.intr_line() == 0xfe) {
|
||||||
kernel.println("[ide] detected ATA device");
|
kernel.println("[ide] detected ATA device", .{});
|
||||||
} else {
|
} else {
|
||||||
kernel.println("Potential SATA device, aborting.");
|
kernel.println("[ide] Potential SATA device. Not implemented. Hanging", .{});
|
||||||
x86.hang();
|
x86.hang();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,6 +410,7 @@ pub fn init(dev: kernel.pci.PciDevice) void {
|
||||||
const BAR2 = @intCast(u16, dev.bar(2));
|
const BAR2 = @intCast(u16, dev.bar(2));
|
||||||
const BAR3 = @intCast(u16, dev.bar(3));
|
const BAR3 = @intCast(u16, dev.bar(3));
|
||||||
const BAR4 = @intCast(u16, dev.bar(4));
|
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].base = if (BAR0 == 0) 0x1f0 else BAR0;
|
||||||
channels[ATA_PRIMARY].ctrl = if (BAR1 == 0) 0x3F6 else BAR1;
|
channels[ATA_PRIMARY].ctrl = if (BAR1 == 0) 0x3F6 else BAR1;
|
||||||
channels[ATA_SECONDARY].base = if (BAR2 == 0) 0x170 else BAR2;
|
channels[ATA_SECONDARY].base = if (BAR2 == 0) 0x170 else BAR2;
|
||||||
|
|
@ -335,92 +418,8 @@ pub fn init(dev: kernel.pci.PciDevice) void {
|
||||||
channels[ATA_PRIMARY].bmide = (BAR4 & 0xFFFC); // Bus Master IDE
|
channels[ATA_PRIMARY].bmide = (BAR4 & 0xFFFC); // Bus Master IDE
|
||||||
channels[ATA_SECONDARY].bmide = (BAR4 & 0xFFFC) + 8; // Bus Master IDE
|
channels[ATA_SECONDARY].bmide = (BAR4 & 0xFFFC) + 8; // Bus Master IDE
|
||||||
|
|
||||||
// turn off irqs
|
ide_device_0 = IDEDevice.init(channels[ATA_PRIMARY], 0) catch unreachable;
|
||||||
ide_write(ATA_PRIMARY, ATA_REG_CONTROL, 2);
|
ide_device_1 = IDEDevice.init(channels[ATA_PRIMARY], 1) catch unreachable;
|
||||||
ide_write(ATA_SECONDARY, ATA_REG_CONTROL, 2);
|
ide_device_2 = IDEDevice.init(channels[ATA_SECONDARY], 0) catch unreachable;
|
||||||
|
ide_device_3 = IDEDevice.init(channels[ATA_SECONDARY], 1) catch unreachable;
|
||||||
// parse identification space
|
|
||||||
var count: usize = 0;
|
|
||||||
var err: u8 = 0;
|
|
||||||
var status: u8 = 0;
|
|
||||||
for ([_]u8{ 0, 1 }) |i| {
|
|
||||||
for ([_]u8{ 0, 1 }) |j| {
|
|
||||||
var idetype: u8 = IDE_ATA;
|
|
||||||
ide_devices[count].reserved = 0; // Assuming that no drive here.
|
|
||||||
|
|
||||||
// (I) Select Drive:
|
|
||||||
ide_write(i, ATA_REG_HDDEVSEL, 0xA0 | (j << 4)); // Select Drive.
|
|
||||||
kernel.task.usleep(1000) catch unreachable; // Wait 1ms for drive select to work.
|
|
||||||
|
|
||||||
// (II) Send ATA Identify Command:
|
|
||||||
ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
|
|
||||||
kernel.task.usleep(1000) catch unreachable;
|
|
||||||
|
|
||||||
if (ide_read(i, ATA_REG_STATUS) == 0) continue; // If Status = 0, No Device.
|
|
||||||
while (true) {
|
|
||||||
status = ide_read(i, ATA_REG_STATUS);
|
|
||||||
if (status & ATA_SR_ERR != 0) {
|
|
||||||
err = 1;
|
|
||||||
break;
|
|
||||||
} // If Err, Device is not ATA.
|
|
||||||
if ((status & ATA_SR_BSY == 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 = ide_read(i, ATA_REG_LBA1);
|
|
||||||
const ch = ide_read(i, 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) continue; // Unknown Type (may not be a device).
|
|
||||||
|
|
||||||
ide_write(i, ATA_REG_COMMAND, ATA_CMD_IDENTIFY_PACKET);
|
|
||||||
kernel.task.usleep(1000) catch unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
// (V) Read Identification Space of the Device:
|
|
||||||
ide_read_buffer(i, ATA_REG_DATA, &ide_buf, 128);
|
|
||||||
|
|
||||||
ide_devices[count].reserved = 1;
|
|
||||||
ide_devices[count].idetype = idetype;
|
|
||||||
ide_devices[count].channel = i;
|
|
||||||
ide_devices[count].drive = j;
|
|
||||||
ide_devices[count].signature = @ptrCast(*const u8, &ide_buf[ATA_IDENT_DEVICETYPE]).*;
|
|
||||||
ide_devices[count].capabilities = @ptrCast(*const u8, &ide_buf[ATA_IDENT_CAPABILITIES]).*;
|
|
||||||
ide_devices[count].commandsets = @ptrCast(*const usize, &ide_buf[ATA_IDENT_COMMANDSETS]).*;
|
|
||||||
|
|
||||||
// (VII) Get Size:
|
|
||||||
if (ide_devices[count].commandsets & (1 << 26) != 0) {
|
|
||||||
// Device uses 48-Bit Addressing:
|
|
||||||
ide_devices[count].size = @ptrCast(*const usize, &ide_buf[ATA_IDENT_MAX_LBA_EXT]).*;
|
|
||||||
} else {
|
|
||||||
// Device uses CHS or 28-bit Addressing:
|
|
||||||
ide_devices[count].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) {
|
|
||||||
ide_devices[count].model[k] = ide_buf[ATA_IDENT_MODEL + k + 1];
|
|
||||||
ide_devices[count].model[k + 1] = ide_buf[ATA_IDENT_MODEL + k];
|
|
||||||
}
|
|
||||||
ide_devices[count].model[40] = 0; // Terminate String.
|
|
||||||
|
|
||||||
count = count + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 4- Print Summary:
|
|
||||||
for ([_]u8{ 0, 1, 2, 3 }) |i| {
|
|
||||||
if (ide_devices[i].reserved == 1) {
|
|
||||||
kernel.println(
|
|
||||||
"[ide] drive {} {} ({}GB) - {}",
|
|
||||||
i,
|
|
||||||
if (ide_devices[i].idetype == 0) "ATA" else "ATAPI",
|
|
||||||
ide_devices[i].size,
|
|
||||||
ide_devices[i].model,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
pub usingnamespace @import("../common.zig");
|
// pub usingnamespace @import("../common.zig");
|
||||||
|
|
||||||
pub const kernel = @import("../index.zig");
|
// pub const kernel = @import("../index.zig");
|
||||||
|
|
||||||
pub const x86 = @import("../arch/x86/index.zig");
|
pub const x86 = @import("../arch/x86/index.zig");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
usingnamespace @import("index.zig");
|
usingnamespace @import("index.zig");
|
||||||
|
|
||||||
pub fn init(dev: kernel.pci.PciDevice) void {
|
pub fn init(dev: kernel.pci.PciDevice) void {
|
||||||
kernel.println("-- virtio-block init --");
|
kernel.println("-- virtio-block init --", .{});
|
||||||
dev.format();
|
dev.format();
|
||||||
assert(dev.header_type() == 0x0); // mass storage device
|
assert(dev.header_type() == 0x0); // mass storage device
|
||||||
assert(dev.subsystem() == 0x2); // virtio-block
|
assert(dev.subsystem() == 0x2); // virtio-block
|
||||||
|
|
@ -10,7 +10,7 @@ pub fn init(dev: kernel.pci.PciDevice) void {
|
||||||
const intr_pin = dev.config_read(u8, 0x3d);
|
const intr_pin = dev.config_read(u8, 0x3d);
|
||||||
const min_grant = dev.config_read(u8, 0x3e);
|
const min_grant = dev.config_read(u8, 0x3e);
|
||||||
const max_lat = dev.config_read(u8, 0x3f);
|
const max_lat = dev.config_read(u8, 0x3f);
|
||||||
kernel.println("{x} {} {} {}", intr_line, intr_pin, min_grant, max_lat);
|
kernel.println("{x} {} {} {}", .{ intr_line, intr_pin, min_grant, max_lat });
|
||||||
|
|
||||||
// all virtio
|
// all virtio
|
||||||
// 0 1 2 3
|
// 0 1 2 3
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
pub usingnamespace @import("common.zig");
|
pub const dq = @import("delta_queue.zig");
|
||||||
pub usingnamespace @import("delta_queue.zig");
|
// pub usingnamespace @import("common.zig");
|
||||||
pub usingnamespace @import("ring_buffer.zig");
|
// pub usingnamespace @import("ring_buffer.zig");
|
||||||
pub usingnamespace @import("vga.zig");
|
|
||||||
|
|
||||||
///drivers
|
pub const vga = @import("vga.zig");
|
||||||
|
|
||||||
|
//drivers
|
||||||
pub const driver = @import("driver/index.zig");
|
pub const driver = @import("driver/index.zig");
|
||||||
|
|
||||||
///arch
|
//arch
|
||||||
pub const x86 = @import("arch/x86/index.zig");
|
pub const x86 = @import("arch/x86/index.zig");
|
||||||
|
|
||||||
///core
|
//core
|
||||||
pub const constants = @import("constants.zig");
|
pub const constants = @import("constants.zig");
|
||||||
pub const layout = @import("layout.zig");
|
pub const layout = @import("layout.zig");
|
||||||
pub const multiboot = @import("multiboot.zig");
|
pub const multiboot = @import("multiboot.zig");
|
||||||
|
|
@ -17,8 +18,8 @@ pub const vmem = @import("vmem.zig");
|
||||||
pub const task = @import("task.zig");
|
pub const task = @import("task.zig");
|
||||||
pub const time = @import("time.zig");
|
pub const time = @import("time.zig");
|
||||||
|
|
||||||
///extra
|
//extra
|
||||||
pub const console = @import("console.zig");
|
// pub const console = @import("console.zig");
|
||||||
pub const bio = @import("bio.zig");
|
// pub const bio = @import("bio.zig");
|
||||||
pub const pci = @import("pci/pci.zig");
|
pub const pci = @import("pci/pci.zig");
|
||||||
pub const ps2 = @import("ps2.zig");
|
pub const ps2 = @import("ps2.zig");
|
||||||
|
|
|
||||||
63
src/main.zig
63
src/main.zig
|
|
@ -1,14 +1,16 @@
|
||||||
usingnamespace @import("kernel");
|
const std = @import("std");
|
||||||
|
const builtin = std.builtin;
|
||||||
|
const kernel = @import("index.zig");
|
||||||
|
|
||||||
// Place the header at the very beginning of the binary.
|
// Place the header at the very beginning of the binary.
|
||||||
export const multiboot_header align(4) linksection(".multiboot") = multiboot: {
|
export const multiboot_header align(4) linksection(".multiboot") = multiboot: {
|
||||||
const MAGIC = u32(0x1BADB002); // multiboot magic
|
const MAGIC = @as(u32, 0x1BADB002); // multiboot magic
|
||||||
const ALIGN = u32(1 << 0); // Align loaded modules at 4k
|
const ALIGN = @as(u32, 1 << 0); // Align loaded modules at 4k
|
||||||
const MEMINFO = u32(1 << 1); // Receive a memory map from the bootloader.
|
const MEMINFO = @as(u32, 1 << 1); // Receive a memory map from the bootloader.
|
||||||
const ADDR = u32(1 << 16); // Load specific addr
|
// const ADDR = @as(u32, 1 << 16); // Load specific addr
|
||||||
const FLAGS = ALIGN | MEMINFO; // Combine the flags.
|
const FLAGS = ALIGN | MEMINFO; // Combine the flags.
|
||||||
|
|
||||||
break :multiboot multiboot.MultibootHeader{
|
break :multiboot kernel.multiboot.MultibootHeader{
|
||||||
.magic = MAGIC,
|
.magic = MAGIC,
|
||||||
.flags = FLAGS,
|
.flags = FLAGS,
|
||||||
.checksum = ~(MAGIC +% FLAGS) +% 1,
|
.checksum = ~(MAGIC +% FLAGS) +% 1,
|
||||||
|
|
@ -16,33 +18,32 @@ export const multiboot_header align(4) linksection(".multiboot") = multiboot: {
|
||||||
};
|
};
|
||||||
|
|
||||||
// arch independant initialization
|
// arch independant initialization
|
||||||
export fn kmain(magic: u32, info: *const multiboot.MultibootInfo) noreturn {
|
export fn kmain(magic: u32, info: *const kernel.multiboot.MultibootInfo) noreturn {
|
||||||
assert(magic == multiboot.MULTIBOOT_BOOTLOADER_MAGIC);
|
std.debug.assert(magic == kernel.multiboot.MULTIBOOT_BOOTLOADER_MAGIC);
|
||||||
clear();
|
kernel.vga.clear();
|
||||||
println("--- x86 initialization ---");
|
kernel.vga.println("--- x86 initialization ---", .{});
|
||||||
x86.x86_main(info);
|
kernel.x86.x86_main(info);
|
||||||
println("--- core initialization ---");
|
kernel.vga.println("--- core initialization ---", .{});
|
||||||
vmem.init();
|
kernel.vmem.init();
|
||||||
pci.scan();
|
kernel.pci.scan();
|
||||||
println("--- finished booting --- ");
|
kernel.vga.println("--- finished booting --- ", .{});
|
||||||
|
|
||||||
task.cleaner_task = task.new(@ptrToInt(task.cleaner_loop)) catch unreachable;
|
// kernel.task.cleaner_task = kernel.task.new(@ptrToInt(kernel.task.cleaner_loop)) catch unreachable;
|
||||||
_ = task.new(@ptrToInt(topbar)) catch unreachable;
|
// _ = kernel.task.new(@ptrToInt(kernel.vga.topbar)) catch unreachable;
|
||||||
_ = task.new(@ptrToInt(console.loop)) catch unreachable;
|
// _ = kernel.task.new(@ptrToInt(kernel.console.loop)) catch unreachable;
|
||||||
|
|
||||||
// driver.ide.blockdev.read(0);
|
// var buf = kernel.vmem.allocator.create([512]u8) catch unreachable;
|
||||||
// println("{}", driver.ide.sectorbuffer[0]);
|
// kernel.vga.println("buf at 0x{x}", .{@ptrToInt(buf)});
|
||||||
// println("{}", driver.ide.sectorbuffer[1]);
|
// kernel.driver.ide.first_ide_drive.read(2, buf);
|
||||||
// println("{}", driver.ide.sectorbuffer[2]);
|
|
||||||
// println("{}", driver.ide.sectorbuffer[3]);
|
|
||||||
// println("{}", driver.ide.sectorbuffer[4]);
|
|
||||||
// println("{}", driver.ide.sectorbuffer[5]);
|
|
||||||
|
|
||||||
task.terminate();
|
// const sig = buf[56..58];
|
||||||
|
// kernel.vga.println("sig: {x}", .{sig});
|
||||||
|
|
||||||
|
// kernel.task.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panic(a: []const u8, b: ?*builtin.StackTrace) noreturn {
|
// pub fn panic(a: []const u8, b: ?*builtin.StackTrace) noreturn {
|
||||||
println("{}", a);
|
// kernel.vga.println("{}", .{a});
|
||||||
println("{}", b);
|
// kernel.vga.println("{}", .{b});
|
||||||
while (true) asm volatile ("hlt");
|
// while (true) asm volatile ("hlt");
|
||||||
}
|
// }
|
||||||
|
|
|
||||||
|
|
@ -29,16 +29,16 @@ pub const MultibootInfo = packed struct {
|
||||||
mods_count: u32,
|
mods_count: u32,
|
||||||
mods_addr: u32,
|
mods_addr: u32,
|
||||||
|
|
||||||
syms: extern union {
|
syms: packed union {
|
||||||
// present if flags[4]
|
// present if flags[4]
|
||||||
nlist: extern struct {
|
nlist: packed struct {
|
||||||
tabsize: u32,
|
tabsize: u32,
|
||||||
strsize: u32,
|
strsize: u32,
|
||||||
addr: u32,
|
addr: u32,
|
||||||
_reserved: u32,
|
_reserved: u32,
|
||||||
},
|
},
|
||||||
// present if flags[5]
|
// present if flags[5]
|
||||||
shdr: extern struct {
|
shdr: packed struct {
|
||||||
num: u32,
|
num: u32,
|
||||||
size: u32,
|
size: u32,
|
||||||
addr: u32,
|
addr: u32,
|
||||||
|
|
@ -124,7 +124,7 @@ pub const MultibootModule = packed struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Multiboot structure to be read by the bootloader.
|
// Multiboot structure to be read by the bootloader.
|
||||||
pub const MultibootHeader = packed struct {
|
pub const MultibootHeader = extern struct {
|
||||||
magic: u32, // Must be equal to header magic number.
|
magic: u32, // Must be equal to header magic number.
|
||||||
flags: u32, // Feature flags.
|
flags: u32, // Feature flags.
|
||||||
checksum: u32, // Above fields plus this one must equal 0 mod 2^32.
|
checksum: u32, // Above fields plus this one must equal 0 mod 2^32.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
pub usingnamespace @import("../index.zig");
|
pub const std = @import("std");
|
||||||
pub const virtio = @import("virtio.zig");
|
|
||||||
pub const ide = @import("ide.zig");
|
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_ADDRESS = 0xCF8;
|
||||||
const PCI_CONFIG_DATA = 0xCFC;
|
const PCI_CONFIG_DATA = 0xCFC;
|
||||||
|
|
@ -41,15 +43,10 @@ pub const PciDevice = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(self: PciDevice) void {
|
pub fn format(self: PciDevice) void {
|
||||||
print("{}:{}.{}", self.bus, self.slot, self.function);
|
kernel.vga.print("{}:{}.{}", .{ self.bus, self.slot, self.function });
|
||||||
print(" {x},{x:2}", self.class(), self.subclass());
|
kernel.vga.print(" {x},{x:2}", .{ self.class(), self.subclass() });
|
||||||
print(" 0x{x} 0x{x}", self.vendor, self.device());
|
kernel.vga.print(" 0x{x} 0x{x}", .{ self.vendor, self.device() });
|
||||||
if (self.driver()) |d| {
|
kernel.vga.println(" {}", .{if (self.driver()) |d| d.name else " (none)"});
|
||||||
print(" {}", d.name);
|
|
||||||
} else {
|
|
||||||
print(" (none)");
|
|
||||||
}
|
|
||||||
println("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn driver(self: PciDevice) ?Driver {
|
pub fn driver(self: PciDevice) ?Driver {
|
||||||
|
|
@ -99,7 +96,7 @@ pub const PciDevice = struct {
|
||||||
return self.config_read(u8, 0x3c);
|
return self.config_read(u8, 0x3c);
|
||||||
}
|
}
|
||||||
pub fn bar(self: PciDevice, comptime n: usize) u32 {
|
pub fn bar(self: PciDevice, comptime n: usize) u32 {
|
||||||
assert(n <= 5);
|
std.debug.assert(n <= 5);
|
||||||
return self.config_read(u32, 0x10 + 4 * n);
|
return self.config_read(u32, 0x10 + 4 * n);
|
||||||
}
|
}
|
||||||
// only for header_type == 0
|
// only for header_type == 0
|
||||||
|
|
@ -107,10 +104,10 @@ pub const PciDevice = struct {
|
||||||
return self.config_read(u8, 0x2e);
|
return self.config_read(u8, 0x2e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub inline fn config_write(self: PciDevice, value: var, comptime offset: u8) void {
|
pub inline fn config_write(self: PciDevice, value: anytype, comptime offset: u8) void {
|
||||||
// ask for access before writing config
|
// ask for access before writing config
|
||||||
x86.outl(PCI_CONFIG_ADDRESS, self.address(offset));
|
x86.outl(PCI_CONFIG_ADDRESS, self.address(offset));
|
||||||
switch (@typeOf(value)) {
|
switch (@TypeOf(value)) {
|
||||||
// read the correct size
|
// read the correct size
|
||||||
u8 => return x86.outb(PCI_CONFIG_DATA, value),
|
u8 => return x86.outb(PCI_CONFIG_DATA, value),
|
||||||
u16 => return x86.outw(PCI_CONFIG_DATA, value),
|
u16 => return x86.outw(PCI_CONFIG_DATA, value),
|
||||||
|
|
@ -158,7 +155,7 @@ const Drivers = [_]Driver{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: factor 2 functions when anonymous fn is released
|
// TODO: factor 2 functions with a closure or a generator when released
|
||||||
pub fn scan() void {
|
pub fn scan() void {
|
||||||
var slot: u5 = 0;
|
var slot: u5 = 0;
|
||||||
// 0..31
|
// 0..31
|
||||||
|
|
@ -177,7 +174,7 @@ pub fn scan() void {
|
||||||
|
|
||||||
pub fn lspci() void {
|
pub fn lspci() void {
|
||||||
var slot: u5 = 0;
|
var slot: u5 = 0;
|
||||||
println("b:s.f c, s vendor device driver");
|
kernel.vga.println("b:s.f c, s vendor device driver", .{});
|
||||||
while (slot < 31) : (slot += 1) {
|
while (slot < 31) : (slot += 1) {
|
||||||
if (PciDevice.init(0, slot, 0)) |dev| {
|
if (PciDevice.init(0, slot, 0)) |dev| {
|
||||||
var function: u3 = 0;
|
var function: u3 = 0;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
usingnamespace @import("index.zig");
|
usingnamespace @import("index.zig");
|
||||||
// const x86 = @import("x86");
|
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!",
|
||||||
|
|
@ -68,8 +67,8 @@ const KEYMAP_US = [_][2]u8{
|
||||||
|
|
||||||
fn ps2_scancode() u8 {
|
fn ps2_scancode() u8 {
|
||||||
var scancode: u8 = 0;
|
var scancode: u8 = 0;
|
||||||
while (true) if (x86.inb(PS2_DATA) != scancode) {
|
while (true) if (x86.io.inb(PS2_DATA) != scancode) {
|
||||||
scancode = x86.inb(PS2_DATA);
|
scancode = x86.io.inb(PS2_DATA);
|
||||||
if (scancode > 0) return scancode;
|
if (scancode > 0) return scancode;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,17 @@ usingnamespace @import("index.zig");
|
||||||
pub fn Ring(comptime T: type) type {
|
pub fn Ring(comptime T: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
const Size = u10; // 0-1024
|
const Size = u10; // 0-1023
|
||||||
const size = @import("std").math.maxInt(Size);
|
const size = @import("std").math.maxInt(Size);
|
||||||
|
allocator: std.mem.Allocator = undefined,
|
||||||
buffer: *[size]T,
|
buffer: *[size]T,
|
||||||
task: ?*task.TaskNode = null,
|
task: ?*task.TaskNode = null,
|
||||||
read_index: Size = 0,
|
read_index: Size = 0,
|
||||||
write_index: Size = 0,
|
write_index: Size = 0,
|
||||||
|
|
||||||
//TODO: allocator argument and remove the namespace
|
pub fn init(ring: *Self, alloc: std.mem.Allocator) !void {
|
||||||
pub fn init(ring: *Self) !void {
|
ring.allocator = alloc;
|
||||||
ring.buffer = try vmem.create(@typeOf(ring.buffer.*));
|
ring.buffer = try ring.allocator.create(@TypeOf(ring.buffer.*));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(ring: *Self, elem: T) void {
|
pub fn write(ring: *Self, elem: T) void {
|
||||||
|
|
@ -24,7 +25,7 @@ pub fn Ring(comptime T: type) type {
|
||||||
pub fn read(ring: *Self) ?T {
|
pub fn read(ring: *Self) ?T {
|
||||||
if (ring.write_index == ring.read_index) return null;
|
if (ring.write_index == ring.read_index) return null;
|
||||||
const id = ring.read_index;
|
const id = ring.read_index;
|
||||||
ring.read_index +%= 1;
|
ring.read_index +%= 1; // add with overflow to loop the ring
|
||||||
return ring.buffer[id];
|
return ring.buffer[id];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
162
src/task.zig
162
src/task.zig
|
|
@ -1,25 +1,27 @@
|
||||||
pub usingnamespace @import("index.zig");
|
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 boot_task = Task{ .tid = 0, .esp = 0x47, .state = .Running, .born = true };
|
||||||
var tid_counter: u16 = 1;
|
var tid_counter: u16 = 1;
|
||||||
|
|
||||||
pub const TaskNode = std.TailQueue(*Task).Node;
|
pub const TaskNode = std.TailQueue(*Task).Node;
|
||||||
pub const SleepNode = DeltaQueue(*TaskNode).Node;
|
pub const SleepNode = kernel.dq.DeltaQueue(*TaskNode).Node;
|
||||||
|
|
||||||
pub var current_task: *TaskNode = &TaskNode.init(&boot_task);
|
pub var current_task: *TaskNode = &TaskNode.init(&boot_task);
|
||||||
pub var cleaner_task: *TaskNode = undefined;
|
pub var cleaner_task: *TaskNode = undefined;
|
||||||
pub var ready_tasks = std.TailQueue(*Task).init();
|
pub var ready_tasks = std.TailQueue(*Task).init();
|
||||||
pub var blocked_tasks = std.TailQueue(*Task).init();
|
pub var blocked_tasks = std.TailQueue(*Task).init();
|
||||||
pub var terminated_tasks = std.TailQueue(*Task).init();
|
pub var terminated_tasks = std.TailQueue(*Task).init();
|
||||||
pub var sleeping_tasks = DeltaQueue(*TaskNode).init();
|
pub var sleeping_tasks = kernel.dq.DeltaQueue(*TaskNode).init();
|
||||||
|
|
||||||
const STACK_SIZE = x86.PAGE_SIZE; // Size of thread stacks.
|
const STACK_SIZE = x86.PAGE_SIZE; // Size of thread stacks.
|
||||||
|
|
||||||
var timer_last_count: u64 = 0;
|
var timer_last_count: u64 = 0;
|
||||||
pub fn update_time_used() void {
|
pub fn update_time_used() void {
|
||||||
const current_count = time.offset_us;
|
const current_count = kernel.time.offset_us;
|
||||||
const elapsed = current_count - timer_last_count;
|
const elapsed = current_count - timer_last_count;
|
||||||
// if (current_task.data.tid == 1) println("{} adding {} time", current_task.data.tid, elapsed);
|
// if (current_task.data.tid == 1) kernel.vga.println("{} adding {} time", current_task.data.tid, elapsed);
|
||||||
timer_last_count = current_count;
|
timer_last_count = current_count;
|
||||||
current_task.data.time_used += elapsed;
|
current_task.data.time_used += elapsed;
|
||||||
}
|
}
|
||||||
|
|
@ -34,6 +36,7 @@ pub const TaskState = enum {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Task = struct {
|
pub const Task = struct {
|
||||||
|
stack: *[STACK_SIZE]u8 = undefined,
|
||||||
esp: usize,
|
esp: usize,
|
||||||
tid: u16,
|
tid: u16,
|
||||||
time_used: u64 = 0,
|
time_used: u64 = 0,
|
||||||
|
|
@ -44,21 +47,22 @@ pub const Task = struct {
|
||||||
|
|
||||||
pub fn create(entrypoint: usize) !*Task {
|
pub fn create(entrypoint: usize) !*Task {
|
||||||
// Allocate and initialize the thread structure.
|
// Allocate and initialize the thread structure.
|
||||||
var t = try vmem.create(Task);
|
var t = try kernel.vmem.allocator.create(Task);
|
||||||
errdefer vmem.destroy(t);
|
errdefer kernel.vmem.allocator.destroy(t);
|
||||||
|
|
||||||
t.time_used = 0;
|
t.time_used = 0;
|
||||||
t.state = .ReadyToRun;
|
t.state = .ReadyToRun;
|
||||||
t.tid = tid_counter;
|
t.tid = tid_counter;
|
||||||
tid_counter +%= 1;
|
tid_counter +%= 1;
|
||||||
assert(tid_counter != 0); //overflow
|
std.debug.assert(tid_counter != 0); //overflow
|
||||||
|
|
||||||
// allocate a new stack
|
// allocate a new stack
|
||||||
t.esp = (try vmem.malloc(STACK_SIZE)) + STACK_SIZE;
|
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
|
// 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!
|
// NOTE: if terminate is called this way it has an incorrect ebp!
|
||||||
t.esp -= 4;
|
t.esp -= 4;
|
||||||
@intToPtr(*usize, t.esp).* = @ptrToInt(task.terminate);
|
@intToPtr(*usize, t.esp).* = @ptrToInt(terminate);
|
||||||
// this will be what ret goes to
|
// this will be what ret goes to
|
||||||
t.esp -= 4;
|
t.esp -= 4;
|
||||||
@intToPtr(*usize, t.esp).* = entrypoint;
|
@intToPtr(*usize, t.esp).* = entrypoint;
|
||||||
|
|
@ -70,8 +74,8 @@ pub const Task = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy(self: *Task) void {
|
pub fn destroy(self: *Task) void {
|
||||||
vmem.free(self.esp);
|
kernel.vmem.allocator.destroy(self.stack);
|
||||||
vmem.destroy(self);
|
kernel.vmem.allocator.destroy(self);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -79,11 +83,11 @@ pub const Task = struct {
|
||||||
extern fn switch_tasks(new_esp: usize, old_esp_addr: usize) void;
|
extern fn switch_tasks(new_esp: usize, old_esp_addr: usize) void;
|
||||||
|
|
||||||
pub fn new(entrypoint: usize) !*TaskNode {
|
pub fn new(entrypoint: usize) !*TaskNode {
|
||||||
task.lock_scheduler();
|
kernel.task.lock_scheduler();
|
||||||
defer task.unlock_scheduler();
|
defer kernel.task.unlock_scheduler();
|
||||||
|
|
||||||
const node = try vmem.create(TaskNode);
|
const node = try kernel.vmem.allocator.create(TaskNode);
|
||||||
errdefer vmem.destroy(node);
|
errdefer kernel.vmem.allocator.destroy(node);
|
||||||
|
|
||||||
node.data = try Task.create(entrypoint);
|
node.data = try Task.create(entrypoint);
|
||||||
ready_tasks.prepend(node);
|
ready_tasks.prepend(node);
|
||||||
|
|
@ -91,16 +95,16 @@ pub fn new(entrypoint: usize) !*TaskNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wakeup_tick(tick: usize) bool {
|
pub fn wakeup_tick(tick: usize) bool {
|
||||||
task.lock_scheduler();
|
kernel.task.lock_scheduler();
|
||||||
defer task.unlock_scheduler();
|
defer kernel.task.unlock_scheduler();
|
||||||
|
|
||||||
task.sleeping_tasks.decrement(tick);
|
kernel.task.sleeping_tasks.decrement(tick);
|
||||||
var popped = false;
|
var popped = false;
|
||||||
while (task.sleeping_tasks.popZero()) |sleepnode| {
|
while (kernel.task.sleeping_tasks.popZero()) |sleepnode| {
|
||||||
const tasknode = sleepnode.data;
|
const tasknode = sleepnode.data;
|
||||||
tasknode.data.state = .ReadyToRun;
|
tasknode.data.state = .ReadyToRun;
|
||||||
vmem.free(@ptrToInt(sleepnode));
|
kernel.vmem.allocator.destroy(sleepnode);
|
||||||
task.ready_tasks.prepend(tasknode);
|
ready_tasks.prepend(tasknode);
|
||||||
popped = true;
|
popped = true;
|
||||||
}
|
}
|
||||||
return popped;
|
return popped;
|
||||||
|
|
@ -108,10 +112,10 @@ pub fn wakeup_tick(tick: usize) bool {
|
||||||
|
|
||||||
// TODO: make a sleep without malloc
|
// TODO: make a sleep without malloc
|
||||||
pub fn usleep(usec: u64) !void {
|
pub fn usleep(usec: u64) !void {
|
||||||
assert(current_task.data.state == .Running);
|
std.debug.assert(current_task.data.state == .Running);
|
||||||
update_time_used();
|
update_time_used();
|
||||||
|
|
||||||
const node = try vmem.create(SleepNode);
|
const node = try kernel.vmem.allocator.create(SleepNode);
|
||||||
|
|
||||||
lock_scheduler();
|
lock_scheduler();
|
||||||
defer unlock_scheduler();
|
defer unlock_scheduler();
|
||||||
|
|
@ -124,9 +128,9 @@ pub fn usleep(usec: u64) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn block(state: TaskState) void {
|
pub fn block(state: TaskState) void {
|
||||||
assert(current_task.data.state == .Running);
|
std.debug.assert(current_task.data.state == .Running);
|
||||||
assert(state != .Running);
|
std.debug.assert(state != .Running);
|
||||||
assert(state != .ReadyToRun);
|
std.debug.assert(state != .ReadyToRun);
|
||||||
update_time_used();
|
update_time_used();
|
||||||
|
|
||||||
// println("blocking {} as {}", current_task.data.tid, state);
|
// println("blocking {} as {}", current_task.data.tid, state);
|
||||||
|
|
@ -159,7 +163,7 @@ pub fn unblock(node: *TaskNode) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminate() noreturn {
|
pub fn terminate() noreturn {
|
||||||
assert(current_task.data.state == .Running);
|
std.debug.assert(current_task.data.state == .Running);
|
||||||
|
|
||||||
lock_scheduler();
|
lock_scheduler();
|
||||||
current_task.data.state = .Terminated;
|
current_task.data.state = .Terminated;
|
||||||
|
|
@ -175,11 +179,11 @@ pub fn terminate() noreturn {
|
||||||
pub fn cleaner_loop() noreturn {
|
pub fn cleaner_loop() noreturn {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (terminated_tasks.popFirst()) |n| {
|
if (terminated_tasks.popFirst()) |n| {
|
||||||
notify("DESTROYING {}", n.data.tid);
|
notify("DESTROYING {}", .{n.data.tid});
|
||||||
n.data.destroy();
|
n.data.destroy();
|
||||||
vmem.destroy(n);
|
kernel.vmem.allocator.destroy(n);
|
||||||
} else {
|
} else {
|
||||||
notify("NOTHING TO CLEAN");
|
notify("NOTHING TO CLEAN", .{});
|
||||||
block(.Paused);
|
block(.Paused);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -189,24 +193,24 @@ 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_counter: isize = 0; // this counter can go negative when we are scheduling after a postpone
|
||||||
pub var postpone_task_switches_flag: bool = false;
|
pub var postpone_task_switches_flag: bool = false;
|
||||||
fn lock_scheduler() void {
|
fn lock_scheduler() void {
|
||||||
if (constants.SMP == false) {
|
if (kernel.constants.SMP == false) {
|
||||||
x86.cli();
|
x86.instr.cli();
|
||||||
IRQ_disable_counter += 1;
|
IRQ_disable_counter += 1;
|
||||||
postpone_task_switches_counter += 1;
|
postpone_task_switches_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn unlock_scheduler() void {
|
fn unlock_scheduler() void {
|
||||||
if (constants.SMP == false) {
|
if (kernel.constants.SMP == false) {
|
||||||
assert(IRQ_disable_counter > 0);
|
std.debug.assert(IRQ_disable_counter > 0);
|
||||||
postpone_task_switches_counter -= 1;
|
postpone_task_switches_counter -= 1;
|
||||||
if (postpone_task_switches_flag == true and postpone_task_switches_counter == 0) {
|
if (postpone_task_switches_flag == true and postpone_task_switches_counter == 0) {
|
||||||
postpone_task_switches_flag = false;
|
postpone_task_switches_flag = false;
|
||||||
notify("AFTER POSTPONE");
|
notify("AFTER POSTPONE", .{});
|
||||||
schedule();
|
schedule();
|
||||||
}
|
}
|
||||||
IRQ_disable_counter -= 1;
|
IRQ_disable_counter -= 1;
|
||||||
// must be the last instruction because we do interrupts inside interrupts
|
// must be the last instruction because we do interrupts inside interrupts
|
||||||
if (IRQ_disable_counter == 0) x86.sti();
|
if (IRQ_disable_counter == 0) x86.instr.sti();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,8 +219,8 @@ pub fn preempt() void {
|
||||||
|
|
||||||
update_time_used();
|
update_time_used();
|
||||||
if (ready_tasks.first == null) {
|
if (ready_tasks.first == null) {
|
||||||
notify("NO PREEMPT SINGLE TASK");
|
notify("NO PREEMPT SINGLE TASK", .{});
|
||||||
time.task_slice_remaining = 0;
|
kernel.time.task_slice_remaining = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,8 +236,8 @@ pub fn preempt() void {
|
||||||
// - scheduler is locked
|
// - scheduler is locked
|
||||||
// - the tasks being switched to will unlock_scheduler()
|
// - the tasks being switched to will unlock_scheduler()
|
||||||
pub fn switch_to(chosen: *TaskNode) void {
|
pub fn switch_to(chosen: *TaskNode) void {
|
||||||
assert(chosen.data.state == .ReadyToRun);
|
std.debug.assert(chosen.data.state == .ReadyToRun);
|
||||||
assert(current_task.data.state != .Running);
|
std.debug.assert(current_task.data.state != .Running);
|
||||||
|
|
||||||
// save old stack
|
// save old stack
|
||||||
const old_task_esp_addr = ¤t_task.data.esp;
|
const old_task_esp_addr = ¤t_task.data.esp;
|
||||||
|
|
@ -241,8 +245,8 @@ pub fn switch_to(chosen: *TaskNode) void {
|
||||||
// switch states
|
// switch states
|
||||||
chosen.data.state = .Running;
|
chosen.data.state = .Running;
|
||||||
current_task = chosen;
|
current_task = chosen;
|
||||||
if (ready_tasks.first == null) time.task_slice_remaining = 0;
|
if (ready_tasks.first == null) kernel.time.task_slice_remaining = 0;
|
||||||
if (ready_tasks.first != null) time.task_slice_remaining = time.TASK_SLICE;
|
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
|
// we don't have any startup code for tasks, so i do it here
|
||||||
if (current_task.data.born == false) {
|
if (current_task.data.born == false) {
|
||||||
|
|
@ -251,7 +255,11 @@ pub fn switch_to(chosen: *TaskNode) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't inline the asm function, it needs to ret
|
// don't inline the asm function, it needs to ret
|
||||||
@noInlineCall(switch_tasks, chosen.data.esp, @ptrToInt(old_task_esp_addr));
|
@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_time: u64 = 0;
|
||||||
|
|
@ -261,13 +269,13 @@ pub var CPU_idle_start_time: u64 = 0;
|
||||||
// unlock_scheduler should be called after
|
// unlock_scheduler should be called after
|
||||||
// current_task is blocked or running (preemption)
|
// current_task is blocked or running (preemption)
|
||||||
pub fn schedule() void {
|
pub fn schedule() void {
|
||||||
assert(IRQ_disable_counter > 0);
|
std.debug.assert(IRQ_disable_counter > 0);
|
||||||
assert(current_task.data.state != .ReadyToRun);
|
std.debug.assert(current_task.data.state != .ReadyToRun);
|
||||||
|
|
||||||
// postponed
|
// postponed
|
||||||
if (postpone_task_switches_counter != 0 and current_task.data.state == .Running) {
|
if (postpone_task_switches_counter != 0 and current_task.data.state == .Running) {
|
||||||
postpone_task_switches_flag = true;
|
postpone_task_switches_flag = true;
|
||||||
notify("POSTPONING SCHEDULE");
|
notify("POSTPONING SCHEDULE", .{});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// next task
|
// next task
|
||||||
|
|
@ -284,8 +292,8 @@ pub fn schedule() void {
|
||||||
}
|
}
|
||||||
// single task
|
// single task
|
||||||
if (current_task.data.state == .Running) {
|
if (current_task.data.state == .Running) {
|
||||||
notify("SINGLE TASK");
|
notify("SINGLE TASK", .{});
|
||||||
time.task_slice_remaining = 0;
|
kernel.time.task_slice_remaining = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// no tasks
|
// no tasks
|
||||||
|
|
@ -293,21 +301,21 @@ pub fn schedule() void {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn idle_mode() void {
|
fn idle_mode() void {
|
||||||
assert(ready_tasks.first == null);
|
std.debug.assert(ready_tasks.first == null);
|
||||||
assert(current_task.data.state != .Running);
|
std.debug.assert(current_task.data.state != .Running);
|
||||||
assert(current_task.data.state != .ReadyToRun);
|
std.debug.assert(current_task.data.state != .ReadyToRun);
|
||||||
|
|
||||||
notify("IDLE");
|
notify("IDLE", .{});
|
||||||
|
|
||||||
// borrow the current task
|
// borrow the current task
|
||||||
const borrow = current_task;
|
const borrow = current_task;
|
||||||
|
|
||||||
CPU_idle_start_time = time.offset_us; //for power management
|
CPU_idle_start_time = kernel.time.offset_us; //for power management
|
||||||
|
|
||||||
while (true) { // idle loop
|
while (true) { // idle loop
|
||||||
if (ready_tasks.popFirst()) |t| { // found a new task
|
if (ready_tasks.popFirst()) |t| { // found a new task
|
||||||
CPU_idle_time += time.offset_us - CPU_idle_start_time; // count time as idle
|
CPU_idle_time += kernel.time.offset_us - CPU_idle_start_time; // count time as idle
|
||||||
timer_last_count = time.offset_us; // don't count time as used
|
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);
|
// println("went into idle mode for {}usecs", time.offset_us - CPU_idle_start_time);
|
||||||
|
|
||||||
if (t == borrow) {
|
if (t == borrow) {
|
||||||
|
|
@ -316,43 +324,43 @@ fn idle_mode() void {
|
||||||
}
|
}
|
||||||
return switch_to(t);
|
return switch_to(t);
|
||||||
} else { // no tasks ready, let the timer fire
|
} else { // no tasks ready, let the timer fire
|
||||||
x86.sti(); // enable interrupts to allow the timer to fire
|
x86.instr.sti(); // enable interrupts to allow the timer to fire
|
||||||
x86.hlt(); // halt and wait for the timer to fire
|
x86.instr.hlt(); // halt and wait for the timer to fire
|
||||||
x86.cli(); // disable interrupts again to see if there is something to do
|
x86.instr.cli(); // disable interrupts again to see if there is something to do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notify(comptime message: []const u8, args: ...) void {
|
pub fn notify(comptime message: []const u8, args: anytype) void {
|
||||||
const bg = vga.background;
|
const bg = kernel.vga.background;
|
||||||
const fg = vga.foreground;
|
const fg = kernel.vga.foreground;
|
||||||
const cursor = vga.cursor;
|
const cursor = kernel.vga.cursor;
|
||||||
vga.background = fg;
|
kernel.vga.background = fg;
|
||||||
vga.foreground = bg;
|
kernel.vga.foreground = bg;
|
||||||
vga.cursor = 80 - message.len;
|
kernel.vga.cursor = 80 - message.len;
|
||||||
vga.cursor_enabled = false;
|
kernel.vga.cursor_enabled = false;
|
||||||
|
|
||||||
print(message, args);
|
kernel.vga.print(message, args);
|
||||||
|
|
||||||
vga.cursor_enabled = true;
|
kernel.vga.cursor_enabled = true;
|
||||||
vga.cursor = cursor;
|
kernel.vga.cursor = cursor;
|
||||||
vga.background = bg;
|
kernel.vga.background = bg;
|
||||||
vga.foreground = fg;
|
kernel.vga.foreground = fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_short() void {
|
pub fn format_short() void {
|
||||||
print("{}R {}B {}S", ready_tasks.len, blocked_tasks.len, sleeping_tasks.len);
|
kernel.vga.print("{}R {}B {}S", .{ ready_tasks.len, blocked_tasks.len, sleeping_tasks.len });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format() void {
|
pub fn format() void {
|
||||||
update_time_used();
|
update_time_used();
|
||||||
|
|
||||||
println("{}", current_task.data);
|
kernel.vga.println("{}", .{current_task.data});
|
||||||
|
|
||||||
var it = ready_tasks.first;
|
var it = ready_tasks.first;
|
||||||
while (it) |node| : (it = node.next) println("{}", node.data);
|
while (it) |node| : (it = node.next) kernel.vga.println("{}", .{node.data});
|
||||||
it = blocked_tasks.first;
|
it = blocked_tasks.first;
|
||||||
while (it) |node| : (it = node.next) println("{}", node.data);
|
while (it) |node| : (it = node.next) kernel.vga.println("{}", .{node.data});
|
||||||
var sit = sleeping_tasks.first;
|
var sit = sleeping_tasks.first;
|
||||||
while (sit) |node| : (sit = node.next) println("{} {}", node.data.data, node.counter);
|
while (sit) |node| : (sit = node.next) kernel.vga.println("{} {}", .{ node.data.data, node.counter });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
src/time.zig
11
src/time.zig
|
|
@ -1,4 +1,5 @@
|
||||||
usingnamespace @import("index.zig");
|
const kernel = @import("index.zig");
|
||||||
|
const x86 = @import("x86");
|
||||||
|
|
||||||
pub var offset_us: u64 = 0;
|
pub var offset_us: u64 = 0;
|
||||||
pub var task_slice_remaining: u64 = 0;
|
pub var task_slice_remaining: u64 = 0;
|
||||||
|
|
@ -8,14 +9,14 @@ pub fn increment() void {
|
||||||
|
|
||||||
offset_us += tick; //global time counter
|
offset_us += tick; //global time counter
|
||||||
|
|
||||||
var should_preempt = task.wakeup_tick(tick);
|
var should_preempt = kernel.task.wakeup_tick(tick);
|
||||||
|
|
||||||
if (task_slice_remaining != 0) {
|
if (task_slice_remaining != 0) {
|
||||||
// There is a time slice length
|
// There is a time slice length
|
||||||
if (task_slice_remaining <= tick) should_preempt = true;
|
if (task_slice_remaining <= tick) should_preempt = true;
|
||||||
if (task_slice_remaining > tick) task_slice_remaining -= tick;
|
if (task_slice_remaining > tick) task_slice_remaining -= tick;
|
||||||
}
|
}
|
||||||
if (should_preempt) task.preempt();
|
if (should_preempt) kernel.task.preempt();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uptime() void {
|
pub fn uptime() void {
|
||||||
|
|
@ -23,9 +24,9 @@ pub fn uptime() void {
|
||||||
const offset_s: u64 = offset_ms / 1000;
|
const offset_s: u64 = offset_ms / 1000;
|
||||||
offset_ms = @mod(offset_ms / 100, 10);
|
offset_ms = @mod(offset_ms / 100, 10);
|
||||||
|
|
||||||
print("{}.{:.3}", offset_s, offset_ms);
|
kernel.vga.print("{}.{:.3}", .{ offset_s, offset_ms });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn utilisation() void {
|
pub fn utilisation() void {
|
||||||
print("{}%", 100 * (offset_us - task.CPU_idle_time) / offset_us);
|
kernel.vga.print("{}%", .{100 * (offset_us - kernel.task.CPU_idle_time) / offset_us});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
65
src/vga.zig
65
src/vga.zig
|
|
@ -1,7 +1,6 @@
|
||||||
usingnamespace @import("index.zig");
|
const std = @import("std");
|
||||||
// const time = @import("time.zig");
|
const kernel = @import("index.zig");
|
||||||
// const x86 = @import("arch/x86/index.zig");
|
|
||||||
// const std = @import("std");
|
|
||||||
// Screen size.
|
// Screen size.
|
||||||
pub const VGA_WIDTH = 80;
|
pub const VGA_WIDTH = 80;
|
||||||
pub const VGA_HEIGHT = 25;
|
pub const VGA_HEIGHT = 25;
|
||||||
|
|
@ -10,7 +9,7 @@ pub var vga = VGA{
|
||||||
.vram = @intToPtr([*]VGAEntry, 0xb8000)[0..0x4000],
|
.vram = @intToPtr([*]VGAEntry, 0xb8000)[0..0x4000],
|
||||||
.cursor = 80 * 2,
|
.cursor = 80 * 2,
|
||||||
.foreground = Color.Black,
|
.foreground = Color.Black,
|
||||||
.background = Color.LightGrey,
|
.background = Color.White,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Color codes.
|
// Color codes.
|
||||||
|
|
@ -42,23 +41,26 @@ pub const VGAEntry = packed struct {
|
||||||
|
|
||||||
// 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 print(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 = print(format ++ "\n", args);
|
pub fn println(comptime format: []const u8, args: anytype) void {
|
||||||
|
print(format ++ "\n", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const time = @import("time.zig");
|
||||||
pub fn clear() void {
|
pub fn clear() void {
|
||||||
vga.clear();
|
vga.clear();
|
||||||
}
|
}
|
||||||
|
|
@ -68,29 +70,28 @@ pub fn topbar() void {
|
||||||
// println("topbar1");
|
// println("topbar1");
|
||||||
while (true) {
|
while (true) {
|
||||||
const cursor = vga.cursor;
|
const cursor = vga.cursor;
|
||||||
vga.background = Color.Green;
|
vga.background = Color.Black;
|
||||||
vga.foreground = Color.LightGrey;
|
vga.foreground = Color.White;
|
||||||
vga.cursor = 0;
|
vga.cursor = 0;
|
||||||
vga.cursor_enabled = false;
|
vga.cursor_enabled = false;
|
||||||
|
|
||||||
time.uptime();
|
kernel.time.uptime();
|
||||||
print(" | ");
|
print(" | ", .{});
|
||||||
time.utilisation();
|
kernel.time.utilisation();
|
||||||
print(" | ");
|
print(" | ", .{});
|
||||||
task.format_short();
|
kernel.task.format_short();
|
||||||
// print(" ({})", task.IRQ_disable_counter);
|
println("", .{});
|
||||||
println("");
|
|
||||||
|
|
||||||
vga.cursor_enabled = true;
|
vga.cursor_enabled = true;
|
||||||
vga.cursor = cursor;
|
vga.cursor = cursor;
|
||||||
vga.background = bg;
|
vga.background = bg;
|
||||||
vga.foreground = fg;
|
vga.foreground = fg;
|
||||||
|
|
||||||
task.usleep(50 * 1000) catch unreachable; // 60ms
|
kernel.task.usleep(50 * 1000) catch unreachable; // 60ms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn printCallback(context: void, string: []const u8) Errors!void {
|
fn printCallback(string: []const u8) anyerror!void {
|
||||||
vga.writeString(string);
|
vga.writeString(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,10 +184,10 @@ 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 {
|
||||||
x86.outb(0x3D4, 0x0F);
|
kernel.x86.io.outb(0x3D4, 0x0F);
|
||||||
x86.outb(0x3D5, @truncate(u8, self.cursor));
|
kernel.x86.io.outb(0x3D5, @truncate(u8, self.cursor));
|
||||||
x86.outb(0x3D4, 0x0E);
|
kernel.x86.io.outb(0x3D4, 0x0E);
|
||||||
x86.outb(0x3D5, @truncate(u8, self.cursor >> 8));
|
kernel.x86.io.outb(0x3D5, @truncate(u8, self.cursor >> 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
////
|
////
|
||||||
|
|
@ -196,11 +197,11 @@ const VGA = struct {
|
||||||
pub fn fetchCursor(self: *VGA) void {
|
pub fn fetchCursor(self: *VGA) void {
|
||||||
var cursor: usize = 0;
|
var cursor: usize = 0;
|
||||||
|
|
||||||
x86.outb(0x3D4, 0x0E);
|
kernel.x86.io.outb(0x3D4, 0x0E);
|
||||||
cursor |= usize(x86.inb(0x3D5)) << 8;
|
cursor |= usize(kernel.x86.io.inb(0x3D5)) << 8;
|
||||||
|
|
||||||
x86.outb(0x3D4, 0x0F);
|
kernel.x86.outb(0x3D4, 0x0F);
|
||||||
cursor |= x86.inb(0x3D5);
|
cursor |= kernel.x86.io.inb(0x3D5);
|
||||||
|
|
||||||
self.cursor = cursor;
|
self.cursor = cursor;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
69
src/vmem.zig
69
src/vmem.zig
|
|
@ -1,5 +1,8 @@
|
||||||
pub usingnamespace @import("index.zig");
|
const std = @import("std");
|
||||||
pub const allocator: std.mem.Allocator = undefined;
|
const kernel = @import("kernel");
|
||||||
|
const x86 = @import("x86");
|
||||||
|
|
||||||
|
pub var allocator: std.mem.Allocator = undefined;
|
||||||
|
|
||||||
// TODO: make a better memory allocator
|
// TODO: make a better memory allocator
|
||||||
// stupid simple virtual memory allocator
|
// stupid simple virtual memory allocator
|
||||||
|
|
@ -7,45 +10,51 @@ pub const allocator: std.mem.Allocator = undefined;
|
||||||
// - no defragmentation
|
// - no defragmentation
|
||||||
// - no allocation bigger than a page
|
// - no allocation bigger than a page
|
||||||
|
|
||||||
const stack_size: usize = (layout.HEAP_END - layout.HEAP) / x86.PAGE_SIZE;
|
const stack_size: usize = (kernel.layout.HEAP_END - kernel.layout.HEAP) / kernel.x86.PAGE_SIZE;
|
||||||
var stack: [stack_size]usize = undefined; // Stack of free virtual addresses
|
|
||||||
var stack_index: usize = 0; // Index into the stack.
|
var stack_index: usize = 0; // Index into the stack.
|
||||||
|
var stack: [stack_size]usize = undefined; // Stack of free virtual addresses
|
||||||
|
|
||||||
pub fn available() usize {
|
pub fn available() usize {
|
||||||
return stack_index * x86.PAGE_SIZE;
|
return stack_index * x86.PAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn malloc(size: usize) !usize {
|
fn free(addr: usize) void {
|
||||||
if (available() == 0) {
|
x86.paging.unmap(addr);
|
||||||
return error.OutOfMemory;
|
stack[stack_index] = addr;
|
||||||
}
|
|
||||||
stack_index -= 1;
|
|
||||||
var vaddr: usize = stack[stack_index];
|
|
||||||
try x86.paging.mmap(vaddr, null);
|
|
||||||
return vaddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(comptime T: type) !*T {
|
|
||||||
assert(@sizeOf(T) < x86.PAGE_SIZE); // this allocator only support 1:1 mapping
|
|
||||||
return @intToPtr(*T, try malloc(@sizeOf(T)));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn destroy(O: var) void {
|
|
||||||
vmem.free(@ptrToInt(O));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn free(address: usize) void {
|
|
||||||
x86.paging.unmap(address);
|
|
||||||
stack[stack_index] = address;
|
|
||||||
stack_index += 1;
|
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 {
|
pub fn init() void {
|
||||||
var addr: usize = layout.HEAP;
|
allocator = std.mem.Allocator{
|
||||||
while (addr < layout.HEAP_END) : (addr += x86.PAGE_SIZE) {
|
.ptr = undefined,
|
||||||
// println("addr {x}", addr);
|
.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[stack_index] = addr;
|
||||||
stack_index += 1;
|
stack_index += 1;
|
||||||
// return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue