paging done

This commit is contained in:
Jack Halford 2019-08-15 21:15:10 +02:00
parent 97aa541b34
commit 07089c2ea1
17 changed files with 247 additions and 253 deletions

View file

@ -29,3 +29,8 @@ slowly porting from rust.
## interrupts ## interrupts
`interrupt` -> `idt[n]` -> `isrN` -> `isrDispatch` -> `handlers[n]` (default `unhandled()`) `interrupt` -> `idt[n]` -> `isrN` -> `isrDispatch` -> `handlers[n]` (default `unhandled()`)
## layout
`0->4Mib` kernel reserved
`1Mib` interrupt stack

View file

@ -12,10 +12,11 @@ start() {
-m 1337M \ -m 1337M \
-curses \ -curses \
-append "Hello" \ -append "Hello" \
-drive file=disk.img,if=virtio\
-kernel ${KERNEL} -kernel ${KERNEL}
# -no-reboot \
# -device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no \ # -device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no \
# build/kernel.iso # build/kernel.iso
"$@"
# this allows this switch to monitor with ^a-c, but doesn't # this allows this switch to monitor with ^a-c, but doesn't
# play nice with irqs apparently... # play nice with irqs apparently...

View file

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

View file

@ -125,9 +125,9 @@ pub fn initialize() void {
loadGDT(&gdtr); loadGDT(&gdtr);
// Initialize TSS. // Initialize TSS.
const tss_entry = makeEntry(@ptrToInt(&tss), @sizeOf(TSS) - 1, TSS_ACCESS, PROTECTED); // const tss_entry = makeEntry(@ptrToInt(&tss), @sizeOf(TSS) - 1, TSS_ACCESS, PROTECTED);
gdt[TSS_DESC / @sizeOf(GDTEntry)] = tss_entry; // gdt[TSS_DESC / @sizeOf(GDTEntry)] = tss_entry;
x86.ltr(TSS_DESC); // x86.ltr(TSS_DESC);
// tty.stepOK(); // tty.stepOK();
} }

View file

@ -1,5 +1,5 @@
// Kernel stack for interrupt handling. // Kernel stack for interrupt handling.
KERNEL_STACK = 0x800000 KERNEL_STACK = 0x10000
// GDT selectors. // GDT selectors.
KERNEL_DS = 0x10 KERNEL_DS = 0x10

6
src/arch/x86/layout.zig Normal file
View file

@ -0,0 +1,6 @@
//https://wiki.osdev.org/Memory_Map_(x86)
pub const KSTACK = 0x80000; // todo: move to .bss
pub const KERNEL = 0x100000;
pub const IDENTITY = 0x400000; // 0->4MiB
pub const HEAP = 0x800000;

View file

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

View file

@ -12,26 +12,11 @@ const x86 = @import("lib/index.zig");
/// x86 specific intialization /// x86 specific intialization
/// first entry point (see linker.ld) /// first entry point (see linker.ld)
pub fn x86_main(info: *const MultibootInfo) void { pub fn x86_main(info: *const MultibootInfo) void {
// set up the physical frame allocator
memory.initialize(info);
println("{}", memory.allocator.allocate(1));
// println("{}", memory.allocator.allocate(1));
// println("{}", memory.allocator.allocate(1));
// println("{}", memory.allocator.allocate(1));
// setup memory segmentation
gdt.initialize(); gdt.initialize();
// setup interrupts
idt.initialize(); idt.initialize();
memory.initialize(info);
paging.initialize();
// enable interrupts // enable interrupts
x86.sti(); x86.sti();
// set up the virtual page mapper
paging.initialize();
// test breakpoint
// x86.int3();
} }

View file

