refactor a ton of code to be multiboot compatible

This commit is contained in:
fekhesk 2022-10-26 00:05:53 -07:00
parent f2ff9da1fb
commit 3222bcbfe2
No known key found for this signature in database
GPG key ID: 6B3D8CB511646891
20 changed files with 382 additions and 197 deletions

3
.gitignore vendored
View file

@ -34,3 +34,6 @@ Cargo.lock
#/target #/target
#Cargo.lock #Cargo.lock
/boot.o
/build/
/OVMF-pure-efi.fd

View file

@ -1,5 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MakefileSettings">
<option name="linkedExternalProjectsSettings">
<MakefileProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="version" value="2" />
</MakefileProjectSettings>
</option>
</component>
<component name="MakefileWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
<component name="SwUserDefinedSpecifications"> <component name="SwUserDefinedSpecifications">
<option name="specTypeByUrl"> <option name="specTypeByUrl">
<map /> <map />

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/windows.iml" filepath="$PROJECT_DIR$/.idea/windows.iml" />
</modules>
</component>
</project>

View file

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/simple_boot/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/alloc/benches" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/alloc/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/alloc/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -3,9 +3,13 @@ name = "wukkOS"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
[lib]
crate-type = ["staticlib"]
[dependencies] [dependencies]
spin = "0.9.1" spin = "0.9.1"
x86_64 = "0.14.10" x86_64 = "0.14.10"
rlibc = "1.0"
[dependencies.lazy_static] [dependencies.lazy_static]
version = "1.4.0" version = "1.4.0"
features = ["spin_no_std"] features = ["spin_no_std"]

51
Makefile Normal file
View file

