x86-64: implement Local+I/O APICs and PS/2 kbd
This commit is contained in:
parent
aec161bc47
commit
3a795f0229
@ -15,11 +15,12 @@ bitflags = "2.3.3"
|
||||
linked_list_allocator = "0.10.5"
|
||||
spinning_top = "0.2.5"
|
||||
# static_assertions = "1.1.0"
|
||||
# tock-registers = "0.8.1"
|
||||
tock-registers = "0.8.1"
|
||||
cfg-if = "1.0.0"
|
||||
bitmap-font = { version = "0.3.0" }
|
||||
embedded-graphics = "0.8.0"
|
||||
git-version = "0.3.5"
|
||||
acpi = "4.1.1"
|
||||
|
||||
[dependencies.elf]
|
||||
version = "0.7.2"
|
||||
@ -33,3 +34,4 @@ aarch64-cpu = "9.3.1"
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
yboot-proto = { git = "https://git.alnyan.me/yggdrasil/yboot-proto.git" }
|
||||
aml = "0.16.4"
|
||||
|
246
src/arch/x86_64/apic/ioapic.rs
Normal file
246
src/arch/x86_64/apic/ioapic.rs
Normal file
@ -0,0 +1,246 @@
|
||||
//! x86-64 I/O APIC driver implementation
|
||||
use abi::error::Error;
|
||||
use acpi::platform::interrupt::{Apic as AcpiApic, Polarity, TriggerMode};
|
||||
|
||||
use crate::{
|
||||
arch::x86_64::apic::local::BSP_APIC_ID,
|
||||
device::{
|
||||
interrupt::{ExternalInterruptController, InterruptSource},
|
||||
Device,
|
||||
},
|
||||
mem::ConvertAddress,
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
use super::{IrqNumber, Table, APIC_EXTERNAL_OFFSET};
|
||||
|
||||
// IRQ 0 is timer, IRQ 1 reserved (for now?), +32 offset for exception entries
|
||||
const IO_APIC_VECTOR_OFFSET: u32 = 32 + APIC_EXTERNAL_OFFSET;
|
||||
|
||||
const REG_IOAPIC_VERSION: u32 = 0x01;
|
||||
const REG_REDIRECTION_BASE: u32 = 0x10;
|
||||
|
||||
const ENTRY_LOW_MASK: u32 = 1 << 16;
|
||||
// const ENTRY_LOW_TRIGGER_LEVEL: u32 = 1 << 15;
|
||||
// const ENTRY_LOW_POLARITY_LOW: u32 = 1 << 13;
|
||||
const ENTRY_LOW_DESTINATION_LOGICAL: u32 = 1 << 11;
|
||||
|
||||
const ENTRY_HIGH_APIC_ID_SHIFT: u32 = 24;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum ExternalTriggerMode {
|
||||
Edge,
|
||||
Level,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct IsaRedirection {
|
||||
gsi_index: u32,
|
||||
polarity: bool,
|
||||
trigger: ExternalTriggerMode,
|
||||
}
|
||||
|
||||
struct Regs {
|
||||
base: usize,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
regs: Regs,
|
||||
max_gsi: u32,
|
||||
}
|
||||
|
||||
/// I/O APIC interface. Provides a way to route and control how interrupts from external devices
|
||||
/// are handled.
|
||||
pub struct IoApic {
|
||||
inner: OneTimeInit<IrqSafeSpinlock<Inner>>,
|
||||
isa_redirections: OneTimeInit<[Option<IsaRedirection>; 16]>,
|
||||
|
||||
table: IrqSafeSpinlock<Table>,
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
#[inline]
|
||||
fn read(&self, reg: u32) -> u32 {
|
||||
let ptr = self.base as *mut u32;
|
||||
|
||||
unsafe {
|
||||
ptr.write_volatile(reg & 0xFF);
|
||||
ptr.add(4).read_volatile()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write(&self, reg: u32, value: u32) {
|
||||
let ptr = self.base as *mut u32;
|
||||
|
||||
unsafe {
|
||||
ptr.write_volatile(reg & 0xFF);
|
||||
ptr.add(4).write_volatile(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn map_gsi(&mut self, gsi: u32, vector: u32, apic_id: u32) -> Result<(), Error> {
|
||||
assert!(gsi < self.max_gsi);
|
||||
assert!(vector < 0x100);
|
||||
|
||||
debugln!("map_irq gsi{}, vec{}, apic{}", gsi, vector, apic_id);
|
||||
|
||||
let mut low = self.regs.read(REG_REDIRECTION_BASE + gsi * 2);
|
||||
let mut high = self.regs.read(REG_REDIRECTION_BASE + gsi * 2 + 1);
|
||||
|
||||
// Vector
|
||||
low &= !0xFF;
|
||||
low |= vector;
|
||||
// Destination - physical
|
||||
low &= !ENTRY_LOW_DESTINATION_LOGICAL;
|
||||
|
||||
// Destination APIC ID
|
||||
high &= !(0xFF << ENTRY_HIGH_APIC_ID_SHIFT);
|
||||
high |= apic_id << ENTRY_HIGH_APIC_ID_SHIFT;
|
||||
|
||||
self.regs.write(REG_REDIRECTION_BASE + gsi * 2, low);
|
||||
self.regs.write(REG_REDIRECTION_BASE + gsi * 2 + 1, high);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_gsi_enabled(&mut self, gsi: u32, enabled: bool) {
|
||||
assert!(gsi < self.max_gsi);
|
||||
|
||||
let low = self.regs.read(REG_REDIRECTION_BASE + gsi * 2);
|
||||
if enabled {
|
||||
debugln!("Unmask GSI #{}", gsi);
|
||||
self.regs
|
||||
.write(REG_REDIRECTION_BASE + gsi * 2, low & !ENTRY_LOW_MASK)
|
||||
} else {
|
||||
self.regs
|
||||
.write(REG_REDIRECTION_BASE + gsi * 2, low | ENTRY_LOW_MASK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for IoApic {
|
||||
unsafe fn init(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"I/O APIC"
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalInterruptController for IoApic {
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error> {
|
||||
let mut inner = self.inner.get().lock();
|
||||
let gsi = self.translate_irq(irq);
|
||||
inner.set_gsi_enabled(gsi, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_handler(
|
||||
&self,
|
||||
irq: Self::IrqNumber,
|
||||
handler: &'static (dyn InterruptSource + Sync),
|
||||
) -> Result<(), Error> {
|
||||
let mut inner = self.inner.get().lock();
|
||||
let mut table = self.table.lock();
|
||||
|
||||
// Allocate a vector
|
||||
let table_vector = table.least_loaded();
|
||||
let gsi_target_vector = (table_vector as u32) + IO_APIC_VECTOR_OFFSET;
|
||||
let bsp_apic = *BSP_APIC_ID.get();
|
||||
|
||||
debugln!("Binding {:?} to {}:{}", irq, bsp_apic, table_vector);
|
||||
table.add_handler(table_vector, handler)?;
|
||||
|
||||
let gsi = self.translate_irq(irq);
|
||||
inner.map_gsi(gsi, gsi_target_vector, *BSP_APIC_ID.get())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IoApic {
|
||||
/// Constructs an uninitialized I/O APIC interface
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
inner: OneTimeInit::new(),
|
||||
isa_redirections: OneTimeInit::new(),
|
||||
table: IrqSafeSpinlock::new(Table::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles an interrupt with given GSI number
|
||||
pub fn handle_irq(&self, gsi: u32) {
|
||||
let table = self.table.lock();
|
||||
|
||||
if let Some(handler) = table.vectors[gsi as usize] {
|
||||
drop(table);
|
||||
handler.handle_irq().expect("IRQ handler failed");
|
||||
} else {
|
||||
panic!("No handler set for GSI #{}", gsi);
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the I/O APIC interface using information provided by ACPI
|
||||
pub fn init_from_acpi(&self, info: &AcpiApic) -> Result<(), Error> {
|
||||
let io_apic = info.io_apics.first().unwrap();
|
||||
|
||||
infoln!("I/O APIC at {:#x}", io_apic.address);
|
||||
|
||||
let mut isa_redirections = [None; 16];
|
||||
|
||||
for redir in info.interrupt_source_overrides.iter() {
|
||||
let index = redir.isa_source as usize;
|
||||
let polarity = match redir.polarity {
|
||||
Polarity::ActiveLow => false,
|
||||
// TODO this may not be correct
|
||||
Polarity::ActiveHigh | Polarity::SameAsBus => true,
|
||||
};
|
||||
let trigger = match redir.trigger_mode {
|
||||
TriggerMode::Edge | TriggerMode::SameAsBus => ExternalTriggerMode::Edge,
|
||||
TriggerMode::Level => ExternalTriggerMode::Level,
|
||||
};
|
||||
|
||||
debugln!(
|
||||
"ISA IRQ #{} -> GSI #{}",
|
||||
index,
|
||||
redir.global_system_interrupt
|
||||
);
|
||||
isa_redirections[index].replace(IsaRedirection {
|
||||
gsi_index: redir.global_system_interrupt,
|
||||
polarity,
|
||||
trigger,
|
||||
});
|
||||
}
|
||||
|
||||
let regs = Regs {
|
||||
base: unsafe { (io_apic.address as usize).virtualize() },
|
||||
};
|
||||
|
||||
let max_gsi = (regs.read(REG_IOAPIC_VERSION) >> 16) & 0xFF;
|
||||
|
||||
let inner = Inner { regs, max_gsi };
|
||||
|
||||
self.inner.init(IrqSafeSpinlock::new(inner));
|
||||
self.isa_redirections.init(isa_redirections);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn translate_irq(&self, irq: IrqNumber) -> u32 {
|
||||
let redir = self.isa_redirections.get();
|
||||
match irq {
|
||||
IrqNumber::Isa(isa) => redir[isa as usize]
|
||||
.map(|t| t.gsi_index)
|
||||
.unwrap_or(isa as u32),
|
||||
IrqNumber::Gsi(gsi) => gsi as _,
|
||||
}
|
||||
}
|
||||
}
|
118
src/arch/x86_64/apic/local.rs
Normal file
118
src/arch/x86_64/apic/local.rs
Normal file
@ -0,0 +1,118 @@
|
||||
//! x86-64 Local APIC driver implementation
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
|
||||
use crate::{arch::x86_64::registers::MSR_IA32_APIC_BASE, mem::ConvertAddress, util::OneTimeInit};
|
||||
|
||||
use super::APIC_TIMER_VECTOR;
|
||||
|
||||
const TIMER_INTERVAL: u32 = 150000;
|
||||
|
||||
/// When initialized, contains the Local APIC ID of the bootstrap processor
|
||||
pub static BSP_APIC_ID: OneTimeInit<u32> = OneTimeInit::new();
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
Id [
|
||||
ApicId OFFSET(24) NUMBITS(8) []
|
||||
],
|
||||
SpuriousVector [
|
||||
SoftwareEnable OFFSET(8) NUMBITS(1),
|
||||
],
|
||||
TimerLocalVectorEntry [
|
||||
Vector OFFSET(0) NUMBITS(8) [],
|
||||
Mask OFFSET(16) NUMBITS(1) [
|
||||
Masked = 1,
|
||||
Unmasked = 0
|
||||
],
|
||||
TimerMode OFFSET(17) NUMBITS(1) [
|
||||
Periodic = 1,
|
||||
OneShot = 0
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case, missing_docs)]
|
||||
Regs {
|
||||
(0x00 => _0),
|
||||
(0x20 => Id: ReadOnly<u32, Id::Register>),
|
||||
(0x24 => _1),
|
||||
(0xB0 => EndOfInterrupt: WriteOnly<u32>),
|
||||
(0xB4 => _2),
|
||||
(0xF0 => SpuriousVector: ReadWrite<u32, SpuriousVector::Register>),
|
||||
(0xF4 => _3),
|
||||
(0x320 => TimerLocalVectorEntry: ReadWrite<u32, TimerLocalVectorEntry::Register>),
|
||||
(0x324 => _4),
|
||||
(0x380 => TimerInitCount: ReadWrite<u32>),
|
||||
(0x384 => _5),
|
||||
(0x390 => TimerCurrentCount: ReadOnly<u32>),
|
||||
(0x394 => _6),
|
||||
(0x3E0 => TimerDivideConfig: ReadWrite<u32>),
|
||||
(0x3E4 => _7),
|
||||
(0x530 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-processor local APIC interface
|
||||
pub struct LocalApic {
|
||||
regs: &'static Regs,
|
||||
}
|
||||
|
||||
impl LocalApic {
|
||||
/// Constructs a new instance of Local APIC.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only meant to be called once per processor during their init.
|
||||
pub unsafe fn new() -> Self {
|
||||
let base = unsafe { Self::base().virtualize() };
|
||||
let regs = unsafe { &*(base as *const Regs) };
|
||||
|
||||
let id = regs.Id.read(Id::ApicId);
|
||||
|
||||
if Self::is_bootstrap_cpu() {
|
||||
BSP_APIC_ID.init(id);
|
||||
}
|
||||
|
||||
Self::enable();
|
||||
|
||||
// Enable timer
|
||||
regs.TimerInitCount.set(TIMER_INTERVAL);
|
||||
regs.TimerDivideConfig.set(0x3);
|
||||
|
||||
regs.TimerLocalVectorEntry.write(
|
||||
TimerLocalVectorEntry::Vector.val(APIC_TIMER_VECTOR + 32)
|
||||
+ TimerLocalVectorEntry::Mask::Unmasked
|
||||
+ TimerLocalVectorEntry::TimerMode::Periodic,
|
||||
);
|
||||
|
||||
regs.SpuriousVector
|
||||
.modify(SpuriousVector::SoftwareEnable::SET);
|
||||
|
||||
Self { regs }
|
||||
}
|
||||
|
||||
/// Signals local APIC that we've handled the IRQ
|
||||
pub fn clear_interrupt(&self) {
|
||||
self.regs.EndOfInterrupt.set(0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn base() -> usize {
|
||||
MSR_IA32_APIC_BASE.read_base() as usize
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_bootstrap_cpu() -> bool {
|
||||
MSR_IA32_APIC_BASE.read(MSR_IA32_APIC_BASE::BootstrapCpuCore) != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn enable() {
|
||||
MSR_IA32_APIC_BASE.modify(MSR_IA32_APIC_BASE::ApicEnable::SET);
|
||||
}
|
||||
}
|
83
src/arch/x86_64/apic/mod.rs
Normal file
83
src/arch/x86_64/apic/mod.rs
Normal file
@ -0,0 +1,83 @@
|
||||
//! x86-64 APIC interface (Local + I/O)
|
||||
|
||||
use abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
arch::{x86_64::cpu::Cpu, PLATFORM},
|
||||
device::interrupt::IrqHandler,
|
||||
};
|
||||
|
||||
use super::exception::{self, ExceptionFrame};
|
||||
|
||||
pub mod ioapic;
|
||||
pub mod local;
|
||||
|
||||
// I/O APIC 0..MAX_EXTERNAL_VECTORS range is mapped to BSP Local APIC 2..
|
||||
|
||||
/// Fixed IRQ vector for Local APIC timer
|
||||
pub const APIC_TIMER_VECTOR: u32 = 0;
|
||||
/// Start of the I/O APIC IRQ range
|
||||
pub const APIC_EXTERNAL_OFFSET: u32 = 2;
|
||||
/// Maximum number of APIC vectors allocated for handling IRQs from I/O APIC
|
||||
pub const MAX_EXTERNAL_VECTORS: u32 = 16;
|
||||
|
||||
/// x86-64 interrupt number wrapper
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum IrqNumber {
|
||||
/// Legacy (ISA) interrupt. Can have value in range 0..16.
|
||||
Isa(u8),
|
||||
/// Global system interrupt. Means an external interrupt for I/O APIC.
|
||||
Gsi(u8),
|
||||
}
|
||||
|
||||
struct Table {
|
||||
vectors: [Option<IrqHandler>; MAX_EXTERNAL_VECTORS as usize],
|
||||
}
|
||||
|
||||
impl Table {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
vectors: [None; MAX_EXTERNAL_VECTORS as usize],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_handler(&mut self, vector: usize, handler: IrqHandler) -> Result<(), Error> {
|
||||
let old = self.vectors[vector].replace(handler);
|
||||
assert!(old.is_none());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn least_loaded(&self) -> usize {
|
||||
self.vectors.iter().position(|p| p.is_none()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills the IDT with interrupt vectors for this APIC
|
||||
pub fn setup_vectors(idt: &mut [exception::Entry]) {
|
||||
extern "C" {
|
||||
static __x86_64_apic_vectors: [usize; 16];
|
||||
}
|
||||
|
||||
for (i, &entry) in unsafe { __x86_64_apic_vectors.iter() }.enumerate() {
|
||||
idt[i] = exception::Entry::new(
|
||||
entry,
|
||||
0x08,
|
||||
exception::Entry::PRESENT | exception::Entry::INT32,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Main handler for APIC interrupts
|
||||
pub extern "C" fn __x86_64_apic_irq_handler(vector: u32, _frame: *mut ExceptionFrame) {
|
||||
let cpu = Cpu::local();
|
||||
cpu.local_apic().clear_interrupt();
|
||||
|
||||
if vector == APIC_TIMER_VECTOR {
|
||||
// TODO
|
||||
} else if (APIC_EXTERNAL_OFFSET..APIC_EXTERNAL_OFFSET + MAX_EXTERNAL_VECTORS).contains(&vector)
|
||||
{
|
||||
PLATFORM.ioapic.handle_irq(vector - APIC_EXTERNAL_OFFSET);
|
||||
} else {
|
||||
panic!("Got an interrupt on undefined vector: {}", vector);
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
//! x86-64 boot and entry functions
|
||||
use core::arch::global_asm;
|
||||
|
||||
use git_version::git_version;
|
||||
@ -8,21 +9,20 @@ use yboot_proto::{
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
x86_64::{exception, gdt},
|
||||
Architecture, ArchitectureImpl,
|
||||
x86_64::{apic::local::LocalApic, cpu::Cpu, exception, gdt},
|
||||
Architecture, ArchitectureImpl, PLATFORM,
|
||||
},
|
||||
debug,
|
||||
device::platform::Platform,
|
||||
mem::{
|
||||
heap,
|
||||
phys::{self, PageUsage},
|
||||
ConvertAddress,
|
||||
ConvertAddress, KERNEL_VIRT_OFFSET,
|
||||
},
|
||||
};
|
||||
|
||||
use super::ARCHITECTURE;
|
||||
|
||||
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
|
||||
const BOOT_STACK_SIZE: usize = 65536;
|
||||
|
||||
#[repr(C, align(0x20))]
|
||||
@ -46,6 +46,8 @@ static mut YBOOT_DATA: LoadProtocolV1 = LoadProtocolV1 {
|
||||
|
||||
memory_map: MemoryMap { address: 0, len: 0 },
|
||||
|
||||
rsdp_address: 0,
|
||||
|
||||
opt_framebuffer: FramebufferOption {
|
||||
req_width: 640,
|
||||
req_height: 480,
|
||||
@ -79,6 +81,11 @@ unsafe extern "C" fn __x86_64_upper_entry() -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
if YBOOT_DATA.rsdp_address != 0 {
|
||||
infoln!("ACPI RSDP at {:#x}", YBOOT_DATA.rsdp_address);
|
||||
PLATFORM.init_rsdp(YBOOT_DATA.rsdp_address as _);
|
||||
}
|
||||
|
||||
gdt::init();
|
||||
exception::init_exceptions(0);
|
||||
|
||||
@ -90,6 +97,12 @@ unsafe extern "C" fn __x86_64_upper_entry() -> ! {
|
||||
.expect("Could not allocate a block for heap");
|
||||
heap::init_heap(heap_base.virtualize(), 16 * 0x1000);
|
||||
|
||||
// Also initializes local APIC
|
||||
Cpu::init_local(LocalApic::new(), 0);
|
||||
|
||||
PLATFORM.init(true).unwrap();
|
||||
|
||||
ArchitectureImpl::set_interrupt_mask(false);
|
||||
loop {
|
||||
ArchitectureImpl::wait_for_interrupt();
|
||||
}
|
||||
|
66
src/arch/x86_64/cpu.rs
Normal file
66
src/arch/x86_64/cpu.rs
Normal file
@ -0,0 +1,66 @@
|
||||
//! Per-CPU information and data structures
|
||||
use core::ptr::null_mut;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use tock_registers::interfaces::Writeable;
|
||||
|
||||
use crate::arch::x86_64::registers::MSR_IA32_KERNEL_GS_BASE;
|
||||
|
||||
use super::apic::local::LocalApic;
|
||||
|
||||
/// Per-CPU data structure, only visible to the executing CPU
|
||||
#[repr(C, align(0x10))]
|
||||
pub struct Cpu {
|
||||
// 0x00
|
||||
this: *mut Cpu,
|
||||
|
||||
local_apic: LocalApic,
|
||||
id: u32,
|
||||
}
|
||||
|
||||
impl Cpu {
|
||||
/// Initializes the per-CPU data structure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only meant to be called once per each CPU during their init.
|
||||
pub unsafe fn init_local(local_apic: LocalApic, id: u32) {
|
||||
let this = Box::new(Cpu {
|
||||
this: null_mut(),
|
||||
local_apic,
|
||||
id,
|
||||
});
|
||||
let this = Box::into_raw(this);
|
||||
(*this).this = this;
|
||||
|
||||
MSR_IA32_KERNEL_GS_BASE.set(this as u64);
|
||||
core::arch::asm!("swapgs");
|
||||
MSR_IA32_KERNEL_GS_BASE.set(this as u64);
|
||||
}
|
||||
|
||||
/// Returns this CPU's local data structure
|
||||
#[inline(always)]
|
||||
pub fn local<'a>() -> &'a Self {
|
||||
Self::get_local().unwrap()
|
||||
}
|
||||
|
||||
/// Returns this CPU's local data structure or None if it hasn't been set up yet
|
||||
#[inline(always)]
|
||||
pub fn get_local<'a>() -> Option<&'a Self> {
|
||||
let mut addr: usize;
|
||||
unsafe {
|
||||
core::arch::asm!("movq %gs:(0), {0}", out(reg) addr, options(att_syntax));
|
||||
(addr as *const Cpu).as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns this CPU's number
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Returns the local APIC associated with this CPU
|
||||
pub fn local_apic(&self) -> &LocalApic {
|
||||
&self.local_apic
|
||||
}
|
||||
}
|
@ -1,7 +1,12 @@
|
||||
//! x86-64 exception and interrupt handling
|
||||
use core::{arch::global_asm, mem::size_of_val};
|
||||
|
||||
use crate::arch::{Architecture, ArchitectureImpl};
|
||||
use crate::arch::{
|
||||
x86_64::apic::{self, __x86_64_apic_irq_handler},
|
||||
Architecture, ArchitectureImpl,
|
||||
};
|
||||
|
||||
/// Set of registers saved when taking an exception/interrupt
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct ExceptionFrame {
|
||||
@ -20,11 +25,15 @@ pub struct ExceptionFrame {
|
||||
r13: usize,
|
||||
r14: usize,
|
||||
r15: usize,
|
||||
exc_number: usize,
|
||||
exc_code: usize,
|
||||
}
|
||||
|
||||
/// Exception table entry
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(packed)]
|
||||
struct Entry {
|
||||
pub struct Entry {
|
||||
base_lo: u16,
|
||||
selector: u16,
|
||||
__res0: u8,
|
||||
@ -34,6 +43,7 @@ struct Entry {
|
||||
__res1: u32,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
struct Pointer {
|
||||
limit: u16,
|
||||
@ -43,10 +53,13 @@ struct Pointer {
|
||||
const SIZE: usize = 256;
|
||||
|
||||
impl Entry {
|
||||
const PRESENT: u8 = 1 << 7;
|
||||
const INT32: u8 = 0xE;
|
||||
/// Entry is valid
|
||||
pub const PRESENT: u8 = 1 << 7;
|
||||
/// Entry is a 32-bit interrupt
|
||||
pub const INT32: u8 = 0xE;
|
||||
|
||||
const NULL: Self = Self {
|
||||
/// Empty entry
|
||||
pub const NULL: Self = Self {
|
||||
base_lo: 0,
|
||||
base_hi: 0,
|
||||
base_ex: 0,
|
||||
@ -56,7 +69,8 @@ impl Entry {
|
||||
__res1: 0,
|
||||
};
|
||||
|
||||
const fn new(base: usize, selector: u16, flags: u8) -> Self {
|
||||
/// Constructs an interrupt table entry
|
||||
pub const fn new(base: usize, selector: u16, flags: u8) -> Self {
|
||||
Self {
|
||||
base_lo: (base & 0xFFFF) as u16,
|
||||
base_hi: ((base >> 16) & 0xFFFF) as u16,
|
||||
@ -71,13 +85,28 @@ impl Entry {
|
||||
|
||||
static mut IDT: [Entry; SIZE] = [Entry::NULL; SIZE];
|
||||
|
||||
extern "C" fn __x86_64_exception_handler() {
|
||||
errorln!("An exception occurrred");
|
||||
extern "C" fn __x86_64_exception_handler(frame: *mut ExceptionFrame) {
|
||||
let frame = unsafe { &*frame };
|
||||
errorln!("Exception {}", frame.exc_number);
|
||||
|
||||
if frame.exc_number == 14 {
|
||||
let cr2: usize;
|
||||
unsafe {
|
||||
core::arch::asm!("mov %cr2, {0}", out(reg) cr2, options(att_syntax));
|
||||
}
|
||||
errorln!("Page fault at {:#x}", cr2);
|
||||
}
|
||||
|
||||
loop {
|
||||
ArchitectureImpl::wait_for_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the interrupt descriptor table for the given CPU.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only meant to be called once per each CPU during their init.
|
||||
pub unsafe fn init_exceptions(_cpu_index: usize) {
|
||||
extern "C" {
|
||||
static __x86_64_exception_vectors: [usize; 32];
|
||||
@ -87,6 +116,8 @@ pub unsafe fn init_exceptions(_cpu_index: usize) {
|
||||
IDT[i] = Entry::new(entry, 0x08, Entry::PRESENT | Entry::INT32);
|
||||
}
|
||||
|
||||
apic::setup_vectors(&mut IDT[32..]);
|
||||
|
||||
let idtr = Pointer {
|
||||
limit: size_of_val(&IDT) as u16 - 1,
|
||||
offset: &IDT as *const _ as usize,
|
||||
@ -98,5 +129,6 @@ pub unsafe fn init_exceptions(_cpu_index: usize) {
|
||||
global_asm!(
|
||||
include_str!("vectors.S"),
|
||||
exception_handler = sym __x86_64_exception_handler,
|
||||
apic_irq_handler = sym __x86_64_apic_irq_handler,
|
||||
options(att_syntax)
|
||||
);
|
||||
|
@ -1,7 +1,8 @@
|
||||
//! x86-64 Global Descriptor Table interface
|
||||
// TODO TSS
|
||||
|
||||
use core::mem::size_of_val;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
struct Entry {
|
||||
limit_lo: u16,
|
||||
@ -12,6 +13,7 @@ struct Entry {
|
||||
base_hi: u8,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(packed)]
|
||||
struct Pointer {
|
||||
limit: u16,
|
||||
@ -24,7 +26,10 @@ impl Entry {
|
||||
const ACC_SYSTEM: u8 = 1 << 4;
|
||||
const ACC_EXECUTE: u8 = 1 << 3;
|
||||
const ACC_WRITE: u8 = 1 << 1;
|
||||
|
||||
#[allow(unused)]
|
||||
const ACC_RING3: u8 = 3 << 5;
|
||||
#[allow(unused)]
|
||||
const ACC_ACCESS: u8 = 1 << 0;
|
||||
|
||||
const NULL: Self = Self {
|
||||
@ -70,6 +75,11 @@ static mut GDT: [Entry; SIZE] = [
|
||||
),
|
||||
];
|
||||
|
||||
/// Initializes the global descriptor table.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only meant to be called by the CPUs during their early init.
|
||||
pub unsafe fn init() {
|
||||
let gdtr = Pointer {
|
||||
limit: size_of_val(&GDT) as u16 - 1,
|
||||
|
@ -1,5 +1,8 @@
|
||||
/// Returns an absolute address to the given symbol
|
||||
//! x86-64 architecture helper functions
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// Returns an absolute address to the given symbol
|
||||
#[macro_export]
|
||||
macro_rules! absolute_address {
|
||||
($sym:expr) => {{
|
||||
@ -10,3 +13,37 @@ macro_rules! absolute_address {
|
||||
_x
|
||||
}};
|
||||
}
|
||||
|
||||
/// Wrapper struct providing access to an x86 I/O port
|
||||
#[derive(Clone, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct IoPort<T> {
|
||||
address: u16,
|
||||
_pd: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl IoPort<u8> {
|
||||
/// Constructs a new I/O port interface
|
||||
pub const fn new(address: u16) -> Self {
|
||||
Self {
|
||||
address,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a byte into the port
|
||||
pub fn write(&self, value: u8) {
|
||||
unsafe {
|
||||
core::arch::asm!("outb %al, %dx", in("al") value, in("dx") self.address, options(att_syntax));
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a byte from the port
|
||||
pub fn read(&self) -> u8 {
|
||||
let value: u8;
|
||||
unsafe {
|
||||
core::arch::asm!("inb %dx, %al", in("dx") self.address, out("al") value, options(att_syntax));
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
//! x86-64 architecture and platform implementation
|
||||
use core::ptr::NonNull;
|
||||
|
||||
use abi::error::Error;
|
||||
use acpi::{AcpiHandler, AcpiTables, InterruptModel, PhysicalMapping};
|
||||
use yboot_proto::{v1::FramebufferOption, AvailableRegion, IterableMemoryMap};
|
||||
|
||||
use crate::{
|
||||
@ -6,32 +10,55 @@ use crate::{
|
||||
debug::DebugSink,
|
||||
device::{
|
||||
display::{fb_console::FramebufferConsole, linear_fb::LinearFramebuffer},
|
||||
interrupt::{ExternalInterruptController, InterruptSource},
|
||||
platform::Platform,
|
||||
Device,
|
||||
},
|
||||
mem::{
|
||||
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
|
||||
ConvertAddress,
|
||||
},
|
||||
mem::phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
use self::{
|
||||
apic::{ioapic::IoApic, IrqNumber},
|
||||
intrinsics::IoPort,
|
||||
peripherals::ps2::PS2Controller,
|
||||
};
|
||||
|
||||
use super::Architecture;
|
||||
|
||||
#[macro_use]
|
||||
pub mod intrinsics;
|
||||
|
||||
pub mod apic;
|
||||
pub mod boot;
|
||||
pub mod cpu;
|
||||
pub mod exception;
|
||||
pub mod gdt;
|
||||
pub mod peripherals;
|
||||
pub mod registers;
|
||||
pub mod table;
|
||||
|
||||
/// Helper trait to provide abstract access to available memory regions
|
||||
pub trait AbstractAvailableRegion {
|
||||
/// Returns page-aligned physical start address of the region
|
||||
fn start_address(&self) -> usize;
|
||||
/// Returns the page count (rounded down) of this region
|
||||
fn page_count(&self) -> usize;
|
||||
}
|
||||
|
||||
/// Helper trait to provide abstract access to memory maps
|
||||
pub trait AbstractMemoryMap<'a>: 'a {
|
||||
/// Available memory region type contained within this memory map
|
||||
type AvailableRegion: AbstractAvailableRegion;
|
||||
/// Iterator type returned by [Self::iter]
|
||||
type Iter: Iterator<Item = &'a Self::AvailableRegion> + Clone;
|
||||
|
||||
/// Returns the physical memory range which contains this memory map
|
||||
fn reserved_range(&self) -> PhysicalMemoryRegion;
|
||||
/// Returns an iterator over the available memory regions
|
||||
fn iter(&self) -> Self::Iter;
|
||||
}
|
||||
|
||||
@ -61,8 +88,43 @@ impl<'a, T: IterableMemoryMap<'a> + 'a> AbstractMemoryMap<'a> for T {
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
pub struct X86_64 {
|
||||
yboot_framebuffer: OneTimeInit<FramebufferOption>,
|
||||
rsdp_phys_base: OneTimeInit<usize>,
|
||||
|
||||
// Static devices with dynamic addresses
|
||||
ioapic: IoApic,
|
||||
|
||||
// Static devices
|
||||
ps2: PS2Controller,
|
||||
}
|
||||
|
||||
impl Architecture for X86_64 {
|
||||
@ -106,9 +168,34 @@ impl Architecture for X86_64 {
|
||||
}
|
||||
|
||||
impl Platform for X86_64 {
|
||||
type IrqNumber = apic::IrqNumber;
|
||||
|
||||
const KERNEL_PHYS_BASE: usize = 0x400000;
|
||||
|
||||
unsafe fn init(&'static self, _is_bsp: bool) -> Result<(), Error> {
|
||||
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()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -145,6 +232,12 @@ impl Platform for X86_64 {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn interrupt_controller(
|
||||
&self,
|
||||
) -> &dyn ExternalInterruptController<IrqNumber = Self::IrqNumber> {
|
||||
&self.ioapic
|
||||
}
|
||||
}
|
||||
|
||||
impl X86_64 {
|
||||
@ -166,10 +259,40 @@ impl X86_64 {
|
||||
}))
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
/// x86-64 architecture + platform implementation
|
||||
pub static ARCHITECTURE: X86_64 = X86_64 {
|
||||
rsdp_phys_base: OneTimeInit::new(),
|
||||
yboot_framebuffer: OneTimeInit::new(),
|
||||
ioapic: IoApic::new(),
|
||||
|
||||
ps2: PS2Controller::new(IrqNumber::Isa(1), IrqNumber::Isa(12), 0x64, 0x60),
|
||||
};
|
||||
|
||||
static LINEAR_FB: OneTimeInit<LinearFramebuffer> = OneTimeInit::new();
|
||||
|
3
src/arch/x86_64/peripherals/mod.rs
Normal file
3
src/arch/x86_64/peripherals/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
//! x86-64 platform peripheral drivers
|
||||
|
||||
pub mod ps2;
|
102
src/arch/x86_64/peripherals/ps2.rs
Normal file
102
src/arch/x86_64/peripherals/ps2.rs
Normal file
@ -0,0 +1,102 @@
|
||||
//! Intel 8042 PS/2 controller driver implemenation
|
||||
use abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
x86_64::{apic::IrqNumber, intrinsics::IoPort},
|
||||
PLATFORM,
|
||||
},
|
||||
device::{interrupt::InterruptSource, platform::Platform, Device},
|
||||
};
|
||||
|
||||
/// PS/2 controller driver
|
||||
pub struct PS2Controller {
|
||||
command: IoPort<u8>,
|
||||
data: IoPort<u8>,
|
||||
primary_irq: IrqNumber,
|
||||
#[allow(unused)]
|
||||
auxiliary_irq: IrqNumber,
|
||||
}
|
||||
|
||||
impl InterruptSource for PS2Controller {
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
let intc = PLATFORM.interrupt_controller();
|
||||
|
||||
intc.register_handler(self.primary_irq, self)?;
|
||||
|
||||
// Disable PS/2 devices from sending any further data
|
||||
self.send_command(0xAD);
|
||||
self.send_command(0xA7);
|
||||
|
||||
// Flush the buffer
|
||||
while self.command.read() & Self::STATUS_OUTPUT_FULL != 0 {
|
||||
self.data.read();
|
||||
}
|
||||
|
||||
// Enable primary port
|
||||
self.send_command(0xAE);
|
||||
|
||||
intc.enable_irq(self.primary_irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_irq(&self) -> Result<bool, Error> {
|
||||
let status = self.command.read();
|
||||
|
||||
if status & 1 == 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut key = self.data.read();
|
||||
|
||||
if key == 0xE0 {
|
||||
key = self.data.read();
|
||||
}
|
||||
|
||||
self.data.read();
|
||||
|
||||
if key < 128 {
|
||||
debugln!("Got key {:#x}", key);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for PS2Controller {
|
||||
unsafe fn init(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"PS/2 Controller"
|
||||
}
|
||||
}
|
||||
|
||||
impl PS2Controller {
|
||||
const STATUS_OUTPUT_FULL: u8 = 1 << 0;
|
||||
const STATUS_INPUT_FULL: u8 = 1 << 1;
|
||||
|
||||
/// Constructs a new instance of the device
|
||||
pub const fn new(
|
||||
primary_irq: IrqNumber,
|
||||
auxiliary_irq: IrqNumber,
|
||||
cmd_port: u16,
|
||||
data_port: u16,
|
||||
) -> Self {
|
||||
Self {
|
||||
primary_irq,
|
||||
auxiliary_irq,
|
||||
command: IoPort::new(cmd_port),
|
||||
data: IoPort::new(data_port),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_command(&self, cmd: u8) {
|
||||
while self.command.read() & Self::STATUS_INPUT_FULL != 0 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.command.write(cmd);
|
||||
}
|
||||
}
|
102
src/arch/x86_64/registers/mod.rs
Normal file
102
src/arch/x86_64/registers/mod.rs
Normal file
@ -0,0 +1,102 @@
|
||||
//! Helper types for interfacing with x86-64 registers
|
||||
|
||||
macro_rules! msr_impl_read {
|
||||
($t:ident, $addr:expr, $register:ty) => {
|
||||
impl tock_registers::interfaces::Readable for $t {
|
||||
type T = u64;
|
||||
type R = $register;
|
||||
|
||||
#[inline]
|
||||
fn get(&self) -> u64 {
|
||||
let (high, low): (u32, u32);
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"rdmsr",
|
||||
in("ecx") $addr,
|
||||
out("eax") low,
|
||||
out("edx") high,
|
||||
options(att_syntax)
|
||||
);
|
||||
}
|
||||
((high as u64) << 32) | (low as u64)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($t:ident, $addr:expr) => { msr_impl_read!($t, $addr, ()); };
|
||||
}
|
||||
|
||||
macro_rules! msr_impl_write {
|
||||
($t:ident, $addr:expr, $register:ty) => {
|
||||
impl tock_registers::interfaces::Writeable for $t {
|
||||
type T = u64;
|
||||
type R = $register;
|
||||
|
||||
#[inline]
|
||||
fn set(&self, value: u64) {
|
||||
let low = value as u32;
|
||||
let high = (value >> 32) as u32;
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"wrmsr",
|
||||
in("ecx") $addr,
|
||||
in("eax") low,
|
||||
in("edx") high,
|
||||
options(att_syntax)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($t:ident, $addr:expr) => { msr_impl_write!($t, $addr, ()); };
|
||||
}
|
||||
|
||||
mod msr_ia32_kernel_gs_base {
|
||||
const ADDR: u32 = 0xC0000102;
|
||||
pub struct Reg;
|
||||
|
||||
msr_impl_read!(Reg, ADDR);
|
||||
msr_impl_write!(Reg, ADDR);
|
||||
|
||||
/// IA32_KERNEL_GS_BASE model-specific register. Provides the base address for %gs-relative
|
||||
/// loads/stores.
|
||||
pub const MSR_IA32_KERNEL_GS_BASE: Reg = Reg;
|
||||
}
|
||||
|
||||
mod msr_ia32_apic_base {
|
||||
use tock_registers::{interfaces::Readable, register_bitfields};
|
||||
|
||||
register_bitfields! {
|
||||
u64,
|
||||
#[allow(missing_docs)]
|
||||
#[doc = "IA32_APIC_BASE model-specific register"]
|
||||
pub MSR_IA32_APIC_BASE [
|
||||
#[doc = "Contains a virtual page number of the Local APIC base address for this processor"]
|
||||
AddressPage OFFSET(12) NUMBITS(40) [],
|
||||
#[doc = "If set, the APIC is enabled"]
|
||||
ApicEnable OFFSET(11) NUMBITS(1) [],
|
||||
#[doc = "If set, this CPU is a bootstrap processor"]
|
||||
BootstrapCpuCore OFFSET(8) NUMBITS(1) [],
|
||||
]
|
||||
}
|
||||
|
||||
const ADDR: u32 = 0x0000001B;
|
||||
pub struct Reg;
|
||||
|
||||
msr_impl_read!(Reg, ADDR, MSR_IA32_APIC_BASE::Register);
|
||||
msr_impl_write!(Reg, ADDR, MSR_IA32_APIC_BASE::Register);
|
||||
|
||||
impl Reg {
|
||||
#[inline]
|
||||
pub fn read_base(&self) -> u64 {
|
||||
self.read(MSR_IA32_APIC_BASE::AddressPage) << 12
|
||||
}
|
||||
}
|
||||
|
||||
/// IA32_APIC_BASE model-specific register
|
||||
pub const MSR_IA32_APIC_BASE: Reg = Reg;
|
||||
}
|
||||
|
||||
pub use msr_ia32_apic_base::MSR_IA32_APIC_BASE;
|
||||
pub use msr_ia32_kernel_gs_base::MSR_IA32_KERNEL_GS_BASE;
|
@ -11,6 +11,7 @@ const KERNEL_PD_COUNT: usize = 4;
|
||||
const DEVICE_MAPPING_L1I: usize = KERNEL_PD_COUNT + 1;
|
||||
const DEVICE_VIRT_OFFSET: usize = (DEVICE_MAPPING_L1I << 30) + KERNEL_VIRT_OFFSET;
|
||||
|
||||
/// Fixed tables for x86-64. Provide device mappings and static kernel mapping.
|
||||
pub struct FixedTables {
|
||||
// Common
|
||||
l0: PageTable<L0>,
|
||||
@ -29,6 +30,7 @@ pub struct FixedTables {
|
||||
}
|
||||
|
||||
impl FixedTables {
|
||||
/// Constructs a set of empty translation tables
|
||||
pub const fn zeroed() -> Self {
|
||||
Self {
|
||||
// Global
|
||||
@ -49,6 +51,7 @@ impl FixedTables {
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a specified count of physical memory pages to the device virtual address space
|
||||
pub fn map_device_pages(&mut self, phys: usize, count: usize) -> Result<usize, Error> {
|
||||
if count > 512 * 512 {
|
||||
panic!("Unsupported device memory mapping size");
|
||||
@ -84,13 +87,20 @@ impl FixedTables {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the physical address of the fixed PML4
|
||||
pub fn physical_address(&self) -> usize {
|
||||
self.l0.physical_address()
|
||||
}
|
||||
}
|
||||
|
||||
/// Instance of fixed translation tables
|
||||
pub static mut KERNEL_TABLES: FixedTables = FixedTables::zeroed();
|
||||
|
||||
/// Initializes the fixed translation tables.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only meant to be called by BSP during early init.
|
||||
pub unsafe fn init_fixed_tables() {
|
||||
// Kernel L2
|
||||
for i in 0..512 * KERNEL_PD_COUNT {
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! x86-64 virtual memory management implementation
|
||||
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut},
|
||||
@ -15,33 +17,41 @@ use crate::mem::{
|
||||
};
|
||||
|
||||
bitflags! {
|
||||
/// Describes how each page table entry is mapped
|
||||
pub struct PageAttributes: u64 {
|
||||
/// When set, the mapping is considered valid and pointing somewhere
|
||||
const PRESENT = 1 << 0;
|
||||
/// For tables, allows writes to further translation levels, for pages/blocks, allows
|
||||
/// writes to the region covered by the entry
|
||||
const WRITABLE = 1 << 1;
|
||||
/// When set for L2 entries, the mapping specifies a 2MiB page instead of a page table
|
||||
/// reference
|
||||
const BLOCK = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a single virtual address space mapping depending on its translation level
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct PageEntry<L: EntryLevel>(u64, PhantomData<L>);
|
||||
|
||||
/// Table describing a single level of address translation
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C, align(0x1000))]
|
||||
pub struct PageTable<L: EntryLevel> {
|
||||
data: [PageEntry<L>; 512],
|
||||
}
|
||||
|
||||
// L0: PML4, 512GiB page
|
||||
/// Translation level 0 (PML4): Entry is 512GiB table
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct L0;
|
||||
// L1: PDPT, 1GiB page
|
||||
/// Translation level 1 (PDPT): Entry is 1GiB table
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct L1;
|
||||
// L2: Page directory, 2MiB page
|
||||
/// Translation level 2 (Page directory): Entry is 2MiB block/table
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct L2;
|
||||
// L3: Page table, 4KiB page
|
||||
/// Translation level 3 (Page table): Entry is 4KiB page
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct L3;
|
||||
|
||||
@ -55,17 +65,12 @@ impl NonTerminalEntryLevel for L2 {
|
||||
type NextLevel = L3;
|
||||
}
|
||||
|
||||
// #[repr(C)]
|
||||
// pub struct AddressSpace {
|
||||
// l0: *mut PageTable<L0>,
|
||||
// }
|
||||
|
||||
impl const EntryLevel for L0 {
|
||||
fn index(addr: usize) -> usize {
|
||||
fn index(_addr: usize) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn page_offset(addr: usize) -> usize {
|
||||
fn page_offset(_addr: usize) -> usize {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@ -101,6 +106,7 @@ impl const EntryLevel for L3 {
|
||||
}
|
||||
|
||||
impl PageEntry<L3> {
|
||||
/// Constructs a mapping which points to a 4KiB page
|
||||
pub fn page(phys: usize, attrs: PageAttributes) -> Self {
|
||||
Self(
|
||||
(phys as u64) | (attrs | PageAttributes::PRESENT).bits(),
|
||||
@ -110,6 +116,7 @@ impl PageEntry<L3> {
|
||||
}
|
||||
|
||||
impl PageEntry<L2> {
|
||||
/// Constructs a mapping which points to a 2MiB block
|
||||
pub fn block(phys: usize, attrs: PageAttributes) -> Self {
|
||||
Self(
|
||||
(phys as u64) | (attrs | PageAttributes::PRESENT | PageAttributes::BLOCK).bits(),
|
||||
@ -119,6 +126,7 @@ impl PageEntry<L2> {
|
||||
}
|
||||
|
||||
impl<L: NonTerminalEntryLevel> PageEntry<L> {
|
||||
/// Constructs a mapping which points to a next-level table
|
||||
pub fn table(phys: usize, attrs: PageAttributes) -> Self {
|
||||
Self(
|
||||
(phys as u64) | (attrs | PageAttributes::PRESENT | PageAttributes::WRITABLE).bits(),
|
||||
@ -128,16 +136,19 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> PageEntry<L> {
|
||||
/// An entry that is not mapped
|
||||
pub const INVALID: Self = Self(0, PhantomData);
|
||||
}
|
||||
|
||||
impl<L: EntryLevel> PageTable<L> {
|
||||
/// Constructs a page table filled with invalid (non-present) entries
|
||||
pub const fn zeroed() -> Self {
|
||||
Self {
|
||||
data: [PageEntry::INVALID; 512],
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the physical address of this table
|
||||
pub fn physical_address(&self) -> usize {
|
||||
unsafe { (self.data.as_ptr() as usize).physicalize() }
|
||||
}
|
||||
|
@ -13,6 +13,26 @@ __x86_64_exc_\n:
|
||||
jmp __x86_64_exc_common
|
||||
.endm
|
||||
|
||||
.macro apic_vector, n
|
||||
__x86_64_apic_irq_\n:
|
||||
cli
|
||||
|
||||
// Push dummy error codes
|
||||
// pushq $0
|
||||
// pushq $0
|
||||
|
||||
EXC_SAVE_STATE
|
||||
mov $\n, %rdi
|
||||
mov %rsp, %rsi
|
||||
call {apic_irq_handler}
|
||||
EXC_RESTORE_STATE
|
||||
|
||||
// Remove dummy error codes
|
||||
// add $16, %rsp
|
||||
|
||||
iretq
|
||||
.endm
|
||||
|
||||
// 16 general-purpose registers
|
||||
.set PT_REGS_SIZE, 15 * 8
|
||||
|
||||
@ -53,9 +73,13 @@ __x86_64_exc_\n:
|
||||
mov 72(%rsp), %r13
|
||||
mov 76(%rsp), %r14
|
||||
mov 80(%rsp), %r15
|
||||
|
||||
addq $PT_REGS_SIZE, %rsp
|
||||
.endm
|
||||
|
||||
.global __x86_64_exception_vectors
|
||||
.global __x86_64_apic_vectors
|
||||
|
||||
.section .text
|
||||
__x86_64_exc_common:
|
||||
EXC_SAVE_STATE
|
||||
@ -101,6 +125,24 @@ ISR_NERR 29
|
||||
ISR_YERR 30
|
||||
ISR_NERR 31
|
||||
|
||||
apic_vector 0
|
||||
apic_vector 1
|
||||
apic_vector 2
|
||||
apic_vector 3
|
||||
apic_vector 4
|
||||
apic_vector 5
|
||||
apic_vector 6
|
||||
apic_vector 7
|
||||
apic_vector 8
|
||||
apic_vector 9
|
||||
apic_vector 10
|
||||
apic_vector 11
|
||||
apic_vector 12
|
||||
apic_vector 13
|
||||
apic_vector 14
|
||||
apic_vector 15
|
||||
|
||||
|
||||
.section .rodata
|
||||
.global __x86_64_exception_vectors
|
||||
.p2align 4
|
||||
@ -137,3 +179,24 @@ __x86_64_exception_vectors:
|
||||
.quad __x86_64_exc_29
|
||||
.quad __x86_64_exc_30
|
||||
.quad __x86_64_exc_31
|
||||
|
||||
.p2align 4
|
||||
__x86_64_apic_vectors:
|
||||
.quad __x86_64_apic_irq_0
|
||||
.quad __x86_64_apic_irq_1
|
||||
.quad __x86_64_apic_irq_2
|
||||
.quad __x86_64_apic_irq_3
|
||||
.quad __x86_64_apic_irq_4
|
||||
.quad __x86_64_apic_irq_5
|
||||
.quad __x86_64_apic_irq_6
|
||||
.quad __x86_64_apic_irq_7
|
||||
.quad __x86_64_apic_irq_8
|
||||
.quad __x86_64_apic_irq_9
|
||||
.quad __x86_64_apic_irq_10
|
||||
.quad __x86_64_apic_irq_11
|
||||
.quad __x86_64_apic_irq_12
|
||||
.quad __x86_64_apic_irq_13
|
||||
.quad __x86_64_apic_irq_14
|
||||
.quad __x86_64_apic_irq_15
|
||||
|
||||
.section .text
|
||||
|
58
src/debug.rs
58
src/debug.rs
@ -27,9 +27,12 @@ pub enum LogLevel {
|
||||
Fatal,
|
||||
}
|
||||
|
||||
/// Generic interface for debug output
|
||||
pub trait DebugSink {
|
||||
/// Sends a single byte to the output
|
||||
fn putc(&self, c: u8) -> Result<(), Error>;
|
||||
|
||||
/// Sends a string of bytes to the output
|
||||
fn puts(&self, s: &str) -> Result<(), Error> {
|
||||
for &byte in s.as_bytes() {
|
||||
self.putc(byte)?;
|
||||
@ -37,8 +40,9 @@ pub trait DebugSink {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn supports_color(&self) -> bool {
|
||||
return false;
|
||||
/// Returns `true` if the device supports vt100-like control sequences
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,27 +90,27 @@ debug_tpl!($ fatal, fatalln, Fatal);
|
||||
#[no_mangle]
|
||||
static DEBUG_PRINTER: OneTimeInit<IrqSafeSpinlock<DebugPrinter>> = OneTimeInit::new();
|
||||
|
||||
impl LogLevel {
|
||||
fn log_prefix(self) -> &'static str {
|
||||
match self {
|
||||
LogLevel::Debug => "",
|
||||
LogLevel::Info => "\x1b[36m\x1b[1m",
|
||||
LogLevel::Warning => "\x1b[33m\x1b[1m",
|
||||
LogLevel::Error => "\x1b[31m\x1b[1m",
|
||||
LogLevel::Fatal => "\x1b[38;2;255;0;0m\x1b[1m",
|
||||
}
|
||||
}
|
||||
|
||||
fn log_suffix(self) -> &'static str {
|
||||
match self {
|
||||
LogLevel::Debug => "",
|
||||
LogLevel::Info => "\x1b[0m",
|
||||
LogLevel::Warning => "\x1b[0m",
|
||||
LogLevel::Error => "\x1b[0m",
|
||||
LogLevel::Fatal => "\x1b[0m",
|
||||
}
|
||||
}
|
||||
}
|
||||
// impl LogLevel {
|
||||
// fn log_prefix(self) -> &'static str {
|
||||
// match self {
|
||||
// LogLevel::Debug => "",
|
||||
// LogLevel::Info => "\x1b[36m\x1b[1m",
|
||||
// LogLevel::Warning => "\x1b[33m\x1b[1m",
|
||||
// LogLevel::Error => "\x1b[31m\x1b[1m",
|
||||
// LogLevel::Fatal => "\x1b[38;2;255;0;0m\x1b[1m",
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn log_suffix(self) -> &'static str {
|
||||
// match self {
|
||||
// LogLevel::Debug => "",
|
||||
// LogLevel::Info => "\x1b[0m",
|
||||
// LogLevel::Warning => "\x1b[0m",
|
||||
// LogLevel::Error => "\x1b[0m",
|
||||
// LogLevel::Fatal => "\x1b[0m",
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl fmt::Write for DebugPrinter {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
@ -152,12 +156,12 @@ pub fn init() {
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn init_with_sink(sink: &'static dyn DebugSink) {
|
||||
DEBUG_PRINTER.init(IrqSafeSpinlock::new(DebugPrinter { sink }));
|
||||
}
|
||||
// pub fn init_with_sink(sink: &'static dyn DebugSink) {
|
||||
// DEBUG_PRINTER.init(IrqSafeSpinlock::new(DebugPrinter { sink }));
|
||||
// }
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn debug_internal(args: Arguments, level: LogLevel) {
|
||||
pub fn debug_internal(args: Arguments, _level: LogLevel) {
|
||||
use fmt::Write;
|
||||
|
||||
if DEBUG_PRINTER.is_initialized() {
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Framebuffer console driver
|
||||
|
||||
use abi::error::Error;
|
||||
use bitmap_font::{BitmapFont, TextStyle};
|
||||
use embedded_graphics::{
|
||||
@ -22,11 +24,13 @@ struct Inner {
|
||||
height_chars: u32,
|
||||
}
|
||||
|
||||
/// Framebuffer console device wrapper
|
||||
pub struct FramebufferConsole {
|
||||
inner: IrqSafeSpinlock<Inner>,
|
||||
}
|
||||
|
||||
impl FramebufferConsole {
|
||||
/// Constructs an instance of console from its framebuffer reference
|
||||
pub fn from_framebuffer(
|
||||
framebuffer: &'static LinearFramebuffer,
|
||||
font: &'static BitmapFont<'static>,
|
||||
@ -114,7 +118,7 @@ impl DebugSink for FramebufferConsole {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn supports_color(&self) -> bool {
|
||||
fn supports_control_sequences(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Abstract linear framebuffer device implementation
|
||||
|
||||
use core::ops::{Index, IndexMut};
|
||||
|
||||
use abi::error::Error;
|
||||
@ -12,17 +14,24 @@ struct Inner {
|
||||
stride: usize,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct FramebufferAccess {
|
||||
dimensions: DisplayDimensions,
|
||||
base: usize,
|
||||
stride: usize,
|
||||
}
|
||||
|
||||
/// Linear framebuffer wrapper
|
||||
pub struct LinearFramebuffer {
|
||||
inner: IrqSafeSpinlock<Inner>,
|
||||
}
|
||||
|
||||
impl LinearFramebuffer {
|
||||
/// Constructs a liner framebuffer struct from its components.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: the caller must ensure the validity of all the arguments.
|
||||
pub unsafe fn from_physical_bits(
|
||||
base: usize,
|
||||
size: usize,
|
||||
@ -49,6 +58,11 @@ impl LinearFramebuffer {
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Temporary function to provide framebuffer access
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: access is not synchronized
|
||||
// TODO doesn't actually lock
|
||||
pub unsafe fn lock(&self) -> FramebufferAccess {
|
||||
let inner = self.inner.lock();
|
||||
@ -78,6 +92,7 @@ impl DisplayDevice for LinearFramebuffer {
|
||||
}
|
||||
|
||||
impl FramebufferAccess {
|
||||
/// Copies `count` rows starting from `src_row` to `dst_row`
|
||||
pub fn copy_rows(&mut self, src_row: u32, dst_row: u32, count: u32) {
|
||||
use core::ffi::c_void;
|
||||
extern "C" {
|
||||
@ -108,6 +123,7 @@ impl FramebufferAccess {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills the specified number of pixel rows with given pixel value
|
||||
pub fn fill_rows(&mut self, start_row: u32, count: u32, value: u32) {
|
||||
use core::ffi::c_void;
|
||||
extern "C" {
|
||||
|
@ -1,14 +1,21 @@
|
||||
//! Display device interfaces
|
||||
|
||||
use super::Device;
|
||||
|
||||
pub mod fb_console;
|
||||
pub mod linear_fb;
|
||||
|
||||
/// Resolution of the display device
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct DisplayDimensions {
|
||||
/// Width of the display in pixels
|
||||
pub width: u32,
|
||||
/// Height of the display in pixels
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
/// Abstract display device interface
|
||||
pub trait DisplayDevice: Device {
|
||||
/// Returns the dimensions of the display in its current mode
|
||||
fn dimensions(&self) -> DisplayDimensions;
|
||||
}
|
||||
|
@ -3,10 +3,11 @@ use core::marker::PhantomData;
|
||||
|
||||
use abi::error::Error;
|
||||
|
||||
use crate::arch::CpuMessage;
|
||||
|
||||
use super::Device;
|
||||
|
||||
/// Convenience type alias for a static IRQ handler
|
||||
pub type IrqHandler = &'static (dyn InterruptSource + Sync);
|
||||
|
||||
/// Specifies the target(s) of interprocessor interrupt delivery
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum IpiDeliveryTarget {
|
||||
@ -25,12 +26,39 @@ pub trait InterruptSource: Device {
|
||||
/// The caller must ensure the function hasn't been called before.
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error>;
|
||||
|
||||
/// Handles the interrupt raised by the device
|
||||
fn handle_irq(&self) -> Result<(), Error>;
|
||||
/// Handles the interrupt raised by the device. Returns `true` if the interrupt was handled,
|
||||
/// `false` if device itself sent no interrupt (may be possible if several interrupt handlers
|
||||
/// share the same IRQ vector).
|
||||
fn handle_irq(&self) -> Result<bool, Error>;
|
||||
}
|
||||
|
||||
/// Interface for a device responsible for routing and handling IRQs
|
||||
pub trait InterruptController: Device {
|
||||
// TODO not yet ready for this
|
||||
// /// Controls per-CPU interrupt delivery
|
||||
// pub trait LocalInterruptController {
|
||||
// type Vector;
|
||||
// type Id;
|
||||
//
|
||||
// /// Allocates a vector for external IRQ
|
||||
// unsafe fn allocate_irq_vector(&self) -> Result<Self::Vector, Error>;
|
||||
//
|
||||
// /// Returns the unique ID of this local interrupt controller
|
||||
// fn id(&self) -> Self::Id;
|
||||
//
|
||||
// /// Sends a message to the requested set of CPUs through an interprocessor interrupt.
|
||||
// ///
|
||||
// /// # Note
|
||||
// ///
|
||||
// /// u64 limits the number of targetable CPUs to (only) 64. Platform-specific implementations
|
||||
// /// may impose narrower restrictions.
|
||||
// ///
|
||||
// /// # Safety
|
||||
// ///
|
||||
// /// As the call may alter the flow of execution on CPUs, this function is unsafe.
|
||||
// unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: CpuMessage) -> Result<(), Error>;
|
||||
// }
|
||||
|
||||
/// Interface for a device responsible for routing and handling IRQs from external sources
|
||||
pub trait ExternalInterruptController: Device {
|
||||
/// Interrupt number wrapper type
|
||||
type IrqNumber;
|
||||
|
||||
@ -43,21 +71,6 @@ pub trait InterruptController: Device {
|
||||
|
||||
/// Enables given interrupt number/vector
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error>;
|
||||
|
||||
/// Handles all pending interrupts on this controller
|
||||
fn handle_pending_irqs<'irq>(&'irq self, ic: &IrqContext<'irq>);
|
||||
|
||||
/// Sends a message to the requested set of CPUs through an interprocessor interrupt.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// u64 limits the number of targetable CPUs to (only) 64. Platform-specific implementations
|
||||
/// may impose narrower restrictions.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// As the call may alter the flow of execution on CPUs, this function is unsafe.
|
||||
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: CpuMessage) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// Token type to indicate that the code is being run from an interrupt handler
|
||||
|
@ -1,8 +1,8 @@
|
||||
//! Device management and interfaces
|
||||
use abi::error::Error;
|
||||
|
||||
// pub mod interrupt;
|
||||
pub mod display;
|
||||
pub mod interrupt;
|
||||
pub mod platform;
|
||||
// pub mod serial;
|
||||
// pub mod timer;
|
||||
|
@ -4,12 +4,12 @@ use abi::error::Error;
|
||||
|
||||
use crate::debug::DebugSink;
|
||||
|
||||
// use super::{interrupt::InterruptController, serial::SerialDevice, timer::TimestampSource};
|
||||
use super::interrupt::ExternalInterruptController;
|
||||
|
||||
/// Platform interface for interacting with a general hardware set
|
||||
pub trait Platform {
|
||||
/// Interrupt number type for the platform
|
||||
// type IrqNumber;
|
||||
type IrqNumber;
|
||||
|
||||
/// Address, to which the kernel is expected to be loaded for this platform
|
||||
const KERNEL_PHYS_BASE: usize;
|
||||
@ -39,12 +39,13 @@ pub trait Platform {
|
||||
/// May not be initialized at the moment of calling.
|
||||
fn primary_debug_sink(&self) -> Option<&dyn DebugSink>;
|
||||
|
||||
// /// Returns a reference to the platform's interrupt controller.
|
||||
// ///
|
||||
// /// # Note
|
||||
// ///
|
||||
// /// May not be initialized at the moment of calling.
|
||||
// fn interrupt_controller(&self) -> &dyn InterruptController<IrqNumber = Self::IrqNumber>;
|
||||
/// Returns a reference to the platform's interrupt controller.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// May not be initialized at the moment of calling.
|
||||
fn interrupt_controller(&self)
|
||||
-> &dyn ExternalInterruptController<IrqNumber = Self::IrqNumber>;
|
||||
|
||||
// /// Returns the platform's primary timestamp source.
|
||||
// ///
|
||||
|
15
src/main.rs
15
src/main.rs
@ -28,27 +28,16 @@ extern crate yggdrasil_abi as abi;
|
||||
// use vfs::{Filesystem, IoContext, VnodeRef};
|
||||
//
|
||||
extern crate alloc;
|
||||
//
|
||||
|
||||
#[macro_use]
|
||||
pub mod debug;
|
||||
#[macro_use]
|
||||
pub mod arch;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(_pi: &core::panic::PanicInfo) -> ! {
|
||||
use arch::{Architecture, ArchitectureImpl};
|
||||
|
||||
fatalln!("KERNEL PANIC");
|
||||
|
||||
loop {
|
||||
ArchitectureImpl::wait_for_interrupt();
|
||||
}
|
||||
}
|
||||
//
|
||||
pub mod device;
|
||||
// pub mod fs;
|
||||
pub mod mem;
|
||||
// pub mod panic;
|
||||
pub mod panic;
|
||||
// pub mod proc;
|
||||
pub mod sync;
|
||||
// pub mod syscall;
|
||||
|
@ -39,6 +39,7 @@ impl DeviceMemory {
|
||||
Ok(Self { name, base, size })
|
||||
}
|
||||
|
||||
/// Returns the base address of this mapping
|
||||
#[inline]
|
||||
pub fn base(&self) -> usize {
|
||||
self.base
|
||||
|
15
src/panic.rs
15
src/panic.rs
@ -2,9 +2,8 @@
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::{
|
||||
arch::{Architecture, ArchitectureImpl, CpuMessage, PLATFORM},
|
||||
arch::{Architecture, ArchitectureImpl},
|
||||
debug::{debug_internal, LogLevel},
|
||||
device::{interrupt::IpiDeliveryTarget, platform::Platform},
|
||||
sync::SpinFence,
|
||||
};
|
||||
|
||||
@ -38,12 +37,12 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
|
||||
.is_ok()
|
||||
{
|
||||
// Let other CPUs know we're screwed
|
||||
unsafe {
|
||||
PLATFORM
|
||||
.interrupt_controller()
|
||||
.send_ipi(IpiDeliveryTarget::AllExceptLocal, CpuMessage::Panic)
|
||||
.ok();
|
||||
}
|
||||
// unsafe {
|
||||
// PLATFORM
|
||||
// .interrupt_controller()
|
||||
// .send_ipi(IpiDeliveryTarget::AllExceptLocal, CpuMessage::Panic)
|
||||
// .ok();
|
||||
// }
|
||||
|
||||
log_print_raw!(LogLevel::Fatal, "--- BEGIN PANIC ---\n");
|
||||
log_print_raw!(LogLevel::Fatal, "Kernel panic ");
|
||||
|
Loading…
x
Reference in New Issue
Block a user