@ -1,124 +1,88 @@
usingnamespace @import("kernel").multiboot; usingnamespace @import("kernel").multiboot;
usingnamespace @import("../../vga.zig"); usingnamespace @import("kernel").vga;
const assert = @import("std").debug.assert; const assert = @import("std").debug.assert;
const std = @import("std");
pub var allocator: bumpAllocator = undefined; var stack: [*]usize = undefined; // Stack of free physical page.
var stack_index: usize = 0; // Index into the stack.
// Boundaries of the frame stack.
pub var stack_size: usize = undefined;
pub var stack_end: usize = undefined;
pub const PAGE_SIZE: usize = 4096;
// 4095 -> 4096
// 4096 -> 4096
// 4097 -> 8192
pub fn pageAlign(address: u32) u32 {
return (address + PAGE_SIZE - 1) & (~PAGE_SIZE +% 1);
}
////
// Return the amount of variable elements (in bytes).
//
pub fn available() usize {
return stack_index * PAGE_SIZE;
}
////
// Request a free physical page and return its address.
//
pub fn allocate() ?usize {
if (available() == 0) {
println("out of memory");
return null;
}
stack_index -= 1;
return stack[stack_index];
}
////
// Free a previously allocated physical page.
//
// Arguments:
// address: Address of the page to be freed.
//
pub fn free(address: usize) void {
stack[stack_index] = address;
stack_index += 1;
}
////
// Scan the memory map to index all available memory.
//
// Arguments:
// info: Information structure from bootloader.
//
pub fn initialize(info: *const MultibootInfo) void { pub fn initialize(info: *const MultibootInfo) void {
// Ensure the bootloader has given us the memory map.
assert((info.flags & MULTIBOOT_INFO_MEMORY) != 0); assert((info.flags & MULTIBOOT_INFO_MEMORY) != 0);
assert((info.flags & MULTIBOOT_INFO_MEM_MAP) != 0); assert((info.flags & MULTIBOOT_INFO_MEM_MAP) != 0);
format_multibootinfo(info); // Place the stack of free pages after the last Multiboot module.
allocator = bumpAllocator.new(info); stack = @intToPtr([*]usize, 0x200000);
} // stack = @intToPtr([*]usize, pageAlign(info.mods_addr));
// Calculate the approximate size of the stack based on the amount of total upper memory.
fn format_multibootentry(entry: *MultibootMMapEntry) void { stack_size = ((info.mem_upper * 1024) / PAGE_SIZE) * @sizeOf(usize);
if (entry.type == MULTIBOOT_MEMORY_AVAILABLE) { stack_end = pageAlign(@ptrToInt(stack) + stack_size);
print(" AVAILABLE: ");
} else {
print("NOT AVAILABLE: ");
}
print("{x} ", entry.addr);
if (entry.len / (1024 * 1024) > 0) {
println("({} MB)", entry.len / (1024 * 1024));
} else {
println("({} kB)", entry.len / (1024));
}
}
fn format_multibootinfo(info: *const MultibootInfo) void {
var cmdline_ptr = @intToPtr([*c]const u8, info.cmdline);
var cmdline = @ptrCast([*c]const u8, cmdline_ptr);
// var cmdline = std.cstr.toSliceConst(info.cmdline);
println("flags: {b}", info.flags);
println("lower: {x}", info.mem_lower);
println("upper: {x}", info.mem_upper);
println("mmap_l: {}", info.mmap_length);
println("mmap_a: {x}", info.mmap_addr);
println("cmdline: {x}", cmdline_ptr);
println("cmdline: {}", cmdline);
var map: usize = info.mmap_addr; var map: usize = info.mmap_addr;
while (map < info.mmap_addr + info.mmap_length) { while (map < info.mmap_addr + info.mmap_length) {
var entry = @intToPtr(*MultibootMMapEntry, map); var entry = @intToPtr(*MultibootMMapEntry, map);
format_multibootentry(entry);
// Calculate the start and end of this memory area.
var start = @truncate(usize, entry.addr);
var end = @truncate(usize, start + entry.len);
// Anything that comes before the end of the stack of free pages is reserved.
start = if (start >= stack_end) start else stack_end;
// Flag all the pages in this memory area as free.
if (entry.type == MULTIBOOT_MEMORY_AVAILABLE) while (start < end) : (start += PAGE_SIZE)
free(start);
// Go to the next entry in the memory map.
map += entry.size + @sizeOf(@typeOf(entry.size)); map += entry.size + @sizeOf(@typeOf(entry.size));
} }
println("available memory: {d} MiB ", available() / 1024 / 1024);
} }
// returns each available physical frame one by one in order
pub const bumpAllocator = struct {
next_free_frame: PhysFrame,
current_area: ?*MultibootMMapEntry,
info: *const MultibootInfo,
pub fn new(info: *const MultibootInfo) bumpAllocator {
const first_area = @intToPtr(*MultibootMMapEntry, info.mmap_addr);
var allocato = bumpAllocator{
.current_area = first_area,
.next_free_frame = PhysFrame.from_addr(@intCast(u32, first_area.addr)),
.info = info,
};
return allocato;
}
pub fn allocate(self: var, count: u32) ?PhysFrame {
if (count == 0) {
return null;
}
if (self.current_area == null) {
return null;
}
if (self.current_area.?.type != MULTIBOOT_MEMORY_AVAILABLE) {
self.choose_next_area();
return self.allocate(count);
}
// <4MB identity mapped kernel, lazy trick
if (PhysFrame.start_addr(self.next_free_frame) < 0x400000) {
self.next_free_frame.number += 1;
return self.allocate(count);
}
const start_frame = self.next_free_frame;
const end_frame = self.next_free_frame.add(count - 1);
const current_area_last_frame = PhysFrame.from_addr(@intCast(u32, self.current_area.?.addr + self.current_area.?.len));
if (end_frame.number > current_area_last_frame.number) {
self.choose_next_area();
return self.allocate(count);
}
self.next_free_frame.number += count;
return start_frame;
}
pub fn choose_next_area(self: var) void {
println("choosing next area");
const current = self.current_area.?;
var next_area = @ptrToInt(current) + current.size + @sizeOf(@typeOf(current));
if (next_area >= self.info.mmap_addr + self.info.mmap_length) {
self.current_area = null;
} else {
self.current_area = @intToPtr(*MultibootMMapEntry, next_area);
format_multibootentry(self.current_area.?);
self.next_free_frame = PhysFrame.from_addr(@intCast(u32, self.current_area.?.addr));
}
}
};
pub const PAGE_SIZE = 4096;
pub const PhysFrame = struct {
number: u32,
pub fn from_addr(addr: u32) PhysFrame {
return PhysFrame{ .number = @divTrunc(addr, PAGE_SIZE) };
}
pub fn add(self: PhysFrame, count: u32) PhysFrame {
return PhysFrame{ .number = self.number + count };
}
pub fn start_addr(self: PhysFrame) u32 {
return (self.number * PAGE_SIZE);
}
};

View file

@ -11,13 +11,15 @@ setupPaging:
mov +4(%esp), %eax // Fetch the phys_pd parameter. mov +4(%esp), %eax // Fetch the phys_pd parameter.
mov %eax, %cr3 // Point CR3 to the page directory. mov %eax, %cr3 // Point CR3 to the page directory.
// Enable Page Size Extension and Page Global. // Enable Page Size Extension
mov %cr4, %eax mov %cr4, %eax
or $0b00010000, %eax or $0x10, %eax
mov %eax, %cr4 mov %eax, %cr4
// Enable Paging. // Enable Paging.
mov %cr0, %eax mov %cr0, %eax
mov $1, %eax
or $(1 << 0), %eax
or $(1 << 31), %eax or $(1 << 31), %eax
mov %eax, %cr0 mov %eax, %cr0

View file

@ -1,89 +1,102 @@
const x86 = @import("lib/index.zig"); const x86 = @import("lib/index.zig");
const allocator = @import("memory.zig").allocator; const memory = @import("memory.zig");
const interrupt = @import("interrupt.zig");
const assert = @import("std").debug.assert; const assert = @import("std").debug.assert;
const println = @import("../../vga.zig").println; const println = @import("../../vga.zig").println;
extern fn setupPaging(phys_pd: usize) void; extern fn setupPaging(phys_pd: usize) void;
pub var mapper: Mapper = undefined; const PageEntry = usize;
pub const PAGE_SIZE = 4096; pub const PAGE_SIZE = 4096;
pub const PT = @intToPtr([*]PageEntry, 0xFFC00000);
pub const PD = @intToPtr([*]PageEntry, 0xFFFFF000);
const PRESENT = 0x1;
const WRITE = 0x2;
const USER = 0x4;
const WRITE_THRU = 0x8;
const NOCACHE = 0x10;
const ACCESSED = 0x20;
const HUGE = 0x80;
pub var pageDirectory: [1024]PageEntry align(4096) linksection(".bss") = [_]PageEntry{0} ** 1024;
fn pageFault() void {
println("pagefault");
while (true) {
asm volatile ("hlt");
}
}
fn pageBase(addr: usize) usize {
return addr & (~PAGE_SIZE +% 1);
}
fn pde(addr: usize) *PageEntry {
return &PD[addr >> 22];
}
fn pte(addr: usize) *PageEntry {
return &PT[addr >> 12];
}
// virtual to physical
pub fn translate(virt: usize) ?usize {
if (pde(virt).* == 0) return null;
return pageBase(pte(virt).*);
}
pub fn unmap(virt: usize) void {
var pte = pte(virt);
if (pte.* == 0) {
println("can't unmap 0x{x}, map is empty.", addr);
return;
}
pte.* = 0;
memory.free(translate(virt));
}
pub fn mmap(virt: usize, phys: ?usize) void {
var pde: *PageEntry = &PD[virt >> 22];
if (pde.* == 0) {
pde.* = memory.allocate() | WRITE | PRESENT;
}
var pte: *PageEntry = &PT[virt >> 12];
pte.* = if (phys) |p| p else allocate() | PRESENT;
}
pub fn addrspace() void {
var i: usize = 1;
i = 0;
while (i < 1024) : (i += 1) {
if (PD[i] == 0) {
continue;
}
println("p2[{}] -> 0x{x}", i, PD[i]);
if (PD[i] & HUGE != 0) {
continue;
}
var j: usize = 0;
while (j < 1024) : (j += 1) {
var entry: PageEntry = PT[i * 1024 + j];
if (entry != 0) {
println("p2[{}]p1[{}] -> 0x{x}", i, j, entry);
}
}
}
}
pub fn initialize() void { pub fn initialize() void {
const empty_page = PageDirectoryEntry{}; var p2 = pageDirectory[0..];
// var p2 = allocator.allocate(1);
var p2 = [_]PageDirectoryEntry{empty_page} ** 1024;
// var p2 = [_]u32{0} ** 1024;
// identity map 0 -> 4MB // identity map 0 -> 4MB
p2[0].pageTable = 0x0;
p2[0].present = true;
p2[0].read_write = true;
p2[0].huge = true;
// p2[0] = @bitReverse(u32, 0b10000011);
println("p2[0] {b}", p2[0]); p2[0] = 0x000000 | PRESENT | WRITE | HUGE;
println("p2[0] {b}", @bitCast(u32, p2[0])); p2[1023] = @ptrToInt(&p2[0]) | PRESENT | WRITE;
// x86.hang();
// paging.s
// setupPaging(@ptrToInt(&p2));
// mapper = Mapper{ assert(memory.stack_end < 0x400000);
// .p2 = p2, // const first: *u32 = @ptrCast(*u32, p2);
// }; // println("p2[0] {b}", first.*);
const addr = mapper.translate(0xfffff000);
} interrupt.register(14, pageFault);
setupPaging(@ptrToInt(&pageDirectory[0]));
const builtin = @import("builtin");
const Mapper = struct { addrspace();
p2: PageDirectory, // const addr = mapper.translate(0xfffff000);
// virt to phys
pub fn translate(self: Mapper, virt: u32) ?u32 {
const map = @bitCast(VirtAddr, virt);
println("{}", builtin.endian);
println("virt {x} -> {}-{}-{x}", virt, map.page_directory, map.page_table, map.offset);
return null;
}
};
pub const VirtAddr = packed struct {
page_directory: u10,
page_table: u10,
offset: u12,
};
pub const PageDirectoryEntry = packed struct {
pageTable: u20 = 0,
available: u3 = 0,
ignored: bool = false,
huge: bool = false,
zero: bool = false,
accessed: bool = false,
cache_disabled: bool = false,
write_thru: bool = false,
supervisor: bool = false,
read_write: bool = false,
present: bool = false,
};
pub const PageTableEntry = packed struct {
addr: u20 = 0,
available: u3 = 0,
global: bool = false,
zero: bool = false,
dirty: bool = false,
accessed: bool = false,
cache_disabled: bool = false,
write_thru: bool = false,
supervisor: bool = false,
read_write: bool = false,
present: bool = false,
};
pub const PageTable = [1024]PageTableEntry;
pub const PageDirectory = [1024]PageDirectoryEntry;
comptime {
assert(@sizeOf(PageDirectoryEntry) == 4); //32 bits
assert(@sizeOf(PageTableEntry) == 4); //32 bits
} }