@ -0,0 +1,51 @@
arch ?= x86_64
kernel := target/$(arch)-custom/debug/libwukkOS.a
iso := build/arch/$(arch)/wukkOS.iso
target ?= $(arch)-custom
final := build/arch/$(arch)/wukkOS.bin
efi_bios := build/arch/$(arch)/OVMF-pure-efi.fd
linker_script := arch/$(arch)/linker.ld
grub_cfg := arch/$(arch)/grub.cfg
assembly_source_files := $(wildcard arch/$(arch)/*.asm)
assembly_object_files := $(patsubst arch/$(arch)/%.asm, \
build/arch/$(arch)/%.o, $(assembly_source_files))
.PHONY: all clean run iso
all: $(final) $(iso)
clean:
@xargo clean
@rm -rf build
run: $(final) $(iso)
@qemu-system-$(arch) -bios $(efi_bios) -cdrom $(iso) \
-chardev stdio,id=char0,mux=on,logfile=serial.log,signal=off \
-serial chardev:char0 -mon chardev=char0
iso: $(iso)
$(iso): $(final) $(grub_cfg)
@cp OVMF-pure-efi.fd build/arch/$(arch)/OVMF-pure-efi.fd # TODO! remove this, it's only for testing and i don't think we can distribute it
@mkdir -p isodir/boot/grub
@cp $(final) isodir/boot/wukkOS.bin
@cp $(grub_cfg) isodir/boot/grub/grub.cfg
@grub-mkrescue -o $(iso) isodir
@rm -rf isodir
$(final): $(kernel) $(linker_script) $(assembly_object_files)
@ld -n -T $(linker_script) -o $(final) $(assembly_object_files) $(kernel) \
--gc-sections
$(kernel):
@RUST_TARGET_PATH=$(shell pwd) xargo build --target $(target) -Zbuild-std=core,alloc
build/arch/$(arch)/%.o: arch/$(arch)/%.asm
@mkdir -p $(shell dirname $@)
@nasm -felf64 $< -o $@
quick_invalidate:
@echo "quick invalidation"
@rm -rf build/arch/$(arch)
@rm -rf $(kernel)

160
arch/x86_64/boot.asm Normal file
View file

@ -0,0 +1,160 @@
global start
extern long_mode_start
section .text
bits 32
;; basic boilerplate stuff from blog_os, expect to be changed
start:
mov esp, stack_top
call check_multiboot
call check_cpuid
call check_long_mode
call setup_page_tables
call enable_paging
lgdt [gdt64.pointer]
jmp gdt64.code:long_mode_start
hlt
check_multiboot:
cmp eax, 0x36d76289
jne .no_multiboot
ret
.no_multiboot:
mov al, "0"
jmp error
check_cpuid:
; Check if CPUID is supported by attempting to flip the ID bit (bit 21)
; in the FLAGS register. If we can flip it, CPUID is available.
; Copy FLAGS in to EAX via stack
pushfd
pop eax
; Copy to ECX as well for comparing later on
mov ecx, eax
; Flip the ID bit
xor eax, 1 << 21
; Copy EAX to FLAGS via the stack
push eax
popfd
; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported)
pushfd
pop eax
; Restore FLAGS from the old version stored in ECX (i.e. flipping the
; ID bit back if it was ever flipped).
push ecx
popfd
; Compare EAX and ECX. If they are equal then that means the bit
; wasn't flipped, and CPUID isn't supported.
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
mov al, "1"
jmp error
check_long_mode:
; test if extended processor info in available
mov eax, 0x80000000 ; implicit argument for cpuid
cpuid ; get highest supported argument
cmp eax, 0x80000001 ; it needs to be at least 0x80000001
jb .no_long_mode ; if it's less, the CPU is too old for long mode
; use extended info to test if long mode is available
mov eax, 0x80000001 ; argument for extended processor info
cpuid ; returns various feature bits in ecx and edx
test edx, 1 << 29 ; test if the LM-bit is set in the D-register
jz .no_long_mode ; If it's not set, there is no long mode
ret
.no_long_mode:
mov al, "2"
jmp error
setup_page_tables:
; map first P4 entry to P3 table
mov eax, p3_table
or eax, 0b11 ; present + writable
mov [p4_table], eax
; map first P3 entry to P2 table
mov eax, p2_table
or eax, 0b11 ; present + writable
mov [p3_table], eax
; map each P2 entry to a huge 2MiB page
mov ecx, 0 ; counter variable
.map_p2_table:
; map ecx-th P2 entry to a huge page that starts at address 2MiB*ecx
mov eax, 0x200000 ; 2MiB
mul ecx ; start address of ecx-th page
or eax, 0b10000011 ; present + writable + huge
mov [p2_table + ecx * 8], eax ; map ecx-th entry
inc ecx ; increase counter
cmp ecx, 512 ; if counter == 512, the whole P2 table is mapped
jne .map_p2_table ; else map the next entry
ret
enable_paging:
; load P4 to cr3 register (cpu uses this to access the P4 table)
mov eax, p4_table
mov cr3, eax
; enable PAE-flag in cr4 (Physical Address Extension)
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
; set the long mode bit in the EFER MSR (model specific register)
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
; enable paging in the cr0 register
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
ret
; Prints `ERR: ` and the given error code to screen and hangs.
; parameter: error code (in ascii) in al
error:
mov dword [0xb8000], 0x4f524f45
mov dword [0xb8004], 0x4f3a4f52
mov dword [0xb8008], 0x4f204f20
mov byte [0xb800a], al
hlt
section .bss
align 4096
p4_table:
resb 4096
p3_table:
resb 4096
p2_table:
resb 4096
stack_bottom:
resb 64
stack_top:
section .rodata
gdt64:
dq 0 ; zero entry
.code: equ $ - gdt64 ; new
dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment
.pointer:
dw $ - gdt64 - 1
dq gdt64

7
arch/x86_64/grub.cfg Normal file
View file

@ -0,0 +1,7 @@
set timeout=0
set default=0
menuentry "wukkOS basic kernel" {
multiboot2 /boot/wukkOS.bin
boot
}

16
arch/x86_64/linker.ld Normal file
View file

@ -0,0 +1,16 @@
ENTRY(start)
SECTIONS {
. = 1M;
.boot :
{
/* ensure that the multiboot header is at the beginning */
KEEP(*(.multiboot_header))
}
.text :
{
*(.text)
}
}

View file

@ -0,0 +1,17 @@
global long_mode_start
extern kernel_main
section .text
bits 64
long_mode_start:
mov ax, 0x00
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
extern kernel_main
call kernel_main
hlt

View file

@ -0,0 +1,16 @@
section .multiboot_header
header_start:
align 4
; multiboot 2 magic
dd 0xe85250d6
dd 0x00000000
dd header_end - header_start
; checksum
dd 0x100000000 - (0xe85250d6 + 0x00000000 + (header_end - header_start))
; required end tag
dw 0 ; type
dw 0 ; flags
dd 8 ; size
header_end:

View file

@ -1,31 +0,0 @@
;; bootstrap code from osdev.org
MBALIGN equ 1 << 0
MEMINFO equ 1 << 1
FLAGS equ MBALIGN | MEMINFO
MAGIC equ 0x1BADB002
CHECKSUM equ -(MAGIC + FLAGS)
section .multiboot
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
section .bss
align 16
stack_bottom:
resb 16384 ; 16 KiB stack
stack_top:
section .text
global _start:function (_start.end - _start)
_start:
mov esp, stack_top
extern kernel_main
call kernel_main
cli
.hang: hlt
jmp .hang
.end:

View file

@ -1,3 +0,0 @@
fn main() {
println!("cargo:rustc-link-arg=-Tlinker.ld");
}

View file

@ -1,29 +0,0 @@
ENTRY(_start)
SECTIONS
{
. = 1M;
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
}

13
serial.log Normal file
View file

