Skip to content

Commit

Permalink
feat(x86_64): read ACPI table SMP info (#347)
Browse files Browse the repository at this point in the history
This commit starts on #345 by using the `acpi` crate to read information
from the ACPI tables related to SMP. It adds an `acpi::bringup_smp`
function in `arch::x86_64`, but this function doesn't *currently*
actually bring up application processors. Instead, it just reads data
from the ACPI tables and logs it as a way to check that we are, in fact,
able to find the information we need.

Additionally, I added a new `ArchInfo` struct that contains opaque
architecture-specific data from the bootloader's boot info. This is
passed into the `kernel_start` function, but the cross-platform kernel
code doesn't consume it --- instead, it's just provided so it can be
passed back into `arch_init` once we've done cross-platform setup stuff.
This felt better than sticking the RSDP addr in a global variable or
something...

This is a first step towards #345. Future commits will actually switch
to IOAPIC interrupt handling and then start the application processors.
  • Loading branch information
hawkw committed Oct 14, 2022
1 parent e130ddd commit 4c348fe
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 28 deletions.
28 changes: 20 additions & 8 deletions src/arch/x86_64.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use hal_core::{boot::BootInfo, mem, PAddr, VAddr};
use hal_core::boot::BootInfo;
use hal_x86_64::{cpu, vga};
pub use hal_x86_64::{cpu::entropy::seed_rng, mm, NAME};
use mycelium_util::sync::InitOnce;

mod acpi;
mod bootloader;
mod boot;
mod framebuf;
pub mod interrupt;
mod oops;
pub mod pci;
pub use self::oops::{oops, Oops};
pub use self::{
boot::ArchInfo,
oops::{oops, Oops},
};

#[cfg(test)]
mod tests;
Expand All @@ -25,10 +27,11 @@ pub fn tick_timer() {
#[cfg(target_os = "none")]
bootloader::entry_point!(arch_entry);

pub fn arch_entry(info: &'static mut bootloader::boot_info::BootInfo) -> ! {
pub fn arch_entry(info: &'static mut bootloader::BootInfo) -> ! {
unsafe {
cpu::intrinsics::cli();
}

if let Some(offset) = info.physical_memory_offset.into_option() {
// Safety: i hate everything
unsafe {
Expand All @@ -39,18 +42,27 @@ pub fn arch_entry(info: &'static mut bootloader::boot_info::BootInfo) -> ! {
// lol we're hosed
} */

let boot_info = bootloader::RustbootBootInfo::from_bootloader(info);
crate::kernel_start(&boot_info);
let (boot_info, archinfo) = boot::RustbootBootInfo::from_bootloader(info);
crate::kernel_start(boot_info, archinfo);
}

pub fn arch_init(info: &impl BootInfo) {
pub fn init(info: &impl BootInfo, archinfo: &ArchInfo) {
pci::init_pci();

if let Some(rsdp_addr) = archinfo.rsdp_addr {
acpi::bringup_smp(rsdp_addr)
.expect("failed to bring up application processors! this is bad news!");
} else {
// TODO(eliza): try using MP Table to bringup application processors?
tracing::warn!("no RSDP from bootloader, skipping SMP bringup");
}
}

// TODO(eliza): this is now in arch because it uses the serial port, would be
// nice if that was cross platform...
#[cfg(test)]
pub fn run_tests() {
use hal_x86_64::serial;
let com1 = serial::com1().expect("if we're running tests, there ought to be a serial port");
let mk = || com1.lock();
match mycotest::runner::run_tests(mk) {
Expand Down
73 changes: 70 additions & 3 deletions src/arch/x86_64/acpi.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,54 @@
use acpi::AcpiHandler;
use core::ptr::NonNull;
use hal_core::PAddr;
use acpi::{AcpiError, AcpiHandler, AcpiTables};
use core::{fmt, ptr::NonNull};
use hal_core::{Address, PAddr};
use hal_x86_64::mm;

#[derive(Debug)]
pub enum Error {
Acpi(AcpiError),
Other(&'static str),
}

#[tracing::instrument(err)]
pub fn bringup_smp(rsdp_addr: PAddr) -> Result<(), Error> {
use acpi::platform::interrupt::InterruptModel;
tracing::info!("trying to parse ACPI tables from RSDP...");
let tables = unsafe { AcpiTables::from_rsdp(IdentityMappedAcpiHandler, rsdp_addr.as_usize()) }?;
tracing::info!("found ACPI tables!");

let platform = tables.platform_info()?;
tracing::info!(?platform.power_profile);

let apic = match platform.interrupt_model {
acpi::InterruptModel::Apic(apic) => {
tracing::info!("APIC interrupt model detected");
apic
}
InterruptModel::Unknown => {
return Err(Error::Other(
"MADT does not indicate support for APIC interrupt model!",
));
}
model => {
tracing::warn!(?model, "unknown interrupt model detected");
return Err(Error::Other(
"MADT does not indicate support for APIC interrupt model!",
));
}
};

tracing::debug!(?apic);

let processors = platform
.processor_info
.ok_or(Error::Other("no processor information found in MADT!"))?;

tracing::debug!(?processors.boot_processor);
tracing::debug!(?processors.application_processors);

Ok(())
}

#[derive(Clone)]
struct IdentityMappedAcpiHandler;

Expand All @@ -26,3 +72,24 @@ impl AcpiHandler for IdentityMappedAcpiHandler {
// we don't need to unmap anything, since we didn't map anything. :)
}
}

// === impl Error ===

impl From<AcpiError> for Error {
fn from(inner: AcpiError) -> Self {
Self::Acpi(inner)
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
// format the ACPI error using its `fmt::Debug` implementation,
// since the ACPI crate doesn't implement `fmt::Display` for its
// errors.
// TODO(eliza): add a `Display` impl upstream...
Self::Acpi(inner) => write!(f, "ACPI error: {inner:?}"),
Self::Other(msg) => fmt::Display::fmt(msg, f),
}
}
}
18 changes: 13 additions & 5 deletions src/arch/x86_64/bootloader.rs → src/arch/x86_64/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use super::framebuf::{self, FramebufWriter};
use bootloader::boot_info;
use hal_core::{boot::BootInfo, mem, PAddr, VAddr};
use hal_x86_64::{mm, serial, vga};
use hal_x86_64::{cpu, mm, serial, vga};
use mycelium_util::sync::InitOnce;

#[derive(Debug)]
Expand All @@ -12,6 +12,11 @@ pub struct RustbootBootInfo {
has_framebuffer: bool,
}

#[derive(Debug)]
pub struct ArchInfo {
pub(in crate::arch) rsdp_addr: Option<PAddr>,
}

type MemRegionIter = core::slice::Iter<'static, boot_info::MemoryRegion>;

impl BootInfo for RustbootBootInfo {
Expand Down Expand Up @@ -123,12 +128,15 @@ impl RustbootBootInfo {
)
}

pub(super) fn from_bootloader(inner: &'static mut boot_info::BootInfo) -> Self {
pub(super) fn from_bootloader(inner: &'static mut boot_info::BootInfo) -> (Self, ArchInfo) {
let has_framebuffer = framebuf::init(inner);

Self {
let archinfo = ArchInfo {
rsdp_addr: inner.rsdp_addr.into_option().map(PAddr::from_u64),
};
let bootinfo = Self {
inner,
has_framebuffer,
}
};
(bootinfo, archinfo)
}
}
24 changes: 12 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ mod tests;
#[cfg_attr(target_os = "none", global_allocator)]
static ALLOC: buddy::Alloc<32> = buddy::Alloc::new(32);

pub fn kernel_start(bootinfo: &impl BootInfo) -> ! {
pub fn kernel_start(bootinfo: impl BootInfo, archinfo: crate::arch::ArchInfo) -> ! {
let mut writer = bootinfo.writer();
writeln!(
writer,
Expand Down Expand Up @@ -147,7 +147,7 @@ pub fn kernel_start(bootinfo: &impl BootInfo) -> ! {

// perform arch-specific initialization once we have an allocator and
// tracing.
arch::init(&bootinfo);
arch::init(&bootinfo, &archinfo);

#[cfg(test)]
arch::run_tests();
Expand All @@ -168,16 +168,16 @@ fn kernel_main() -> ! {
})
}

rt::spawn(async move {
loop {
let result = futures_util::try_join! {
spawn_sleep(time::Duration::from_secs(2)),
spawn_sleep(time::Duration::from_secs(5)),
spawn_sleep(time::Duration::from_secs(10)),
};
tracing::info!(?result);
}
});
// rt::spawn(async move {
// loop {
// let result = futures_util::try_join! {
// spawn_sleep(time::Duration::from_secs(2)),
// spawn_sleep(time::Duration::from_secs(5)),
// spawn_sleep(time::Duration::from_secs(10)),
// };
// tracing::info!(?result);
// }
// });

let mut core = rt::Core::new();
loop {
Expand Down

0 comments on commit 4c348fe

Please sign in to comment.