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();
|
static PCI_ECAM_CFG_SPACE: Once<IoMem> = Once::new();
|
||||||
|
|
||||||
pub(crate) fn write32(location: &PciDeviceLocation, offset: u32, value: u32) -> Result<(), Error> {
|
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(
|
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,
|
&value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read32(location: &PciDeviceLocation, offset: u32) -> Result<u32, Error> {
|
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
|
PCI_ECAM_CFG_SPACE
|
||||||
.get()
|
.get()
|
||||||
.ok_or(Error::IoError)?
|
.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.
|
/// Encodes the bus, device, and function into an address offset in the PCI MMIO region.
|
||||||
fn encode_as_address_offset(location: &PciDeviceLocation) -> u32 {
|
fn encode_as_address_offset(location: &PciDeviceLocation) -> u32 {
|
||||||
// We only support ECAM here for LoongArch platforms. Offsets are from
|
// 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();
|
static PCI_ECAM_CFG_SPACE: Once<IoMem> = Once::new();
|
||||||
|
|
||||||
pub(crate) fn write32(location: &PciDeviceLocation, offset: u32, value: u32) -> Result<(), Error> {
|
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(
|
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,
|
&value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read32(location: &PciDeviceLocation, offset: u32) -> Result<u32, Error> {
|
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
|
PCI_ECAM_CFG_SPACE
|
||||||
.get()
|
.get()
|
||||||
.ok_or(Error::IoError)?
|
.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.
|
/// Encodes the bus, device, and function into an address offset in the PCI MMIO region.
|
||||||
fn encode_as_address_offset(location: &PciDeviceLocation) -> u32 {
|
fn encode_as_address_offset(location: &PciDeviceLocation) -> u32 {
|
||||||
// We only support ECAM here for RISC-V platforms. Offsets are from
|
// We only support ECAM here for RISC-V platforms. Offsets are from
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,12 @@ use core::ops::RangeInclusive;
|
||||||
|
|
||||||
use ostd::{
|
use ostd::{
|
||||||
Error,
|
Error,
|
||||||
arch::device::io_port::{ReadWriteAccess, WriteOnlyAccess},
|
arch::{
|
||||||
io::IoPort,
|
device::io_port::{ReadWriteAccess, WriteOnlyAccess},
|
||||||
|
kernel::ACPI_INFO,
|
||||||
|
},
|
||||||
|
io::{IoMem, IoPort},
|
||||||
|
mm::VmIoOnce,
|
||||||
sync::SpinLock,
|
sync::SpinLock,
|
||||||
};
|
};
|
||||||
use spin::Once;
|
use spin::Once;
|
||||||
|
|
@ -21,23 +25,66 @@ struct AddressAndDataPort {
|
||||||
|
|
||||||
static PCI_PIO_CFG_SPACE: Once<SpinLock<AddressAndDataPort>> = Once::new();
|
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> {
|
pub(crate) fn write32(location: &PciDeviceLocation, offset: u32, value: u32) -> Result<(), Error> {
|
||||||
let pio = PCI_PIO_CFG_SPACE.get().ok_or(Error::IoError)?.lock();
|
if let Some(ecam) = PCI_ECAM_CFG_SPACE.get() {
|
||||||
pio.address_port
|
if offset > PCI_ECAM_MAX_OFFSET {
|
||||||
.write(encode_as_port(location) | (offset & BIT32_ALIGN_MASK));
|
return Err(Error::InvalidArgs);
|
||||||
pio.data_port.write(value.to_le());
|
}
|
||||||
Ok(())
|
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> {
|
pub(crate) fn read32(location: &PciDeviceLocation, offset: u32) -> Result<u32, Error> {
|
||||||
let pio = PCI_PIO_CFG_SPACE.get().ok_or(Error::IoError)?.lock();
|
if let Some(ecam) = PCI_ECAM_CFG_SPACE.get() {
|
||||||
pio.address_port
|
if offset > PCI_ECAM_MAX_OFFSET {
|
||||||
.write(encode_as_port(location) | (offset & BIT32_ALIGN_MASK));
|
return Err(Error::InvalidArgs);
|
||||||
Ok(pio.data_port.read().to_le())
|
}
|
||||||
|
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.
|
/// 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 {
|
fn encode_as_port(location: &PciDeviceLocation) -> u32 {
|
||||||
// 1 << 31: Configuration enable
|
// 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.
|
/// Returns a range for the PCI bus number, or [`None`] if there is no PCI bus.
|
||||||
pub(crate) fn init() -> Option<RangeInclusive<u8>> {
|
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
|
// 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
|
// reset control register in the PIIX4. Although the two ports overlap in their I/O range, they
|
||||||
// serve completely different purposes. See
|
// serve completely different purposes. See
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use acpi::{
|
||||||
AcpiHandler, AcpiTables,
|
AcpiHandler, AcpiTables,
|
||||||
address::AddressSpace,
|
address::AddressSpace,
|
||||||
fadt::{Fadt, IaPcBootArchFlags},
|
fadt::{Fadt, IaPcBootArchFlags},
|
||||||
|
mcfg::Mcfg,
|
||||||
rsdp::Rsdp,
|
rsdp::Rsdp,
|
||||||
};
|
};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
@ -85,6 +86,19 @@ pub struct AcpiInfo {
|
||||||
pub boot_flags: Option<IaPcBootArchFlags>,
|
pub boot_flags: Option<IaPcBootArchFlags>,
|
||||||
/// An I/O port to reset the machine by writing the specified value.
|
/// An I/O port to reset the machine by writing the specified value.
|
||||||
pub reset_port_and_val: Option<(u16, u8)>,
|
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.
|
/// The [`AcpiInfo`] singleton.
|
||||||
|
|
@ -95,11 +109,15 @@ pub(in crate::arch) fn init() {
|
||||||
century_register: None,
|
century_register: None,
|
||||||
boot_flags: None,
|
boot_flags: None,
|
||||||
reset_port_and_val: None,
|
reset_port_and_val: None,
|
||||||
|
pci_ecam_region: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(acpi_tables) = get_acpi_tables()
|
let Some(acpi_tables) = get_acpi_tables() else {
|
||||||
&& let Ok(fadt) = acpi_tables.find_table::<Fadt>()
|
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.
|
// A zero means that the century register does not exist.
|
||||||
acpi_info.century_register = NonZeroU8::new(fadt.century);
|
acpi_info.century_register = NonZeroU8::new(fadt.century);
|
||||||
acpi_info.boot_flags = Some(fadt.iapc_boot_arch);
|
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);
|
log::info!("[ACPI]: Collected information {:?}", acpi_info);
|
||||||
|
|
||||||
ACPI_INFO.call_once(|| acpi_info);
|
ACPI_INFO.call_once(|| acpi_info);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue