349 lines
9.5 KiB
Rust
349 lines
9.5 KiB
Rust
#![allow(missing_docs)]
|
|
use core::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use abi::error::Error;
|
|
use alloc::sync::Arc;
|
|
use boot::multiboot::MultibootInfo;
|
|
use device_api::{
|
|
device::Device,
|
|
interrupt::{IpiDeliveryTarget, IpiMessage, Irq},
|
|
ResetDevice,
|
|
};
|
|
use git_version::git_version;
|
|
use kernel_arch::{Architecture, ArchitectureImpl};
|
|
use kernel_arch_i686::{gdt, mem::table::L3, PerCpuData};
|
|
use kernel_arch_x86::cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures};
|
|
use libk::{
|
|
arch::Cpu,
|
|
debug::{self, LogLevel},
|
|
devfs,
|
|
device::{
|
|
display::{
|
|
console::{add_console_autoflush, ConsoleWrapper},
|
|
DriverFlags,
|
|
},
|
|
manager::DEVICE_REGISTRY,
|
|
},
|
|
task::runtime,
|
|
vfs::{Terminal, TerminalInput},
|
|
};
|
|
use libk_mm::{
|
|
address::{PhysicalAddress, Virtualize},
|
|
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
|
|
table::EntryLevelExt,
|
|
};
|
|
use libk_util::sync::IrqSafeSpinlock;
|
|
use peripherals::textfb::TextFramebuffer;
|
|
use ygg_driver_pci::{LegacyPciAccess, PciBusManager};
|
|
|
|
mod boot;
|
|
pub mod exception;
|
|
mod peripherals;
|
|
|
|
use crate::{
|
|
arch::x86::{
|
|
intrinsics::IoPortAccess,
|
|
peripherals::{i8253::I8253, ps2::PS2Controller, rtc::Rtc},
|
|
ISA_IRQ_OFFSET,
|
|
},
|
|
fs::{Initrd, INITRD_DATA},
|
|
};
|
|
|
|
use super::{
|
|
x86::{
|
|
intrinsics::IoPort,
|
|
peripherals::{i8259::I8259, serial::ComPort},
|
|
},
|
|
Platform,
|
|
};
|
|
|
|
struct LegacyPciInner {
|
|
address: IoPort<u32>,
|
|
data: IoPort<u32>,
|
|
}
|
|
|
|
struct LegacyPci {
|
|
inner: IrqSafeSpinlock<LegacyPciInner>,
|
|
}
|
|
|
|
pub struct I686;
|
|
|
|
static SWITCH_TO_GRAPHIC: AtomicBool = AtomicBool::new(false);
|
|
|
|
static PCI: LegacyPci = LegacyPci {
|
|
inner: IrqSafeSpinlock::new(LegacyPciInner {
|
|
address: IoPort::new(0xCF8),
|
|
data: IoPort::new(0xCFC),
|
|
}),
|
|
};
|
|
|
|
pub static PLATFORM: I686 = I686;
|
|
|
|
impl Platform for I686 {
|
|
const KERNEL_VIRT_OFFSET: usize = 0xC0000000;
|
|
type L3 = L3;
|
|
|
|
unsafe fn reset(&self) -> ! {
|
|
ArchitectureImpl::halt();
|
|
}
|
|
|
|
unsafe fn send_ipi(&self, _target: IpiDeliveryTarget, _msg: IpiMessage) -> Result<bool, Error> {
|
|
Ok(false)
|
|
}
|
|
|
|
unsafe fn start_application_processors(&self) {
|
|
// No APs in i686, go get a better CPU
|
|
}
|
|
|
|
fn register_reset_device(&self, _reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
|
|
unimplemented!()
|
|
}
|
|
}
|
|
|
|
impl I686 {
|
|
fn init_initrd(initrd_start: PhysicalAddress, initrd_end: PhysicalAddress) {
|
|
if initrd_start.is_zero() || initrd_end <= initrd_start {
|
|
log::info!("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() 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);
|
|
}
|
|
|
|
unsafe fn init_platform(&'static self, multiboot_info: &MultibootInfo) -> Result<(), Error> {
|
|
reserve_region(
|
|
"lowmem",
|
|
PhysicalMemoryRegion {
|
|
base: PhysicalAddress::ZERO,
|
|
size: 1024 * 1024,
|
|
},
|
|
);
|
|
|
|
let modules = multiboot_info.modules();
|
|
if !modules.is_empty() {
|
|
let initrd = &modules[0];
|
|
let start = PhysicalAddress::from_u32(initrd.mod_start);
|
|
let end = PhysicalAddress::from_u32(initrd.mod_end);
|
|
|
|
if initrd.mod_start < initrd.mod_end {
|
|
let size = initrd.mod_end - initrd.mod_start;
|
|
|
|
reserve_region(
|
|
"initrd",
|
|
PhysicalMemoryRegion {
|
|
base: start,
|
|
size: size as _,
|
|
},
|
|
);
|
|
}
|
|
|
|
Self::init_initrd(start, end);
|
|
}
|
|
|
|
// Initialize physical memory
|
|
phys::init_from_iter(multiboot_info.memory_map_iter(), |_, _, _| Ok(()))?;
|
|
|
|
debug::init();
|
|
devfs::init();
|
|
|
|
let com1_3 = ComPort::setup(0x3F8, 0x3E8, Irq::External(ISA_IRQ_OFFSET + 4))?;
|
|
|
|
unsafe { init_gdt() };
|
|
unsafe { exception::init_exceptions() };
|
|
|
|
let (have_features, will_features) = cpuid::setup_features(
|
|
CpuFeatures {
|
|
ecx: EcxFeatures::SSE3 | EcxFeatures::XSAVE | EcxFeatures::OSXSAVE,
|
|
edx: EdxFeatures::SSE2 | EdxFeatures::FXSR,
|
|
},
|
|
CpuFeatures {
|
|
ecx: EcxFeatures::empty(),
|
|
edx: EdxFeatures::FPU | EdxFeatures::SSE | EdxFeatures::PSE,
|
|
},
|
|
);
|
|
let will_features = will_features.expect("Could not enable needed CPU features");
|
|
|
|
let cpu_data = PerCpuData {
|
|
available_features: have_features,
|
|
enabled_features: will_features,
|
|
};
|
|
Cpu::init_local(Some(0), cpu_data);
|
|
|
|
runtime::init_task_queue();
|
|
|
|
// Register the PCI drivers
|
|
// TODO make this implicit init
|
|
// TODO AHCI hangs inside interrupt handler in i686
|
|
// ygg_driver_pci::register_class_driver(
|
|
// "AHCI SATA Controller",
|
|
// 0x01,
|
|
// Some(0x06),
|
|
// Some(0x01),
|
|
// ygg_driver_ahci::probe,
|
|
// );
|
|
ygg_driver_pci::register_class_driver(
|
|
"USB xHCI",
|
|
0x0C,
|
|
Some(0x03),
|
|
Some(0x30),
|
|
ygg_driver_usb_xhci::probe,
|
|
);
|
|
ygg_driver_pci::register_vendor_driver(
|
|
"Virtio PCI GPU Device",
|
|
0x1AF4,
|
|
0x1050,
|
|
ygg_driver_virtio_gpu::probe,
|
|
);
|
|
ygg_driver_pci::register_vendor_driver(
|
|
"Virtio PCI Network Device",
|
|
0x1AF4,
|
|
0x1000,
|
|
ygg_driver_virtio_net::probe,
|
|
);
|
|
|
|
DEVICE_REGISTRY.display.set_callback(|device, _| {
|
|
log::info!("Display registered: {:?}", device.display_name());
|
|
if device
|
|
.driver_flags()
|
|
.contains(DriverFlags::REPLACES_BOOT_FRAMEBUFFER)
|
|
{
|
|
SWITCH_TO_GRAPHIC.store(true, Ordering::Release);
|
|
}
|
|
});
|
|
|
|
let i8259 = I8259::setup().expect("Could not initialize i8259 PIC");
|
|
let i8253 = I8253::setup().expect("Could not initialize i8253 Timer");
|
|
|
|
i8259.set_i8253(i8253);
|
|
|
|
if let Err(error) = PS2Controller::setup() {
|
|
log::error!("Could not initialize PS/2 Controller: {error:?}");
|
|
}
|
|
if let Err(error) = Rtc::setup() {
|
|
log::error!("RTC init error: {error:?}");
|
|
}
|
|
if let Err(error) = unsafe { com1_3.port_a().clone().init_irq() } {
|
|
log::error!("COM port IRQ init error: {error:?}");
|
|
}
|
|
|
|
// Setup text framebuffer
|
|
// TODO check if video mode is set from boot info
|
|
let textfb = Arc::new(TextFramebuffer::new(
|
|
PhysicalAddress::from_u32(0xB8000),
|
|
80,
|
|
25,
|
|
)?);
|
|
debug::add_sink(textfb.clone(), LogLevel::Info);
|
|
add_console_autoflush(textfb.clone());
|
|
|
|
let textfb_console = Arc::new(Terminal::from_parts(
|
|
Default::default(),
|
|
TerminalInput::with_capacity(256).unwrap(),
|
|
ConsoleWrapper(textfb),
|
|
));
|
|
let keyboard_input = ygg_driver_input::setup();
|
|
|
|
runtime::spawn(
|
|
textfb_console
|
|
.clone()
|
|
.consume_keyboard(keyboard_input, Some(&SWITCH_TO_GRAPHIC)),
|
|
)
|
|
.ok();
|
|
DEVICE_REGISTRY.terminal.register(textfb_console).ok();
|
|
|
|
log::info!(
|
|
"Yggdrasil v{} ({})",
|
|
env!("CARGO_PKG_VERSION"),
|
|
git_version!()
|
|
);
|
|
|
|
PciBusManager::add_legacy_segment(&PCI)?;
|
|
PciBusManager::setup_bus_devices()?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl LegacyPciAccess for LegacyPci {
|
|
fn write_u32(&self, bus: u8, slot: u8, func: u8, offset: u8, value: u32) {
|
|
assert_eq!(offset & 0x3, 0);
|
|
let inner = self.inner.lock();
|
|
inner.address.write(Self::addr(bus, slot, func, offset));
|
|
inner.data.write(value);
|
|
}
|
|
|
|
fn read_u32(&self, bus: u8, slot: u8, func: u8, offset: u8) -> u32 {
|
|
assert_eq!(offset & 0x3, 0);
|
|
let inner = self.inner.lock();
|
|
inner.address.write(Self::addr(bus, slot, func, offset));
|
|
inner.data.read()
|
|
}
|
|
}
|
|
|
|
impl LegacyPci {
|
|
#[inline]
|
|
const fn addr(bus: u8, slot: u8, func: u8, offset: u8) -> u32 {
|
|
((bus as u32) << 16)
|
|
| ((slot as u32) << 11)
|
|
| ((func as u32) << 8)
|
|
| (offset as u32 & 0xFC)
|
|
| (1 << 31)
|
|
}
|
|
}
|
|
|
|
unsafe fn load_gdt(gdt: &'static [gdt::Entry]) {
|
|
let gdt_addr = gdt.as_ptr().addr();
|
|
let gdtr = gdt::Pointer {
|
|
limit: size_of_val(gdt) as u16 - 1,
|
|
offset: gdt_addr,
|
|
};
|
|
|
|
core::arch::asm!(
|
|
r#"
|
|
wbinvd
|
|
lgdt ({0})
|
|
|
|
ljmpl $0x08, $1f
|
|
1:
|
|
mov $0x10, %ax
|
|
mov %ax, %ds
|
|
mov %ax, %es
|
|
mov %ax, %fs
|
|
mov %ax, %gs
|
|
mov %ax, %ss
|
|
|
|
mov $0x28, %ax
|
|
ltr %ax
|
|
"#,
|
|
in(reg) &gdtr,
|
|
out("eax") _,
|
|
out("ecx") _,
|
|
options(att_syntax)
|
|
);
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// Only meant to be called during early init.
|
|
unsafe fn init_gdt() -> usize {
|
|
let (gdt, tss) = gdt::create_gdt();
|
|
load_gdt(gdt);
|
|
(tss as *const gdt::Tss).addr()
|
|
}
|