add support for multiboot2

the bump allocator isn't ported
qemu -kernel doesn't support this
This commit is contained in:
Jack Halford 2019-08-12 17:22:15 +02:00
parent e619364f71
commit e0eda2690c
9 changed files with 145 additions and 276 deletions

View file

@ -2,7 +2,7 @@ const Builder = @import("std").build.Builder;
const builtin = @import("builtin"); const builtin = @import("builtin");
pub fn build(b: *Builder) void { pub fn build(b: *Builder) void {
const kernel = b.addExecutable("bzImage", "src/arch/x86/main.zig"); const kernel = b.addExecutable("kernel", "src/arch/x86/main.zig");
kernel.addPackagePath("kernel", "src/index.zig"); kernel.addPackagePath("kernel", "src/index.zig");
kernel.addPackagePath("arch", "src/arch/x86/lib/index.zig"); kernel.addPackagePath("arch", "src/arch/x86/lib/index.zig");
kernel.setOutputDir("build"); kernel.setOutputDir("build");

14
grub.sh
View file

@ -5,6 +5,16 @@ exit_missing() {
which xorriso || exit_missing which xorriso || exit_missing
which grub-mkrescue || exit_missing which grub-mkrescue || exit_missing
mkdir -p build/iso/boot mkdir -p build/iso/boot/grub
cp build/bzImage build/iso/boot cp build/kernel build/iso/boot
>build/iso/boot/grub/grub.cfg <<EOF
set timeout=0
set default=0
menuentry "OS" {
multiboot2 /boot/kernel
boot
}
EOF
grub-mkrescue -o build/kernel.iso build/iso grub-mkrescue -o build/kernel.iso build/iso

21
qemu.sh
View file

@ -1,30 +1,27 @@
QEMU_SOCKET=/tmp/qemu.sock QEMU_SOCKET=/tmp/qemu.sock
QEMU_MONITOR="socat - UNIX-CONNECT:${QEMU_SOCKET}" QEMU_MONITOR="socat - UNIX-CONNECT:${QEMU_SOCKET}"
QEMU_GDB_PORT=4242 QEMU_GDB_PORT=4242
KERNEL=build/bzImage KERNEL=build/kernel
start() { start() {
sudo pkill -9 qemu
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 1337M \ -m 50M \
-display curses \
-serial mon:stdio \ -serial mon:stdio \
-append "Hello" \ -curses \
-device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no \
-kernel ${KERNEL} -kernel ${KERNEL}
# build/kernel.iso # -cdrom ${KERNEL}.iso
# -append "Hello" \
# -S
# -device virtio-net,netdev=network0 -netdev tap,id=network0,ifname=tap0,script=no,downscript=no \
# build/kernel.iso
"$@" "$@"
} }
monitor() { monitor() {
if [ "$1" == "" ]; then [ "$1" == "" ] && sudo ${QEMU_MONITOR} || echo "$1" | sudo ${QEMU_MONITOR} >/dev/null
sudo ${QEMU_MONITOR}
else
echo "$1" | sudo ${QEMU_MONITOR} >/dev/null
fi
} }
reload() { reload() {

View file

@ -9,7 +9,7 @@ const printf = @import("../../vga.zig").printf;
/// 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 {
var allocator = mem.initialize(info); var allocator = mem.initialize(info);
// gdt.initialize(); // gdt.initialize();

View file

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

View file

@ -1,3 +1,3 @@
pub const vga = @import("vga.zig"); pub const vga = @import("vga.zig");
pub const main = @import("main.zig"); pub const main = @import("main.zig");
pub const multiboot = @import("multiboot.zig"); pub const multiboot = @import("multiboot2.zig");

View file

@ -1,24 +1,29 @@
usingnamespace @import("multiboot.zig"); usingnamespace @import("multiboot2.zig");
usingnamespace @import("vga.zig"); usingnamespace @import("vga.zig");
const pci = @import("pci.zig");
const arch = @import("arch/x86/lib/index.zig"); const arch = @import("arch/x86/lib/index.zig");
const console = @import("console.zig");
const x86 = @import("arch/x86/main.zig"); const x86 = @import("arch/x86/main.zig");
const multiboot = @import("multiboot2.zig");
const assert = @import("std").debug.assert; const assert = @import("std").debug.assert;
const pci = @import("pci.zig");
const console = @import("console.zig");
// arch independant initialization // arch independant initialization
export fn kmain(magic: u32, info: *const MultibootInfo) noreturn { export fn kmain(magic: u32, info_addr: u32) noreturn {
assert(magic == MULTIBOOT_BOOTLOADER_MAGIC); println("--- hello x86_main ---");
assert(magic == MULTIBOOT2_BOOTLOADER_MAGIC);
const info = multiboot.load(info_addr);
console.initialize(); console.initialize();
printf("--- hello x86_main ---\n"); println("--- hello x86_main ---");
x86.x86_main(info); x86.x86_main(&info);
// pagefault_test(0xfeffc000); // pagefault_test(0xfeffc000);
printf("\n--- arch indepent boot ---\n"); println("--- end ---");
while (true) {} while (true) {}
} }

View file

@ -1,131 +0,0 @@
// MULTIBOOT1
// https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
// zig fmt: off
const tty = @import("tty.zig");
const cstr = @import("std").cstr;
const Process = @import("process.zig").Process;
// This should be in EAX.
pub const MULTIBOOT_BOOTLOADER_MAGIC = 0x2BADB002;
// Is there basic lower/upper memory information?
pub const MULTIBOOT_INFO_MEMORY = 0x00000001;
// Is there a full memory map?
pub const MULTIBOOT_INFO_MEM_MAP = 0x00000040;
// System information structure passed by the bootloader.
pub const MultibootInfo = packed struct {
// Multiboot info version number.
flags: u32,
// Available memory from BIOS.
mem_lower: u32,
mem_upper: u32,
// "root" partition.
boot_device: u32,
// Kernel command line.
cmdline: u32,
// Boot-Module list.
mods_count: u32,
mods_addr: u32,
// TODO: use the real types here.
u: u128,
// Memory Mapping buffer.
mmap_length: u32,
mmap_addr: u32,
// Drive Info buffer.
drives_length: u32,
drives_addr: u32,
// ROM configuration table.
config_table: u32,
// Boot Loader Name.
boot_loader_name: u32,
// APM table.
apm_table: u32,
// Video.
vbe_control_info: u32,
vbe_mode_info: u32,
vbe_mode: u16,
vbe_interface_seg: u16,
vbe_interface_off: u16,
vbe_interface_len: u16,
////
// Return the ending address of the last module.
//
pub fn lastModuleEnd(self: *const MultibootInfo) usize {
const mods = @intToPtr([*]MultibootModule, self.mods_addr);
return mods[self.mods_count - 1].mod_end;
}
////
// Load all the modules passed by the bootloader.
//
// pub fn loadModules(self: *const MultibootInfo) void {
// const mods = @intToPtr([*]MultibootModule, self.mods_addr)[0..self.mods_count];
// for (mods) |mod| {
// const cmdline = cstr.toSlice(@intToPtr([*]u8, mod.cmdline));
// tty.step("Loading \"{}\"", cmdline);
// _ = Process.create(mod.mod_start, null);
// // TODO: deallocate the original memory.
// tty.stepOK();
// }
// }
};
// Types of memory map entries.
pub const MULTIBOOT_MEMORY_AVAILABLE = 1;
pub const MULTIBOOT_MEMORY_RESERVED = 2;
// Entries in the memory map.
pub const MultibootMMapEntry = packed struct {
size: u32,
addr: u64,
len: u64,
type: u32,
};
pub const MultibootModule = packed struct {
// The memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive.
mod_start: u32,
mod_end: u32,
cmdline: u32, // Module command line.
pad: u32, // Padding to take it to 16 bytes (must be zero).
};
// Multiboot structure to be read by the bootloader.
const MultibootHeader = packed struct {
magic: u32, // Must be equal to header magic number.
flags: u32, // Feature flags.
checksum: u32, // Above fields plus this one must equal 0 mod 2^32.
};
// NOTE: this structure is incomplete.
// Place the header at the very beginning of the binary.
export const multiboot_header align(4) linksection(".multiboot") = multiboot: {
const MAGIC = u32(0x1BADB002); // multiboot magic
const ALIGN = u32(1 << 0); // Align loaded modules.
const MEMINFO = u32(1 << 1); // Receive a memory map from the bootloader.
const CMDLINE = u32(1 << 2); // Receive a cmdline from the bootloader
const FLAGS = ALIGN | MEMINFO | CMDLINE; // Combine the flags.
break :multiboot MultibootHeader {
.magic = MAGIC,
.flags = FLAGS,
.checksum = ~(MAGIC +% FLAGS) +% 1,
};
};

View file

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