Files
Redox-From-Scratch/bootloader/src/os/bios/mod.rs
2024-12-11 18:21:07 -08:00

311 lines
8.3 KiB
Rust

use alloc::alloc::{alloc_zeroed, Layout};
use core::{convert::TryFrom, mem, ptr, slice};
use linked_list_allocator::LockedHeap;
use spin::Mutex;
use crate::logger::LOGGER;
use crate::os::{Os, OsHwDesc, OsKey, OsVideoMode};
use crate::KernelArgs;
use self::disk::DiskBios;
use self::memory_map::memory_map;
use self::thunk::ThunkData;
use self::vbe::VideoModeIter;
use self::vga::{Vga, VgaTextColor};
#[macro_use]
mod macros;
mod disk;
mod memory_map;
mod panic;
pub(crate) mod serial;
mod thunk;
mod vbe;
mod vga;
// Real mode memory allocation, for use with thunk
// 0x500 to 0x7BFF is free
const DISK_BIOS_ADDR: usize = 0x70000; // 64 KiB at 448 KiB, ends at 512 KiB
const VBE_CARD_INFO_ADDR: usize = 0x1000; // 512 bytes, ends at 0x11FF
const VBE_MODE_INFO_ADDR: usize = 0x1200; // 256 bytes, ends at 0x12FF
const VBE_EDID_ADDR: usize = 0x1300; // 128 bytes, ends at 0x137F
const MEMORY_MAP_ADDR: usize = 0x1380; // 24 bytes, ends at 0x1397
const DISK_ADDRESS_PACKET_ADDR: usize = 0x1398; // 16 bytes, ends at 0x13A7
const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards
const VGA_ADDR: usize = 0xB8000;
#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty();
pub(crate) static VGA: Mutex<Vga> = Mutex::new(unsafe { Vga::new(VGA_ADDR, 80, 25) });
pub struct OsBios {
boot_disk: usize,
thunk10: extern "C" fn(),
thunk13: extern "C" fn(),
thunk15: extern "C" fn(),
thunk16: extern "C" fn(),
}
#[allow(dead_code)]
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Rsdp {
signature: [u8; 8],
checksum: u8,
oemid: [u8; 6],
revision: u8,
rsdt_address: u32,
}
#[allow(dead_code)]
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Xsdp {
rsdp: Rsdp,
length: u32,
xsdt_address: u64,
extended_checksum: u8,
reserved: [u8; 3],
}
unsafe fn search_rsdp(start: usize, end: usize) -> Option<(u64, u64)> {
// Align start up to 16 bytes
let mut addr = ((start + 15) / 16) * 16;
// Search until reading the end of the Rsdp would be past the end of the memory area
while addr + mem::size_of::<Rsdp>() <= end {
let rsdp = ptr::read(addr as *const Rsdp);
if &rsdp.signature == b"RSD PTR " {
//TODO: check checksum?
if rsdp.revision == 0 {
return Some((addr as u64, mem::size_of::<Rsdp>() as u64));
} else if rsdp.revision == 2 {
let xsdp = ptr::read(addr as *const Xsdp);
//TODO: check extended checksum?
return Some((addr as u64, xsdp.length as u64));
}
}
// Rsdp is always aligned to 16 bytes
addr += 16;
}
None
}
impl Os<DiskBios, VideoModeIter> for OsBios {
fn name(&self) -> &str {
"x86/BIOS"
}
fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8 {
assert!(size != 0);
let page_size = self.page_size();
let pages = (size + page_size - 1) / page_size;
let ptr =
unsafe { alloc_zeroed(Layout::from_size_align(pages * page_size, page_size).unwrap()) };
assert!(!ptr.is_null());
ptr
}
fn page_size(&self) -> usize {
4096
}
fn filesystem(
&self,
password_opt: Option<&[u8]>,
) -> syscall::Result<redoxfs::FileSystem<DiskBios>> {
let disk = DiskBios::new(u8::try_from(self.boot_disk).unwrap(), self.thunk13);
//TODO: get block from partition table
let block = 2 * crate::MIBI as u64 / redoxfs::BLOCK_SIZE;
redoxfs::FileSystem::open(disk, password_opt, Some(block), false)
}
fn hwdesc(&self) -> OsHwDesc {
// See ACPI specification - Finding the RSDP on IA-PC Systems
unsafe {
let ebda_segment = ptr::read(0x40E as *const u16);
let ebda_addr = (ebda_segment as usize) << 4;
if let Some((addr, size)) =
search_rsdp(ebda_addr, ebda_addr + 1024).or(search_rsdp(0xE0000, 0xFFFFF))
{
// Copy to a page
let page_aligned = self.alloc_zeroed_page_aligned(size as usize);
ptr::copy(addr as *const u8, page_aligned, size as usize);
return OsHwDesc::Acpi(page_aligned as u64, size);
}
}
OsHwDesc::NotFound
}
fn video_outputs(&self) -> usize {
//TODO: return 1 only if vbe supported?
1
}
fn video_modes(&self, _output_i: usize) -> VideoModeIter {
VideoModeIter::new(self.thunk10)
}
fn set_video_mode(&self, _output_i: usize, mode: &mut OsVideoMode) {
// Set video mode
let mut data = ThunkData::new();
data.eax = 0x4F02;
data.ebx = mode.id;
unsafe {
data.with(self.thunk10);
}
//TODO: check result
}
fn best_resolution(&self, _output_i: usize) -> Option<(u32, u32)> {
let mut data = ThunkData::new();
data.eax = 0x4F15;
data.ebx = 0x01;
data.ecx = 0;
data.edx = 0;
data.edi = VBE_EDID_ADDR as u32;
unsafe {
data.with(self.thunk10);
}
if data.eax == 0x4F {
let edid = unsafe { slice::from_raw_parts(VBE_EDID_ADDR as *const u8, 128) };
Some((
(edid[0x38] as u32) | (((edid[0x3A] as u32) & 0xF0) << 4),
(edid[0x3B] as u32) | (((edid[0x3D] as u32) & 0xF0) << 4),
))
} else {
log::warn!("Failed to get VBE EDID: 0x{:X}", { data.eax });
None
}
}
fn get_key(&self) -> OsKey {
// Read keypress
let mut data = ThunkData::new();
unsafe {
data.with(self.thunk16);
}
match (data.eax >> 8) as u8 {
0x4B => OsKey::Left,
0x4D => OsKey::Right,
0x48 => OsKey::Up,
0x50 => OsKey::Down,
0x0E => OsKey::Backspace,
0x53 => OsKey::Delete,
0x1C => OsKey::Enter,
_ => match data.eax as u8 {
0 => OsKey::Other,
b => OsKey::Char(b as char),
},
}
}
fn clear_text(&self) {
//TODO: clear screen for VGA
}
fn get_text_position(&self) -> (usize, usize) {
let vga = VGA.lock();
(vga.x, vga.y)
}
fn set_text_position(&self, x: usize, y: usize) {
//TODO: ensure this is inside bounds!
let mut vga = VGA.lock();
vga.x = x;
vga.y = y;
}
fn set_text_highlight(&self, highlight: bool) {
let mut vga = VGA.lock();
if highlight {
vga.bg = VgaTextColor::Gray;
vga.fg = VgaTextColor::Black;
} else {
vga.bg = VgaTextColor::Black;
vga.fg = VgaTextColor::Gray;
}
}
}
#[no_mangle]
pub unsafe extern "C" fn start(
kernel_entry: extern "C" fn(
page_table: usize,
stack: u64,
func: u64,
args: *const KernelArgs,
long_mode: usize,
) -> !,
boot_disk: usize,
thunk10: extern "C" fn(),
thunk13: extern "C" fn(),
thunk15: extern "C" fn(),
thunk16: extern "C" fn(),
) -> ! {
#[cfg(feature = "serial_debug")]
{
let mut com1 = serial::COM1.lock();
com1.init();
com1.write(b"SERIAL\n");
}
{
// 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 mut os = OsBios {
boot_disk,
thunk10,
thunk13,
thunk15,
thunk16,
};
let (heap_start, heap_size) = memory_map(os.thunk15).expect("No memory for heap");
ALLOCATOR.lock().init(heap_start as *mut u8, heap_size);
let (page_phys, func, args) = crate::main(&mut os);
kernel_entry(
page_phys,
args.stack_base
+ args.stack_size
+ if crate::KERNEL_64BIT {
crate::arch::x64::PHYS_OFFSET as u64
} else {
crate::arch::x32::PHYS_OFFSET as u64
},
func,
&args,
if crate::KERNEL_64BIT { 1 } else { 0 },
);
}