x86-64: implement Local+I/O APICs and PS/2 kbd

This commit is contained in:
Mark Poliakov 2023-08-01 11:09:56 +03:00
parent aec161bc47
commit 3a795f0229
26 changed files with 1162 additions and 107 deletions

View File

@ -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"

View 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 _,
}
}
}

View 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);
}
}

View 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);
}
}

View File

@ -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
View 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
}
}

View File

@ -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)
);

View File

@ -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,

View File

@ -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
}
}

View File

@ -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();

View File

@ -0,0 +1,3 @@
//! x86-64 platform peripheral drivers
pub mod ps2;

View 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);
}
}

View 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;

View File

@ -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 {

View File

@ -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() }
}

View File

@ -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

View File

@ -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() {

View File

@ -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
}
}

View File

@ -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" {

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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.
// ///

View File

@ -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;

View File

@ -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

View File

@ -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 ");