540 lines
16 KiB
Rust
Raw Normal View History

2023-09-15 00:02:14 +03:00
// TODO fix all TODOs
2023-10-03 10:17:13 +03:00
use core::{mem::size_of, ops::DerefMut, sync::atomic::Ordering};
2023-07-29 19:31:56 +03:00
use abi::error::Error;
2023-09-15 00:02:14 +03:00
use acpi_lib::{mcfg::Mcfg, AcpiTables, InterruptModel};
use alloc::boxed::Box;
use device_api::{
input::KeyboardProducer, interrupt::ExternalInterruptController,
timer::MonotonicTimestampProviderDevice, Device,
};
2023-08-05 16:32:12 +03:00
use git_version::git_version;
use kernel_util::{sync::SpinFence, util::OneTimeInit};
use yboot_proto::{v1::AvailableMemoryRegion, LoadProtocolV1};
mod acpi;
mod apic;
mod boot;
pub mod context;
pub mod cpu;
mod cpuid;
mod exception;
mod gdt;
mod intrinsics;
pub mod mem;
mod peripherals;
mod registers;
mod smp;
mod syscall;
2023-07-28 14:26:39 +03:00
2023-07-29 21:11:15 +03:00
use crate::{
2023-08-05 16:32:12 +03:00
arch::x86_64::{
intrinsics::{IoPort, IoPortAccess},
2023-10-03 10:17:13 +03:00
mem::{
map_heap_block,
table::{PageTable, L2},
HEAP_MAPPING_OFFSET,
},
2023-08-05 16:32:12 +03:00
},
debug::{self, LogLevel},
2023-07-29 21:11:15 +03:00
device::{
self,
2023-09-15 00:02:14 +03:00
bus::pci::PciBusManager,
2023-08-22 10:00:56 +03:00
display::{console, fb_console::FramebufferConsole, linear_fb::LinearFramebuffer},
tty::CombinedTerminal,
},
fs::{
devfs::{self, CharDeviceType},
2023-08-05 16:32:12 +03:00
Initrd, INITRD_DATA,
},
mem::{
address::{FromRaw, IntoRaw},
device::RawDeviceMemoryMapping,
heap,
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
table::EntryLevel,
PhysicalAddress,
2023-07-29 21:11:15 +03:00
},
};
2023-07-28 14:26:39 +03:00
use self::{
2023-08-31 13:40:17 +03:00
acpi::{AcpiAllocator, AcpiHandlerImpl},
apic::{ioapic::IoApic, local::LocalApic},
boot::BootData,
cpu::Cpu,
cpuid::{ProcessorFeatures, PROCESSOR_FEATURES},
mem::{
init_fixed_tables,
table::{PageAttributes, PageEntry, L1, L3},
2023-10-01 16:58:46 +03:00
EarlyMapping, MEMORY_LIMIT, RAM_MAPPING_L1, RAM_MAPPING_OFFSET,
},
peripherals::{i8253::I8253, ps2::PS2Controller, serial::ComPort},
2023-08-05 16:32:12 +03:00
smp::CPU_COUNT,
};
2023-08-05 16:32:12 +03:00
use super::{Architecture, CpuMessage};
2023-07-28 14:26:39 +03:00
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum IrqNumber {
Isa(u8),
Gsi(u8),
}
2023-08-05 16:32:12 +03:00
2023-07-29 21:11:15 +03:00
pub struct X86_64 {
boot_data: OneTimeInit<BootData>,
acpi: OneTimeInit<AcpiTables<AcpiHandlerImpl>>,
// Display
framebuffer: OneTimeInit<LinearFramebuffer>,
fbconsole: OneTimeInit<FramebufferConsole>,
tty: OneTimeInit<CombinedTerminal>,
ioapic: OneTimeInit<IoApic>,
timer: OneTimeInit<I8253>,
2023-07-29 21:11:15 +03:00
}
2023-07-28 14:26:39 +03:00
static SHUTDOWN_FENCE: SpinFence = SpinFence::new();
pub static ARCHITECTURE: X86_64 = X86_64 {
boot_data: OneTimeInit::new(),
acpi: OneTimeInit::new(),
framebuffer: OneTimeInit::new(),
fbconsole: OneTimeInit::new(),
tty: OneTimeInit::new(),
ioapic: OneTimeInit::new(),
timer: OneTimeInit::new(),
};
2023-07-29 19:31:56 +03:00
impl Architecture for X86_64 {
const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
type IrqNumber = IrqNumber;
2023-09-15 00:02:14 +03:00
unsafe fn start_application_processors(&self) {
if let Some(acpi) = self.acpi.try_get() {
let Some(pinfo) = acpi
.platform_info_in(AcpiAllocator)
.ok()
.and_then(|p| p.processor_info)
else {
return;
};
smp::start_ap_cores(&pinfo);
}
}
fn cpu_count() -> usize {
CPU_COUNT.load(Ordering::Acquire)
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
}
2023-08-05 16:32:12 +03:00
fn wait_for_interrupt() {
unsafe {
core::arch::asm!("hlt");
}
2023-08-05 16:32:12 +03:00
}
2023-07-30 16:40:30 +03:00
#[inline]
unsafe fn map_device_memory(
&self,
base: PhysicalAddress,
size: usize,
) -> Result<RawDeviceMemoryMapping, Error> {
mem::map_device_memory(base, size)
}
#[inline]
2023-09-15 00:02:14 +03:00
unsafe fn unmap_device_memory(&self, map: &RawDeviceMemoryMapping) {
mem::unmap_device_memory(map)
}
fn map_physical_memory<I: Iterator<Item = PhysicalMemoryRegion> + Clone>(
&self,
2023-10-03 10:17:13 +03:00
it: I,
_memory_start: PhysicalAddress,
memory_end: PhysicalAddress,
) -> Result<(), Error> {
let end_l1i = (IntoRaw::<usize>::into_raw(memory_end) + (1 << 30) - 1) >> 30;
if end_l1i > 512 {
2023-09-15 00:09:26 +03:00
todo!(
"Cannot handle {}GiB of RAM",
end_l1i * L1::SIZE / (1024 * 1024 * 1024)
);
}
2023-07-29 21:11:15 +03:00
MEMORY_LIMIT.init(memory_end.into_raw());
// Check if 1GiB pages are supported
if PROCESSOR_FEATURES
.get()
.contains(ProcessorFeatures::PDPE1GB)
{
// Just map gigabytes of RAM
for l1i in 0..end_l1i {
// TODO NX
unsafe {
RAM_MAPPING_L1[l1i] = PageEntry::<L1>::block(
PhysicalAddress::from_raw(l1i << 30),
PageAttributes::WRITABLE,
);
}
}
} else {
2023-10-03 10:17:13 +03:00
// Allocate the intermediate tables first
let l2_tables_start = phys::find_contiguous_region(it, end_l1i)
.expect("Could not allocate the memory for RAM mapping L2 tables");
unsafe {
reserve_region(
"ram-l2-tables",
PhysicalMemoryRegion {
base: l2_tables_start,
size: end_l1i * 0x1000,
},
);
}
// Fill in the tables
for l1i in 0..end_l1i {
let l2_phys_addr = l2_tables_start.add(l1i * 0x1000);
// TODO (minor) the slice is uninitialized, maybe find some way to deal with that
// case nicely
// Safety: ok, the mapping is done to the memory obtained from
// find_contiguous_region()
let mut l2_data =
unsafe { EarlyMapping::<[PageEntry<L2>; 512]>::map(l2_phys_addr)? };
// Safety: ok, the slice comes from EarlyMapping of a page-aligned region
let l2 = unsafe { PageTable::from_raw_slice_mut(l2_data.deref_mut()) };
for l2i in 0..512 {
// TODO NX
l2[l2i] = PageEntry::<L2>::block(
PhysicalAddress::from_raw((l1i << 30) | (l2i << 21)),
PageAttributes::WRITABLE,
);
}
// Point the L1 entry to the L2 table
unsafe {
RAM_MAPPING_L1[l1i] =
PageEntry::<L1>::table(l2_phys_addr, PageAttributes::WRITABLE)
};
intrinsics::flush_cpu_cache();
// The EarlyMapping is then dropped
}
}
2023-10-03 10:17:13 +03:00
Ok(())
}
2023-08-05 19:10:44 +03:00
#[inline]
fn virtualize(address: PhysicalAddress) -> Result<usize, Error> {
let raw: usize = address.into_raw();
if raw < *mem::MEMORY_LIMIT.get() {
Ok(raw + RAM_MAPPING_OFFSET)
} else {
errorln!("Invalid physical address: {:#x}", address);
Err(Error::InvalidMemoryOperation)
}
2023-07-29 21:11:15 +03:00
}
#[inline]
fn physicalize(address: usize) -> Result<PhysicalAddress, Error> {
if address < RAM_MAPPING_OFFSET || address - RAM_MAPPING_OFFSET >= *mem::MEMORY_LIMIT.get()
{
errorln!("Not a virtualized physical address: {:#x}", address);
return Err(Error::InvalidMemoryOperation);
}
Ok(PhysicalAddress::from_raw(address - RAM_MAPPING_OFFSET))
2023-07-29 21:11:15 +03:00
}
fn external_interrupt_controller(
&'static self,
) -> &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber> {
self.ioapic.get()
}
fn monotonic_timer(&'static self) -> &'static dyn MonotonicTimestampProviderDevice {
self.timer.get()
}
}
impl X86_64 {
2023-10-01 16:58:46 +03:00
unsafe fn handle_ipi(&self, _msg: CpuMessage) {
warnln!("Received an IPI");
loop {}
}
fn set_boot_data(&self, data: BootData) {
match data {
BootData::YBoot(data) => {
// Reserve the memory map
unsafe {
reserve_region(
"mmap",
PhysicalMemoryRegion {
base: PhysicalAddress::from_raw(data.memory_map.address),
size: data.memory_map.len as usize * size_of::<AvailableMemoryRegion>(),
},
);
}
// Reserve initrd, if not NULL
if data.initrd_address != 0 && data.initrd_size != 0 {
let aligned_start = data.initrd_address & !0xFFF;
let aligned_end = (data.initrd_address + data.initrd_size + 0xFFF) & !0xFFF;
unsafe {
reserve_region(
"initrd",
PhysicalMemoryRegion {
base: PhysicalAddress::from_raw(aligned_start),
size: (aligned_end - aligned_start) as usize,
},
);
}
}
}
}
2023-08-03 18:49:29 +03:00
self.boot_data.init(data);
2023-08-03 18:49:29 +03:00
}
2023-08-05 16:32:12 +03:00
2023-09-15 23:23:20 +03:00
unsafe fn init_physical_memory_from_yboot(data: &LoadProtocolV1) -> Result<(), Error> {
let mmap = EarlyMapping::<AvailableMemoryRegion>::map_slice(
PhysicalAddress::from_raw(data.memory_map.address),
data.memory_map.len as usize,
2023-09-15 23:23:20 +03:00
)?;
phys::init_from_iter(mmap.as_ref().iter().map(|reg| PhysicalMemoryRegion {
base: PhysicalAddress::from_raw(reg.start_address),
size: reg.page_count as usize * 0x1000,
2023-09-15 23:23:20 +03:00
}))
2023-08-05 16:32:12 +03:00
}
2023-07-28 14:26:39 +03:00
2023-09-15 23:23:20 +03:00
unsafe fn init_memory_management(&self) -> Result<(), Error> {
const HEAP_PAGES: usize = 16;
init_fixed_tables();
// Reserve lower 4MiB just in case
2023-07-30 16:40:30 +03:00
reserve_region(
"lowmem",
2023-07-30 16:40:30 +03:00
PhysicalMemoryRegion {
base: PhysicalAddress::ZERO,
size: 4 * 1024 * 1024,
2023-07-30 16:40:30 +03:00
},
);
match self.boot_data.get() {
2023-09-15 23:23:20 +03:00
&BootData::YBoot(data) => Self::init_physical_memory_from_yboot(data)?,
}
// Setup heap
for i in 0..HEAP_PAGES {
// Allocate in 2MiB chunks
2023-09-15 23:23:20 +03:00
let l2_page = phys::alloc_2m_page()?;
map_heap_block(i, l2_page);
}
heap::init_heap(HEAP_MAPPING_OFFSET, HEAP_PAGES * L2::SIZE);
2023-09-15 23:23:20 +03:00
Ok(())
2023-08-05 16:32:12 +03:00
}
2023-09-15 23:23:20 +03:00
unsafe fn init_platform(&'static self, cpu_id: usize) -> Result<(), Error> {
Cpu::init_local(LocalApic::new(), cpu_id as _);
2023-08-05 16:32:12 +03:00
if cpu_id == 0 {
match self.boot_data.get() {
&BootData::YBoot(data) => {
let start = PhysicalAddress::from_raw(data.initrd_address);
Self::init_initrd(start, start.add(data.initrd_size as usize));
}
}
2023-09-15 23:23:20 +03:00
self.init_acpi_from_boot_data()?;
Self::disable_8259();
2023-08-05 16:32:12 +03:00
self.timer.init(I8253::new());
let com1_3 = Box::leak(Box::new(ComPort::new(0x3F8, 0x3E8, IrqNumber::Isa(4))));
debug::add_sink(com1_3.port_a(), LogLevel::Debug);
2023-09-15 23:23:20 +03:00
self.init_framebuffer()?;
2023-08-22 10:00:56 +03:00
debug::init();
infoln!(
"Yggdrasil v{} ({})",
env!("CARGO_PKG_VERSION"),
git_version!()
);
let ps2 = Box::leak(Box::new(PS2Controller::new(
IrqNumber::Isa(1),
IrqNumber::Isa(12),
0x64,
0x60,
)));
2023-09-15 23:23:20 +03:00
ps2.init()?;
if let Some(acpi) = self.acpi.try_get() {
2023-09-15 23:23:20 +03:00
self.init_platform_from_acpi(acpi)?;
}
2023-08-05 16:32:12 +03:00
2023-09-15 23:23:20 +03:00
self.timer.get().init_irq()?;
ps2.connect(self.tty.get());
2023-09-15 23:23:20 +03:00
ps2.init_irq()?;
2023-08-05 16:32:12 +03:00
device::register_device(self.ioapic.get());
device::register_device(ps2);
2023-08-05 16:32:12 +03:00
2023-09-15 23:23:20 +03:00
PciBusManager::setup_bus_devices()?;
}
2023-09-15 23:23:20 +03:00
Ok(())
}
2023-09-15 23:23:20 +03:00
unsafe fn init_acpi_from_boot_data(&self) -> Result<(), Error> {
match self.boot_data.get() {
&BootData::YBoot(data) => self.init_acpi_from_rsdp(data.rsdp_address as usize),
}
}
2023-09-15 23:23:20 +03:00
unsafe fn init_acpi_from_rsdp(&self, rsdp: usize) -> Result<(), Error> {
let acpi_tables = AcpiTables::from_rsdp(AcpiHandlerImpl, rsdp).map_err(|err| {
errorln!("Could not initialize ACPI tables: {:?}", err);
Error::InvalidArgument
})?;
self.acpi.init(acpi_tables);
2023-09-15 23:23:20 +03:00
Ok(())
}
2023-09-15 23:23:20 +03:00
unsafe fn init_platform_from_acpi(
&self,
acpi: &'static AcpiTables<AcpiHandlerImpl>,
) -> Result<(), Error> {
let platform_info = acpi.platform_info_in(AcpiAllocator).map_err(|err| {
errorln!("Could not get ACPI platform info: {:?}", err);
Error::InvalidArgument
})?;
let InterruptModel::Apic(apic_info) = platform_info.interrupt_model else {
panic!("The processor does not support APIC");
};
2023-09-15 23:23:20 +03:00
self.ioapic.init(IoApic::from_acpi(&apic_info)?);
2023-10-03 10:17:13 +03:00
// acpi::init_acpi(acpi).unwrap();
2023-09-15 00:02:14 +03:00
if let Ok(mcfg) = acpi.find_table::<Mcfg>() {
for entry in mcfg.entries() {
2023-09-15 23:23:20 +03:00
PciBusManager::add_segment_from_mcfg(entry)?;
2023-09-15 00:02:14 +03:00
}
}
2023-09-15 23:23:20 +03:00
Ok(())
}
2023-09-15 23:23:20 +03:00
unsafe fn init_framebuffer(&'static self) -> Result<(), Error> {
match self.boot_data.get() {
&BootData::YBoot(data) => {
let info = &data.opt_framebuffer;
2023-09-15 23:23:20 +03:00
self.framebuffer.init(LinearFramebuffer::from_physical_bits(
PhysicalAddress::from_raw(info.res_address),
info.res_size as usize,
info.res_stride as usize,
info.res_width,
info.res_height,
)?);
}
}
2023-09-15 23:23:20 +03:00
self.fbconsole.init(FramebufferConsole::from_framebuffer(
self.framebuffer.get(),
None,
)?);
debug::add_sink(self.fbconsole.get(), LogLevel::Info);
self.tty.init(CombinedTerminal::new(self.fbconsole.get()));
2023-09-15 23:23:20 +03:00
devfs::add_char_device(self.tty.get(), CharDeviceType::TtyRegular)?;
console::add_console_autoflush(self.fbconsole.get());
2023-09-15 23:23:20 +03:00
Ok(())
}
fn init_initrd(initrd_start: PhysicalAddress, initrd_end: PhysicalAddress) {
if initrd_start.is_zero() || initrd_end <= initrd_start {
infoln!("No initrd loaded");
return;
}
let start_aligned = initrd_start.page_align_down::<L3>();
let end_aligned = initrd_start.page_align_up::<L3>();
let data = unsafe {
core::slice::from_raw_parts(
start_aligned.virtualize_raw() as *const u8,
initrd_end - initrd_start,
)
};
let initrd = Initrd {
phys_page_start: start_aligned,
phys_page_len: end_aligned - start_aligned,
data,
};
INITRD_DATA.init(initrd);
2023-08-05 16:32:12 +03:00
}
unsafe fn disable_8259() {
infoln!("Disabling i8259 PIC");
// TODO should I make a module for 8259 if I don't even use it?
2023-08-31 09:21:24 +03:00
let pic_master_cmd = IoPort::<u8>::new(0x20);
let pic_master_data = IoPort::<u8>::new(0x21);
let pic_slave_cmd = IoPort::<u8>::new(0xA0);
let pic_slave_data = IoPort::<u8>::new(0xA1);
2023-08-05 16:32:12 +03:00
// Remap PIC IRQ vectors to 32..
pic_master_cmd.write(0x11);
pic_slave_cmd.write(0x11);
2023-08-05 16:32:12 +03:00
pic_master_data.write(32);
pic_slave_data.write(32 + 8);
2023-08-05 16:32:12 +03:00
pic_slave_data.write(0xFF);
pic_master_data.write(0xFF);
2023-08-05 16:32:12 +03:00
pic_master_cmd.write(0x20);
pic_slave_cmd.write(0x20);
2023-08-05 16:32:12 +03:00
}
}