Do not require unreal mode, load stage 2 in conventional memory

This commit is contained in:
Jeremy Soller
2022-02-07 13:49:03 -07:00
parent a692d3e2ce
commit 9db76a58b8
13 changed files with 197 additions and 261 deletions
+7
View File
@@ -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
+27 -27
View File
@@ -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,6 +29,30 @@ cpuid_check:
mov si, .msg_line
call print
mov si, .msg_edx
call print
pop ebx
push ebx
shr ebx, 16
call print_hex
pop ebx
call print_hex
mov si, .msg_must_contain
call print
mov ebx, cpuid_required_features.edx
shr ebx, 16
call print_hex
mov ebx, cpuid_required_features.edx
call print_hex
mov si, .msg_line
call print
mov si, .msg_ecx
call print
@@ -53,30 +77,6 @@ cpuid_check:
mov si, .msg_line
call print
mov si, .msg_edx
call print
pop ebx
push ebx
rol ebx, 16
call print_hex
pop ebx
call print_hex
mov si, .msg_must_contain
call print
mov ebx, cpuid_required_features.edx
shr ebx, 16
call print_hex
mov ebx, cpuid_required_features.edx
call print_hex
mov si, .msg_line
call print
.halt:
cli
hlt
@@ -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:
-33
View File
@@ -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
+4 -6
View File
@@ -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:
+7 -97
View File
@@ -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
; entry point
stage3.entry:
; stage3 stack at 256 KiB
mov esp, 0x40000
; 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
-35
View File
@@ -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
+2 -1
View File
@@ -2,7 +2,8 @@ ENTRY(kstart)
OUTPUT_FORMAT(elf32-i386)
SECTIONS {
. = 0x100000;
/* The start address must match bootloader.asm */
. = 0x9000;
. += SIZEOF_HEADERS;
. = ALIGN(4096);
+71 -50
View File
@@ -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);
let mut fs = redoxfs::FileSystem::open(disk, Some(block))
.expect("Failed to open RedoxFS");
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);
}
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;
}
+1 -5
View File
@@ -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());
}
}
+16
View File
@@ -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)*));
}
+54
View File
@@ -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;
}
}
}
+4 -3
View File
@@ -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");
}
}
+4 -4
View File
@@ -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,
)
}