Make it possible to compile acpi system on any arch

This commit is contained in:
Jeremy Soller
2024-10-30 11:43:21 -06:00
parent 939c9567ee
commit 4dd6a26742
11 changed files with 376 additions and 357 deletions
-329
View File
@@ -1,329 +0,0 @@
use core::mem;
use crate::{
memory::{allocate_p2frame, Frame},
paging::{Page, PageFlags, PhysicalAddress, RmmA, RmmArch, VirtualAddress, PAGE_SIZE},
};
use super::{find_sdt, sdt::Sdt};
use core::sync::atomic::{AtomicU8, Ordering};
use crate::{
device::local_apic::the_local_apic,
interrupt,
memory::KernelMapper,
start::{kstart_ap, AP_READY, CPU_COUNT},
};
/// The Multiple APIC Descriptor Table
#[derive(Clone, Copy, Debug)]
pub struct Madt {
sdt: &'static Sdt,
pub local_address: u32,
pub flags: u32,
}
const TRAMPOLINE: usize = 0x8000;
static TRAMPOLINE_DATA: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/trampoline"));
pub static mut MADT: Option<Madt> = None;
pub const FLAG_PCAT: u32 = 1;
impl Madt {
pub fn init() {
let madt_sdt = find_sdt("APIC");
let madt = if madt_sdt.len() == 1 {
Madt::new(madt_sdt[0])
} else {
println!("Unable to find MADT");
return;
};
if let Some(madt) = madt {
// safe because no APs have been started yet.
unsafe { MADT = Some(madt) };
println!(" APIC: {:>08X}: {}", madt.local_address, madt.flags);
let local_apic = unsafe { the_local_apic() };
let me = local_apic.id() as u8;
if local_apic.x2 {
println!(" X2APIC {}", me);
} else {
println!(" XAPIC {}: {:>08X}", me, local_apic.address);
}
if cfg!(feature = "multi_core") {
// Map trampoline
let trampoline_frame = Frame::containing(PhysicalAddress::new(TRAMPOLINE));
let trampoline_page = Page::containing_address(VirtualAddress::new(TRAMPOLINE));
let (result, page_table_physaddr) = unsafe {
//TODO: do not have writable and executable!
let mut mapper = KernelMapper::lock();
let result = mapper
.get_mut()
.expect("expected kernel page table not to be recursively locked while initializing MADT")
.map_phys(trampoline_page.start_address(), trampoline_frame.base(), PageFlags::new().execute(true).write(true))
.expect("failed to map trampoline");
(result, mapper.table().phys().data())
};
result.flush();
// Write trampoline, make sure TRAMPOLINE page is free for use
for i in 0..TRAMPOLINE_DATA.len() {
unsafe {
(*((TRAMPOLINE as *mut u8).add(i) as *const AtomicU8))
.store(TRAMPOLINE_DATA[i], Ordering::SeqCst);
}
}
for madt_entry in madt.iter() {
println!(" {:?}", madt_entry);
match madt_entry {
MadtEntry::LocalApic(ap_local_apic) => {
if ap_local_apic.id == me {
println!(" This is my local APIC");
} else {
if ap_local_apic.flags & 1 == 1 {
// Increase CPU ID
CPU_COUNT.fetch_add(1, Ordering::SeqCst);
// Allocate a stack
let stack_start = allocate_p2frame(4)
.expect("no more frames in acpi stack_start")
.base()
.data()
+ crate::PHYS_OFFSET;
let stack_end = stack_start + (PAGE_SIZE << 4);
let ap_ready = (TRAMPOLINE + 8) as *mut u64;
let ap_cpu_id = unsafe { ap_ready.add(1) };
let ap_page_table = unsafe { ap_ready.add(2) };
let ap_stack_start = unsafe { ap_ready.add(3) };
let ap_stack_end = unsafe { ap_ready.add(4) };
let ap_code = unsafe { ap_ready.add(5) };
// Set the ap_ready to 0, volatile
unsafe {
ap_ready.write(0);
ap_cpu_id.write(ap_local_apic.id.into());
ap_page_table.write(page_table_physaddr as u64);
ap_stack_start.write(stack_start as u64);
ap_stack_end.write(stack_end as u64);
ap_code.write(kstart_ap as u64);
// TODO: Is this necessary (this fence)?
core::arch::asm!("");
};
AP_READY.store(false, Ordering::SeqCst);
print!(" AP {}:", ap_local_apic.id);
// Send INIT IPI
{
let mut icr = 0x4500;
if local_apic.x2 {
icr |= (ap_local_apic.id as u64) << 32;
} else {
icr |= (ap_local_apic.id as u64) << 56;
}
print!(" IPI...");
local_apic.set_icr(icr);
}
// Send START IPI
{
//Start at 0x0800:0000 => 0x8000. Hopefully the bootloader code is still there
let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
let mut icr = 0x4600 | ap_segment as u64;
if local_apic.x2 {
icr |= (ap_local_apic.id as u64) << 32;
} else {
icr |= (ap_local_apic.id as u64) << 56;
}
print!(" SIPI...");
local_apic.set_icr(icr);
}
// Wait for trampoline ready
print!(" Wait...");
while unsafe {
(*ap_ready.cast::<AtomicU8>()).load(Ordering::SeqCst)
} == 0
{
interrupt::pause();
}
print!(" Trampoline...");
while !AP_READY.load(Ordering::SeqCst) {
interrupt::pause();
}
println!(" Ready");
unsafe {
RmmA::invalidate_all();
}
} else {
println!(" CPU Disabled");
}
}
}
_ => (),
}
}
// Unmap trampoline
let (_frame, _, flush) = unsafe {
KernelMapper::lock()
.get_mut()
.expect("expected kernel page table not to be recursively locked while initializing MADT")
.unmap_phys(trampoline_page.start_address(), true)
.expect("failed to unmap trampoline page")
};
flush.flush();
}
}
}
pub fn new(sdt: &'static Sdt) -> Option<Madt> {
if &sdt.signature == b"APIC" && sdt.data_len() >= 8 {
//Not valid if no local address and flags
let local_address = unsafe { (sdt.data_address() as *const u32).read_unaligned() };
let flags = unsafe {
(sdt.data_address() as *const u32)
.offset(1)
.read_unaligned()
};
Some(Madt {
sdt,
local_address,
flags,
})
} else {
None
}
}
pub fn iter(&self) -> MadtIter {
MadtIter {
sdt: self.sdt,
i: 8, // Skip local controller address and flags
}
}
}
/// MADT Local APIC
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtLocalApic {
/// Processor ID
pub processor: u8,
/// Local APIC ID
pub id: u8,
/// Flags. 1 means that the processor is enabled
pub flags: u32,
}
/// MADT I/O APIC
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtIoApic {
/// I/O APIC ID
pub id: u8,
/// reserved
_reserved: u8,
/// I/O APIC address
pub address: u32,
/// Global system interrupt base
pub gsi_base: u32,
}
/// MADT Interrupt Source Override
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtIntSrcOverride {
/// Bus Source
pub bus_source: u8,
/// IRQ Source
pub irq_source: u8,
/// Global system interrupt base
pub gsi_base: u32,
/// Flags
pub flags: u16,
}
/// MADT Entries
#[derive(Debug)]
pub enum MadtEntry {
LocalApic(&'static MadtLocalApic),
InvalidLocalApic(usize),
IoApic(&'static MadtIoApic),
InvalidIoApic(usize),
IntSrcOverride(&'static MadtIntSrcOverride),
InvalidIntSrcOverride(usize),
Unknown(u8),
}
pub struct MadtIter {
sdt: &'static Sdt,
i: usize,
}
impl Iterator for MadtIter {
type Item = MadtEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.i + 1 < self.sdt.data_len() {
let entry_type = unsafe { *(self.sdt.data_address() as *const u8).add(self.i) };
let entry_len =
unsafe { *(self.sdt.data_address() as *const u8).add(self.i + 1) } as usize;
if self.i + entry_len <= self.sdt.data_len() {
let item = match entry_type {
0 => {
if entry_len == mem::size_of::<MadtLocalApic>() + 2 {
MadtEntry::LocalApic(unsafe {
&*((self.sdt.data_address() + self.i + 2) as *const MadtLocalApic)
})
} else {
MadtEntry::InvalidLocalApic(entry_len)
}
}
1 => {
if entry_len == mem::size_of::<MadtIoApic>() + 2 {
MadtEntry::IoApic(unsafe {
&*((self.sdt.data_address() + self.i + 2) as *const MadtIoApic)
})
} else {
MadtEntry::InvalidIoApic(entry_len)
}
}
2 => {
if entry_len == mem::size_of::<MadtIntSrcOverride>() + 2 {
MadtEntry::IntSrcOverride(unsafe {
&*((self.sdt.data_address() + self.i + 2)
as *const MadtIntSrcOverride)
})
} else {
MadtEntry::InvalidIntSrcOverride(entry_len)
}
}
_ => MadtEntry::Unknown(entry_type),
};
self.i += entry_len;
Some(item)
} else {
None
}
} else {
None
}
}
}
+5
View File
@@ -0,0 +1,5 @@
use super::Madt;
pub(super) fn init(_madt: Madt) {
log::warn!("MADT not yet handled on this platform");
}
+157
View File
@@ -0,0 +1,157 @@
use core::sync::atomic::{AtomicU8, Ordering};
use crate::{
device::local_apic::the_local_apic,
interrupt,
memory::{allocate_p2frame, Frame, KernelMapper},
paging::{Page, PageFlags, PhysicalAddress, RmmA, RmmArch, VirtualAddress, PAGE_SIZE},
start::{kstart_ap, AP_READY, CPU_COUNT},
};
use super::{Madt, MadtEntry};
const TRAMPOLINE: usize = 0x8000;
static TRAMPOLINE_DATA: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/trampoline"));
pub(super) fn init(madt: Madt) {
let local_apic = unsafe { the_local_apic() };
let me = local_apic.id() as u8;
if local_apic.x2 {
println!(" X2APIC {}", me);
} else {
println!(" XAPIC {}: {:>08X}", me, local_apic.address);
}
if cfg!(feature = "multi_core") {
// Map trampoline
let trampoline_frame = Frame::containing(PhysicalAddress::new(TRAMPOLINE));
let trampoline_page = Page::containing_address(VirtualAddress::new(TRAMPOLINE));
let (result, page_table_physaddr) = unsafe {
//TODO: do not have writable and executable!
let mut mapper = KernelMapper::lock();
let result = mapper
.get_mut()
.expect("expected kernel page table not to be recursively locked while initializing MADT")
.map_phys(trampoline_page.start_address(), trampoline_frame.base(), PageFlags::new().execute(true).write(true))
.expect("failed to map trampoline");
(result, mapper.table().phys().data())
};
result.flush();
// Write trampoline, make sure TRAMPOLINE page is free for use
for i in 0..TRAMPOLINE_DATA.len() {
unsafe {
(*((TRAMPOLINE as *mut u8).add(i) as *const AtomicU8))
.store(TRAMPOLINE_DATA[i], Ordering::SeqCst);
}
}
for madt_entry in madt.iter() {
println!(" {:?}", madt_entry);
match madt_entry {
MadtEntry::LocalApic(ap_local_apic) => {
if ap_local_apic.id == me {
println!(" This is my local APIC");
} else {
if ap_local_apic.flags & 1 == 1 {
// Increase CPU ID
CPU_COUNT.fetch_add(1, Ordering::SeqCst);
// Allocate a stack
let stack_start = allocate_p2frame(4)
.expect("no more frames in acpi stack_start")
.base()
.data()
+ crate::PHYS_OFFSET;
let stack_end = stack_start + (PAGE_SIZE << 4);
let ap_ready = (TRAMPOLINE + 8) as *mut u64;
let ap_cpu_id = unsafe { ap_ready.add(1) };
let ap_page_table = unsafe { ap_ready.add(2) };
let ap_stack_start = unsafe { ap_ready.add(3) };
let ap_stack_end = unsafe { ap_ready.add(4) };
let ap_code = unsafe { ap_ready.add(5) };
// Set the ap_ready to 0, volatile
unsafe {
ap_ready.write(0);
ap_cpu_id.write(ap_local_apic.id.into());
ap_page_table.write(page_table_physaddr as u64);
ap_stack_start.write(stack_start as u64);
ap_stack_end.write(stack_end as u64);
ap_code.write(kstart_ap as u64);
// TODO: Is this necessary (this fence)?
core::arch::asm!("");
};
AP_READY.store(false, Ordering::SeqCst);
print!(" AP {}:", ap_local_apic.id);
// Send INIT IPI
{
let mut icr = 0x4500;
if local_apic.x2 {
icr |= (ap_local_apic.id as u64) << 32;
} else {
icr |= (ap_local_apic.id as u64) << 56;
}
print!(" IPI...");
local_apic.set_icr(icr);
}
// Send START IPI
{
//Start at 0x0800:0000 => 0x8000. Hopefully the bootloader code is still there
let ap_segment = (TRAMPOLINE >> 12) & 0xFF;
let mut icr = 0x4600 | ap_segment as u64;
if local_apic.x2 {
icr |= (ap_local_apic.id as u64) << 32;
} else {
icr |= (ap_local_apic.id as u64) << 56;
}
print!(" SIPI...");
local_apic.set_icr(icr);
}
// Wait for trampoline ready
print!(" Wait...");
while unsafe { (*ap_ready.cast::<AtomicU8>()).load(Ordering::SeqCst) }
== 0
{
interrupt::pause();
}
print!(" Trampoline...");
while !AP_READY.load(Ordering::SeqCst) {
interrupt::pause();
}
println!(" Ready");
unsafe {
RmmA::invalidate_all();
}
} else {
println!(" CPU Disabled");
}
}
}
_ => (),
}
}
// Unmap trampoline
let (_frame, _, flush) = unsafe {
KernelMapper::lock()
.get_mut()
.expect("expected kernel page table not to be recursively locked while initializing MADT")
.unmap_phys(trampoline_page.start_address(), true)
.expect("failed to unmap trampoline page")
};
flush.flush();
}
}
+180
View File
@@ -0,0 +1,180 @@
use core::mem;
use super::{find_sdt, sdt::Sdt};
/// The Multiple APIC Descriptor Table
#[derive(Clone, Copy, Debug)]
pub struct Madt {
sdt: &'static Sdt,
pub local_address: u32,
pub flags: u32,
}
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[path = "arch/x86.rs"]
mod arch;
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
#[path = "arch/other.rs"]
mod arch;
pub static mut MADT: Option<Madt> = None;
pub const FLAG_PCAT: u32 = 1;
impl Madt {
pub fn init() {
let madt_sdt = find_sdt("APIC");
let madt = if madt_sdt.len() == 1 {
Madt::new(madt_sdt[0])
} else {
println!("Unable to find MADT");
return;
};
if let Some(madt) = madt {
// safe because no APs have been started yet.
unsafe { MADT = Some(madt) };
println!(" APIC: {:>08X}: {}", madt.local_address, madt.flags);
arch::init(madt);
}
}
pub fn new(sdt: &'static Sdt) -> Option<Madt> {
if &sdt.signature == b"APIC" && sdt.data_len() >= 8 {
//Not valid if no local address and flags
let local_address = unsafe { (sdt.data_address() as *const u32).read_unaligned() };
let flags = unsafe {
(sdt.data_address() as *const u32)
.offset(1)
.read_unaligned()
};
Some(Madt {
sdt,
local_address,
flags,
})
} else {
None
}
}
pub fn iter(&self) -> MadtIter {
MadtIter {
sdt: self.sdt,
i: 8, // Skip local controller address and flags
}
}
}
/// MADT Local APIC
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtLocalApic {
/// Processor ID
pub processor: u8,
/// Local APIC ID
pub id: u8,
/// Flags. 1 means that the processor is enabled
pub flags: u32,
}
/// MADT I/O APIC
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtIoApic {
/// I/O APIC ID
pub id: u8,
/// reserved
_reserved: u8,
/// I/O APIC address
pub address: u32,
/// Global system interrupt base
pub gsi_base: u32,
}
/// MADT Interrupt Source Override
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MadtIntSrcOverride {
/// Bus Source
pub bus_source: u8,
/// IRQ Source
pub irq_source: u8,
/// Global system interrupt base
pub gsi_base: u32,
/// Flags
pub flags: u16,
}
/// MADT Entries
#[derive(Debug)]
pub enum MadtEntry {
LocalApic(&'static MadtLocalApic),
InvalidLocalApic(usize),
IoApic(&'static MadtIoApic),
InvalidIoApic(usize),
IntSrcOverride(&'static MadtIntSrcOverride),
InvalidIntSrcOverride(usize),
Unknown(u8),
}
pub struct MadtIter {
sdt: &'static Sdt,
i: usize,
}
impl Iterator for MadtIter {
type Item = MadtEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.i + 1 < self.sdt.data_len() {
let entry_type = unsafe { *(self.sdt.data_address() as *const u8).add(self.i) };
let entry_len =
unsafe { *(self.sdt.data_address() as *const u8).add(self.i + 1) } as usize;
if self.i + entry_len <= self.sdt.data_len() {
let item = match entry_type {
0 => {
if entry_len == mem::size_of::<MadtLocalApic>() + 2 {
MadtEntry::LocalApic(unsafe {
&*((self.sdt.data_address() + self.i + 2) as *const MadtLocalApic)
})
} else {
MadtEntry::InvalidLocalApic(entry_len)
}
}
1 => {
if entry_len == mem::size_of::<MadtIoApic>() + 2 {
MadtEntry::IoApic(unsafe {
&*((self.sdt.data_address() + self.i + 2) as *const MadtIoApic)
})
} else {
MadtEntry::InvalidIoApic(entry_len)
}
}
2 => {
if entry_len == mem::size_of::<MadtIntSrcOverride>() + 2 {
MadtEntry::IntSrcOverride(unsafe {
&*((self.sdt.data_address() + self.i + 2)
as *const MadtIntSrcOverride)
})
} else {
MadtEntry::InvalidIntSrcOverride(entry_len)
}
}
_ => MadtEntry::Unknown(entry_type),
};
self.i += entry_len;
Some(item)
} else {
None
}
} else {
None
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ use spin::MutexGuard;
use crate::log::{Log, LOG};
#[cfg(feature = "serial_debug")]
use super::device::serial::{COM1, SerialKind};
use super::device::serial::{SerialKind, COM1};
#[cfg(feature = "graphical_debug")]
use crate::devices::graphical_debug::{DebugDisplay, DEBUG_DISPLAY};
+1 -4
View File
@@ -43,10 +43,7 @@ impl GenericInterruptController {
}
}
pub fn parse(fdt: &Fdt) -> Result<(usize, usize, usize, usize)> {
if let Some(node) = fdt.find_compatible(&[
"arm,cortex-a15-gic",
"arm,gic-400",
]) {
if let Some(node) = fdt.find_compatible(&["arm,cortex-a15-gic", "arm,gic-400"]) {
return GenericInterruptController::parse_inner(&node);
} else {
return Err(Error::new(EINVAL));
+10 -13
View File
@@ -16,26 +16,23 @@ impl InterruptHandler for Null {
impl InterruptController for Null {
fn irq_init(
&mut self,
fdt: &Fdt,
irq_desc: &mut [IrqDesc; 1024],
ic_idx: usize,
irq_idx: &mut usize,
_fdt: &Fdt,
_irq_desc: &mut [IrqDesc; 1024],
_ic_idx: usize,
_irq_idx: &mut usize,
) -> Result<()> {
Ok(())
}
fn irq_ack(&mut self) -> u32 {
unimplemented!()
}
fn irq_eoi(&mut self, irq_num: u32) {
}
fn irq_enable(&mut self, irq_num: u32) {
}
fn irq_disable(&mut self, irq_num: u32) {
}
fn irq_xlate(&self, irq_data: &[u32; 3]) -> Result<usize> {
fn irq_eoi(&mut self, _irq_num: u32) {}
fn irq_enable(&mut self, _irq_num: u32) {}
fn irq_disable(&mut self, _irq_num: u32) {}
fn irq_xlate(&self, _irq_data: &[u32; 3]) -> Result<usize> {
Err(Error::new(EINVAL))
}
fn irq_to_virq(&self, hwirq: u32) -> Option<usize> {
fn irq_to_virq(&self, _hwirq: u32) -> Option<usize> {
None
}
}
}
+11 -7
View File
@@ -1,7 +1,6 @@
use alloc::boxed::Box;
use spin::Mutex;
use crate::{
arch::device::irqchip::ic_for_chip,
device::uart_pl011,
@@ -28,8 +27,8 @@ impl SerialKind {
pub fn enable_irq(&mut self) {
//TODO: implement for NS16550
match self {
Self::Ns16550u8(_) => {},
Self::Ns16550u32(_) => {},
Self::Ns16550u8(_) => {}
Self::Ns16550u32(_) => {}
Self::Pl011(inner) => inner.enable_irq(),
}
}
@@ -42,13 +41,13 @@ impl SerialKind {
debug_input(c);
}
debug_notify();
},
}
Self::Ns16550u32(inner) => {
while let Some(c) = inner.receive() {
debug_input(c);
}
debug_notify();
},
}
Self::Pl011(inner) => inner.receive(),
}
}
@@ -105,9 +104,14 @@ pub unsafe fn init_early(dtb: &Fdt) {
Some(serial) => {
info!("UART {:?} at {:#X} size {:#X}", compatible, virt, size);
*COM1.lock() = Some(serial);
},
}
None => {
log::warn!("UART {:?} at {:#X} size {:#X}: no driver found", compatible, virt, size);
log::warn!(
"UART {:?} at {:#X} size {:#X}: no driver found",
compatible,
virt,
size
);
}
}
}
+9
View File
@@ -78,3 +78,12 @@ impl Iterator for PageIter {
}
}
}
/// Round down to the nearest multiple of page size
pub fn round_down_pages(number: usize) -> usize {
number - number % PAGE_SIZE
}
/// Round up to the nearest multiple of page size
pub fn round_up_pages(number: usize) -> usize {
round_down_pages(number + PAGE_SIZE - 1)
}
+1 -1
View File
@@ -90,7 +90,7 @@ use crate::arch::*;
mod allocator;
/// ACPI table parsing
#[cfg(all(feature = "acpi", any(target_arch = "x86", target_arch = "x86_64")))]
#[cfg(feature = "acpi")]
#[allow(dead_code)] // TODO
mod acpi;
+1 -2
View File
@@ -8,7 +8,6 @@ use core::{
};
use alloc::{collections::BTreeMap, string::String, vec::Vec};
use log::{debug, info};
use spin::{Mutex, Once, RwLock};
use syscall::dirent::{DirEntry, DirentBuf, DirentKind};
@@ -187,7 +186,7 @@ impl IrqScheme {
let irq_number = IRQ_CHIP
.irq_xlate(ic_idx, &addr)
.or(Err(Error::new(ENOENT)))?;
debug!("open_phandle_irq virq={}", irq_number);
log::debug!("open_phandle_irq virq={}", irq_number);
if flags & O_STAT == 0 {
if is_reserved(LogicalCpuId::new(0), irq_number as u8) {
return Err(Error::new(EEXIST));