Check the existence of i8042 and RTC CMOS
This commit is contained in:
parent
9a8e6fd372
commit
668876aeee
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
use bitflags::bitflags;
|
||||
use ostd::{
|
||||
arch::device::io_port::ReadWriteAccess,
|
||||
arch::{device::io_port::ReadWriteAccess, kernel::ACPI_INFO},
|
||||
io::IoPort,
|
||||
sync::{LocalIrqDisabled, SpinLock},
|
||||
};
|
||||
|
|
@ -112,8 +112,17 @@ const MAX_WAITING_COUNT: usize = 64;
|
|||
|
||||
impl I8042Controller {
|
||||
fn new() -> Result<Self, I8042ControllerError> {
|
||||
// TODO: Check the flags in the ACPI table to determine if the PS/2 controller exists. See:
|
||||
// <https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#ia-pc-boot-architecture-flags>.
|
||||
if ACPI_INFO
|
||||
.get()
|
||||
.unwrap()
|
||||
.boot_flags
|
||||
.is_some_and(|flags| !flags.motherboard_implements_8042())
|
||||
{
|
||||
// The PS/2 controller does not exist. See:
|
||||
// <https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#ia-pc-boot-architecture-flags>.
|
||||
return Err(I8042ControllerError::NotPresent);
|
||||
}
|
||||
|
||||
let controller = Self {
|
||||
data_port: IoPort::acquire(0x60).unwrap(),
|
||||
status_or_command_port: IoPort::acquire(0x64).unwrap(),
|
||||
|
|
@ -212,6 +221,7 @@ impl I8042Controller {
|
|||
/// Errors that can occur when initializing the i8042 controller.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(super) enum I8042ControllerError {
|
||||
NotPresent,
|
||||
ControllerTestFailed,
|
||||
FirstPortTestFailed,
|
||||
SecondPortTestFailed,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,22 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! CMOS RTC.
|
||||
//!
|
||||
//! "CMOS" is a tiny bit of very low power static memory that lives on the same chip as the
|
||||
//! Real-Time Clock (RTC).
|
||||
//!
|
||||
//! According to the Linux implementation, in x86, if the CMOS/RTC is present at the legacy
|
||||
//! addresses (I/O Ports 0x70 and 0x71), then it is an MC146818 CMOS/RTC. Therefore, in this
|
||||
//! module, the register addresses and data interpretation are based on the MC146818 datasheet.
|
||||
//!
|
||||
//! Reference:
|
||||
//! <https://elixir.bootlin.com/linux/v6.17.5/source/arch/x86/kernel/rtc.c#L69>
|
||||
//! <https://www.scs.stanford.edu/23wi-cs212/pintos/specs/mc146818a.pdf>
|
||||
|
||||
use core::num::NonZeroU8;
|
||||
|
||||
use log::warn;
|
||||
use ostd::{arch::device::io_port::{ReadWriteAccess, WriteOnlyAccess}, io::IoPort, sync::SpinLock};
|
||||
use ostd::{arch::{device::io_port::{ReadWriteAccess, WriteOnlyAccess}, kernel::ACPI_INFO}, io::IoPort, sync::SpinLock};
|
||||
|
||||
use crate::SystemTime;
|
||||
use super::Driver;
|
||||
|
|
@ -27,6 +40,15 @@ impl Driver for RtcCmos {
|
|||
const IOPORT_SEL: u16 = 0x70;
|
||||
const IOPORT_VAL: u16 = 0x71;
|
||||
|
||||
let acpi_info = ACPI_INFO.get().unwrap();
|
||||
|
||||
if acpi_info.boot_flags.is_some_and(|flags| flags.use_time_and_alarm_namespace_for_rtc()) {
|
||||
// "If set, indicates that the CMOS RTC is either not implemented, or does not exist at
|
||||
// the legacy addresses". See:
|
||||
// <https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#ia-pc-boot-architecture-flags>.
|
||||
return None;
|
||||
}
|
||||
|
||||
let (io_sel, io_val) = match (IoPort::acquire(IOPORT_SEL), IoPort::acquire(IOPORT_VAL)) {
|
||||
(Ok(io_sel), Ok(io_val)) => (io_sel, io_val),
|
||||
_ => {
|
||||
|
|
@ -35,7 +57,7 @@ impl Driver for RtcCmos {
|
|||
}
|
||||
};
|
||||
|
||||
let century_register = ostd::arch::device::cmos::century_register().and_then(NonZeroU8::new);
|
||||
let century_register = acpi_info.century_register;
|
||||
|
||||
let mut access = CmosAccess {
|
||||
io_sel,
|
||||
|
|
@ -44,6 +66,14 @@ impl Driver for RtcCmos {
|
|||
};
|
||||
let status_b = access.read_status_b();
|
||||
|
||||
// Ideally, the absence of the CMOS RTC should be reported in the ACPI tables (We've
|
||||
// checked the flag above). However, the ACPI tables are sometimes unreliable (e.g., QEMU
|
||||
// never sets the flag), so we need to perform additional checks.
|
||||
if !access.check_presence() {
|
||||
warn!("CMOS RTC reports unexpected status, ignoring this device");
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
access: SpinLock::new(access),
|
||||
status_b,
|
||||
|
|
@ -72,6 +102,8 @@ enum Register {
|
|||
|
||||
StatusA = 0x0A,
|
||||
StatusB = 0x0B,
|
||||
// `StatusC` is not used.
|
||||
StatusD = 0x0D,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
|
|
@ -94,6 +126,15 @@ bitflags::bitflags! {
|
|||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
struct StatusD: u8 {
|
||||
/// The valid RAM and time (VRT) bit.
|
||||
///
|
||||
/// This bit is set when the RAM and time are valid.
|
||||
const VRT = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
impl CmosAccess {
|
||||
pub(self) fn read_register(&mut self, reg: Register) -> u8 {
|
||||
self.read_register_impl(reg as u8)
|
||||
|
|
@ -111,6 +152,12 @@ impl CmosAccess {
|
|||
StatusB::from_bits_truncate(self.read_register_impl(Register::StatusB as u8))
|
||||
}
|
||||
|
||||
pub(self) fn check_presence(&mut self) -> bool {
|
||||
// If a working CMOS RTC is present, `VRT` should be set and all other reserved bits should
|
||||
// not be set.
|
||||
self.read_register_impl(Register::StatusD as u8) == StatusD::VRT.bits()
|
||||
}
|
||||
|
||||
fn read_register_impl(&mut self, reg: u8) -> u8 {
|
||||
self.io_sel.write(reg);
|
||||
self.io_val.read()
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ macro_rules! declare_rtc_drivers {
|
|||
}
|
||||
)*
|
||||
|
||||
log::warn!("No RTC device found, falling back to a dummy RTC.");
|
||||
log::warn!("No RTC device found, falling back to a dummy RTC");
|
||||
|
||||
Arc::new(RtcDummy)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Device-related APIs.
|
||||
//! This module mainly contains the APIs that should exposed to the device driver like PCI, RTC
|
||||
|
||||
pub mod io_port;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Device-related APIs.
|
||||
//! This module mainly contains the APIs that should exposed to the device driver like PCI, RTC
|
||||
|
||||
pub mod io_port;
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Provides CMOS information.
|
||||
//!
|
||||
//! "CMOS" is a tiny bit of very low power static memory that lives on the same chip as the
|
||||
//! Real-Time Clock (RTC).
|
||||
//!
|
||||
//! Reference: <https://wiki.osdev.org/CMOS>
|
||||
//!
|
||||
|
||||
use acpi::fadt::Fadt;
|
||||
|
||||
use crate::arch::kernel::acpi::get_acpi_tables;
|
||||
|
||||
/// Gets the century register location.
|
||||
///
|
||||
/// This function is used to get the century value from the Real-Time Clock (RTC).
|
||||
pub fn century_register() -> Option<u8> {
|
||||
let acpi_tables = get_acpi_tables()?;
|
||||
match acpi_tables.find_table::<Fadt>() {
|
||||
Ok(fadt) => Some(fadt.century),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Device-related APIs.
|
||||
//! This module mainly contains the APIs that should exposed to the device driver like PCI, RTC
|
||||
|
||||
pub mod cmos;
|
||||
pub mod io_port;
|
||||
pub mod serial;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub mod dmar;
|
||||
pub mod remapping;
|
||||
pub(in crate::arch) mod dmar;
|
||||
pub(in crate::arch) mod remapping;
|
||||
|
||||
use core::ptr::NonNull;
|
||||
use core::{num::NonZeroU8, ptr::NonNull};
|
||||
|
||||
use acpi::{rsdp::Rsdp, AcpiHandler, AcpiTables};
|
||||
use acpi::{
|
||||
fadt::{Fadt, IaPcBootArchFlags},
|
||||
rsdp::Rsdp,
|
||||
AcpiHandler, AcpiTables,
|
||||
};
|
||||
use log::warn;
|
||||
use spin::Once;
|
||||
|
||||
use crate::{
|
||||
boot::{self, BootloaderAcpiArg},
|
||||
|
|
@ -14,7 +19,7 @@ use crate::{
|
|||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AcpiMemoryHandler {}
|
||||
pub(crate) struct AcpiMemoryHandler {}
|
||||
|
||||
impl AcpiHandler for AcpiMemoryHandler {
|
||||
unsafe fn map_physical_region<T>(
|
||||
|
|
@ -65,3 +70,38 @@ pub(crate) fn get_acpi_tables() -> Option<AcpiTables<AcpiMemoryHandler>> {
|
|||
|
||||
Some(acpi_tables)
|
||||
}
|
||||
|
||||
/// The platform information provided by the ACPI tables.
|
||||
///
|
||||
/// Currently, this structure contains only a limited set of fields, far fewer than those in all
|
||||
/// ACPI tables. However, the goal is to expand it properly to keep the simplicity of the OSTD code
|
||||
/// while enabling OSTD users to safely retrieve information from the ACPI tables.
|
||||
#[derive(Debug)]
|
||||
pub struct AcpiInfo {
|
||||
/// The RTC CMOS RAM index to the century of data value; the "CENTURY" field in the FADT.
|
||||
pub century_register: Option<NonZeroU8>,
|
||||
/// IA-PC Boot Architecture Flags; the "IAPC_BOOT_ARCH" field in the FADT.
|
||||
pub boot_flags: Option<IaPcBootArchFlags>,
|
||||
}
|
||||
|
||||
/// The [`AcpiInfo`] singleton.
|
||||
pub static ACPI_INFO: Once<AcpiInfo> = Once::new();
|
||||
|
||||
pub(in crate::arch) fn init() {
|
||||
let mut acpi_info = AcpiInfo {
|
||||
century_register: None,
|
||||
boot_flags: None,
|
||||
};
|
||||
|
||||
if let Some(acpi_tables) = get_acpi_tables()
|
||||
&& let Ok(fadt) = acpi_tables.find_table::<Fadt>()
|
||||
{
|
||||
// A zero means that the century register does not exist.
|
||||
acpi_info.century_register = NonZeroU8::new(fadt.century);
|
||||
acpi_info.boot_flags = Some(fadt.iapc_boot_arch);
|
||||
};
|
||||
|
||||
log::info!("[ACPI]: Collected information {:?}", acpi_info);
|
||||
|
||||
ACPI_INFO.call_once(|| acpi_info);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,3 +8,5 @@
|
|||
pub(super) mod acpi;
|
||||
pub(super) mod apic;
|
||||
pub(super) mod tsc;
|
||||
|
||||
pub use acpi::{AcpiInfo, ACPI_INFO};
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub mod device;
|
|||
pub(crate) mod io;
|
||||
pub(crate) mod iommu;
|
||||
pub mod irq;
|
||||
mod kernel;
|
||||
pub mod kernel;
|
||||
pub(crate) mod mm;
|
||||
pub mod qemu;
|
||||
pub(crate) mod serial;
|
||||
|
|
@ -65,6 +65,8 @@ pub(crate) unsafe fn late_init_on_bsp() {
|
|||
kernel::tsc::init_tsc_freq();
|
||||
timer::init_on_bsp();
|
||||
|
||||
kernel::acpi::init();
|
||||
|
||||
// SAFETY: We're on the BSP and we're ready to boot all APs.
|
||||
unsafe { crate::boot::smp::boot_all_aps() };
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue