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
`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 \
-curses \
-append "Hello" \
-drive file=disk.img,if=virtio\
-kernel ${KERNEL}
# -no-reboot \
# -device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no \
# build/kernel.iso
"$@"
# this allows this switch to monitor with ^a-c, but doesn't
# play nice with irqs apparently...

View file

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

View file

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

View file

@ -1,5 +1,5 @@
// Kernel stack for interrupt handling.
KERNEL_STACK = 0x800000
KERNEL_STACK = 0x10000
// GDT selectors.
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;
. += 80 * 25 * 2;
/* lower half kernel */
/* lower half kernel <1MiB */
. = 0x10000;
/* ensure that the multiboot header is at the beginning */
@ -37,32 +37,12 @@ SECTIONS {
. = ALIGN(4K);
}
/* NOT A GOOD IDEA TO GROUP debug_* SYMBOLS ! */
/* .debug : */
/* { */
/* /1* KEEP(*(.debug_*)) *1/ */
/* *(.debug_*) */
/* . = ALIGN(4K); */
/* } */
.gdt :
{
*(.gdt)
. = ALIGN(4K);
}
.got :
{
*(.got)
. = ALIGN(4K);
}
.got.plt :
{
*(.got.plt)
. = ALIGN(4K);
}
.bss :
{
*(.bss .bss.*)

View file

@ -12,26 +12,11 @@ const x86 = @import("lib/index.zig");
/// x86 specific intialization
/// first entry point (see linker.ld)
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();
// setup interrupts
idt.initialize();
memory.initialize(info);
paging.initialize();
// enable interrupts
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("../../vga.zig");
usingnamespace @import("kernel").vga;
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 {
// Ensure the bootloader has given us the memory map.
assert((info.flags & MULTIBOOT_INFO_MEMORY) != 0);
assert((info.flags & MULTIBOOT_INFO_MEM_MAP) != 0);
format_multibootinfo(info);
allocator = bumpAllocator.new(info);
}
fn format_multibootentry(entry: *MultibootMMapEntry) void {
if (entry.type == MULTIBOOT_MEMORY_AVAILABLE) {
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);
// Place the stack of free pages after the last Multiboot module.
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.
stack_size = ((info.mem_upper * 1024) / PAGE_SIZE) * @sizeOf(usize);
stack_end = pageAlign(@ptrToInt(stack) + stack_size);
var map: usize = info.mmap_addr;
while (map < info.mmap_addr + info.mmap_length) {
var entry = @intToPtr(*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));
}
}
// 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;
println("available memory: {d} MiB ", available() / 1024 / 1024);
}
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 %eax, %cr3 // Point CR3 to the page directory.
// Enable Page Size Extension and Page Global.
// Enable Page Size Extension
mov %cr4, %eax
or $0b00010000, %eax
or $0x10, %eax
mov %eax, %cr4
// Enable Paging.
mov %cr0, %eax
mov $1, %eax
or $(1 << 0), %eax
or $(1 << 31), %eax
mov %eax, %cr0

View file

@ -1,89 +1,102 @@
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 println = @import("../../vga.zig").println;
extern fn setupPaging(phys_pd: usize) void;
pub var mapper: Mapper = undefined;
const PageEntry = usize;
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 {
const empty_page = PageDirectoryEntry{};
// var p2 = allocator.allocate(1);
var p2 = [_]PageDirectoryEntry{empty_page} ** 1024;
// var p2 = [_]u32{0} ** 1024;
var p2 = pageDirectory[0..];
// 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]);
println("p2[0] {b}", @bitCast(u32, p2[0]));
// x86.hang();
// paging.s
// setupPaging(@ptrToInt(&p2));
p2[0] = 0x000000 | PRESENT | WRITE | HUGE;
p2[1023] = @ptrToInt(&p2[0]) | PRESENT | WRITE;
// mapper = Mapper{
// .p2 = p2,
// };
const addr = mapper.translate(0xfffff000);
}
const builtin = @import("builtin");
const Mapper = struct {
p2: PageDirectory,
// 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
assert(memory.stack_end < 0x400000);
// const first: *u32 = @ptrCast(*u32, p2);
// println("p2[0] {b}", first.*);
interrupt.register(14, pageFault);
setupPaging(@ptrToInt(&pageDirectory[0]));
addrspace();
// const addr = mapper.translate(0xfffff000);
}

View file

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

View file

@ -8,9 +8,11 @@ const assert = @import("std").debug.assert;
// arch independant initialization
export fn kmain(magic: u32, info: *const MultibootInfo) noreturn {
clear();
assert(magic == MULTIBOOT_BOOTLOADER_MAGIC);
println("--- x86 initialization ---");
x86.x86_main(info);
// 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.
//
pub fn lastModuleEnd(self: *const MultibootInfo) usize {
if (self.mods_count > 0) {
const mods = @intToPtr([*]MultibootModule, self.mods_addr);
return mods[self.mods_count - 1].mod_end;
} else {
return self.mods_addr;
}
}
////

View file

@ -17,31 +17,28 @@ pub const PciDevice = struct {
bus: u8,
slot: u5,
function: u3,
device: u16,
vendor: u16,
class: u8,
subclass: u8,
header_type: u8,
device: u16 = undefined,
vendor: u16 = undefined,
class: u8 = undefined,
subclass: u8 = undefined,
header_type: u8 = undefined,
driver: ?Driver = null,
pub fn init(bus: u8, slot: u5, function: u3) ?PciDevice {
var pcidevice = PciDevice{
var dev = PciDevice{
.bus = bus,
.slot = slot,
.function = function,
.device = undefined,
.class = undefined,
.subclass = undefined,
.header_type = undefined,
.vendor = undefined,
};
pcidevice.vendor = pci_config_read_word(pcidevice, 0);
if (pcidevice.vendor == 0xffff)
dev.vendor = dev.pci_config_read_word(0);
if (dev.vendor == 0xffff)
return null;
pcidevice.device = pci_config_read_word(pcidevice, 2);
pcidevice.subclass = pci_config_read_byte(pcidevice, 10);
pcidevice.class = pci_config_read_byte(pcidevice, 11);
pcidevice.header_type = pci_config_read_byte(pcidevice, 14);
return (pcidevice);
dev.device = dev.pci_config_read_word(2);
dev.subclass = dev.pci_config_read_byte(10);
dev.class = dev.pci_config_read_byte(11);
dev.header_type = dev.pci_config_read_byte(14);
dev.driver = dev.get_driver();
return (dev);
}
pub fn address(self: PciDevice, offset: u8) u32 {
@ -57,7 +54,11 @@ pub const PciDevice = struct {
}
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 {
@ -78,13 +79,33 @@ pub const PciDevice = struct {
self.access(offset);
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 {
var slot: u5 = 0;
while (true) {
if (PciDevice.init(0, slot, 0)) |device| {
device.format();
var function: u3 = 0;
while (true) {
if (PciDevice.init(0, slot, function)) |vf|

View file

@ -63,6 +63,9 @@ pub fn print(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);
}
pub fn clear() void {
vga.clear();
}
fn printCallback(context: void, string: []const u8) Errors!void {
vga.writeString(string);