View file

@ -40,7 +40,6 @@ pub fn keypress(char: u8) void {
} }
pub fn initialize() void { pub fn initialize() void {
// vga.clear();
interrupt.registerIRQ(1, ps2.keyboard_handler); interrupt.registerIRQ(1, ps2.keyboard_handler);
vga.writeString("> "); vga.writeString("> ");
} }

View file

@ -8,9 +8,11 @@ const assert = @import("std").debug.assert;
// arch independant initialization // arch independant initialization
export fn kmain(magic: u32, info: *const MultibootInfo) noreturn { export fn kmain(magic: u32, info: *const MultibootInfo) noreturn {
clear();
assert(magic == MULTIBOOT_BOOTLOADER_MAGIC); assert(magic == MULTIBOOT_BOOTLOADER_MAGIC);
println("--- x86 initialization ---"); println("--- x86 initialization ---");
x86.x86_main(info); x86.x86_main(info);
// pagefault_test(0xfeffc000); // pagefault_test(0xfeffc000);

8
src/memory.zig Normal file
View file

@ -0,0 +1,8 @@
const paging = @import("arch/x86/paging.zig");
pub fn alloc(size: usize) usize {
}
pub fn free() {
}

View file

@ -81,8 +81,12 @@ pub const MultibootInfo = packed struct {
// Return the ending address of the last module. // Return the ending address of the last module.
// //
pub fn lastModuleEnd(self: *const MultibootInfo) usize { pub fn lastModuleEnd(self: *const MultibootInfo) usize {
const mods = @intToPtr([*]MultibootModule, self.mods_addr); if (self.mods_count > 0) {
return mods[self.mods_count - 1].mod_end; const mods = @intToPtr([*]MultibootModule, self.mods_addr);
return mods[self.mods_count - 1].mod_end;
} else {
return self.mods_addr;
}
} }
//// ////

View file

@ -17,31 +17,28 @@ pub const PciDevice = struct {
bus: u8, bus: u8,
slot: u5, slot: u5,
function: u3, function: u3,
device: u16, device: u16 = undefined,
vendor: u16, vendor: u16 = undefined,
class: u8, class: u8 = undefined,
subclass: u8, subclass: u8 = undefined,
header_type: u8, header_type: u8 = undefined,
driver: ?Driver = null,
pub fn init(bus: u8, slot: u5, function: u3) ?PciDevice { pub fn init(bus: u8, slot: u5, function: u3) ?PciDevice {
var pcidevice = PciDevice{ var dev = PciDevice{
.bus = bus, .bus = bus,
.slot = slot, .slot = slot,
.function = function, .function = function,
.device = undefined,
.class = undefined,
.subclass = undefined,
.header_type = undefined,
.vendor = undefined,
}; };
pcidevice.vendor = pci_config_read_word(pcidevice, 0); dev.vendor = dev.pci_config_read_word(0);
if (pcidevice.vendor == 0xffff) if (dev.vendor == 0xffff)
return null; return null;
pcidevice.device = pci_config_read_word(pcidevice, 2); dev.device = dev.pci_config_read_word(2);
pcidevice.subclass = pci_config_read_byte(pcidevice, 10); dev.subclass = dev.pci_config_read_byte(10);
pcidevice.class = pci_config_read_byte(pcidevice, 11); dev.class = dev.pci_config_read_byte(11);
pcidevice.header_type = pci_config_read_byte(pcidevice, 14); dev.header_type = dev.pci_config_read_byte(14);
return (pcidevice); dev.driver = dev.get_driver();
return (dev);
} }
pub fn address(self: PciDevice, offset: u8) u32 { pub fn address(self: PciDevice, offset: u8) u32 {
@ -57,7 +54,11 @@ pub const PciDevice = struct {
} }
pub fn format(self: PciDevice) void { pub fn format(self: PciDevice) void {
println("{}:{}.{} {x},{x}: {x} {x}", self.bus, self.slot, self.function, self.class, self.subclass, self.vendor, self.device); print("{}:{}.{}", self.bus, self.slot, self.function);
print(" {x},{x}: {x} {x}", self.class, self.subclass, self.vendor, self.device);
if (self.driver) |d|
print(" {}", d.name);
println("");
} }
pub fn access(self: PciDevice, offset: u8) void { pub fn access(self: PciDevice, offset: u8) void {
@ -78,13 +79,33 @@ pub const PciDevice = struct {
self.access(offset); self.access(offset);
return (arch.inl(PCI_CONFIG_DATA)); return (arch.inl(PCI_CONFIG_DATA));
} }
pub fn get_driver(self: PciDevice) ?Driver {
var i: usize = 0;
while (i < Drivers.len) : (i += 1) {
var driver = Drivers[i];
if (self.class == driver.class and self.subclass == driver.subclass and (driver.vendor == null or self.vendor == driver.vendor.?)) {
return driver;
}
}
return null;
}
}; };
const Driver = struct {
name: [*]u8,
class: u8,
subclass: u8,
vendor: ?u16 = null,
};
const name = "virtio-blk";
pub var Drivers: [1]Driver = [_]Driver{Driver{ .name = &name, .class = 0x1, .subclass = 0x0, .vendor = 0x1af4 }};
pub fn lspci() void { pub fn lspci() void {
var slot: u5 = 0; var slot: u5 = 0;
while (true) { while (true) {
if (PciDevice.init(0, slot, 0)) |device| { if (PciDevice.init(0, slot, 0)) |device| {
device.format();
var function: u3 = 0; var function: u3 = 0;
while (true) { while (true) {
if (PciDevice.init(0, slot, function)) |vf| if (PciDevice.init(0, slot, function)) |vf|

View file

@ -63,6 +63,9 @@ pub fn print(comptime format: []const u8, args: ...) void {
pub fn println(comptime format: []const u8, args: ...) void { pub fn println(comptime format: []const u8, args: ...) void {
var a = std.fmt.format({}, Errors, printCallback, format ++ "\n", args); var a = std.fmt.format({}, Errors, printCallback, format ++ "\n", args);
} }
pub fn clear() void {
vga.clear();
}
fn printCallback(context: void, string: []const u8) Errors!void { fn printCallback(context: void, string: []const u8) Errors!void {
vga.writeString(string); vga.writeString(string);