Refactor into arch and os, remove warnings
This commit is contained in:
@@ -21,7 +21,7 @@ endif
|
||||
clean:
|
||||
rm -rf build
|
||||
|
||||
$(BUILD)/libbootloader.a: Cargo.lock Cargo.toml src/**
|
||||
$(BUILD)/libbootloader.a: Cargo.lock Cargo.toml $(shell find src -type f)
|
||||
mkdir -p $(BUILD)
|
||||
cargo rustc --lib --target $(TARGET) --release -- -C soft-float -C debuginfo=2 --emit link=$@
|
||||
|
||||
@@ -31,7 +31,7 @@ $(BUILD)/bootloader.elf: linkers/$(TARGET).ld $(BUILD)/libbootloader.a
|
||||
$(OBJCOPY) --only-keep-debug $@ $@.sym && \
|
||||
$(OBJCOPY) --strip-debug $@
|
||||
|
||||
$(BUILD)/bootloader.bin: $(BUILD)/bootloader.elf asm/$(TARGET)/**
|
||||
$(BUILD)/bootloader.bin: $(BUILD)/bootloader.elf $(shell find asm/$(TARGET) -type f)
|
||||
mkdir -p $(BUILD)
|
||||
nasm -f bin -o $@ -l $@.lst -D STAGE3=$< -iasm/$(TARGET) asm/$(TARGET)/bootloader.asm
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
pub use self::x86_64::*;
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
mod x86_64;
|
||||
@@ -0,0 +1,4 @@
|
||||
pub use self::paging::paging_create;
|
||||
|
||||
mod paging;
|
||||
mod panic;
|
||||
@@ -1,9 +1,9 @@
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use alloc::alloc;
|
||||
use core::slice;
|
||||
|
||||
unsafe fn paging_allocate() -> Option<&'static mut [u64]> {
|
||||
let ptr = crate::ALLOCATOR.alloc_zeroed(
|
||||
Layout::from_size_align(4096, 4096).unwrap()
|
||||
let ptr = alloc::alloc_zeroed(
|
||||
alloc::Layout::from_size_align(4096, 4096).unwrap()
|
||||
);
|
||||
if ! ptr.is_null() {
|
||||
Some(slice::from_raw_parts_mut(
|
||||
@@ -1,7 +1,6 @@
|
||||
//! Intrinsics for panic handling
|
||||
|
||||
use core::alloc::Layout;
|
||||
use core::fmt::Write;
|
||||
use core::panic::PanicInfo;
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
@@ -13,7 +12,7 @@ pub extern "C" fn rust_eh_personality() {}
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_begin_unwind(info: &PanicInfo) -> ! {
|
||||
unsafe {
|
||||
let _ = writeln!(crate::VGA.lock(), "BOOTLOADER PANIC:\n{}", info);
|
||||
println!("BOOTLOADER PANIC:\n{}", info);
|
||||
loop {
|
||||
llvm_asm!("hlt" : : : : "intel", "volatile");
|
||||
}
|
||||
+2
-389
@@ -6,395 +6,8 @@
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{
|
||||
string::String,
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
cmp,
|
||||
convert::TryFrom,
|
||||
fmt::{self, Write},
|
||||
ptr,
|
||||
slice,
|
||||
};
|
||||
use linked_list_allocator::LockedHeap;
|
||||
use log::error;
|
||||
use spin::Mutex;
|
||||
|
||||
use self::os::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 logger;
|
||||
mod memory_map;
|
||||
mod os;
|
||||
mod paging;
|
||||
mod panic;
|
||||
mod thunk;
|
||||
mod vbe;
|
||||
mod vga;
|
||||
|
||||
// Real mode memory allocation, for use with thunk
|
||||
// 0x500 to 0x7BFF is free
|
||||
const DISK_BIOS_ADDR: usize = 0x1000; // 4096 bytes, ends at 0x1FFF
|
||||
const VBE_CARD_INFO_ADDR: usize = 0x2000; // 512 bytes, ends at 0x21FF
|
||||
const VBE_MODE_INFO_ADDR: usize = 0x2200; // 256 bytes, ends at 0x22FF
|
||||
const MEMORY_MAP_ADDR: usize = 0x2300; // 24 bytes, ends at 0x2317
|
||||
const DISK_ADDRESS_PACKET_ADDR: usize = 0x2318; // 16 bytes, ends at 0x2327
|
||||
const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards
|
||||
const VGA_ADDR: usize = 0xB8000;
|
||||
|
||||
const PHYS_OFFSET: u64 = 0xFFFF800000000000;
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||
|
||||
static VGA: Mutex<Vga> = Mutex::new(
|
||||
unsafe { Vga::new(VGA_ADDR, 80, 25) }
|
||||
);
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct KernelArgs {
|
||||
kernel_base: u64,
|
||||
kernel_size: u64,
|
||||
stack_base: u64,
|
||||
stack_size: u64,
|
||||
env_base: u64,
|
||||
env_size: u64,
|
||||
|
||||
/// The base 64-bit pointer to an array of saved RSDPs. It's up to the kernel (and possibly
|
||||
/// userspace), to decide which RSDP to use. The buffer will be a linked list containing a
|
||||
/// 32-bit relative (to this field) next, and the actual struct afterwards.
|
||||
///
|
||||
/// This field can be NULL, and if so, the system has not booted with UEFI or in some other way
|
||||
/// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS
|
||||
/// memory instead. On UEFI systems, searching is not guaranteed to actually work though.
|
||||
acpi_rsdps_base: u64,
|
||||
/// The size of the RSDPs region.
|
||||
acpi_rsdps_size: u64,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn kstart(
|
||||
kernel_entry: extern "C" fn(
|
||||
page_table: usize,
|
||||
stack: u64,
|
||||
func: u64,
|
||||
args: *const KernelArgs,
|
||||
) -> !,
|
||||
boot_disk: usize,
|
||||
thunk10: extern "C" fn(),
|
||||
thunk13: extern "C" fn(),
|
||||
thunk15: extern "C" fn(),
|
||||
thunk16: extern "C" fn(),
|
||||
) -> ! {
|
||||
{
|
||||
// Make sure we are in mode 3 (80x25 text mode)
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x03;
|
||||
data.with(thunk10);
|
||||
}
|
||||
|
||||
{
|
||||
// Disable cursor
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x0100;
|
||||
data.ecx = 0x3F00;
|
||||
data.with(thunk10);
|
||||
}
|
||||
|
||||
// Clear screen
|
||||
VGA.lock().clear();
|
||||
|
||||
// Set logger
|
||||
LOGGER.init();
|
||||
|
||||
let (heap_start, heap_size) = memory_map(thunk15)
|
||||
.expect("No memory for heap");
|
||||
|
||||
ALLOCATOR.lock().init(heap_start, heap_size);
|
||||
|
||||
// Locate kernel on RedoxFS
|
||||
let disk = DiskBios::new(u8::try_from(boot_disk).unwrap(), thunk13);
|
||||
|
||||
//TODO: get block from partition table
|
||||
let block = 1024 * 1024 / redoxfs::BLOCK_SIZE;
|
||||
let mut fs = redoxfs::FileSystem::open(disk, Some(block))
|
||||
.expect("Failed to open RedoxFS");
|
||||
|
||||
print!("RedoxFS ");
|
||||
for i in 0..fs.header.1.uuid.len() {
|
||||
if i == 4 || i == 6 || i == 8 || i == 10 {
|
||||
print!("-");
|
||||
}
|
||||
|
||||
print!("{:>02x}", fs.header.1.uuid[i]);
|
||||
}
|
||||
println!(": {} MiB", fs.header.1.size / 1024 / 1024);
|
||||
|
||||
let mut modes = Vec::new();
|
||||
{
|
||||
// Get card info
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F00;
|
||||
data.edi = VBE_CARD_INFO_ADDR as u32;
|
||||
data.with(thunk10);
|
||||
if data.eax == 0x004F {
|
||||
let card_info = ptr::read(VBE_CARD_INFO_ADDR as *const VbeCardInfo);
|
||||
|
||||
let mut mode_ptr = card_info.videomodeptr as *const u16;
|
||||
loop {
|
||||
// Ask for linear frame buffer with mode
|
||||
let mode = *mode_ptr | (1 << 14);
|
||||
if mode == 0xFFFF {
|
||||
break;
|
||||
}
|
||||
mode_ptr = mode_ptr.add(1);
|
||||
|
||||
// Get mode info
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F01;
|
||||
data.ecx = mode as u32;
|
||||
data.edi = VBE_MODE_INFO_ADDR as u32;
|
||||
data.with(thunk10);
|
||||
if data.eax == 0x004F {
|
||||
let mode_info = ptr::read(VBE_MODE_INFO_ADDR as *const VbeModeInfo);
|
||||
|
||||
// We only support 32-bits per pixel modes
|
||||
if mode_info.bitsperpixel != 32 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let w = mode_info.xresolution as u32;
|
||||
let h = mode_info.yresolution as u32;
|
||||
|
||||
let mut aspect_w = w;
|
||||
let mut aspect_h = h;
|
||||
for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) {
|
||||
while aspect_w % i == 0 && aspect_h % i == 0 {
|
||||
aspect_w /= i;
|
||||
aspect_h /= i;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: support resolutions that are not perfect multiples of 4
|
||||
if w % 4 != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
modes.push((
|
||||
mode,
|
||||
w, h,
|
||||
mode_info.physbaseptr,
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("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)));
|
||||
|
||||
println!();
|
||||
println!("Arrow keys and enter select mode");
|
||||
println!();
|
||||
print!(" ");
|
||||
|
||||
//TODO 0x4F03 VBE function to get current mode
|
||||
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);
|
||||
while ! modes.is_empty() {
|
||||
let mut row = 0;
|
||||
let mut col = 0;
|
||||
for (mode, w, h, ptr, text) in modes.iter() {
|
||||
if row >= rows {
|
||||
col += 1;
|
||||
row = 0;
|
||||
}
|
||||
|
||||
VGA.lock().x = off_x + col * 20;
|
||||
VGA.lock().y = off_y + row;
|
||||
|
||||
if *mode == selected {
|
||||
VGA.lock().bg = VgaTextColor::White;
|
||||
VGA.lock().fg = VgaTextColor::Black;
|
||||
} else {
|
||||
VGA.lock().bg = VgaTextColor::DarkGray;
|
||||
VGA.lock().fg = VgaTextColor::White;
|
||||
}
|
||||
|
||||
print!("{}", text);
|
||||
|
||||
row += 1;
|
||||
}
|
||||
|
||||
// Read keypress
|
||||
let mut data = ThunkData::new();
|
||||
data.with(thunk16);
|
||||
match (data.eax >> 8) as u8 {
|
||||
0x4B /* Left */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
if mode_i < rows {
|
||||
while mode_i < modes.len() {
|
||||
mode_i += rows;
|
||||
}
|
||||
}
|
||||
mode_i -= rows;
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x4D /* Right */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
mode_i += rows;
|
||||
if mode_i >= modes.len() {
|
||||
mode_i = mode_i % rows;
|
||||
}
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x48 /* Up */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
if mode_i % rows == 0 {
|
||||
mode_i += rows;
|
||||
if mode_i > modes.len() {
|
||||
mode_i = modes.len();
|
||||
}
|
||||
}
|
||||
mode_i -= 1;
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x50 /* Down */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
mode_i += 1;
|
||||
if mode_i % rows == 0 {
|
||||
mode_i -= rows;
|
||||
}
|
||||
if mode_i >= modes.len() {
|
||||
mode_i = mode_i - mode_i % rows;
|
||||
}
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x1C /* Enter */ => {
|
||||
break;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
VGA.lock().x = 0;
|
||||
VGA.lock().y = off_y + rows;
|
||||
VGA.lock().bg = VgaTextColor::DarkGray;
|
||||
VGA.lock().fg = VgaTextColor::White;
|
||||
println!();
|
||||
|
||||
let kernel = {
|
||||
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");
|
||||
|
||||
print!("Kernel: 0/{} 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 kernel = slice::from_raw_parts_mut(
|
||||
ptr,
|
||||
size as usize
|
||||
);
|
||||
|
||||
let mut i = 0;
|
||||
for chunk in kernel.chunks_mut(1024 * 1024) {
|
||||
print!("\rKernel: {}/{} MiB", i / 1024 / 1024, size / 1024 / 1024);
|
||||
i += fs.read_node(node.0, i, chunk, 0, 0)
|
||||
.expect("Failed to read kernel file") as u64;
|
||||
}
|
||||
println!("\rKernel: {}/{} MiB", i / 1024 / 1024, size / 1024 / 1024);
|
||||
|
||||
kernel
|
||||
};
|
||||
|
||||
let page_phys = paging::paging_create(kernel.as_ptr() as usize)
|
||||
.expect("Failed to set up paging");
|
||||
|
||||
//TODO: properly reserve page table allocations so kernel does not re-use them
|
||||
|
||||
let stack_size = 0x20000;
|
||||
let stack_base = ALLOCATOR.alloc_zeroed(
|
||||
Layout::from_size_align(stack_size, 4096).unwrap()
|
||||
);
|
||||
if stack_base.is_null() {
|
||||
panic!("Failed to allocate memory for stack");
|
||||
}
|
||||
|
||||
let mut env = String::with_capacity(4096);
|
||||
|
||||
if let Some(mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
if let Some((mode, w, h, ptr, text)) = modes.get(mode_i) {
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F02;
|
||||
data.ebx = *mode as u32;
|
||||
data.with(thunk10);
|
||||
|
||||
env.push_str(&format!("FRAMEBUFFER_ADDR={:016x}\n", ptr));
|
||||
env.push_str(&format!("FRAMEBUFFER_WIDTH={:016x}\n", w));
|
||||
env.push_str(&format!("FRAMEBUFFER_HEIGHT={:016x}\n", h));
|
||||
}
|
||||
}
|
||||
env.push_str(&format!("REDOXFS_BLOCK={:016x}\n", fs.block));
|
||||
env.push_str("REDOXFS_UUID=");
|
||||
for i in 0..fs.header.1.uuid.len() {
|
||||
if i == 4 || i == 6 || i == 8 || i == 10 {
|
||||
env.push('-');
|
||||
}
|
||||
|
||||
env.push_str(&format!("{:>02x}", fs.header.1.uuid[i]));
|
||||
}
|
||||
|
||||
let args = KernelArgs {
|
||||
kernel_base: kernel.as_ptr() as u64,
|
||||
kernel_size: kernel.len() as u64,
|
||||
stack_base: stack_base as u64,
|
||||
stack_size: stack_size as u64,
|
||||
env_base: env.as_ptr() as u64,
|
||||
env_size: env.len() as u64,
|
||||
acpi_rsdps_base: 0,
|
||||
acpi_rsdps_size: 0,
|
||||
};
|
||||
|
||||
kernel_entry(
|
||||
page_phys,
|
||||
args.stack_base + args.stack_size + PHYS_OFFSET,
|
||||
*(kernel.as_ptr().add(0x18) as *const u64),
|
||||
&args,
|
||||
);
|
||||
}
|
||||
mod arch;
|
||||
mod logger;
|
||||
|
||||
+2
-3
@@ -1,5 +1,4 @@
|
||||
use core::fmt::Write;
|
||||
use log::{Level, LevelFilter, Log, Metadata, Record, SetLoggerError};
|
||||
use log::{LevelFilter, Log, Metadata, Record};
|
||||
|
||||
pub static LOGGER: Logger = Logger;
|
||||
|
||||
@@ -13,7 +12,7 @@ impl Logger {
|
||||
}
|
||||
|
||||
impl Log for Logger {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -2,8 +2,9 @@ use core::{mem, ptr};
|
||||
use redoxfs::{BLOCK_SIZE, Disk};
|
||||
use syscall::error::{Error, Result, EIO};
|
||||
|
||||
use crate::{DISK_ADDRESS_PACKET_ADDR, DISK_BIOS_ADDR, ThunkData};
|
||||
use super::{DISK_ADDRESS_PACKET_ADDR, DISK_BIOS_ADDR, ThunkData};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(packed)]
|
||||
pub struct DiskAddressPacket {
|
||||
@@ -55,7 +56,7 @@ impl Disk for DiskBios {
|
||||
data.with(self.thunk13);
|
||||
|
||||
//TODO: return result on error
|
||||
assert_eq!(data.eax, 0);
|
||||
assert_eq!({ data.eax }, 0);
|
||||
|
||||
//TODO: check blocks transferred
|
||||
dap = ptr::read(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ({
|
||||
use core::fmt::Write;
|
||||
write!($crate::VGA.lock(), $($arg)*).unwrap();
|
||||
let _ = write!($crate::os::VGA.lock(), $($arg)*);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core::{cmp, mem, ptr};
|
||||
|
||||
use crate::thunk::ThunkData;
|
||||
use super::{MEMORY_MAP_ADDR, thunk::ThunkData};
|
||||
|
||||
#[repr(packed)]
|
||||
struct MemoryMapEntry {
|
||||
@@ -13,26 +13,24 @@ 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.edi = 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);
|
||||
assert_eq!({ data.eax }, 0x534D4150);
|
||||
assert_eq!({ data.ecx }, mem::size_of::<MemoryMapEntry>() as u32);
|
||||
let entry = ptr::read(MEMORY_MAP_ADDR as *const MemoryMapEntry);
|
||||
|
||||
//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 (
|
||||
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
|
||||
+389
-1
@@ -1,3 +1,391 @@
|
||||
pub use self::disk::DiskBios;
|
||||
use alloc::{
|
||||
string::String,
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
cmp,
|
||||
convert::TryFrom,
|
||||
ptr,
|
||||
slice,
|
||||
};
|
||||
use linked_list_allocator::LockedHeap;
|
||||
use log::error;
|
||||
use spin::Mutex;
|
||||
|
||||
use crate::logger::LOGGER;
|
||||
use crate::arch::paging_create;
|
||||
|
||||
use self::disk::DiskBios;
|
||||
use self::memory_map::memory_map;
|
||||
use self::thunk::ThunkData;
|
||||
use self::vbe::{VbeCardInfo, VbeModeInfo};
|
||||
use self::vga::{VgaTextColor, Vga};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod disk;
|
||||
mod memory_map;
|
||||
mod thunk;
|
||||
mod vbe;
|
||||
mod vga;
|
||||
|
||||
// Real mode memory allocation, for use with thunk
|
||||
// 0x500 to 0x7BFF is free
|
||||
const DISK_BIOS_ADDR: usize = 0x1000; // 4096 bytes, ends at 0x1FFF
|
||||
const VBE_CARD_INFO_ADDR: usize = 0x2000; // 512 bytes, ends at 0x21FF
|
||||
const VBE_MODE_INFO_ADDR: usize = 0x2200; // 256 bytes, ends at 0x22FF
|
||||
const MEMORY_MAP_ADDR: usize = 0x2300; // 24 bytes, ends at 0x2317
|
||||
const DISK_ADDRESS_PACKET_ADDR: usize = 0x2318; // 16 bytes, ends at 0x2327
|
||||
const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards
|
||||
const VGA_ADDR: usize = 0xB8000;
|
||||
|
||||
const PHYS_OFFSET: u64 = 0xFFFF800000000000;
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||
|
||||
pub(crate) static VGA: Mutex<Vga> = Mutex::new(
|
||||
unsafe { Vga::new(VGA_ADDR, 80, 25) }
|
||||
);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
pub struct KernelArgs {
|
||||
kernel_base: u64,
|
||||
kernel_size: u64,
|
||||
stack_base: u64,
|
||||
stack_size: u64,
|
||||
env_base: u64,
|
||||
env_size: u64,
|
||||
|
||||
/// The base 64-bit pointer to an array of saved RSDPs. It's up to the kernel (and possibly
|
||||
/// userspace), to decide which RSDP to use. The buffer will be a linked list containing a
|
||||
/// 32-bit relative (to this field) next, and the actual struct afterwards.
|
||||
///
|
||||
/// This field can be NULL, and if so, the system has not booted with UEFI or in some other way
|
||||
/// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS
|
||||
/// memory instead. On UEFI systems, searching is not guaranteed to actually work though.
|
||||
acpi_rsdps_base: u64,
|
||||
/// The size of the RSDPs region.
|
||||
acpi_rsdps_size: u64,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn kstart(
|
||||
kernel_entry: extern "C" fn(
|
||||
page_table: usize,
|
||||
stack: u64,
|
||||
func: u64,
|
||||
args: *const KernelArgs,
|
||||
) -> !,
|
||||
boot_disk: usize,
|
||||
thunk10: extern "C" fn(),
|
||||
thunk13: extern "C" fn(),
|
||||
thunk15: extern "C" fn(),
|
||||
thunk16: extern "C" fn(),
|
||||
) -> ! {
|
||||
{
|
||||
// Make sure we are in mode 3 (80x25 text mode)
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x03;
|
||||
data.with(thunk10);
|
||||
}
|
||||
|
||||
{
|
||||
// Disable cursor
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x0100;
|
||||
data.ecx = 0x3F00;
|
||||
data.with(thunk10);
|
||||
}
|
||||
|
||||
// Clear screen
|
||||
VGA.lock().clear();
|
||||
|
||||
// Set logger
|
||||
LOGGER.init();
|
||||
|
||||
let (heap_start, heap_size) = memory_map(thunk15)
|
||||
.expect("No memory for heap");
|
||||
|
||||
ALLOCATOR.lock().init(heap_start, heap_size);
|
||||
|
||||
// Locate kernel on RedoxFS
|
||||
let disk = DiskBios::new(u8::try_from(boot_disk).unwrap(), thunk13);
|
||||
|
||||
//TODO: get block from partition table
|
||||
let block = 1024 * 1024 / redoxfs::BLOCK_SIZE;
|
||||
let mut fs = redoxfs::FileSystem::open(disk, Some(block))
|
||||
.expect("Failed to open RedoxFS");
|
||||
|
||||
print!("RedoxFS ");
|
||||
for i in 0..fs.header.1.uuid.len() {
|
||||
if i == 4 || i == 6 || i == 8 || i == 10 {
|
||||
print!("-");
|
||||
}
|
||||
|
||||
print!("{:>02x}", fs.header.1.uuid[i]);
|
||||
}
|
||||
println!(": {} MiB", fs.header.1.size / 1024 / 1024);
|
||||
|
||||
let mut modes = Vec::new();
|
||||
{
|
||||
// Get card info
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F00;
|
||||
data.edi = VBE_CARD_INFO_ADDR as u32;
|
||||
data.with(thunk10);
|
||||
if data.eax == 0x004F {
|
||||
let card_info = ptr::read(VBE_CARD_INFO_ADDR as *const VbeCardInfo);
|
||||
|
||||
let mut mode_ptr = card_info.videomodeptr as *const u16;
|
||||
loop {
|
||||
// Ask for linear frame buffer with mode
|
||||
let mode = *mode_ptr | (1 << 14);
|
||||
if mode == 0xFFFF {
|
||||
break;
|
||||
}
|
||||
mode_ptr = mode_ptr.add(1);
|
||||
|
||||
// Get mode info
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F01;
|
||||
data.ecx = mode as u32;
|
||||
data.edi = VBE_MODE_INFO_ADDR as u32;
|
||||
data.with(thunk10);
|
||||
if data.eax == 0x004F {
|
||||
let mode_info = ptr::read(VBE_MODE_INFO_ADDR as *const VbeModeInfo);
|
||||
|
||||
// We only support 32-bits per pixel modes
|
||||
if mode_info.bitsperpixel != 32 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let w = mode_info.xresolution as u32;
|
||||
let h = mode_info.yresolution as u32;
|
||||
|
||||
let mut aspect_w = w;
|
||||
let mut aspect_h = h;
|
||||
for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) {
|
||||
while aspect_w % i == 0 && aspect_h % i == 0 {
|
||||
aspect_w /= i;
|
||||
aspect_h /= i;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: support resolutions that are not perfect multiples of 4
|
||||
if w % 4 != 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
modes.push((
|
||||
mode,
|
||||
w, h,
|
||||
mode_info.physbaseptr,
|
||||
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 });
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("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)));
|
||||
|
||||
println!();
|
||||
println!("Arrow keys and enter select mode");
|
||||
println!();
|
||||
print!(" ");
|
||||
|
||||
//TODO 0x4F03 VBE function to get current mode
|
||||
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);
|
||||
while ! modes.is_empty() {
|
||||
let mut row = 0;
|
||||
let mut col = 0;
|
||||
for (mode, _w, _h, _ptr, text) in modes.iter() {
|
||||
if row >= rows {
|
||||
col += 1;
|
||||
row = 0;
|
||||
}
|
||||
|
||||
VGA.lock().x = off_x + col * 20;
|
||||
VGA.lock().y = off_y + row;
|
||||
|
||||
if *mode == selected {
|
||||
VGA.lock().bg = VgaTextColor::White;
|
||||
VGA.lock().fg = VgaTextColor::Black;
|
||||
} else {
|
||||
VGA.lock().bg = VgaTextColor::DarkGray;
|
||||
VGA.lock().fg = VgaTextColor::White;
|
||||
}
|
||||
|
||||
print!("{}", text);
|
||||
|
||||
row += 1;
|
||||
}
|
||||
|
||||
// Read keypress
|
||||
let mut data = ThunkData::new();
|
||||
data.with(thunk16);
|
||||
match (data.eax >> 8) as u8 {
|
||||
0x4B /* Left */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
if mode_i < rows {
|
||||
while mode_i < modes.len() {
|
||||
mode_i += rows;
|
||||
}
|
||||
}
|
||||
mode_i -= rows;
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x4D /* Right */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
mode_i += rows;
|
||||
if mode_i >= modes.len() {
|
||||
mode_i = mode_i % rows;
|
||||
}
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x48 /* Up */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
if mode_i % rows == 0 {
|
||||
mode_i += rows;
|
||||
if mode_i > modes.len() {
|
||||
mode_i = modes.len();
|
||||
}
|
||||
}
|
||||
mode_i -= 1;
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x50 /* Down */ => {
|
||||
if let Some(mut mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
mode_i += 1;
|
||||
if mode_i % rows == 0 {
|
||||
mode_i -= rows;
|
||||
}
|
||||
if mode_i >= modes.len() {
|
||||
mode_i = mode_i - mode_i % rows;
|
||||
}
|
||||
if let Some(new) = modes.get(mode_i) {
|
||||
selected = new.0;
|
||||
}
|
||||
}
|
||||
},
|
||||
0x1C /* Enter */ => {
|
||||
break;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
VGA.lock().x = 0;
|
||||
VGA.lock().y = off_y + rows;
|
||||
VGA.lock().bg = VgaTextColor::DarkGray;
|
||||
VGA.lock().fg = VgaTextColor::White;
|
||||
println!();
|
||||
|
||||
let kernel = {
|
||||
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");
|
||||
|
||||
print!("Kernel: 0/{} 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 kernel = slice::from_raw_parts_mut(
|
||||
ptr,
|
||||
size as usize
|
||||
);
|
||||
|
||||
let mut i = 0;
|
||||
for chunk in kernel.chunks_mut(1024 * 1024) {
|
||||
print!("\rKernel: {}/{} MiB", i / 1024 / 1024, size / 1024 / 1024);
|
||||
i += fs.read_node(node.0, i, chunk, 0, 0)
|
||||
.expect("Failed to read kernel file") as u64;
|
||||
}
|
||||
println!("\rKernel: {}/{} MiB", i / 1024 / 1024, size / 1024 / 1024);
|
||||
|
||||
kernel
|
||||
};
|
||||
|
||||
let page_phys = paging_create(kernel.as_ptr() as usize)
|
||||
.expect("Failed to set up paging");
|
||||
|
||||
//TODO: properly reserve page table allocations so kernel does not re-use them
|
||||
|
||||
let stack_size = 0x20000;
|
||||
let stack_base = ALLOCATOR.alloc_zeroed(
|
||||
Layout::from_size_align(stack_size, 4096).unwrap()
|
||||
);
|
||||
if stack_base.is_null() {
|
||||
panic!("Failed to allocate memory for stack");
|
||||
}
|
||||
|
||||
let mut env = String::with_capacity(4096);
|
||||
|
||||
if let Some(mode_i) = modes.iter().position(|x| x.0 == selected) {
|
||||
if let Some((mode, w, h, ptr, _text)) = modes.get(mode_i) {
|
||||
let mut data = ThunkData::new();
|
||||
data.eax = 0x4F02;
|
||||
data.ebx = *mode as u32;
|
||||
data.with(thunk10);
|
||||
|
||||
env.push_str(&format!("FRAMEBUFFER_ADDR={:016x}\n", ptr));
|
||||
env.push_str(&format!("FRAMEBUFFER_WIDTH={:016x}\n", w));
|
||||
env.push_str(&format!("FRAMEBUFFER_HEIGHT={:016x}\n", h));
|
||||
}
|
||||
}
|
||||
env.push_str(&format!("REDOXFS_BLOCK={:016x}\n", fs.block));
|
||||
env.push_str("REDOXFS_UUID=");
|
||||
for i in 0..fs.header.1.uuid.len() {
|
||||
if i == 4 || i == 6 || i == 8 || i == 10 {
|
||||
env.push('-');
|
||||
}
|
||||
|
||||
env.push_str(&format!("{:>02x}", fs.header.1.uuid[i]));
|
||||
}
|
||||
|
||||
let args = KernelArgs {
|
||||
kernel_base: kernel.as_ptr() as u64,
|
||||
kernel_size: kernel.len() as u64,
|
||||
stack_base: stack_base as u64,
|
||||
stack_size: stack_size as u64,
|
||||
env_base: env.as_ptr() as u64,
|
||||
env_size: env.len() as u64,
|
||||
acpi_rsdps_base: 0,
|
||||
acpi_rsdps_size: 0,
|
||||
};
|
||||
|
||||
kernel_entry(
|
||||
page_phys,
|
||||
args.stack_base + args.stack_size + PHYS_OFFSET,
|
||||
*(kernel.as_ptr().add(0x18) as *const u64),
|
||||
&args,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use core::ptr;
|
||||
|
||||
use crate::THUNK_STACK_ADDR;
|
||||
use super::THUNK_STACK_ADDR;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(packed)]
|
||||
pub struct ThunkData {
|
||||
@@ -7,6 +7,7 @@ pub struct VgaTextBlock {
|
||||
pub color: u8,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum VgaTextColor {
|
||||
@@ -2,10 +2,12 @@
|
||||
pub use self::bios::*;
|
||||
|
||||
#[cfg(all(target_arch = "x86", target_os = "none"))]
|
||||
#[macro_use]
|
||||
mod bios;
|
||||
|
||||
#[cfg(target_os = "uefi")]
|
||||
pub use self::uefi::*;
|
||||
|
||||
#[cfg(target_os = "uefi")]
|
||||
#[macro_use]
|
||||
mod uefi;
|
||||
|
||||
Reference in New Issue
Block a user