2023-08-01 11:09:56 +03:00
|
|
|
//! x86-64 architecture and platform implementation
|
|
|
|
use core::ptr::NonNull;
|
|
|
|
|
2023-07-29 19:31:56 +03:00
|
|
|
use abi::error::Error;
|
2023-08-01 11:09:56 +03:00
|
|
|
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},
|
2023-08-01 11:09:56 +03:00
|
|
|
interrupt::{ExternalInterruptController, InterruptSource},
|
2023-07-29 21:11:15 +03:00
|
|
|
platform::Platform,
|
2023-08-01 11:09:56 +03:00
|
|
|
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
|
|
|
|
2023-08-01 11:09:56 +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;
|
|
|
|
|
2023-08-01 11:09:56 +03:00
|
|
|
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;
|
2023-08-01 11:09:56 +03:00
|
|
|
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;
|
2023-08-01 11:09:56 +03:00
|
|
|
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
|
|
|
|
2023-08-01 11:09:56 +03:00
|
|
|
/// Helper trait to provide abstract access to available memory regions
|
2023-07-30 16:40:30 +03:00
|
|
|
pub trait AbstractAvailableRegion {
|
2023-08-01 11:09:56 +03:00
|
|
|
/// Returns page-aligned physical start address of the region
|
2023-07-30 16:40:30 +03:00
|
|
|
fn start_address(&self) -> usize;
|
2023-08-01 11:09:56 +03:00
|
|
|
/// Returns the page count (rounded down) of this region
|
2023-07-30 16:40:30 +03:00
|
|
|
fn page_count(&self) -> usize;
|
|
|
|
}
|
|
|
|
|
2023-08-01 11:09:56 +03:00
|
|
|
/// Helper trait to provide abstract access to memory maps
|
2023-07-30 16:40:30 +03:00
|
|
|
pub trait AbstractMemoryMap<'a>: 'a {
|
2023-08-01 11:09:56 +03:00
|
|
|
/// Available memory region type contained within this memory map
|
2023-07-30 16:40:30 +03:00
|
|
|
type AvailableRegion: AbstractAvailableRegion;
|
2023-08-01 11:09:56 +03:00
|
|
|
/// Iterator type returned by [Self::iter]
|
2023-07-30 16:40:30 +03:00
|
|
|
type Iter: Iterator<Item = &'a Self::AvailableRegion> + Clone;
|
|
|
|
|
2023-08-01 11:09:56 +03:00
|
|
|
/// Returns the physical memory range which contains this memory map
|
2023-07-30 16:40:30 +03:00
|
|
|
fn reserved_range(&self) -> PhysicalMemoryRegion;
|
2023-08-01 11:09:56 +03:00
|
|
|
/// 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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-01 11:09:56 +03:00
|
|
|
#[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>,
|
2023-08-01 11:09:56 +03:00
|
|
|
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 {
|
2023-08-01 11:09:56 +03:00
|
|
|
type IrqNumber = apic::IrqNumber;
|
|
|
|
|
2023-07-30 16:40:30 +03:00
|
|
|
const KERNEL_PHYS_BASE: usize = 0x400000;
|
|
|
|
|
2023-08-01 11:09:56 +03:00
|
|
|
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
|
|
|
}
|
2023-08-01 11:09: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");
|
|
|
|
}
|
2023-08-01 11:09:56 +03:00
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2023-08-01 11:09:56 +03:00
|
|
|
/// x86-64 architecture + platform implementation
|
2023-07-29 21:11:15 +03:00
|
|
|
pub static ARCHITECTURE: X86_64 = X86_64 {
|
2023-08-01 11:09:56 +03:00
|
|
|
rsdp_phys_base: OneTimeInit::new(),
|
2023-07-29 21:11:15 +03:00
|
|
|
yboot_framebuffer: OneTimeInit::new(),
|
2023-08-01 11:09:56 +03:00
|
|
|
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();
|