Use PCI ECAM in x86 if possible
This commit is contained in:
parent
a09de99c1d
commit
a82d185154
|
|
@ -17,19 +17,28 @@ use crate::PciDeviceLocation;
|
|||
static PCI_ECAM_CFG_SPACE: Once<IoMem> = Once::new();
|
||||
|
||||
pub(crate) fn write32(location: &PciDeviceLocation, offset: u32, value: u32) -> Result<(), Error> {
|
||||
if offset > PCI_ECAM_MAX_OFFSET {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
PCI_ECAM_CFG_SPACE.get().ok_or(Error::IoError)?.write_once(
|
||||
(encode_as_address_offset(location) | (offset & 0xfc)) as usize,
|
||||
(encode_as_address_offset(location) | offset) as usize,
|
||||
&value,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn read32(location: &PciDeviceLocation, offset: u32) -> Result<u32, Error> {
|
||||
if offset > PCI_ECAM_MAX_OFFSET {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
PCI_ECAM_CFG_SPACE
|
||||
.get()
|
||||
.ok_or(Error::IoError)?
|
||||
.read_once((encode_as_address_offset(location) | (offset & 0xfc)) as usize)
|
||||
.read_once((encode_as_address_offset(location) | offset) as usize)
|
||||
}
|
||||
|
||||
/// The maximum offset in the 12-bit configuration space when using [`encode_as_address_offset`].
|
||||
const PCI_ECAM_MAX_OFFSET: u32 = 0xffc;
|
||||
|
||||
/// Encodes the bus, device, and function into an address offset in the PCI MMIO region.
|
||||
fn encode_as_address_offset(location: &PciDeviceLocation) -> u32 {
|
||||
// We only support ECAM here for LoongArch platforms. Offsets are from
|
||||
|
|
|
|||
|
|
@ -13,19 +13,28 @@ use crate::PciDeviceLocation;
|
|||
static PCI_ECAM_CFG_SPACE: Once<IoMem> = Once::new();
|
||||
|
||||
pub(crate) fn write32(location: &PciDeviceLocation, offset: u32, value: u32) -> Result<(), Error> {
|
||||
if offset > PCI_ECAM_MAX_OFFSET {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
PCI_ECAM_CFG_SPACE.get().ok_or(Error::IoError)?.write_once(
|
||||
(encode_as_address_offset(location) | (offset & 0xfc)) as usize,
|
||||
(encode_as_address_offset(location) | offset) as usize,
|
||||
&value,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn read32(location: &PciDeviceLocation, offset: u32) -> Result<u32, Error> {
|
||||
if offset > PCI_ECAM_MAX_OFFSET {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
PCI_ECAM_CFG_SPACE
|
||||
.get()
|
||||
.ok_or(Error::IoError)?
|
||||
.read_once((encode_as_address_offset(location) | (offset & 0xfc)) as usize)
|
||||
.read_once((encode_as_address_offset(location) | offset) as usize)
|
||||
}
|
||||
|
||||
/// The maximum offset in the 12-bit configuration space when using [`encode_as_address_offset`].
|
||||
const PCI_ECAM_MAX_OFFSET: u32 = 0xffc;
|
||||
|
||||
/// Encodes the bus, device, and function into an address offset in the PCI MMIO region.
|
||||
fn encode_as_address_offset(location: &PciDeviceLocation) -> u32 {
|
||||
// We only support ECAM here for RISC-V platforms. Offsets are from
|
||||
|
|
|
|||
|
|
@ -6,8 +6,12 @@ use core::ops::RangeInclusive;
|
|||
|
||||
use ostd::{
|
||||
Error,
|
||||
arch::device::io_port::{ReadWriteAccess, WriteOnlyAccess},
|
||||
io::IoPort,
|
||||
arch::{
|
||||
device::io_port::{ReadWriteAccess, WriteOnlyAccess},
|
||||
kernel::ACPI_INFO,
|
||||
},
|
||||
io::{IoMem, IoPort},
|
||||
mm::VmIoOnce,
|
||||
sync::SpinLock,
|
||||
};
|
||||
use spin::Once;
|
||||
|
|
@ -21,23 +25,66 @@ struct AddressAndDataPort {
|
|||
|
||||
static PCI_PIO_CFG_SPACE: Once<SpinLock<AddressAndDataPort>> = Once::new();
|
||||
|
||||
const BIT32_ALIGN_MASK: u32 = 0xFFFC;
|
||||
static PCI_ECAM_CFG_SPACE: Once<IoMem> = Once::new();
|
||||
|
||||
pub(crate) fn write32(location: &PciDeviceLocation, offset: u32, value: u32) -> Result<(), Error> {
|
||||
let pio = PCI_PIO_CFG_SPACE.get().ok_or(Error::IoError)?.lock();
|
||||
pio.address_port
|
||||
.write(encode_as_port(location) | (offset & BIT32_ALIGN_MASK));
|
||||
pio.data_port.write(value.to_le());
|
||||
Ok(())
|
||||
if let Some(ecam) = PCI_ECAM_CFG_SPACE.get() {
|
||||
if offset > PCI_ECAM_MAX_OFFSET {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
ecam.write_once(
|
||||
(encode_as_address_offset(location) | offset) as usize,
|
||||
&value,
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(pio_ports) = PCI_PIO_CFG_SPACE.get() {
|
||||
if offset > PCI_PIO_MAX_OFFSET {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
let pio = pio_ports.lock();
|
||||
pio.address_port.write(encode_as_port(location) | offset);
|
||||
pio.data_port.write(value.to_le());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Err(Error::IoError)
|
||||
}
|
||||
|
||||
pub(crate) fn read32(location: &PciDeviceLocation, offset: u32) -> Result<u32, Error> {
|
||||
let pio = PCI_PIO_CFG_SPACE.get().ok_or(Error::IoError)?.lock();
|
||||
pio.address_port
|
||||
.write(encode_as_port(location) | (offset & BIT32_ALIGN_MASK));
|
||||
Ok(pio.data_port.read().to_le())
|
||||
if let Some(ecam) = PCI_ECAM_CFG_SPACE.get() {
|
||||
if offset > PCI_ECAM_MAX_OFFSET {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
return ecam.read_once((encode_as_address_offset(location) | offset) as usize);
|
||||
}
|
||||
|
||||
if let Some(pio_ports) = PCI_PIO_CFG_SPACE.get() {
|
||||
if offset > PCI_PIO_MAX_OFFSET {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
let pio = pio_ports.lock();
|
||||
pio.address_port.write(encode_as_port(location) | offset);
|
||||
return Ok(pio.data_port.read().to_le());
|
||||
}
|
||||
|
||||
Err(Error::IoError)
|
||||
}
|
||||
|
||||
/// The maximum offset in the 12-bit configuration space when using [`encode_as_address_offset`].
|
||||
const PCI_ECAM_MAX_OFFSET: u32 = 0xffc;
|
||||
|
||||
/// Encodes the bus, device, and function into an address offset in the PCI MMIO region.
|
||||
fn encode_as_address_offset(location: &PciDeviceLocation) -> u32 {
|
||||
((location.bus as u32) << 20)
|
||||
| ((location.device as u32) << 15)
|
||||
| ((location.function as u32) << 12)
|
||||
}
|
||||
|
||||
/// The maximum offset in the 8-bit configuration space when using [`encode_as_port`].
|
||||
const PCI_PIO_MAX_OFFSET: u32 = 0xfc;
|
||||
|
||||
/// Encodes the bus, device, and function into a port address for use with the PCI I/O port.
|
||||
fn encode_as_port(location: &PciDeviceLocation) -> u32 {
|
||||
// 1 << 31: Configuration enable
|
||||
|
|
@ -51,6 +98,19 @@ fn encode_as_port(location: &PciDeviceLocation) -> u32 {
|
|||
///
|
||||
/// Returns a range for the PCI bus number, or [`None`] if there is no PCI bus.
|
||||
pub(crate) fn init() -> Option<RangeInclusive<u8>> {
|
||||
if let Some(ecam) = ACPI_INFO.get().unwrap().pci_ecam_region.as_ref() {
|
||||
let bus_start = ecam.bus_start;
|
||||
let bus_end = ecam.bus_end;
|
||||
|
||||
let addr_start = ecam.base_address as usize;
|
||||
// Note that the base address always corresponds to the bus number 0, regardless of the
|
||||
// actual value of `bus_start`.
|
||||
let addr_end = addr_start + (bus_end as usize + 1) * (1 << 20);
|
||||
PCI_ECAM_CFG_SPACE.call_once(|| IoMem::acquire(addr_start..addr_end).unwrap());
|
||||
|
||||
return Some(bus_start..=bus_end);
|
||||
}
|
||||
|
||||
// We use `acquire_overlapping` to acquire the port at 0xCF8 because 0xCF9 may be used as a
|
||||
// reset control register in the PIIX4. Although the two ports overlap in their I/O range, they
|
||||
// serve completely different purposes. See
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use acpi::{
|
|||
AcpiHandler, AcpiTables,
|
||||
address::AddressSpace,
|
||||
fadt::{Fadt, IaPcBootArchFlags},
|
||||
mcfg::Mcfg,
|
||||
rsdp::Rsdp,
|
||||
};
|
||||
use log::warn;
|
||||
|
|
@ -85,6 +86,19 @@ pub struct AcpiInfo {
|
|||
pub boot_flags: Option<IaPcBootArchFlags>,
|
||||
/// An I/O port to reset the machine by writing the specified value.
|
||||
pub reset_port_and_val: Option<(u16, u8)>,
|
||||
/// A memory region that is stolen for PCI configuration space.
|
||||
pub pci_ecam_region: Option<PciEcamRegion>,
|
||||
}
|
||||
|
||||
/// A memory region that is stolen for PCI configuration space.
|
||||
#[derive(Debug)]
|
||||
pub struct PciEcamRegion {
|
||||
/// The base address of the memory region.
|
||||
pub base_address: u64,
|
||||
/// The start of the bus number.
|
||||
pub bus_start: u8,
|
||||
/// The end of the bus number.
|
||||
pub bus_end: u8,
|
||||
}
|
||||
|
||||
/// The [`AcpiInfo`] singleton.
|
||||
|
|
@ -95,11 +109,15 @@ pub(in crate::arch) fn init() {
|
|||
century_register: None,
|
||||
boot_flags: None,
|
||||
reset_port_and_val: None,
|
||||
pci_ecam_region: None,
|
||||
};
|
||||
|
||||
if let Some(acpi_tables) = get_acpi_tables()
|
||||
&& let Ok(fadt) = acpi_tables.find_table::<Fadt>()
|
||||
{
|
||||
let Some(acpi_tables) = get_acpi_tables() else {
|
||||
ACPI_INFO.call_once(|| acpi_info);
|
||||
return;
|
||||
};
|
||||
|
||||
if 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);
|
||||
|
|
@ -111,6 +129,18 @@ pub(in crate::arch) fn init() {
|
|||
}
|
||||
};
|
||||
|
||||
if let Ok(mcfg) = acpi_tables.find_table::<Mcfg>()
|
||||
// TODO: Support multiple PCIe segment groups instead of assuming only one
|
||||
// PCIe segment group is in use.
|
||||
&& let Some(mcfg_entry) = mcfg.entries().first()
|
||||
{
|
||||
acpi_info.pci_ecam_region = Some(PciEcamRegion {
|
||||
base_address: mcfg_entry.base_address,
|
||||
bus_start: mcfg_entry.bus_number_start,
|
||||
bus_end: mcfg_entry.bus_number_end,
|
||||
});
|
||||
}
|
||||
|
||||
log::info!("[ACPI]: Collected information {:?}", acpi_info);
|
||||
|
||||
ACPI_INFO.call_once(|| acpi_info);
|
||||
|
|
|
|||
Loading…
Reference in New Issue