@ -0,0 +1,13 @@
[=3h[=3h[=3h[=3hBdsDxe: loading Boot0001 "UEFI QEMU DVD-ROM QM00003 " from PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Master,0x0)
BdsDxe: starting Boot0001 "UEFI QEMU DVD-ROM QM00003 " from PciRoot(0x0)/Pci(0x1,0x1)/Ata(Secondary,Master,0x0)
Welcome to GRUB!
 Booting `wukkOS basic kernel'
WARNING: no console will be available to OS
error: no suitable video mode found.
welcome to wukkOS!
(c) 2022 Real Microsoft, LLC

View file

@ -2,108 +2,11 @@
use core::borrow::{BorrowMut}; use core::borrow::{BorrowMut};
use crate::{InterruptStackFrame, font, FACEBOOK}; use crate::{InterruptStackFrame, font};
use crate::internals::WhyDoTheyCallItOvenWhenYouOfInTheColdFoodOfOutHotEatTheFood::{COMMUNIST_RED, CUM_WHITE, Colour}; use crate::internals::WhyDoTheyCallItOvenWhenYouOfInTheColdFoodOfOutHotEatTheFood::{COMMUNIST_RED, CUM_WHITE, Colour};
fn draw_box(x: usize, y: usize, width: usize, height: usize, color: Colour, mut fb: &mut [u8]) {
let pixelwidth = FACEBOOK.fb_pixelwidth.lock();
let pitch = FACEBOOK.fb_pitch.lock();
let colourtype = FACEBOOK.fb_colourtype.lock();
for i in 0..width {
for j in 0..height {
let pixel = (y * &*pitch) + (x * &*pixelwidth) + (i * &*pixelwidth) + (j * &*pitch);
if *colourtype == 0 { //BGR
unsafe {
fb[pixel + 0] = color.b;
fb[pixel + 1] = color.g;
fb[pixel + 2] = color.r;
}
} else if *colourtype == 1 { //RGB
unsafe {
fb[pixel + 0] = color.r;
fb[pixel + 1] = color.g;
fb[pixel + 2] = color.b;
}
} else {
// average values
let avg = (color.r as u16 + color.g as u16 + color.b as u16) / 3;
unsafe {fb[pixel + 0] = avg as u8;}
}
}
}
drop(pixelwidth);
drop(pitch);
drop(colourtype);
}
fn put_pixel(x: usize, y: usize, color: Colour, mut fb: &mut [u8]) {
let pixelwidth = FACEBOOK.fb_pixelwidth.lock();
let pitch = FACEBOOK.fb_pitch.lock();
let colourtype = FACEBOOK.fb_colourtype.lock();
let pixel = (y * &*pitch) + (x * &*pixelwidth);
if *colourtype == 0 { //BGR
unsafe {
fb[pixel + 0] = color.b;
fb[pixel + 1] = color.g;
fb[pixel + 2] = color.r;
}
} else if *colourtype == 1 { //RGB
unsafe {
fb[pixel + 0] = color.r;
fb[pixel + 1] = color.g;
fb[pixel + 2] = color.b;
}
} else {
// average values
let avg = (color.r as u16 + color.g as u16 + color.b as u16) / 3;
unsafe {fb[pixel + 0] = avg as u8;}
}
drop(pixelwidth);
drop(pitch);
drop(colourtype);
}
fn draw_char(x: usize, y: usize, c: char, color: Colour, mut fb: &mut [u8]) {
let font = font::BASIC_LEGACY;
// font is 8x8, stored in a 2d array of bytes
let char_width = 8;
let char_height = 8;
let char_index = c as usize;
let char_data = font[char_index];
for row in 0..char_height {
for col in 0..char_width {
let bit = (char_data[row] >> col) & 1;
if bit >= 1 {
put_pixel(x + col, y + row, color, fb);
}
}
}
}
fn draw_horizcentre_string(width: usize, y: usize, s: &str, color: Colour, fb: &mut [u8]) {
let mut x_tmp = (width - s.len() * 8) / 2;
let mut y_tmp = y;
for c in s.chars() {
if c == '\n' {
x_tmp = (width - s.len() * 8) / 2;
y_tmp += 8;
} else {
draw_char(x_tmp, y_tmp, c, color, fb.borrow_mut());
x_tmp += 8;
}
}
}
pub extern "x86-interrupt" fn breakpoint_exception(stack_frame: InterruptStackFrame) { pub extern "x86-interrupt" fn breakpoint_exception(stack_frame: InterruptStackFrame) {
/*
// cover the screen in a nice communist red (: // cover the screen in a nice communist red (:
let mut fb = FACEBOOK.fb_mutex.lock(); let mut fb = FACEBOOK.fb_mutex.lock();
let fb_width = FACEBOOK.fb_width.lock(); let fb_width = FACEBOOK.fb_width.lock();
@ -118,4 +21,5 @@ pub extern "x86-interrupt" fn breakpoint_exception(stack_frame: InterruptStackFr
drop(fb_width); drop(fb_width);
drop(fb_height); drop(fb_height);
drop(fb); drop(fb);
*/
} }

View file

@ -3,27 +3,28 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
use alloc::format;
use alloc::sync::Arc;
use allocator::*; use allocator::*;
extern crate alloc; extern crate alloc;
extern crate rlibc;
use alloc::vec::Vec; use alloc::vec::Vec;
use spin::Mutex; use spin::Mutex;
use core::arch::asm; use core::arch::asm;
use core::ops::Deref;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use core::panic::PanicInfo; use core::panic::PanicInfo;
use crate::internals::WhyDoTheyCallItOvenWhenYouOfInTheColdFoodOfOutHotEatTheFood::*;
use crate::serial::potential_serial_ports;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
use crate::internals::WhyDoTheyCallItOvenWhenYouOfInTheColdFoodOfOutHotEatTheFood::*;
use crate::serial::{Port, potential_serial_ports, terminal_helpers, terminal::ST};
mod font; mod font;
mod serial; mod serial;
mod internals; mod internals;
mod allocator; mod allocator;
// THIS IS THE ONLY GLOBAL VARIABLE WE WILL EVER HAVE, MARK THIS ON MY FUCKING GRAVE
//pub static mut FRAMEBUFFER: Option<FBInfo> = None;
lazy_static! { lazy_static! {
static ref IDT: InterruptDescriptorTable = { static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new(); let mut idt = InterruptDescriptorTable::new();
@ -39,12 +40,27 @@ const RAINBOW : [Colour; 6] = [Colour{r:255,g:0,b:0}, Colour{r:255,g:127,b:0}, C
#[panic_handler] #[panic_handler]
fn panic(_info: &PanicInfo) -> ! { loop {} } fn panic(_info: &PanicInfo) -> ! { loop {} }
fn KernelPanic(msg: KernelError, fb: &mut FrameBuffer) { #[no_mangle]
// cover the screen in red pub extern fn kernel_main() -> ! {
// initialise serial
let mut serial_ports = serial::init_serial();
let mut console_port = None;
for (i, enabled) in serial_ports.ports_enabled.iter().enumerate() {
if *enabled {
console_port = Some(i);
}
} }
#[no_mangle] if let Some(i) = console_port {
fn kernel_main() -> ! { let port = &serial_ports.ports[i];
ST.init_from_port(*port);
}
ST.logln("");
ST.logln("");
ST.logln("");
ST.logln("welcome to wukkOS!");
ST.logln("(c) 2022 Real Microsoft, LLC");
loop {} loop {}
} }

View file

@ -5,6 +5,8 @@ use core::borrow::{Borrow, BorrowMut};
use core::ops::Deref; use core::ops::Deref;
pub mod ps2; pub mod ps2;
pub mod terminal_helpers;
pub mod terminal;
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum potential_serial_ports { pub enum potential_serial_ports {
@ -84,6 +86,12 @@ impl Port {
command(self.base as u16 + serial_offsets::DATA as u16, data); command(self.base as u16 + serial_offsets::DATA as u16, data);
} }
pub fn transmit_string(&self, data: &str) {
for c in data.chars() {
self.transmit(c as u8);
}
}
fn is_recv_full(&self) -> bool { fn is_recv_full(&self) -> bool {
let status = read(self.base as u16 + serial_offsets::LINE_STATUS as u16); let status = read(self.base as u16 + serial_offsets::LINE_STATUS as u16);
status & 0x01 == 0x01 status & 0x01 == 0x01

37
src/serial/terminal.rs Normal file
View file

@ -0,0 +1,37 @@
use alloc::sync::Arc;
use core::ops::Deref;
use lazy_static::lazy_static;
use spin::Mutex;
use crate::serial::Port;
pub struct SerialTerminal {
pub port: Arc<Mutex<Option<Port>>>,
}
lazy_static! {
pub static ref ST: SerialTerminal = {
let mut serial_terminal = SerialTerminal {
port: Arc::new(Mutex::new(None)),
};
serial_terminal
};
}
impl SerialTerminal {
pub fn init_from_port(&self, port: Port) {
self.port.lock().replace(port);
}
pub fn log(&self, message: &str) {
if let Some(port) = self.port.lock().deref() {
port.transmit_string(message);
}
}
pub fn logln(&self, message: &str) {
if let Some(port) = self.port.lock().deref() {
port.transmit_string(message);
port.transmit_string("\r\n");
}
}
}

View file

@ -0,0 +1,9 @@
pub fn clear_screen() -> &'static str {
//"\033[2J"
"\x1B[2J"
}
pub fn init_cursor() -> &'static str {
//"\033[?25l"
"\x1B[H"
}