drivers: refactor x86 local & IO APIC drivers

This commit is contained in:
Yuekai Jia 2021-09-23 00:38:14 +08:00
parent 4021ef4c30
commit d13afc3d24
25 changed files with 451 additions and 519 deletions

View File

@ -21,5 +21,9 @@ bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator", rev =
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "2b3c6cf", optional = true } virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "2b3c6cf", optional = true }
async-std = { version = "1.10", optional = true } async-std = { version = "1.10", optional = true }
[target.'cfg(target_arch = "x86_64")'.dependencies]
acpi = "4.0"
x2apic = { git = "https://github.com/equation314/x2apic-rs", rev = "a14b1d8" }
[target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies] [target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies]
riscv = { version = "0.7", features = ["inline-asm"] } riscv = { version = "0.7", features = ["inline-asm"] }

View File

@ -3,7 +3,15 @@ cfg_if::cfg_if! {
mod riscv_intc; mod riscv_intc;
mod riscv_plic; mod riscv_plic;
pub use riscv_intc::{RiscvIntc, RiscvScauseIntCode}; pub mod riscv {
pub use riscv_plic::RiscvPlic; pub use super::riscv_intc::{Intc, ScauseIntCode};
pub use super::riscv_plic::Plic;
}
} else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
mod x86_apic;
pub mod x86 {
pub use super::x86_apic::Apic;
}
} }
} }

View File

