302 lines
8.3 KiB
Rust
Raw Normal View History

//! x86-64 architecture and platform implementation
use core::ptr::NonNull;
2023-07-29 19:31:56 +03:00
use abi::error::Error;
use acpi::{AcpiHandler, AcpiTables, InterruptModel, PhysicalMapping};
2023-07-30 16:40:30 +03:00
use yboot_proto::{v1::FramebufferOption, AvailableRegion, IterableMemoryMap};
2023-07-28 14:26:39 +03:00
2023-07-29 21:11:15 +03:00
use crate::{
arch::x86_64::table::{init_fixed_tables, KERNEL_TABLES},
debug::DebugSink,
device::{
display::{fb_console::FramebufferConsole, linear_fb::LinearFramebuffer},
interrupt::{ExternalInterruptController, InterruptSource},
2023-07-29 21:11:15 +03:00
platform::Platform,
Device,
},
mem::{
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
ConvertAddress,
2023-07-29 21:11:15 +03:00
},
util::OneTimeInit,
};
2023-07-28 14:26:39 +03:00
use self::{
apic::{ioapic::IoApic, IrqNumber},
intrinsics::IoPort,
peripherals::ps2::PS2Controller,
};
2023-07-29 19:31:56 +03:00
use super::Architecture;
2023-07-28 14:26:39 +03:00
2023-07-30 16:40:30 +03:00
#[macro_use]
pub mod intrinsics;
pub mod apic;
2023-07-29 19:31:56 +03:00
pub mod boot;
2023-08-01 18:05:10 +03:00
pub mod context;
pub mod cpu;
2023-08-01 18:05:10 +03:00
pub mod cpuid;
2023-07-30 16:40:30 +03:00
pub mod exception;
pub mod gdt;
pub mod peripherals;
pub mod registers;
2023-07-29 19:31:56 +03:00
pub mod table;
2023-07-28 14:26:39 +03:00
/// Helper trait to provide abstract access to available memory regions
2023-07-30 16:40:30 +03:00
pub trait AbstractAvailableRegion {
/// Returns page-aligned physical start address of the region
2023-07-30 16:40:30 +03:00
fn start_address(&self) -> usize;
/// Returns the page count (rounded down) of this region
2023-07-30 16:40:30 +03:00
fn page_count(&self) -> usize;
}
/// Helper trait to provide abstract access to memory maps
2023-07-30 16:40:30 +03:00
pub trait AbstractMemoryMap<'a>: 'a {
/// Available memory region type contained within this memory map
2023-07-30 16:40:30 +03:00
type AvailableRegion: AbstractAvailableRegion;
/// Iterator type returned by [Self::iter]
2023-07-30 16:40:30 +03:00
type Iter: Iterator<Item = &'a Self::AvailableRegion> + Clone;
/// Returns the physical memory range which contains this memory map
2023-07-30 16:40:30 +03:00
fn reserved_range(&self) -> PhysicalMemoryRegion;
/// Returns an iterator over the available memory regions
2023-07-30 16:40:30 +03:00
fn iter(&self) -> Self::Iter;
}
impl<T: AvailableRegion> AbstractAvailableRegion for T {
fn start_address(&self) -> usize {
<T as AvailableRegion>::start_address(self)
}
fn page_count(&self) -> usize {
<T as AvailableRegion>::page_count(self)
}
}
impl<'a, T: IterableMemoryMap<'a> + 'a> AbstractMemoryMap<'a> for T {
type AvailableRegion = T::Entry;
type Iter = T::Iter;
fn reserved_range(&self) -> PhysicalMemoryRegion {
PhysicalMemoryRegion {
base: self.data_physical_base(),
size: (self.data_size() + 0xFFF) & !0xFFF,
}
}
fn iter(&self) -> Self::Iter {
<T as IterableMemoryMap>::iter_with_offset(self, X86_64::KERNEL_VIRT_OFFSET)
}
}
#[derive(Clone, Copy)]
struct AcpiHandlerImpl;
impl AcpiHandler for AcpiHandlerImpl {
// No actual address space modification is performed
unsafe fn map_physical_region<T>(
&self,
physical_address: usize,
size: usize,
) -> PhysicalMapping<Self, T> {
if physical_address <= 0xFFFFFFFF {
PhysicalMapping::new(
physical_address,
NonNull::new_unchecked(physical_address.virtualize() as *mut T),
size,
size,
*self,
)
} else {
todo!()
}
}
// Unmap nothing, these addresses are "virtualized" to high address space
fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
}
/// x86-64 architecture + platform implementation
2023-07-29 21:11:15 +03:00
pub struct X86_64 {
yboot_framebuffer: OneTimeInit<FramebufferOption>,
rsdp_phys_base: OneTimeInit<usize>,
// Static devices with dynamic addresses
ioapic: IoApic,
// Static devices
ps2: PS2Controller,
2023-07-29 21:11:15 +03:00
}
2023-07-28 14:26:39 +03:00
2023-07-29 19:31:56 +03:00
impl Architecture for X86_64 {
const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
2023-07-28 14:26:39 +03:00
2023-07-29 19:31:56 +03:00
unsafe fn init_mmu(&self, bsp: bool) {
if bsp {
init_fixed_tables();
}
2023-07-28 14:26:39 +03:00
2023-07-29 19:31:56 +03:00
let cr3 = KERNEL_TABLES.physical_address();
core::arch::asm!("wbinvd; mov {0}, %cr3", in(reg) cr3, options(att_syntax));
}
2023-07-28 14:26:39 +03:00
2023-07-29 19:31:56 +03:00
fn map_device_pages(&self, phys: usize, count: usize) -> Result<usize, Error> {
unsafe { KERNEL_TABLES.map_device_pages(phys, count) }
}
2023-07-28 14:26:39 +03:00
2023-07-29 19:31:56 +03:00
fn wait_for_interrupt() {
2023-07-29 21:11:15 +03:00
unsafe {
core::arch::asm!("hlt");
}
2023-07-29 19:31:56 +03:00
}
2023-07-28 14:26:39 +03:00
2023-07-29 19:31:56 +03:00
unsafe fn set_interrupt_mask(mask: bool) {
2023-07-29 21:11:15 +03:00
if mask {
core::arch::asm!("cli");
} else {
core::arch::asm!("sti");
}
2023-07-29 19:31:56 +03:00
}
2023-07-28 14:26:39 +03:00
2023-07-29 19:31:56 +03:00
fn interrupt_mask() -> bool {
2023-07-29 21:11:15 +03:00
let mut flags: u64;
unsafe {
2023-08-01 18:05:10 +03:00
core::arch::asm!("pushfq; pop {0}", out(reg) flags, options(att_syntax));
2023-07-29 21:11:15 +03:00
}
// If IF is zero, interrupts are disabled (masked)
flags & (1 << 9) == 0
}
}
impl Platform for X86_64 {
type IrqNumber = apic::IrqNumber;
2023-07-30 16:40:30 +03:00
const KERNEL_PHYS_BASE: usize = 0x400000;
unsafe fn init(&'static self, is_bsp: bool) -> Result<(), Error> {
// self.apic.init()?;
if is_bsp {
Self::disable_8259();
// Initialize I/O APIC from ACPI
let rsdp = *self.rsdp_phys_base.get();
let acpi_tables = AcpiTables::from_rsdp(AcpiHandlerImpl, rsdp).unwrap();
let platform_info = acpi_tables.platform_info().unwrap();
let InterruptModel::Apic(apic_info) = &platform_info.interrupt_model else {
panic!("Processor does not have APIC");
};
self.ioapic.init_from_acpi(apic_info)?;
// Initialize static devices
self.ps2.init()?;
// Enable IRQs for the devices
self.ps2.init_irq()?;
}
2023-07-29 21:11:15 +03:00
Ok(())
}
unsafe fn init_primary_debug_sink(&self) {
let Some(fb) = self.yboot_framebuffer.try_get() else {
// TODO fallback to serial as primary
return;
};
LINEAR_FB.init(
LinearFramebuffer::from_physical_bits(
fb.res_address as _,
fb.res_size as _,
fb.res_stride as _,
fb.res_width,
fb.res_height,
)
.unwrap(),
);
FB_CONSOLE.init(FramebufferConsole::from_framebuffer(
LINEAR_FB.get(),
&bitmap_font::tamzen::FONT_6x12,
));
}
fn name(&self) -> &'static str {
"x86-64"
}
fn primary_debug_sink(&self) -> Option<&dyn DebugSink> {
if let Some(console) = FB_CONSOLE.try_get() {
Some(console)
} else {
None
}
2023-07-29 19:31:56 +03:00
}
fn interrupt_controller(
&self,
) -> &dyn ExternalInterruptController<IrqNumber = Self::IrqNumber> {
&self.ioapic
}
2023-07-29 19:31:56 +03:00
}
2023-07-28 14:26:39 +03:00
2023-07-30 16:40:30 +03:00
impl X86_64 {
unsafe fn init_physical_memory<'a, M: AbstractMemoryMap<'a>>(memory_map: &M) {
// Reserve the lower 8MiB of memory
reserve_region(
"lower-memory",
PhysicalMemoryRegion {
base: 0,
size: 8 << 21,
},
);
// Reserve memory map
reserve_region("memory-map", memory_map.reserved_range());
phys::init_from_iter(memory_map.iter().map(|r| PhysicalMemoryRegion {
base: r.start_address(),
size: r.page_count() * 0x1000,
}))
.expect("Failed to initialize the physical memory manager");
}
fn init_rsdp(&self, phys: usize) {
self.rsdp_phys_base.init(phys);
}
unsafe fn disable_8259() {
// TODO should I make a module for 8259 if I don't even use it?
let pic_master_cmd = IoPort::new(0x20);
let pic_master_data = IoPort::new(0x21);
let pic_slave_cmd = IoPort::new(0xA0);
let pic_slave_data = IoPort::new(0xA1);
// Remap PIC IRQ vectors to 32..
pic_master_cmd.write(0x11);
pic_slave_cmd.write(0x11);
pic_master_data.write(32);
pic_slave_data.write(32 + 8);
pic_slave_data.write(0xFF);
pic_master_data.write(0xFF);
pic_master_cmd.write(0x20);
pic_slave_cmd.write(0x20);
}
2023-07-30 16:40:30 +03:00
}
/// x86-64 architecture + platform implementation
2023-07-29 21:11:15 +03:00
pub static ARCHITECTURE: X86_64 = X86_64 {
rsdp_phys_base: OneTimeInit::new(),
2023-07-29 21:11:15 +03:00
yboot_framebuffer: OneTimeInit::new(),
ioapic: IoApic::new(),
ps2: PS2Controller::new(IrqNumber::Isa(1), IrqNumber::Isa(12), 0x64, 0x60),
2023-07-29 21:11:15 +03:00
};
static LINEAR_FB: OneTimeInit<LinearFramebuffer> = OneTimeInit::new();
static FB_CONSOLE: OneTimeInit<FramebufferConsole> = OneTimeInit::new();