diff --git a/asm/x86-unknown-none/bootloader.asm b/asm/x86-unknown-none/bootloader.asm index 95ec773de5e8f1be81ea83b5d70d2f2f08bea3c3..d616e5584b860c981ac277961e88911bacf0b36e 100644 --- a/asm/x86-unknown-none/bootloader.asm +++ b/asm/x86-unknown-none/bootloader.asm @@ -7,8 +7,15 @@ stage2: align 512, db 0 stage2.end: +; the maximum size of stage1 + stage2 is 5 KiB +; this places stage3 at 0x9000 +times 5120-($-$$) db 0 + stage3: %defstr STAGE3_STR %[STAGE3] incbin STAGE3_STR align 512, db 0 .end: + +; the maximum size of the boot loader portion is 128 KiB +times 131072-($-$$) db 0 diff --git a/asm/x86-unknown-none/cpuid.asm b/asm/x86-unknown-none/cpuid.asm index a1c4f755dce6ebb1df29f15c983407d876ad218c..715733d9d085c054ca80569aae81796284b7f368 100644 --- a/asm/x86-unknown-none/cpuid.asm +++ b/asm/x86-unknown-none/cpuid.asm @@ -3,7 +3,7 @@ USE16 cpuid_required_features: .edx equ cpuid_edx.fpu | cpuid_edx.sse | cpuid_edx.pae | cpuid_edx.pse | cpuid_edx.pge | cpuid_edx.fxsr - .ecx equ cpuid_ecx.xsave + .ecx equ 0 cpuid_check: mov eax, 1 @@ -20,8 +20,8 @@ cpuid_check: ret .error: - push edx push ecx + push edx mov si, .msg_features call print @@ -29,7 +29,7 @@ cpuid_check: mov si, .msg_line call print - mov si, .msg_ecx + mov si, .msg_edx call print pop ebx @@ -43,22 +43,22 @@ cpuid_check: mov si, .msg_must_contain call print - mov ebx, cpuid_required_features.ecx + mov ebx, cpuid_required_features.edx shr ebx, 16 call print_hex - mov ebx, cpuid_required_features.ecx + mov ebx, cpuid_required_features.edx call print_hex mov si, .msg_line call print - mov si, .msg_edx + mov si, .msg_ecx call print pop ebx push ebx - rol ebx, 16 + shr ebx, 16 call print_hex pop ebx @@ -67,11 +67,11 @@ cpuid_check: mov si, .msg_must_contain call print - mov ebx, cpuid_required_features.edx + mov ebx, cpuid_required_features.ecx shr ebx, 16 call print_hex - mov ebx, cpuid_required_features.edx + mov ebx, cpuid_required_features.ecx call print_hex mov si, .msg_line @@ -84,8 +84,8 @@ cpuid_check: .msg_features: db "Required CPU features are not present",0 .msg_line: db 13,10,0 -.msg_ecx: db "ECX ",0 .msg_edx: db "EDX ",0 +.msg_ecx: db "ECX ",0 .msg_must_contain: db " must contain ",0 cpuid_edx: diff --git a/asm/x86-unknown-none/memory_map.asm b/asm/x86-unknown-none/memory_map.asm deleted file mode 100644 index 460ea27b746200d233eb8418f842aece1c0b2d50..0000000000000000000000000000000000000000 --- a/asm/x86-unknown-none/memory_map.asm +++ /dev/null @@ -1,33 +0,0 @@ -SECTION .text -USE16 - -;Generate a memory map at 0x500 to 0x5000 (available memory not used for kernel or bootloader) -memory_map: -.start equ 0x0500 -.end equ 0x5000 -.length equ .end - .start - - xor eax, eax - mov di, .start - mov ecx, .length / 4 ; moving 4 Bytes at once - cld - rep stosd - - mov di, .start - mov edx, 0x534D4150 - xor ebx, ebx -.lp: - mov eax, 0xE820 - mov ecx, 24 - - int 0x15 - jc .done ; Error or finished - - cmp ebx, 0 - je .done ; Finished - - add di, 24 - cmp di, .end - jb .lp ; Still have buffer space -.done: - ret diff --git a/asm/x86-unknown-none/stage1.asm b/asm/x86-unknown-none/stage1.asm index 815d1af4274c4b8c5e7ea4495719b99efcd34414..26740c958e39e017e9e79581f506e28fe376dc31 100644 --- a/asm/x86-unknown-none/stage1.asm +++ b/asm/x86-unknown-none/stage1.asm @@ -28,11 +28,10 @@ stage1: ; dl comes with disk mov eax, (stage2 - stage1) / 512 mov bx, stage2 - mov cx, (stage2.end - stage2) / 512 - xor dx, dx + mov cx, (stage3.end - stage2) / 512 + mov dx, 0 call load - call print_line mov si, finished call print call print_line @@ -79,9 +78,6 @@ load: ret print_dapack: - mov al, 13 - call print_char - mov bx, [DAPACK.addr + 2] call print_hex @@ -106,6 +102,8 @@ print_dapack: mov bx, [DAPACK.buf] call print_hex + call print_line + ret error: diff --git a/asm/x86-unknown-none/stage2.asm b/asm/x86-unknown-none/stage2.asm index baa3f6b0ee0586a25c81f408630b62ba25e508eb..7455820188f5ae7459eb39800ad300eb08bafcb6 100644 --- a/asm/x86-unknown-none/stage2.asm +++ b/asm/x86-unknown-none/stage2.asm @@ -10,112 +10,22 @@ stage2.entry: or al, 2 out 0x92, al - ; load memory map - ;TODO: rewrite this in Rust - call memory_map - - mov edi, [args.stage3_base] - mov ecx, (stage3.end - stage3) - mov [args.stage3_size], ecx - - mov eax, (stage3 - stage1)/512 - add ecx, 511 - shr ecx, 9 - call load_extent - mov dword [protected_mode.func], stage3.entry jmp protected_mode.entry -args: - .stage3_base dq 0x100000 - .stage3_size dq 0 - -; load a disk extent into high memory -; eax - sector address -; ecx - sector count -; edi - destination -load_extent: - ; loading stage3 to 1MiB - ; move part of stage3 to stage2.end via bootsector#load and then copy it up - ; repeat until all of the stage3 is loaded - buffer_size_sectors equ 127 - -.lp: - cmp ecx, buffer_size_sectors - jb .break - - ; saving counter - push eax - push ecx - - push edi - - ; populating buffer - mov ecx, buffer_size_sectors - mov bx, stage2.end - mov dx, 0x0 - - ; load sectors - call load - - ; set up unreal mode - call unreal - - pop edi - - ; move data - mov esi, stage2.end - mov ecx, buffer_size_sectors * 512 / 4 - cld - a32 rep movsd - - pop ecx - pop eax - - add eax, buffer_size_sectors - sub ecx, buffer_size_sectors - jmp .lp - -.break: - ; load the part of the stage3 that does not fill the buffer completely - test ecx, ecx - jz .finish ; if cx = 0 => skip - - push ecx - push edi - - mov bx, stage2.end - mov dx, 0x0 - call load - - ; moving remnants of stage3 - call unreal - - pop edi - pop ecx - - mov esi, stage2.end - shl ecx, 7 ; * 512 / 4 - cld - a32 rep movsd - -.finish: - call print_line - ret - %include "cpuid.asm" %include "gdt.asm" %include "long_mode.asm" -%include "memory_map.asm" %include "protected_mode.asm" %include "thunk.asm" -%include "unreal.asm" USE32 + stage3.entry: - mov esp, 0x800000 - 128 + ; stage3 stack at 256 KiB + mov esp, 0x40000 - ; entry point + ; push arguments mov eax, thunk.int16 push eax mov eax, thunk.int15 @@ -127,8 +37,8 @@ stage3.entry: xor eax, eax mov al, [disk] push eax - mov eax, [args.stage3_base] - call [eax + 0x18] + mov eax, [stage3 + 0x18] + call eax .halt: cli hlt diff --git a/asm/x86-unknown-none/unreal.asm b/asm/x86-unknown-none/unreal.asm deleted file mode 100644 index b26397102eea203602788181ebb080831396e8e6..0000000000000000000000000000000000000000 --- a/asm/x86-unknown-none/unreal.asm +++ /dev/null @@ -1,35 +0,0 @@ -SECTION .text -USE16 - -; switch to unreal mode; ds and es can address up to 4GiB -unreal: - cli - - lgdt [gdtr] - - push es - push ds - - mov eax, cr0 ; switch to pmode by - or al,1 ; set pmode bit - mov cr0, eax - - jmp $+2 - -; http://wiki.osdev.org/Babystep7 -; When this register given a "selector", a "segment descriptor cache register" -; is filled with the descriptor values, including the size (or limit). After -; the switch back to real mode, these values are not modified, regardless of -; what value is in the 16-bit segment register. So the 64k limit is no longer -; valid and 32-bit offsets can be used with the real-mode addressing rules - mov bx, gdt.pm32_data - mov es, bx - mov ds, bx - - and al,0xFE ; back to realmode - mov cr0, eax ; by toggling bit again - - pop ds - pop es - sti - ret diff --git a/linkers/x86-unknown-none.ld b/linkers/x86-unknown-none.ld index abaf92bd00f32cff73fc111d2a596cdb1ec43193..7eb1b86dc32aaa159824bc12573190313445e037 100644 --- a/linkers/x86-unknown-none.ld +++ b/linkers/x86-unknown-none.ld @@ -2,7 +2,8 @@ ENTRY(kstart) OUTPUT_FORMAT(elf32-i386) SECTIONS { - . = 0x100000; + /* The start address must match bootloader.asm */ + . = 0x9000; . += SIZEOF_HEADERS; . = ALIGN(4096); diff --git a/src/lib.rs b/src/lib.rs index ad715ac46dac40f6a38d5cf8fa5487e41482b06c..dcb365d7b8dcfa41a1ce957e75222be438a04cda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,22 +8,28 @@ extern crate alloc; use alloc::vec::Vec; use core::{ + alloc::{GlobalAlloc, Layout}, cmp, fmt::{self, Write}, ptr, slice, }; use linked_list_allocator::LockedHeap; -use log::{error, info}; +use spin::Mutex; use self::disk::DiskBios; use self::logger::LOGGER; +use self::memory_map::memory_map; use self::thunk::ThunkData; use self::vbe::{VbeCardInfo, VbeModeInfo}; use self::vga::{VgaTextBlock, VgaTextColor, Vga}; +#[macro_use] +mod macros; + mod disk; mod logger; +mod memory_map; mod paging; mod panic; mod thunk; @@ -34,6 +40,7 @@ mod vga; // 0x500 to 0x7BFF is free const VBE_CARD_INFO_ADDR: usize = 0x500; // 512 bytes, ends at 0x6FF const VBE_MODE_INFO_ADDR: usize = 0x700; // 256 bytes, ends at 0x7FF +const MEMORY_MAP_ADDR: usize = 0x800; // 24 bytes, ends at 0x817 const DISK_ADDRESS_PACKET_ADDR: usize = 0x0FF0; // 16 bytes, ends at 0x0FFF const DISK_BIOS_ADDR: usize = 0x1000; // 4096 bytes, ends at 0x1FFF const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards @@ -42,7 +49,9 @@ const VGA_ADDR: usize = 0xB8000; #[global_allocator] static ALLOCATOR: LockedHeap = LockedHeap::empty(); -static mut VGA: Vga = unsafe { Vga::new(VGA_ADDR as *mut VgaTextBlock, 80, 25) }; +static VGA: Mutex<Vga> = Mutex::new( + unsafe { Vga::new(VGA_ADDR, 80, 25) } +); static mut KERNEL_PHYS: u64 = 0; @@ -71,59 +80,71 @@ pub unsafe extern "C" fn kstart( { // Clear VGA console - let blocks = VGA.blocks(); + let mut vga = VGA.lock(); + let blocks = vga.blocks(); for i in 0..blocks.len() { blocks[i] = VgaTextBlock { char: 0, - color: ((VGA.bg as u8) << 4) | (VGA.fg as u8), + color: ((vga.bg as u8) << 4) | (vga.fg as u8), }; } } - // Initialize allocator at the end of stage 3 with a meager 1 MiB, which we can be pretty - // sure will be available. - extern "C" { - static mut __end: u8; - } - let heap_start = &__end as *const _ as usize; - let heap_size = 1024 * 1024; - ALLOCATOR.lock().init(heap_start, heap_size); - // Set logger LOGGER.init(); - // Locate RedoxFS - { + let (heap_start, heap_size) = memory_map(thunk15).expect("no memory for heap"); + + println!("HEAP: {:X}:{:X}", heap_start, heap_size); + ALLOCATOR.lock().init(heap_start, heap_size); + + // Locate kernel on RedoxFS + let kernel = { //TODO: ensure boot_disk is 8-bit - info!("DISK {:02X}", boot_disk); + println!("BIOS Disk: {:02X}", boot_disk); let disk = DiskBios::new(boot_disk as u8, thunk13); + //TODO: get block from partition table let block = 1024 * 1024 / redoxfs::BLOCK_SIZE; - match redoxfs::FileSystem::open(disk, Some(block)) { - Ok(mut fs) => { - info!("RedoxFS {} MiB", fs.header.1.size / 1024 / 1024); - - match fs.find_node("kernel", fs.header.1.root) { - Ok(node) => match fs.node_len(node.0) { - Ok(len) => { - info!("Kernel {} MiB", len / 1024 / 1024); - }, - Err(err) => { - error!("Failed to read kernel file length: {:?}", err); - } - }, - Err(err) => { - error!("Failed to find kernel file: {:?}", err); - } - } - }, - Err(err) => { - error!("Failed to open RedoxFS: {:?}", err); - } + let mut fs = redoxfs::FileSystem::open(disk, Some(block)) + .expect("Failed to open RedoxFS"); + + println!("RedoxFS Size: {} MiB", fs.header.1.size / 1024 / 1024); + + let node = fs.find_node("kernel", fs.header.1.root) + .expect("failed to find kernel file"); + + let size = fs.node_len(node.0) + .expect("failed to read kernel size"); + + println!("Kernel Size: {} MiB", size / 1024 / 1024); + + let ptr = ALLOCATOR.alloc_zeroed( + Layout::from_size_align(size as usize, 4096).unwrap() + ); + if ptr.is_null() { + panic!("Failed to allocate memory for kernel"); } - } - //let page_phys = paging::paging_create(KERNEL_PHYS); + let kernel = slice::from_raw_parts_mut( + ptr, + size as usize + ); + + let mut i = 0; + for chunk in kernel.chunks_mut(1024 * 1024) { + print!("\rKernel Loading: {}%", i * 100 / size); + i += fs.read_node(node.0, i, chunk, 0, 0) + .expect("Failed to read kernel file") as u64; + } + println!("\rKernel Loading: 100%"); + + kernel + }; + + println!("Kernel Phys: 0x{:X}", kernel.as_ptr() as u64); + let page_phys = paging::paging_create(kernel.as_ptr() as u64); + panic!("kernel entry not implemented"); let mut modes = Vec::new(); { @@ -182,22 +203,22 @@ pub unsafe extern "C" fn kstart( format!("{:>4}x{:<4} {:>3}:{:<3}", w, h, aspect_w, aspect_h) )); } else { - error!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, data.eax); + panic!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, data.eax); } } } else { - error!("Failed to read VBE card info: 0x{:04X}", data.eax); + panic!("Failed to read VBE card info: 0x{:04X}", data.eax); } } // Sort modes by pixel area, reversed modes.sort_by(|a, b| (b.1 * b.2).cmp(&(a.1 * a.2))); - writeln!(VGA, "Arrow keys and enter select mode").unwrap(); + println!("Arrow keys and enter select mode"); //TODO 0x4F03 VBE function to get current mode - let off_x = VGA.x; - let off_y = VGA.y; + let off_x = VGA.lock().x; + let off_y = VGA.lock().y; let rows = 12; let mut selected = modes.get(0).map_or(0, |x| x.0); loop { @@ -209,18 +230,18 @@ pub unsafe extern "C" fn kstart( row = 0; } - VGA.x = off_x + col * 20; - VGA.y = off_y + row; + VGA.lock().x = off_x + col * 20; + VGA.lock().y = off_y + row; if *mode == selected { - VGA.bg = VgaTextColor::White; - VGA.fg = VgaTextColor::Black; + VGA.lock().bg = VgaTextColor::White; + VGA.lock().fg = VgaTextColor::Black; } else { - VGA.bg = VgaTextColor::DarkGray; - VGA.fg = VgaTextColor::White; + VGA.lock().bg = VgaTextColor::DarkGray; + VGA.lock().fg = VgaTextColor::White; } - write!(VGA, "{}", text).unwrap(); + print!("{}", text); row += 1; } diff --git a/src/logger.rs b/src/logger.rs index d6c225f6c81dcedfcac1586e1b5fe593ce835e58..7c498a9a91b68159772e0d4f6ed4e0bd503e5546 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,8 +1,6 @@ use core::fmt::Write; use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError}; -use crate::VGA; - pub static LOGGER: Logger = Logger; pub struct Logger; @@ -21,9 +19,7 @@ impl Log for Logger { fn log(&self, record: &Record) { if self.enabled(record.metadata()) { - unsafe { - writeln!(VGA, "{} - {}", record.level(), record.args()).unwrap(); - } + println!("{} - {}", record.level(), record.args()); } } diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000000000000000000000000000000000000..0775c1c536f7691916bc33c4f2b799c2758468e7 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,16 @@ +/// Print to console +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => ({ + use core::fmt::Write; + write!($crate::VGA.lock(), $($arg)*).unwrap(); + }); +} + +/// Print with new line to console +#[macro_export] +macro_rules! println { + () => (print!("\n")); + ($fmt:expr) => (print!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); +} diff --git a/src/memory_map.rs b/src/memory_map.rs new file mode 100644 index 0000000000000000000000000000000000000000..8aa986f55f2005f3cbe51dfff341a8d257ab9cac --- /dev/null +++ b/src/memory_map.rs @@ -0,0 +1,54 @@ +use core::{cmp, mem, ptr}; + +use crate::thunk::ThunkData; + +#[repr(packed)] +struct MemoryMapEntry { + pub base: u64, + pub length: u64, + pub kind: u32, +} + +pub unsafe fn memory_map(thunk15: extern "C" fn()) -> Option<(usize, usize)> { + let mut heap_limits = None; + let mut data = ThunkData::new(); + loop { + let index = data.ebx; + + data.eax = 0xE820; + data.ecx = mem::size_of::<MemoryMapEntry>() as u32; + data.edx = 0x534D4150; + data.edi = crate::MEMORY_MAP_ADDR as u32; + + data.with(thunk15); + + assert_eq!(data.eax, 0x534D4150); + assert_eq!(data.ecx, mem::size_of::<MemoryMapEntry>() as u32); + let entry = ptr::read(crate::MEMORY_MAP_ADDR as *const MemoryMapEntry); + + println!("MEM {}: 0x{:016X} 0x{:016X} 0x{:08X}", index, entry.base, entry.length, entry.kind); + + //TODO: There is a problem with QEMU crashing if we write at about 8 MiB, so skip to 16 + let heap_start = 16 * 1024 * 1024; + if ( + entry.kind == 1 && + entry.base <= heap_start as u64 && + (entry.base + entry.length) >= heap_start as u64 + ) { + let heap_end = cmp::min( + entry.base + entry.length, + usize::MAX as u64 + ) as usize; + if heap_end >= heap_start { + heap_limits = Some(( + heap_start, + heap_end - heap_start + )); + } + } + + if data.ebx == 0 { + return heap_limits; + } + } +} diff --git a/src/panic.rs b/src/panic.rs index 9d817bfe1545ea3c10598caa46aecdb3472efb25..d2cf0dfbc357f90daae75ea59ed7a95143ef2ff8 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -1,6 +1,7 @@ //! Intrinsics for panic handling use core::alloc::Layout; +use core::fmt::Write; use core::panic::PanicInfo; #[lang = "eh_personality"] @@ -11,9 +12,9 @@ pub extern "C" fn rust_eh_personality() {} #[panic_handler] #[no_mangle] pub extern "C" fn rust_begin_unwind(info: &PanicInfo) -> ! { - log::error!("BOOTLOADER PANIC: {}", info); - loop { - unsafe { + unsafe { + let _ = writeln!(crate::VGA.lock(), "BOOTLOADER PANIC:\n{}", info); + loop { llvm_asm!("hlt" : : : : "intel", "volatile"); } } diff --git a/src/vga.rs b/src/vga.rs index 1987005296c07b924824dee0f1e979e101117fe1..61ae13e05714797948a6af3d6616680be38c09b3 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -29,7 +29,7 @@ pub enum VgaTextColor { } pub struct Vga { - pub ptr: *mut VgaTextBlock, + pub base: usize, pub width: usize, pub height: usize, pub x: usize, @@ -39,9 +39,9 @@ pub struct Vga { } impl Vga { - pub const unsafe fn new(ptr: *mut VgaTextBlock, width: usize, height: usize) -> Self { + pub const unsafe fn new(base: usize, width: usize, height: usize) -> Self { Self { - ptr, + base, width, height, x: 0, @@ -53,7 +53,7 @@ impl Vga { pub unsafe fn blocks(&mut self) -> &'static mut [VgaTextBlock] { slice::from_raw_parts_mut( - self.ptr, + self.base as *mut VgaTextBlock, self.width * self.height, ) }