@ -9,19 +9,19 @@ const S_TIMER: usize = 5;
const S_EXT: usize = 9; const S_EXT: usize = 9;
#[repr(usize)] #[repr(usize)]
pub enum RiscvScauseIntCode { pub enum ScauseIntCode {
SupervisorSoft = S_SOFT, SupervisorSoft = S_SOFT,
SupervisorTimer = S_TIMER, SupervisorTimer = S_TIMER,
SupervisorExternal = S_EXT, SupervisorExternal = S_EXT,
} }
pub struct RiscvIntc { pub struct Intc {
soft_handler: Mutex<Option<IrqHandler>>, soft_handler: Mutex<Option<IrqHandler>>,
timer_handler: Mutex<Option<IrqHandler>>, timer_handler: Mutex<Option<IrqHandler>>,
ext_handler: Mutex<Option<IrqHandler>>, ext_handler: Mutex<Option<IrqHandler>>,
} }
impl RiscvIntc { impl Intc {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
soft_handler: Mutex::new(None), soft_handler: Mutex::new(None),
@ -46,13 +46,13 @@ impl RiscvIntc {
} }
} }
impl Default for RiscvIntc { impl Default for Intc {
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl Scheme for RiscvIntc { impl Scheme for Intc {
fn handle_irq(&self, cause: usize) { fn handle_irq(&self, cause: usize) {
self.with_handler(cause, |opt| { self.with_handler(cause, |opt| {
if let Some(h) = opt { if let Some(h) = opt {
@ -66,7 +66,7 @@ impl Scheme for RiscvIntc {
} }
} }
impl IrqScheme for RiscvIntc { impl IrqScheme for Intc {
fn mask(&self, cause: usize) { fn mask(&self, cause: usize) {
unsafe { unsafe {
match cause { match cause {

View File

@ -22,7 +22,7 @@ struct PlicUnlocked {
manager: IrqManager<1024>, manager: IrqManager<1024>,
} }
pub struct RiscvPlic { pub struct Plic {
inner: Mutex<PlicUnlocked>, inner: Mutex<PlicUnlocked>,
} }
@ -70,7 +70,7 @@ impl PlicUnlocked {
} }
} }
impl RiscvPlic { impl Plic {
pub fn new(base: usize) -> Self { pub fn new(base: usize) -> Self {
let mut inner = PlicUnlocked { let mut inner = PlicUnlocked {
priority_base: unsafe { Mmio::<u32>::from_base(base + PLIC_PRIORITY_BASE) }, priority_base: unsafe { Mmio::<u32>::from_base(base + PLIC_PRIORITY_BASE) },
@ -85,11 +85,11 @@ impl RiscvPlic {
} }
} }
impl Scheme for RiscvPlic { impl Scheme for Plic {
fn handle_irq(&self, _unused: usize) { fn handle_irq(&self, _unused: usize) {
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
while let Some(irq_num) = inner.pending_irq() { while let Some(irq_num) = inner.pending_irq() {
if inner.manager.handle(irq_num as _).is_err() { if inner.manager.handle(irq_num).is_err() {
warn!("no registered handler for IRQ {}!", irq_num); warn!("no registered handler for IRQ {}!", irq_num);
} }
inner.eoi(irq_num); inner.eoi(irq_num);
@ -97,7 +97,7 @@ impl Scheme for RiscvPlic {
} }
} }
impl IrqScheme for RiscvPlic { impl IrqScheme for Plic {
fn mask(&self, irq_num: usize) { fn mask(&self, irq_num: usize) {
self.inner.lock().toggle(irq_num, false) self.inner.lock().toggle(irq_num, false)
} }

View File

@ -0,0 +1,9 @@
// TODO: configurable
pub const X86_INT_BASE: usize = 0x20;
pub const X86_INT_MAX: usize = 0xff;
pub const X86_INT_LOCAL_APIC_BASE: usize = 0xf0;
pub const X86_INT_APIC_SPURIOUS: usize = X86_INT_LOCAL_APIC_BASE + 0x0;
pub const X86_INT_APIC_TIMER: usize = X86_INT_LOCAL_APIC_BASE + 0x1;
pub const X86_INT_APIC_ERROR: usize = X86_INT_LOCAL_APIC_BASE + 0x2;

View File

@ -0,0 +1,146 @@
use alloc::vec::Vec;
use core::{fmt, ptr::NonNull};
use acpi::platform::interrupt::InterruptModel;
use acpi::{AcpiHandler, AcpiTables, PhysicalMapping};
use spin::Mutex;
use x2apic::ioapic::IoApic as IoApicInner;
use super::Phys2VirtFn;
const PAGE_SIZE: usize = 4096;
#[derive(Clone)]
struct AcpiMapHandler {
phys_to_virt: Phys2VirtFn,
}
impl AcpiHandler for AcpiMapHandler {
unsafe fn map_physical_region<T>(
&self,
physical_address: usize,
size: usize,
) -> PhysicalMapping<Self, T> {
let aligned_start = physical_address & !(PAGE_SIZE - 1);
let aligned_end = (physical_address + size + PAGE_SIZE - 1) & !(PAGE_SIZE - 1);
let phys_to_virt = self.phys_to_virt;
PhysicalMapping::new(
physical_address,
NonNull::new_unchecked(phys_to_virt(physical_address) as *mut T),
size,
aligned_end - aligned_start,
self.clone(),
)
}
fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
}
pub struct IoApic {
id: u8,
gsi_start: u32,
max_entry: u8,
inner: Mutex<IoApicInner>,
}
#[derive(Debug)]
pub struct IoApicList {
io_apics: Vec<IoApic>,
}
impl IoApic {
pub fn new(id: u8, base_vaddr: usize, gsi_start: u32) -> Self {
let mut inner = unsafe { IoApicInner::new(base_vaddr as u64) };
let max_entry = unsafe { inner.max_table_entry() };
unsafe {
assert_eq!(id, inner.id());
inner.init(super::consts::X86_INT_BASE as _);
}
Self {
id,
gsi_start,
max_entry,
inner: Mutex::new(inner),
}
}
pub fn toggle(&self, gsi: u32, enabled: bool) {
let idx = (gsi - self.gsi_start) as u8;
unsafe {
if enabled {
self.inner.lock().enable_irq(idx);
} else {
self.inner.lock().disable_irq(idx);
}
}
}
pub fn get_vector(&self, gsi: u32) -> u8 {
let idx = (gsi - self.gsi_start) as u8;
unsafe { self.inner.lock().table_entry(idx).vector() }
}
pub fn map_vector(&self, gsi: u32, vector: u8) {
let idx = (gsi - self.gsi_start) as u8;
let mut inner = self.inner.lock();
unsafe {
let mut entry = inner.table_entry(idx);
entry.set_vector(vector);
inner.set_table_entry(idx, entry);
}
}
}
impl IoApicList {
pub fn new(acpi_rsdp: usize, phys_to_virt: Phys2VirtFn) -> Self {
let handler = AcpiMapHandler { phys_to_virt };
let tables = unsafe { AcpiTables::from_rsdp(handler, acpi_rsdp).unwrap() };
let io_apics =
if let InterruptModel::Apic(apic) = tables.platform_info().unwrap().interrupt_model {
apic.io_apics
.iter()
.map(|i| {
IoApic::new(
i.id,
phys_to_virt(i.address as usize),
i.global_system_interrupt_base,
)
})
.collect()
} else {
Vec::new()
};
Self { io_apics }
}
pub fn find(&self, gsi: u32) -> Option<&IoApic> {
self.io_apics
.iter()
.find(|i| i.gsi_start <= gsi && gsi <= i.gsi_start + i.max_entry as u32)
}
}
impl fmt::Debug for IoApic {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
struct RedirTable<'a>(&'a IoApic);
impl<'a> fmt::Debug for RedirTable<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut inner = self.0.inner.lock();
let count = self.0.max_entry + 1;
f.debug_list()
.entries((0..count).map(|i| unsafe { inner.table_entry(i) }))
.finish()
}
}
let version = unsafe { self.inner.lock().version() };
f.debug_struct("IoApic")
.field("id", &self.id)
.field("version", &version)
.field("gsi_start", &self.gsi_start)
.field("max_entry", &self.max_entry)
.field("redir_table", &RedirTable(self))
.finish()
}
}

View File

@ -0,0 +1,40 @@
use x2apic::lapic::{xapic_base, LocalApic as LocalApicInner, LocalApicBuilder};
use super::{consts, Phys2VirtFn};
static mut LOCAL_APIC: Option<LocalApic> = None;
pub struct LocalApic {
inner: LocalApicInner,
}
impl LocalApic {
pub fn eoi(&mut self) {
unsafe { self.inner.end_of_interrupt() }
}
}
pub unsafe fn get_local_apic<'a>() -> &'a mut LocalApic {
LOCAL_APIC
.as_mut()
.expect("Local APIC is not initialized by BSP")
}
pub unsafe fn init_bsp(phys_to_virt: Phys2VirtFn) {
let base_vaddr = phys_to_virt(xapic_base() as usize);
let mut inner = LocalApicBuilder::new()
.timer_vector(consts::X86_INT_APIC_TIMER)
.error_vector(consts::X86_INT_APIC_ERROR)
.spurious_vector(consts::X86_INT_APIC_SPURIOUS)
.set_xapic_base(base_vaddr as u64)
.build()
.unwrap_or_else(|err| panic!("{}", err));
assert!(inner.is_bsp());
inner.enable();
LOCAL_APIC = Some(LocalApic { inner });
}
pub unsafe fn init_ap() {
get_local_apic().inner.enable()
}

View File

@ -0,0 +1,100 @@
mod consts;
mod ioapic;
mod lapic;
use core::ops::Range;
use spin::Mutex;
use self::consts::{X86_INT_BASE, X86_INT_LOCAL_APIC_BASE, X86_INT_MAX};
use self::ioapic::{IoApic, IoApicList};
use self::lapic::LocalApic;
use crate::scheme::{IrqHandler, IrqScheme, Scheme};
use crate::{utils::IrqManager, DeviceError, DeviceResult};
const IRQ_RANGE: Range<usize> = X86_INT_BASE..X86_INT_MAX + 1;
type Phys2VirtFn = fn(usize) -> usize;
pub struct Apic {
ioapic_list: IoApicList,
manager: Mutex<IrqManager<256>>,
}
impl Apic {
pub fn new(acpi_rsdp: usize, phys_to_virt: Phys2VirtFn) -> Self {
Self {
manager: Mutex::new(IrqManager::new(IRQ_RANGE)),
ioapic_list: IoApicList::new(acpi_rsdp, phys_to_virt),
}
}
fn with_ioapic<F>(&self, gsi: u32, op: F) -> DeviceResult
where
F: FnOnce(&IoApic) -> DeviceResult,
{
if let Some(apic) = self.ioapic_list.find(gsi) {
op(apic)
} else {
error!(
"cannot find IOAPIC for global system interrupt number {}",
gsi
);
Err(DeviceError::InvalidParam)
}
}
pub fn init_local_apic_bsp(phys_to_virt: Phys2VirtFn) {
unsafe { self::lapic::init_bsp(phys_to_virt) }
}
pub fn init_local_apic_ap() {
unsafe { self::lapic::init_ap() }
}
pub fn local_apic<'a>() -> &'a mut LocalApic {
unsafe { self::lapic::get_local_apic() }
}
pub fn register_local_apic_handler(&self, vector: usize, handler: IrqHandler) -> DeviceResult {
if vector >= X86_INT_LOCAL_APIC_BASE {
self.manager.lock().register_handler(vector, handler)?;
Ok(())
} else {
error!("invalid local APIC interrupt vector {}", vector);
Err(DeviceError::InvalidParam)
}
}
}
impl Scheme for Apic {
fn handle_irq(&self, vector: usize) {
if self.manager.lock().handle(vector).is_err() {
warn!("no registered handler for interrupt vector {}!", vector);
}
Self::local_apic().eoi();
}
}
impl IrqScheme for Apic {
fn mask(&self, gsi: usize) {
self.with_ioapic(gsi as _, |apic| Ok(apic.toggle(gsi as _, false)))
.ok();
}
fn unmask(&self, gsi: usize) {
self.with_ioapic(gsi as _, |apic| Ok(apic.toggle(gsi as _, true)))
.ok();
}
fn register_handler(&self, gsi: usize, handler: IrqHandler) -> DeviceResult {
let gsi = gsi as u32;
self.with_ioapic(gsi, |apic| {
let vector = apic.get_vector(gsi) as _; // if not mapped, allocate an available vector by `register_handler()`.
let vector = self.manager.lock().register_handler(vector, handler)? as u8;
apic.map_vector(gsi, vector);
apic.toggle(gsi, true);
Ok(())
})
}
}

View File

@ -33,15 +33,18 @@ impl<const IRQ_COUNT: usize> IrqManager<IRQ_COUNT> {
self.allocator.free(start, count) self.allocator.free(start, count)
} }
/// Add a handler to IRQ table. Return the specified irq or an allocated irq on success /// Add a handler to IRQ table. if `irq_num == 0`, we need to allocate one.
/// Returns the specified IRQ number or an allocated IRQ on success.
pub fn register_handler(&mut self, irq_num: usize, handler: IrqHandler) -> DeviceResult<usize> { pub fn register_handler(&mut self, irq_num: usize, handler: IrqHandler) -> DeviceResult<usize> {
info!("IRQ add handler {:#x?}", irq_num); info!("IRQ add handler {:#x?}", irq_num);
let irq_num = if !self.irq_range.contains(&irq_num) { let irq_num = if irq_num == 0 {
// allocate a valid irq number // allocate a valid IRQ number
self.allocator.alloc()? self.allocator.alloc()?
} else { } else if self.irq_range.contains(&irq_num) {
self.allocator.alloc_fixed(irq_num)?; self.allocator.alloc_fixed(irq_num)?;
irq_num irq_num
} else {
return Err(DeviceError::InvalidParam);
}; };
self.table[irq_num] = Some(handler); self.table[irq_num] = Some(handler);
Ok(irq_num) Ok(irq_num)

View File

@ -20,7 +20,6 @@ numeric-enum-macro = "0.2"
spin = "0.9" spin = "0.9"
git-version = "0.3" git-version = "0.3"
cfg-if = "1.0" cfg-if = "1.0"
acpi = "1.1"
zcore-drivers = { path = "../drivers" } zcore-drivers = { path = "../drivers" }
# LibOS mode # LibOS mode
@ -41,7 +40,6 @@ bitmap-allocator = { git = "https://github.com/rcore-os/bitmap-allocator", rev =
[target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies] [target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies]
x86_64 = "0.14" x86_64 = "0.14"
raw-cpuid = "9.0" raw-cpuid = "9.0"
apic = { git = "https://github.com/rcore-os/apic-rs", rev = "fb86bd7" }
x86-smpboot = { git = "https://github.com/rcore-os/x86-smpboot", rev = "43ffedf" } x86-smpboot = { git = "https://github.com/rcore-os/x86-smpboot", rev = "43ffedf" }
# Bare-metal mode on riscv64 # Bare-metal mode on riscv64

View File

@ -1,39 +1,35 @@
use alloc::boxed::Box; use alloc::boxed::Box;
use zcore_drivers::irq::{RiscvIntc, RiscvPlic, RiscvScauseIntCode}; use zcore_drivers::irq::riscv::{Intc, Plic, ScauseIntCode};
use zcore_drivers::scheme::{AsScheme, EventListener, IrqScheme}; use zcore_drivers::scheme::{AsScheme, EventListener, IrqScheme};
use zcore_drivers::uart::Uart16550Mmio; use zcore_drivers::uart::Uart16550Mmio;
use zcore_drivers::DeviceResult;
use super::{consts, trap}; use super::{consts, trap};
use crate::drivers::{IRQ, UART}; use crate::drivers::{IRQ, UART};
use crate::mem::phys_to_virt; use crate::mem::phys_to_virt;
use crate::utils::init_once::InitOnce; use crate::utils::init_once::InitOnce;
static PLIC: InitOnce<RiscvPlic> = InitOnce::new(); static PLIC: InitOnce<Plic> = InitOnce::new();
pub(super) fn init() { pub(super) fn init() -> DeviceResult {
IRQ.init_by(Box::new(RiscvIntc::new()));
UART.init_by(Box::new(EventListener::new(unsafe { UART.init_by(Box::new(EventListener::new(unsafe {
Uart16550Mmio::<u8>::new(phys_to_virt(consts::UART_BASE)) Uart16550Mmio::<u8>::new(phys_to_virt(consts::UART_BASE))
}))); })));
IRQ.init_by(Box::new(Intc::new()));
PLIC.init_by(RiscvPlic::new(phys_to_virt(consts::PLIC_BASE))); PLIC.init_by(Plic::new(phys_to_virt(consts::PLIC_BASE)));
PLIC.register_device(consts::UART0_INT_NUM, UART.as_scheme()) PLIC.register_device(consts::UART0_INT_NUM, UART.as_scheme())?;
.unwrap();
IRQ.register_handler( IRQ.register_handler(
RiscvScauseIntCode::SupervisorSoft as _, ScauseIntCode::SupervisorSoft as _,
Box::new(|_| trap::super_soft()), Box::new(|_| trap::super_soft()),
) )?;
.unwrap();
IRQ.register_handler( IRQ.register_handler(
RiscvScauseIntCode::SupervisorTimer as _, ScauseIntCode::SupervisorTimer as _,
Box::new(|_| trap::super_timer()), Box::new(|_| trap::super_timer()),
) )?;
.unwrap(); IRQ.register_device(ScauseIntCode::SupervisorExternal as _, PLIC.as_scheme())?;
IRQ.register_device(
RiscvScauseIntCode::SupervisorExternal as _, Ok(())
PLIC.as_scheme(),
)
.unwrap();
} }

View File

@ -16,7 +16,7 @@ pub mod vm;
pub fn init() { pub fn init() {
vm::remap_the_kernel().unwrap(); vm::remap_the_kernel().unwrap();
drivers::init(); drivers::init().unwrap();
interrupt::init(); interrupt::init();
timer::init(); timer::init();

View File

@ -1,118 +0,0 @@
#![allow(dead_code)]
use alloc::vec::Vec;
use core::ptr::NonNull;
use acpi::interrupt::{InterruptModel, InterruptSourceOverride, IoApic, Polarity, TriggerMode};
use acpi::{parse_rsdp, Acpi, AcpiHandler, PhysicalMapping};
use spin::Mutex;
use crate::{mem::phys_to_virt, PAGE_SIZE};
pub struct AcpiTable {
inner: Acpi,
}
lazy_static! {
static ref ACPI_TABLE: Mutex<Option<AcpiTable>> = Mutex::default();
}
/// Build ACPI Table
struct AcpiHelper;
impl AcpiHandler for AcpiHelper {
unsafe fn map_physical_region<T>(
&mut self,
physical_address: usize,
size: usize,
) -> PhysicalMapping<T> {
#[allow(non_snake_case)]
let OFFSET = 0;
let page_start = physical_address / PAGE_SIZE;
let page_end = (physical_address + size + PAGE_SIZE - 1) / PAGE_SIZE;
PhysicalMapping::<T> {
physical_start: physical_address,
virtual_start: NonNull::new_unchecked(phys_to_virt(physical_address + OFFSET) as *mut T),
mapped_length: size,
region_length: PAGE_SIZE * (page_end - page_start),
}
}
fn unmap_physical_region<T>(&mut self, _region: PhysicalMapping<T>) {}
}
fn get_acpi_table() -> Option<Acpi> {
let mut handler = AcpiHelper;
match unsafe {
parse_rsdp(
&mut handler,
super::special::pc_firmware_tables().0 as usize,
)
} {
Ok(table) => Some(table),
Err(info) => {
warn!("get_acpi_table error: {:#x?}", info);
None
}
}
}
impl AcpiTable {
fn initialize_check() {
#[cfg(target_arch = "x86_64")]
{
let mut table = ACPI_TABLE.lock();
if table.is_none() {
*table = get_acpi_table().map(|x| AcpiTable { inner: x });
}
}
}
pub fn invalidate() {
*ACPI_TABLE.lock() = None;
}
pub fn get_ioapic() -> Vec<IoApic> {
Self::initialize_check();
let table = ACPI_TABLE.lock();
match &*table {
None => Vec::default(),
Some(table) => match table.inner.interrupt_model.as_ref().unwrap() {
InterruptModel::Apic(apic) => {
apic.io_apics.iter().map(|x| IoApic { ..*x }).collect()
}
_ => Vec::default(),
},
}
}
pub fn get_interrupt_source_overrides() -> Vec<InterruptSourceOverride> {
Self::initialize_check();
let table = ACPI_TABLE.lock();
match &*table {
None => Vec::default(),
Some(table) => match table.inner.interrupt_model.as_ref().unwrap() {
InterruptModel::Apic(apic) => apic
.interrupt_source_overrides
.iter()
.map(|x| InterruptSourceOverride {
polarity: Self::clone_polarity(&x.polarity),
trigger_mode: Self::clone_trigger_mode(&x.trigger_mode),
..*x
})
.collect(),
_ => Vec::default(),
},
}
}
fn clone_polarity(x: &Polarity) -> Polarity {
match x {
Polarity::SameAsBus => Polarity::SameAsBus,
Polarity::ActiveHigh => Polarity::ActiveHigh,
Polarity::ActiveLow => Polarity::ActiveLow,
}
}
fn clone_trigger_mode(x: &TriggerMode) -> TriggerMode {
match x {
TriggerMode::SameAsBus => TriggerMode::SameAsBus,
TriggerMode::Edge => TriggerMode::Edge,
TriggerMode::Level => TriggerMode::Level,
}
}
}

View File

@ -1,22 +0,0 @@
use apic::{IoApic, LocalApic, XApic};
use crate::mem::phys_to_virt;
const LAPIC_ADDR: usize = 0xfee0_0000;
const IOAPIC_ADDR: usize = 0xfec0_0000;
pub fn get_lapic() -> XApic {
unsafe { XApic::new(phys_to_virt(LAPIC_ADDR)) }
}
pub fn get_ioapic() -> IoApic {
unsafe { IoApic::new(phys_to_virt(IOAPIC_ADDR)) }
}
pub fn lapic_id() -> u8 {
get_lapic().id() as u8
}
pub fn init() {
get_lapic().cpu_init();
}

View File

@ -1,7 +1,9 @@
use raw_cpuid::CpuId;
lazy_static! { lazy_static! {
static ref TSC_FREQUENCY: u16 = { static ref TSC_FREQUENCY: u16 = {
const DEFAULT: u16 = 2600; const DEFAULT: u16 = 4000;
if let Some(info) = raw_cpuid::CpuId::new().get_processor_frequency_info() { if let Some(info) = CpuId::new().get_processor_frequency_info() {
let f = info.processor_base_frequency(); let f = info.processor_base_frequency();
return if f == 0 { DEFAULT } else { f }; return if f == 0 { DEFAULT } else { f };
} }
@ -13,7 +15,10 @@ lazy_static! {
hal_fn_impl! { hal_fn_impl! {
impl mod crate::hal_fn::cpu { impl mod crate::hal_fn::cpu {
fn cpu_id() -> u8 { fn cpu_id() -> u8 {
super::apic::lapic_id() CpuId::new()
.get_feature_info()
.unwrap()
.initial_local_apic_id() as u8
} }
fn cpu_frequency() -> u16 { fn cpu_frequency() -> u16 {

View File

@ -1,10 +1,27 @@
use alloc::boxed::Box; use alloc::boxed::Box;
use zcore_drivers::scheme::EventListener; use zcore_drivers::irq::x86::Apic;
use zcore_drivers::scheme::{EventListener, IrqScheme};
use zcore_drivers::uart::Uart16550Pio; use zcore_drivers::uart::Uart16550Pio;
use zcore_drivers::DeviceResult;
use crate::drivers::UART; use super::trap;
use crate::drivers::{IRQ, UART};
pub(super) fn init() { pub(super) fn init() -> DeviceResult {
UART.init_by(Box::new(EventListener::new(Uart16550Pio::new(0x3F8)))); UART.init_by(Box::new(EventListener::new(Uart16550Pio::new(0x3F8))));
Apic::init_local_apic_bsp(crate::mem::phys_to_virt);
let irq = Box::new(Apic::new(
super::special::pc_firmware_tables().0 as usize,
crate::mem::phys_to_virt,
));
irq.register_device(trap::X86_ISA_IRQ_COM1, UART.as_scheme())?;
irq.register_local_apic_handler(
trap::X86_INT_APIC_TIMER,
Box::new(|_| crate::timer::timer_tick()),
)?;
IRQ.init_by(irq);
Ok(())
} }

View File

@ -1,37 +1,9 @@
#![allow(dead_code)] use alloc::boxed::Box;
#![allow(non_upper_case_globals)]
use alloc::{boxed::Box, vec::Vec};
use core::ops::Range; use core::ops::Range;
use apic::IoApic; use crate::HalResult;
use spin::Mutex;
use super::acpi_table::AcpiTable; type IrqHandler = Box<dyn Fn() + Send + Sync>;
use crate::utils::irq_manager::{IrqHandler, IrqManager};
use crate::{mem::phys_to_virt, HalError, HalResult};
const IRQ0: u32 = 32;
const IRQ_MIN_ID: u32 = 0x20;
const IRQ_MAX_ID: u32 = 0xff;
// IRQ
const Timer: u32 = 0;
const Keyboard: u32 = 1;
const COM2: u32 = 3;
const COM1: u32 = 4;
const Mouse: u32 = 12;
const IDE: u32 = 14;
const Error: u32 = 19;
const Spurious: u32 = 31;
const IO_APIC_NUM_REDIRECTIONS: u8 = 120;
lazy_static! {
static ref IRQ_MANAGER: Mutex<IrqManager> = Mutex::new(IrqManager::new(0x20, 0xff));
static ref MAX_INSTR_TABLE: Mutex<Vec<(usize, u8)>> = Mutex::default();
}
/* /*
lazy_static! { lazy_static! {
@ -64,158 +36,42 @@ fn mouse() {
} }
*/ */
fn ioapic_maxinstr(ioapic_addr: u32) -> Option<u8> {
let mut table = MAX_INSTR_TABLE.lock();
for (addr, v) in table.iter() {
if *addr == ioapic_addr as usize {
return Some(*v);
}
}
let mut ioapic = unsafe { IoApic::new(phys_to_virt(ioapic_addr as usize)) };
let v = ioapic.maxintr();
table.push((ioapic_addr as usize, v));
Some(v)
}
unsafe fn init_ioapic() {
for ioapic in AcpiTable::get_ioapic() {
info!("Ioapic found: {:#x?}", ioapic);
let mut ip = IoApic::new(phys_to_virt(ioapic.address as usize));
ip.disable_all();
}
let mut ip = super::apic::get_ioapic();
ip.disable_all();
}
fn get_ioapic(irq: u32) -> Option<acpi::interrupt::IoApic> {
for i in AcpiTable::get_ioapic() {
let num_instr = core::cmp::min(
ioapic_maxinstr(i.address).unwrap(),
IO_APIC_NUM_REDIRECTIONS - 1,
);
if i.global_system_interrupt_base <= irq
&& irq <= i.global_system_interrupt_base + num_instr as u32
{
return Some(i);
}
}
None
}
fn ioapic_controller(i: &acpi::interrupt::IoApic) -> IoApic {
unsafe { IoApic::new(phys_to_virt(i.address as usize)) }
}
hal_fn_impl! { hal_fn_impl! {
impl mod crate::hal_fn::interrupt { impl mod crate::hal_fn::interrupt {
fn enable_irq(irq: u32) { fn enable_irq(irq: u32) {
info!("enable_irq irq={:#x?}", irq); todo!()
// if irq == 1 {
// irq_enable_raw(irq as u8, irq as u8 + IRQ0);
// return;
// }
if let Some(x) = get_ioapic(irq) {
let mut ioapic = ioapic_controller(&x);
ioapic.enable((irq - x.global_system_interrupt_base) as u8, 0);
}
} }
fn disable_irq(irq: u32) { fn disable_irq(irq: u32) {
info!("disable_irq"); todo!()
if let Some(x) = get_ioapic(irq) {
let mut ioapic = ioapic_controller(&x);
ioapic.disable((irq - x.global_system_interrupt_base) as u8);
}
} }
fn is_valid_irq(irq: u32) -> bool { fn is_valid_irq(irq: u32) -> bool {
trace!("is_valid_irq: irq={:#x?}", irq); todo!()
get_ioapic(irq).is_some()
} }
fn configure_irq(vector: u32, trig_mode: bool, polarity: bool) -> HalResult { fn configure_irq(vector: u32, trig_mode: bool, polarity: bool) -> HalResult {
info!( todo!()
"configure_irq: vector={:#x?}, trig_mode={:#x?}, polarity={:#x?}",
vector, trig_mode, polarity
);
let dest = super::apic::lapic_id();
get_ioapic(vector)
.map(|x| {
let mut ioapic = ioapic_controller(&x);
ioapic.config(
(vector - x.global_system_interrupt_base) as u8,
0,
dest,
trig_mode,
polarity,
false, /* physical */
true, /* mask */
);
})
.ok_or(HalError)
} }
fn register_irq_handler(global_irq: u32, handler: IrqHandler) -> HalResult<u32> { fn register_irq_handler(global_irq: u32, handler: IrqHandler) -> HalResult<u32> {
info!("register_irq_handler irq={:#x?}", global_irq); todo!()
// if global_irq == 1 {
// irq_add_handler(global_irq as u8 + IRQ0, handler);
// return Some(global_irq as u8 + IRQ0);
// }
let ioapic_info = get_ioapic(global_irq).ok_or(HalError)?;
let mut ioapic = ioapic_controller(&ioapic_info);
let offset = (global_irq - ioapic_info.global_system_interrupt_base) as u8;
let x86_vector = ioapic.irq_vector(offset);
let new_handler = if global_irq == 0x1 {
Box::new(move || {
handler();
// keyboard();
// mouse();
})
} else {
handler
};
let x86_vector = IRQ_MANAGER
.lock()
.register_handler(x86_vector as u32, new_handler)?;
info!(
"irq_set_handle: mapping from {:#x?} to {:#x?}",
global_irq, x86_vector
);
ioapic.set_irq_vector(offset, x86_vector as u8);
Ok(x86_vector)
} }
fn unregister_irq_handler(global_irq: u32) -> HalResult { fn unregister_irq_handler(global_irq: u32) -> HalResult {
info!("unregister_irq_handler irq={:#x}", global_irq); todo!()
let ioapic_info = if let Some(x) = get_ioapic(global_irq) {
x
} else {
return Err(HalError);
};
let mut ioapic = ioapic_controller(&ioapic_info);
let offset = (global_irq - ioapic_info.global_system_interrupt_base) as u8;
let x86_vector = ioapic.irq_vector(offset);
// TODO: ioapic redirection entries associated with this should be reset.
IRQ_MANAGER.lock().unregister_handler(x86_vector as u32)
} }
fn handle_irq(vector: u32) { fn handle_irq(vector: u32) {
use apic::LocalApic; crate::drivers::IRQ.handle_irq(vector as usize);
let mut lapic = super::apic::get_lapic();
lapic.eoi();
IRQ_MANAGER.lock().handle(vector);
} }
fn msi_allocate_block(requested_irqs: u32) -> HalResult<Range<u32>> { fn msi_allocate_block(requested_irqs: u32) -> HalResult<Range<u32>> {
let alloc_size = requested_irqs.next_power_of_two(); todo!()
let start = IRQ_MANAGER.lock().alloc_block(alloc_size)?;
Ok(start..start + alloc_size)
} }
fn msi_free_block(block: Range<u32>) -> HalResult { fn msi_free_block(block: Range<u32>) -> HalResult {
IRQ_MANAGER todo!()
.lock()
.free_block(block.start, block.len() as u32)
} }
fn msi_register_handler( fn msi_register_handler(
@ -223,22 +79,11 @@ hal_fn_impl! {
msi_id: u32, msi_id: u32,
handler: Box<dyn Fn() + Send + Sync>, handler: Box<dyn Fn() + Send + Sync>,
) -> HalResult { ) -> HalResult {
IRQ_MANAGER todo!()
.lock()
.overwrite_handler(block.start + msi_id, handler)
} }
} }
} }
fn irq57test() {
warn!("irq 57");
// poll_ifaces();
}
fn timer() {
crate::timer::timer_tick();
}
/* /*
fn keyboard() { fn keyboard() {
use pc_keyboard::{DecodedKey, KeyCode}; use pc_keyboard::{DecodedKey, KeyCode};
@ -261,33 +106,3 @@ fn keyboard() {
} }
} }
*/ */
fn irq_enable_raw(irq: u32, vector: u32) {
info!("irq_enable_raw: irq={:#x?}, vector={:#x?}", irq, vector);
let mut ioapic = super::apic::get_ioapic();
ioapic.set_irq_vector(irq as u8, vector as u8);
ioapic.enable(irq as u8, 0)
}
pub(super) fn init() {
// MOUSE.lock().init().unwrap();
// MOUSE.lock().set_on_complete(mouse_on_complete);
unsafe {
init_ioapic();
}
let mut im = IRQ_MANAGER.lock();
im.register_handler(Timer + IRQ_MIN_ID, Box::new(timer))
.ok();
// im.register_handler(Keyboard + IRQ_MIN_ID, Box::new(keyboard));
// im.register_handler(Mouse + IRQ_MIN_ID, Box::new(mouse));
im.register_handler(
COM1 + IRQ_MIN_ID,
Box::new(|| crate::drivers::UART.handle_irq(COM1 as usize)),
)
.ok();
im.register_handler(57u32, Box::new(irq57test)).ok();
// register_handler(Keyboard, Keyboard + IRQ_MIN_ID);
// register_handler(Mouse, Mouse + IRQ_MIN_ID);
irq_enable_raw(COM1, COM1 + IRQ_MIN_ID);
}

View File

@ -1,5 +1,5 @@
mod acpi_table; // mod acpi_table;
mod apic; // mod apic;
mod drivers; mod drivers;
mod trap; mod trap;
@ -15,30 +15,33 @@ pub mod vm;
use x86_64::registers::control::{Cr4, Cr4Flags}; use x86_64::registers::control::{Cr4, Cr4Flags};
pub fn init() { pub fn init() {
apic::init(); drivers::init().unwrap();
drivers::init();
interrupt::init();
fn ap_main() { let stack_fn = |pid: usize| -> usize {
info!("processor {} started", cpu::cpu_id());
unsafe { trapframe::init() };
apic::init();
let ap_fn = crate::KCONFIG.ap_fn;
ap_fn();
}
fn stack_fn(pid: usize) -> usize {
// split and reuse the current stack // split and reuse the current stack
let mut stack: usize; let mut stack: usize;
unsafe { asm!("mov {}, rsp", out(reg) stack) }; unsafe { asm!("mov {}, rsp", out(reg) stack) };
stack -= 0x4000 * pid; stack -= 0x4000 * pid;
stack stack
} };
unsafe { unsafe {
// enable global page // enable global page
Cr4::update(|f| f.insert(Cr4Flags::PAGE_GLOBAL)); Cr4::update(|f| f.insert(Cr4Flags::PAGE_GLOBAL));
// start multi-processors // start multi-processors
x86_smpboot::start_application_processors(ap_main, stack_fn, crate::mem::phys_to_virt); x86_smpboot::start_application_processors(
|| {
init_ap();
let ap_fn = crate::KCONFIG.ap_fn;
ap_fn();
},
stack_fn,
crate::mem::phys_to_virt,
);
} }
} }
pub fn init_ap() {
info!("processor {} started", cpu::cpu_id());
unsafe { trapframe::init() };
zcore_drivers::irq::x86::Apic::init_local_apic_ap();
}

View File

@ -1,32 +1,48 @@
#![allow(dead_code)] #![allow(dead_code)]
#![allow(non_upper_case_globals)]
use trapframe::TrapFrame; use trapframe::TrapFrame;
// Reference: https://wiki.osdev.org/Exceptions // Reference: https://wiki.osdev.org/Exceptions
const DivideError: u8 = 0; const DIVIDE_ERROR: usize = 0;
const Debug: u8 = 1; const DEBUG: usize = 1;
const NonMaskableInterrupt: u8 = 2; const NON_MASKABLE_INTERRUPT: usize = 2;
const Breakpoint: u8 = 3; const BREAKPOINT: usize = 3;
const Overflow: u8 = 4; const OVERFLOW: usize = 4;
const BoundRangeExceeded: u8 = 5; const BOUND_RANGE_EXCEEDED: usize = 5;
const InvalidOpcode: u8 = 6; const INVALID_OPCODE: usize = 6;
const DeviceNotAvailable: u8 = 7; const DEVICE_NOT_AVAILABLE: usize = 7;
const DoubleFault: u8 = 8; const DOUBLE_FAULT: usize = 8;
const CoprocessorSegmentOverrun: u8 = 9; const COPROCESSOR_SEGMENT_OVERRUN: usize = 9;
const InvalidTSS: u8 = 10; const INVALID_TSS: usize = 10;
const SegmentNotPresent: u8 = 11; const SEGMENT_NOT_PRESENT: usize = 11;
const StackSegmentFault: u8 = 12; const STACK_SEGMENT_FAULT: usize = 12;
const GeneralProtectionFault: u8 = 13; const GENERAL_PROTECTION_FAULT: usize = 13;
const PageFault: u8 = 14; const PAGE_FAULT: usize = 14;
const FloatingPointException: u8 = 16; const FLOATING_POINTEXCEPTION: usize = 16;
const AlignmentCheck: u8 = 17; const ALIGNMENT_CHECK: usize = 17;
const MachineCheck: u8 = 18; const MACHINE_CHECK: usize = 18;
const SIMDFloatingPointException: u8 = 19; const SIMD_FLOATING_POINT_EXCEPTION: usize = 19;
const VirtualizationException: u8 = 20; const VIRTUALIZATION_EXCEPTION: usize = 20;
const SecurityException: u8 = 30; const SECURITY_EXCEPTION: usize = 30;
const IRQ0: u8 = 32; // IRQ vectors
pub(super) const X86_INT_BASE: usize = 0x20;
pub(super) const X86_INT_MAX: usize = 0xff;
pub(super) const X86_INT_LOCAL_APIC_BASE: usize = 0xf0;
pub(super) const X86_INT_APIC_SPURIOUS: usize = X86_INT_LOCAL_APIC_BASE + 0x0;
pub(super) const X86_INT_APIC_TIMER: usize = X86_INT_LOCAL_APIC_BASE + 0x1;
pub(super) const X86_INT_APIC_ERROR: usize = X86_INT_LOCAL_APIC_BASE + 0x2;
// ISA IRQ numbers
pub(super) const X86_ISA_IRQ_PIT: usize = 0;
pub(super) const X86_ISA_IRQ_KEYBOARD: usize = 1;
pub(super) const X86_ISA_IRQ_PIC2: usize = 2;
pub(super) const X86_ISA_IRQ_COM2: usize = 3;
pub(super) const X86_ISA_IRQ_COM1: usize = 4;
pub(super) const X86_ISA_IRQ_CMOSRTC: usize = 8;
pub(super) const X86_ISA_IRQ_MOUSE: usize = 12;
pub(super) const X86_ISA_IRQ_IDE: usize = 14;
fn breakpoint() { fn breakpoint() {
panic!("\nEXCEPTION: Breakpoint"); panic!("\nEXCEPTION: Breakpoint");
@ -44,11 +60,11 @@ fn page_fault(tf: &mut TrapFrame) {
#[no_mangle] #[no_mangle]
pub extern "C" fn trap_handler(tf: &mut TrapFrame) { pub extern "C" fn trap_handler(tf: &mut TrapFrame) {
trace!("Interrupt: {:#x} @ CPU{}", tf.trap_num, 0); // TODO 0 should replace in multi-core case trace!("Interrupt: {:#x} @ CPU{}", tf.trap_num, 0); // TODO 0 should replace in multi-core case
match tf.trap_num as u8 { match tf.trap_num {
Breakpoint => breakpoint(), BREAKPOINT => breakpoint(),
DoubleFault => double_fault(tf), DOUBLE_FAULT => double_fault(tf),
PageFault => page_fault(tf), PAGE_FAULT => page_fault(tf),
IRQ0..=63 => crate::interrupt::handle_irq(tf.trap_num as u32), X86_INT_BASE..=X86_INT_MAX => crate::interrupt::handle_irq(tf.trap_num as u32),
_ => panic!("Unhandled interrupt {:x} {:#x?}", tf.trap_num, tf), _ => panic!("Unhandled interrupt {:x} {:#x?}", tf.trap_num, tf),
} }
} }

View File

@ -56,7 +56,7 @@ hal_fn_def! {
/// Is a valid IRQ number. /// Is a valid IRQ number.
pub fn is_valid_irq(vector: u32) -> bool; pub fn is_valid_irq(vector: u32) -> bool;
/// Configure the specified interrupt vector. If it is invoked, it muust be /// Configure the specified interrupt vector. If it is invoked, it must be
/// invoked prior to interrupt registration. /// invoked prior to interrupt registration.
pub fn configure_irq(vector: u32, trig_mode: bool, polarity: bool) -> HalResult; pub fn configure_irq(vector: u32, trig_mode: bool, polarity: bool) -> HalResult;

View File

@ -2,7 +2,7 @@
#![cfg_attr(not(feature = "libos"), no_std)] #![cfg_attr(not(feature = "libos"), no_std)]
#![feature(asm)] #![feature(asm)]
#![deny(warnings)] // #![deny(warnings)]
extern crate alloc; extern crate alloc;
#[macro_use] #[macro_use]

View File

@ -1,87 +0,0 @@
use alloc::boxed::Box;
use bitmap_allocator::{BitAlloc, BitAlloc256};
use core::ops::Range;
use crate::{HalError, HalResult};
pub type IrqHandler = Box<dyn Fn() + Send + Sync>;
const IRQ_COUNT: usize = 256;
pub struct IrqManager {
irq_range: Range<u32>,
table: [Option<IrqHandler>; IRQ_COUNT],
allocator: BitAlloc256,
}
impl IrqManager {
pub fn new(irq_min_id: u32, irq_max_id: u32) -> Self {
const EMPTY_HANDLER: Option<Box<dyn Fn() + Send + Sync>> = None;
let mut allocator = BitAlloc256::DEFAULT;
allocator.insert(irq_min_id as usize..irq_max_id as usize + 1);
Self {
irq_range: irq_min_id..irq_max_id + 1,
table: [EMPTY_HANDLER; IRQ_COUNT],
allocator,
}
}
pub fn alloc_block(&mut self, count: u32) -> HalResult<u32> {
debug_assert!(count.is_power_of_two());
let align_log2 = 31 - count.leading_zeros();
self.allocator
.alloc_contiguous(count as usize, align_log2 as usize)
.map(|start| start as u32)
.ok_or(HalError)
}
pub fn free_block(&mut self, start: u32, count: u32) -> HalResult {
self.allocator
.insert(start as usize..(start + count) as usize);
Ok(())
}
/// Add a handler to IRQ table. Return the specified irq or an allocated irq on success
pub fn register_handler(&mut self, vector: u32, handler: IrqHandler) -> HalResult<u32> {
info!("IRQ add handler {:#x?}", vector);
let vector = if !self.irq_range.contains(&vector) {
// allocate a valid irq number
self.alloc_block(1)?
} else if self.allocator.test(vector as usize) {
self.allocator.remove(vector as usize..vector as usize + 1);
vector
} else {
return Err(HalError);
};
self.table[vector as usize] = Some(handler);
Ok(vector)
}
pub fn unregister_handler(&mut self, vector: u32) -> HalResult {
info!("IRQ remove handler {:#x?}", vector);
if self.allocator.test(vector as usize) {
Err(HalError)
} else {
self.free_block(vector, 1)?;
self.table[vector as usize] = None;
Ok(())
}
}
pub fn overwrite_handler(&mut self, vector: u32, handler: IrqHandler) -> HalResult {
info!("IRQ overwrite handle {:#x?}", vector);
if self.allocator.test(vector as usize) {
Err(HalError)
} else {
self.table[vector as usize] = Some(handler);
Ok(())
}
}
pub fn handle(&self, vector: u32) {
match &self.table[vector as usize] {
Some(f) => f(),
None => panic!("unhandled external IRQ number: {}", vector),
}
}
}

View File

@ -2,7 +2,6 @@
cfg_if! { cfg_if! {
if #[cfg(not(feature = "libos"))] { if #[cfg(not(feature = "libos"))] {
pub(crate) mod irq_manager;
pub(crate) mod page_table; pub(crate) mod page_table;
} }
} }

View File

@ -92,7 +92,7 @@ async fn new_thread(thread: CurrentThread) {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
match cx.trap_num { match cx.trap_num {
0x100 => handle_syscall(&thread, &mut cx.general).await, 0x100 => handle_syscall(&thread, &mut cx.general).await,
0x20..=0x3f => { 0x20..=0xff => {
kernel_hal::interrupt::handle_irq(cx.trap_num as u32); kernel_hal::interrupt::handle_irq(cx.trap_num as u32);
if cx.trap_num == 0x20 { if cx.trap_num == 0x20 {
kernel_hal::thread::yield_now().await; kernel_hal::thread::yield_now().await;

View File

@ -216,7 +216,7 @@ async fn new_thread(thread: CurrentThread) {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
match trap_num { match trap_num {
0x100 => handle_syscall(&thread).await, 0x100 => handle_syscall(&thread).await,
0x20..=0x3f => { 0x20..=0xff => {
kernel_hal::interrupt::handle_irq(trap_num as u32); kernel_hal::interrupt::handle_irq(trap_num as u32);
if trap_num == 0x20 { if trap_num == 0x20 {
EXCEPTIONS_TIMER.add(1); EXCEPTIONS_TIMER.add(1);