Compare commits

...

5 Commits

47 changed files with 3240 additions and 797 deletions

743
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,7 @@ ahash = { version = "0.8.11", default-features = false, features = ["no-rng"] }
# acpi
acpi = { git = "https://github.com/alnyan/acpi.git", package = "acpi", branch = "acpi-system" }
rsdp = { git = "https://github.com/alnyan/acpi.git", package = "rsdp", branch = "acpi-system" }
aml = { git = "https://github.com/alnyan/acpi.git", branch = "acpi-system" }
acpi-system = { git = "https://github.com/alnyan/acpi-system.git" }

View File

@ -61,12 +61,12 @@ kernel-arch-riscv64.workspace = true
yboot-proto.workspace = true
kernel-arch-x86_64.workspace = true
kernel-arch-x86.workspace = true
ygg_driver_acpi.path = "driver/acpi"
ygg_driver_nvme = { path = "driver/block/nvme" }
ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
acpi.workspace = true
aml.workspace = true
acpi-system.workspace = true
[target.'cfg(target_arch = "x86")'.dependencies]
kernel-arch-i686.workspace = true
@ -87,6 +87,9 @@ kernel-arch-x86.workspace = true
kernel-arch-aarch64.workspace = true
kernel-arch-riscv64.workspace = true
ygg_driver_acpi.path = "driver/acpi"
ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
[features]
default = ["fb_console"]
fb_console = []

View File

@ -6,4 +6,10 @@ extern crate alloc;
pub mod cpuid;
pub mod gdt;
pub mod intrinsics;
pub mod registers;
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
pub const ISA_IRQ_OFFSET: u32 = 1024;
#[cfg(any(target_arch = "x86", rust_analyzer))]
pub const ISA_IRQ_OFFSET: u32 = 0;

View File

@ -0,0 +1,18 @@
[package]
name = "ygg_driver_acpi"
version = "0.1.0"
edition = "2024"
[dependencies]
libk-util.workspace = true
libk-mm.workspace = true
libk.workspace = true
device-api.workspace = true
kernel-arch-x86.path = "../../arch/x86"
acpi.workspace = true
rsdp.workspace = true
aml.workspace = true
acpi-system.workspace = true
log.workspace = true

View File

@ -0,0 +1,131 @@
use core::time::Duration;
use crate::AcpiHandlerImpl;
impl aml::Handler for AcpiHandlerImpl {
fn read_io_u8(&self, port: u16) -> u8 {
<Self as acpi_system::Handler>::io_read_u8(port)
}
fn read_io_u16(&self, port: u16) -> u16 {
<Self as acpi_system::Handler>::io_read_u16(port)
}
fn read_io_u32(&self, port: u16) -> u32 {
<Self as acpi_system::Handler>::io_read_u32(port)
}
fn write_io_u8(&self, port: u16, value: u8) {
<Self as acpi_system::Handler>::io_write_u8(port, value)
}
fn write_io_u16(&self, port: u16, value: u16) {
<Self as acpi_system::Handler>::io_write_u16(port, value)
}
fn write_io_u32(&self, port: u16, value: u32) {
<Self as acpi_system::Handler>::io_write_u32(port, value)
}
fn read_u8(&self, address: usize) -> u8 {
<Self as acpi_system::Handler>::mem_read_u8(address as u64)
}
fn read_u16(&self, address: usize) -> u16 {
<Self as acpi_system::Handler>::mem_read_u16(address as u64)
}
fn read_u32(&self, address: usize) -> u32 {
<Self as acpi_system::Handler>::mem_read_u32(address as u64)
}
fn read_u64(&self, address: usize) -> u64 {
<Self as acpi_system::Handler>::mem_read_u64(address as u64)
}
fn write_u8(&self, address: usize, value: u8) {
<Self as acpi_system::Handler>::mem_write_u8(address as u64, value)
}
fn write_u16(&self, address: usize, value: u16) {
<Self as acpi_system::Handler>::mem_write_u16(address as u64, value)
}
fn write_u32(&self, address: usize, value: u32) {
<Self as acpi_system::Handler>::mem_write_u32(address as u64, value)
}
fn write_u64(&self, address: usize, value: u64) {
<Self as acpi_system::Handler>::mem_write_u64(address as u64, value)
}
fn read_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u8 {
0xFF
}
fn read_pci_u16(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
) -> u16 {
0xFFFF
}
fn read_pci_u32(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
) -> u32 {
0xFFFFFFFF
}
fn write_pci_u8(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
_value: u8,
) {
}
fn write_pci_u16(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
_value: u16,
) {
}
fn write_pci_u32(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
_value: u32,
) {
}
fn read_ec_u8(&self, _address: u64) -> u8 {
0x00
}
fn write_ec_u8(&self, _address: u64, _value: u8) {}
fn sleep(&self, _duration: Duration) {
todo!()
// util::polling_sleep(duration).unwrap();
}
}

View File

@ -0,0 +1,171 @@
use core::{ptr::NonNull, time::Duration};
use acpi::PhysicalMapping;
use acpi_system::AcpiSystemError;
use alloc::sync::Arc;
use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq},
};
use kernel_arch_x86::{intrinsics, ISA_IRQ_OFFSET};
use libk::device::external_interrupt_controller;
use libk_mm::{
address::{PhysicalAddress, Virtualize},
pointer::PhysicalRef,
};
use crate::{
mem::{read_memory, write_memory},
ACPI_SYSTEM,
};
#[derive(Clone, Copy)]
#[doc(hidden)]
pub struct AcpiHandlerImpl;
struct SciHandler;
impl acpi_system::Handler for AcpiHandlerImpl {
type MappedSlice = PhysicalRef<'static, [u8]>;
unsafe fn map_slice(address: u64, length: u64) -> Self::MappedSlice {
unsafe {
PhysicalRef::map_slice(
PhysicalAddress::from_u64(address),
length.try_into().unwrap(),
)
}
}
fn io_read_u8(port: u16) -> u8 {
let value = unsafe { intrinsics::inb(port) };
log::trace!("io_read_u8 {:#x} <- {:#x}", port, value);
value
}
fn io_read_u16(port: u16) -> u16 {
let value = unsafe { intrinsics::inw(port) };
log::trace!("io_read_u16 {:#x} <- {:#x}", port, value);
value
}
fn io_read_u32(port: u16) -> u32 {
let value = unsafe { intrinsics::inl(port) };
log::trace!("io_read_u32 {:#x} <- {:#x}", port, value);
value
}
fn io_write_u8(port: u16, value: u8) {
log::trace!("io_write_u8 {:#x}, {:#x}", port, value);
unsafe { intrinsics::outb(port, value) }
}
fn io_write_u16(port: u16, value: u16) {
log::trace!("io_write_u16 {:#x}, {:#x}", port, value);
unsafe { intrinsics::outw(port, value) }
}
fn io_write_u32(port: u16, value: u32) {
log::trace!("io_write_u32 {:#x}, {:#x}", port, value);
unsafe { intrinsics::outl(port, value) }
}
fn mem_read_u8(address: u64) -> u8 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u8 {:#x} -> {:#x}", address, value);
value
}
fn mem_read_u16(address: u64) -> u16 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u16 {:#x} -> {:#x}", address, value);
value
}
fn mem_read_u32(address: u64) -> u32 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u32 {:#x} -> {:#x}", address, value);
value
}
fn mem_read_u64(address: u64) -> u64 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u64 {:#x} -> {:#x}", address, value);
value
}
fn mem_write_u8(address: u64, value: u8) {
log::trace!("mem_write_u8 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn mem_write_u16(address: u64, value: u16) {
log::trace!("mem_write_u16 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn mem_write_u32(address: u64, value: u32) {
log::trace!("mem_write_u32 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn mem_write_u64(address: u64, value: u64) {
log::trace!("mem_write_u64 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn install_interrupt_handler(irq: u32) -> Result<(), AcpiSystemError> {
log::info!("Installing ACPI SCI handler at IRQ #{}", irq);
let intc = external_interrupt_controller().expect("No external intc");
let handler = Arc::new(SciHandler);
let irq = Irq::External(irq + ISA_IRQ_OFFSET);
intc.register_irq(irq, Default::default(), handler).unwrap();
intc.enable_irq(irq).unwrap();
Ok(())
}
fn stall(_duration: Duration) {
// TODO polling_sleep is not yet implemented properly
todo!()
// util::polling_sleep(duration).ok();
}
}
impl rsdp::handler::AcpiHandler for AcpiHandlerImpl {
unsafe fn map_physical_region<T>(
&self,
physical_address: usize,
size: usize,
) -> PhysicalMapping<Self, T> {
unsafe {
PhysicalMapping::new(
physical_address,
NonNull::new_unchecked(
PhysicalAddress::from_usize(physical_address).virtualize() as *mut T
),
size,
size,
*self,
)
}
}
fn unmap_physical_region<T>(_region: &acpi::PhysicalMapping<Self, T>) {}
}
impl InterruptHandler for SciHandler {
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
log::trace!("ACPI SCI received");
ACPI_SYSTEM.get().lock().handle_sci();
true
}
}
impl Device for SciHandler {
fn display_name(&self) -> &str {
"ACPI SCI handler"
}
}

View File

@ -0,0 +1,89 @@
#![feature(allocator_api)]
#![no_std]
use acpi::AcpiTables;
use acpi_system::{AcpiInterruptMethod, AcpiSystem};
use alloc::boxed::Box;
use libk::error::Error;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
extern crate alloc;
pub mod mem;
pub use mem::AcpiAllocator;
pub mod handler;
pub use handler::AcpiHandlerImpl;
pub mod aml_handler;
pub use acpi_system::{
EventAction, FixedEvent, InterruptPolarity, InterruptTrigger, IrqDescriptor, PciPin,
};
static ACPI_SYSTEM: OneTimeInit<IrqSafeSpinlock<AcpiSystem<AcpiHandlerImpl>>> = OneTimeInit::new();
pub fn add_event_handler<F: Fn(&AcpiSystem<AcpiHandlerImpl>) -> EventAction + 'static>(
event: &FixedEvent,
handler: F,
) -> Result<(), Error> {
ACPI_SYSTEM
.get()
.lock()
.enable_fixed_event(event, Box::new(handler))
.map_err(|_| Error::InvalidArgument)
}
pub fn get_pci_route(
aml_path: &str,
device: u16,
function: u16,
pin: PciPin,
) -> Option<IrqDescriptor> {
ACPI_SYSTEM
.get()
.lock()
.pci_route(aml_path, device, function, pin)
.ok()
}
/// Initializes ACPI management
pub fn switch_to_acpi(tables: &'static AcpiTables<AcpiHandlerImpl>) -> Result<(), Error> {
// NOTE mostly broken for real HW
let mut system = AcpiSystem::new(tables, Box::new(AcpiHandlerImpl)).unwrap();
system.initialize(AcpiInterruptMethod::Apic).unwrap();
// system
// .enable_fixed_event(
// &FixedEvent::POWER_BUTTON,
// Box::new(|_| {
// log::info!("Power button was pressed");
// // TODO the correct way would be to
// // 1. Nicely ask all the processes to quit
// // 2. Wait for some time
// // 3. Kill the remaining ones
// // 4. Halt other cores
// // 5. Sync filesystem
// // 6. Do something with the devices
// // 7. Actually enter the S5 state
// unsafe {
// PLATFORM
// .send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)
// .unwrap();
// }
// SHUTDOWN_FENCE.signal();
// SHUTDOWN_FENCE.wait_all(CPU_COUNT.load(Ordering::Acquire));
// log::info!("CPUs are parked, can shutdown now");
// EventAction::EnterSleepState(AcpiSleepState::S5)
// }),
// )
// .unwrap();
ACPI_SYSTEM.init(IrqSafeSpinlock::new(system));
Ok(())
}

View File

@ -0,0 +1,64 @@
//! ACPI memory IO and management functions
use core::{
alloc::{AllocError, Allocator, GlobalAlloc, Layout},
ptr::NonNull,
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryMapping, heap::GLOBAL_HEAP};
#[derive(Clone, Copy)]
#[doc(hidden)]
pub struct AcpiAllocator;
unsafe impl Allocator for AcpiAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let ptr = unsafe { GLOBAL_HEAP.alloc(layout) };
log::trace!("ACPI alloc: {:?} -> {:p}", layout, ptr);
if ptr.is_null() {
Err(AllocError)
} else {
unsafe {
Ok(NonNull::slice_from_raw_parts(
NonNull::new_unchecked(ptr),
layout.size(),
))
}
}
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
log::trace!("ACPI dealloc: {:?}, {:?}", ptr, layout);
unsafe { GLOBAL_HEAP.dealloc(ptr.as_ptr(), layout) };
}
}
// TODO don't map memory as device if not necessary
pub unsafe fn read_memory<T>(address: PhysicalAddress) -> T {
let io =
unsafe { DeviceMemoryMapping::map(address, size_of::<T>(), Default::default()).unwrap() };
let address = io.address();
unsafe {
if address % align_of::<T>() == 0 {
(address as *const T).read_volatile()
} else {
(address as *const T).read_unaligned()
}
}
}
pub unsafe fn write_memory<T>(address: PhysicalAddress, value: T) {
let io =
unsafe { DeviceMemoryMapping::map(address, size_of::<T>(), Default::default()).unwrap() };
let address = io.address();
unsafe {
if address % align_of::<T>() == 0 {
(address as *mut T).write_volatile(value)
} else {
(address as *mut T).write_unaligned(value)
}
}
}

View File

@ -202,7 +202,7 @@ pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
info.config_space.set_command(cmd.bits());
info.init_interrupts(PreferredInterruptMode::Msi)?;
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
// // TODO support regular PCI interrupts (ACPI dependency)
// let Some(mut msi) = info.config_space.capability::<MsiCapability>() else {

View File

@ -428,7 +428,7 @@ pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
.as_memory()
.expect("Expected a memory BAR0");
info.init_interrupts(PreferredInterruptMode::Msi)?;
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);

View File

@ -16,7 +16,9 @@ bitflags.workspace = true
tock-registers.workspace = true
[target.'cfg(target_arch = "x86_64")'.dependencies]
ygg_driver_acpi.path = "../../acpi"
acpi.workspace = true
kernel-arch-x86.workspace = true
[lints]
workspace = true

View File

@ -1,9 +1,12 @@
//! PCI capability structures and queries
use core::mem::offset_of;
use alloc::{sync::Arc, vec, vec::Vec};
use device_api::interrupt::{
InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo,
};
use kernel_arch_x86::intrinsics;
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use tock_registers::{
interfaces::{Readable, Writeable},
@ -11,6 +14,8 @@ use tock_registers::{
};
use yggdrasil_abi::error::Error;
use crate::PciBaseAddress;
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized {
@ -41,6 +46,9 @@ pub trait VirtioCapability {
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>;
}
/// Power management capability entry
pub struct PowerManagementCapability;
/// MSI-X capability query
pub struct MsiXCapability;
@ -57,6 +65,15 @@ pub struct VirtioNotifyConfigCapability;
/// VirtIO interrupt status
pub struct VirtioInterruptStatusCapability;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DevicePowerState {
D0,
D1,
D2,
D3Cold,
D3Hot,
}
/// Represents an entry in MSI-X vector table
#[repr(C)]
pub struct MsiXEntry {
@ -68,8 +85,20 @@ pub struct MsiXEntry {
pub control: ReadWrite<u32>,
}
enum MsiXVectorTableAccess<'a> {
Memory(DeviceMemoryIoMut<'a, [MsiXEntry]>),
Io(u16),
}
pub struct MsiXVectorTable<'a> {
vectors: DeviceMemoryIoMut<'a, [MsiXEntry]>,
access: MsiXVectorTableAccess<'a>,
len: usize,
}
/// PCI Power Management capability data structure
pub struct PowerManagementData<'s, S: PciConfigurationSpace + ?Sized + 's> {
space: &'s S,
offset: usize,
}
/// MSI-X capability data structure
@ -122,6 +151,19 @@ impl<T: VirtioCapability> PciCapability for T {
}
}
impl PciCapability for PowerManagementCapability {
const ID: PciCapabilityId = PciCapabilityId::PowerManagement;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PowerManagementData<'a, S>;
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
space: &'s S,
offset: usize,
_len: usize,
) -> Self::CapabilityData<'s, S> {
PowerManagementData { space, offset }
}
}
impl PciCapability for MsiXCapability {
const ID: PciCapabilityId = PciCapabilityId::MsiX;
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiXData<'a, S>;
@ -246,6 +288,40 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PowerManagementData<'s, S> {
pub fn set_device_power_state(&self, state: DevicePowerState) {
let pmcsr = self.space.read_u16(self.offset + 4) & !0x3;
let current = self.get_device_power_state();
if state == current {
return;
}
log::info!("Set device power state: {state:?}");
match state {
DevicePowerState::D0 => {
// power = 0b00 | PME_EN
self.space.write_u16(self.offset + 4, pmcsr);
}
_ => {
log::warn!("TODO: {state:?} power state");
}
}
}
pub fn get_device_power_state(&self) -> DevicePowerState {
let pmcsr = self.space.read_u16(self.offset + 4);
match pmcsr & 0x3 {
0b00 => DevicePowerState::D0,
0b01 => DevicePowerState::D1,
0b10 => DevicePowerState::D2,
0b11 => DevicePowerState::D3Hot,
_ => unreachable!(),
}
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
// TODO use pending bits as well
/// Maps and returns the vector table associated with the device's MSI-X capability
@ -260,13 +336,27 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
let Some(base) = self.space.bar(bir) else {
return Err(Error::DoesNotExist);
};
let Some(base) = base.as_memory() else {
return Err(Error::InvalidOperation);
};
log::debug!("MSI-X table address: {:#x}", base.add(table_offset));
unsafe { MsiXVectorTable::from_raw_parts(base.add(table_offset), table_size) }
match base {
PciBaseAddress::Memory32(mem32) => unsafe {
log::info!("MSI-X table address: {:#x}", mem32 + table_offset as u32);
MsiXVectorTable::memory_from_raw_parts(
PhysicalAddress::from_u32(mem32).add(table_offset),
table_size,
)
},
PciBaseAddress::Memory64(mem64) => unsafe {
log::info!("MSI-X table address: {:#x}", mem64 + table_offset as u64);
MsiXVectorTable::memory_from_raw_parts(
PhysicalAddress::from_u64(mem64).add(table_offset),
table_size,
)
},
PciBaseAddress::Io(io) => unsafe {
log::info!("MSI-X table I/O: {:#x}", io + table_offset as u16);
MsiXVectorTable::io_from_raw_parts(io + table_offset as u16, table_size)
},
}
}
/// Changes the global enable status for the device's MSI-X capability. If set, regular IRQs
@ -292,15 +382,79 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
}
}
impl MsiXVectorTableAccess<'_> {
fn set_vector_masked(&mut self, vector: usize, masked: bool) {
let old = self.read_control(vector);
let new = if masked { old | 1 } else { old & !1 };
if old != new {
self.write_control(vector, new);
}
}
fn read_control(&mut self, vector: usize) -> u32 {
match self {
&mut Self::Io(base) => unsafe {
let a = base
+ (vector * size_of::<MsiXEntry>() + offset_of!(MsiXEntry, control)) as u16;
intrinsics::inl(a)
},
Self::Memory(vectors) => vectors[vector].control.get(),
}
}
fn write_address(&mut self, vector: usize, value: u64) {
match self {
&mut Self::Io(base) => unsafe {
let a = base + (vector * size_of::<MsiXEntry>()) as u16;
intrinsics::outl(a, value as u32);
intrinsics::outl(a + 4, (value >> 32) as u32);
},
Self::Memory(vectors) => vectors[vector].address.set(value),
}
}
fn write_data(&mut self, vector: usize, value: u32) {
match self {
&mut Self::Io(base) => unsafe {
let a =
base + (vector * size_of::<MsiXEntry>() + offset_of!(MsiXEntry, data)) as u16;
intrinsics::outl(a, value)
},
Self::Memory(vectors) => vectors[vector].data.set(value),
}
}
fn write_control(&mut self, vector: usize, value: u32) {
match self {
&mut Self::Io(base) => unsafe {
let a = base
+ (vector * size_of::<MsiXEntry>() + offset_of!(MsiXEntry, control)) as u16;
intrinsics::outl(a, value)
},
Self::Memory(vectors) => vectors[vector].control.set(value),
}
}
}
impl MsiXVectorTable<'_> {
unsafe fn from_raw_parts(base: PhysicalAddress, len: usize) -> Result<Self, Error> {
unsafe fn memory_from_raw_parts(base: PhysicalAddress, len: usize) -> Result<Self, Error> {
let vectors = DeviceMemoryIoMut::map_slice(base, len, Default::default())?;
Ok(Self { vectors })
Ok(Self {
access: MsiXVectorTableAccess::Memory(vectors),
len,
})
}
unsafe fn io_from_raw_parts(base: u16, len: usize) -> Result<Self, Error> {
Ok(Self {
access: MsiXVectorTableAccess::Io(base),
len,
})
}
pub fn mask_all(&mut self) {
for vector in self.vectors.iter_mut() {
vector.set_masked(true);
for i in 0..self.len {
self.access.set_vector_masked(i, true);
}
}
@ -324,26 +478,15 @@ impl MsiXVectorTable<'_> {
for (i, info) in range.iter().enumerate() {
let index = i + start;
self.vectors[index].address.set(info.address as _);
self.vectors[index].data.set(info.value);
self.vectors[index].set_masked(false);
self.access.write_address(index, info.address as _);
self.access.write_data(index, info.value);
self.access.set_vector_masked(index, false);
}
Ok(range)
}
}
impl MsiXEntry {
/// If set, prevents the MSI-X interrupt from being delivered
fn set_masked(&mut self, masked: bool) {
if masked {
self.control.set(self.control.get() | 1);
} else {
self.control.set(self.control.get() & !1);
}
}
}
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> {
pub fn register<C: MessageInterruptController + ?Sized>(
&mut self,

View File

@ -11,7 +11,7 @@ use yggdrasil_abi::error::Error;
use crate::{
capability::{MsiCapability, MsiXCapability, MsiXVectorTable},
PciAddress, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
PciAddress, PciCommandRegister, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
};
/// Describes a PCI device
@ -19,6 +19,16 @@ use crate::{
pub struct PciDeviceInfo {
/// Address of the device
pub address: PciAddress,
/// Class field of the configuration space
pub class: u8,
/// Subclass field of the configuration space
pub subclass: u8,
/// Prog IF field of the configuration space
pub prog_if: u8,
/// Vendor ID field of the configuration space
pub vendor_id: u16,
/// Device ID field of the configuration space
pub device_id: u16,
/// Configuration space access method
pub config_space: PciConfigSpace,
/// Describes the PCI segment this device is a part of
@ -43,7 +53,7 @@ pub enum PciInterruptPin {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PreferredInterruptMode {
Msi,
Msi(bool),
Legacy,
}
@ -75,6 +85,7 @@ pub enum PciMatch {
}
pub struct PciDriver {
#[allow(unused)]
pub(crate) name: &'static str,
pub(crate) check: PciMatch,
pub(crate) probe: fn(&PciDeviceInfo) -> Result<Arc<dyn Device>, Error>,
@ -83,26 +94,75 @@ pub struct PciDriver {
/// Used to store PCI bus devices which were enumerated by the kernel
pub struct PciBusDevice {
pub(crate) info: PciDeviceInfo,
pub(crate) driver: Option<Arc<dyn Device>>,
pub(crate) device: Option<Arc<dyn Device>>,
pub(crate) driver_name: Option<&'static str>,
}
impl PciDeviceInfo {
pub fn set_command(
&self,
enable_irq: bool,
enable_mem: bool,
enable_io: bool,
enable_bus_master: bool,
) {
let command = PciCommandRegister::from_bits_retain(self.config_space.command());
let mut new = command;
if enable_irq {
new &= !PciCommandRegister::DISABLE_INTERRUPTS;
} else {
new |= PciCommandRegister::DISABLE_INTERRUPTS;
}
if enable_mem {
new |= PciCommandRegister::ENABLE_MEMORY;
} else {
new &= !PciCommandRegister::ENABLE_MEMORY;
}
if enable_io {
new |= PciCommandRegister::ENABLE_IO;
} else {
new &= !PciCommandRegister::ENABLE_IO;
}
if enable_bus_master {
new |= PciCommandRegister::BUS_MASTER;
} else {
new &= !PciCommandRegister::BUS_MASTER;
}
if new != command {
self.config_space.set_command(new.bits());
}
}
pub fn init_interrupts(&self, preferred_mode: PreferredInterruptMode) -> Result<(), Error> {
self.interrupt_config
.try_init_with(|| {
let configured_mode =
if self.segment.has_msi && preferred_mode == PreferredInterruptMode::Msi {
if let Some(mut msix) = self.config_space.capability::<MsiXCapability>() {
let mut vt = msix.vector_table().unwrap();
let configured_mode = if self.segment.has_msi
&& let PreferredInterruptMode::Msi(want_msix) = preferred_mode
{
// Try MSI-X first
let mut result = None;
if want_msix
&& let Some(mut msix) = self.config_space.capability::<MsiXCapability>()
{
if let Ok(mut vt) = msix.vector_table() {
vt.mask_all();
msix.set_function_mask(false);
msix.set_enabled(true);
ConfiguredInterruptMode::MsiX(vt)
} else if self.config_space.capability::<MsiCapability>().is_some() {
ConfiguredInterruptMode::Msi
result = Some(ConfiguredInterruptMode::MsiX(vt));
}
}
// Then try MSI
if result.is_none() && self.config_space.capability::<MsiCapability>().is_some()
{
result = Some(ConfiguredInterruptMode::Msi)
}
// And then fall back to legacy
if let Some(result) = result {
result
} else {
self.legacy_interrupt_mode()
}
@ -110,7 +170,6 @@ impl PciDeviceInfo {
// Ignore preferred_mode, the only supported is Legacy
self.legacy_interrupt_mode()
};
IrqSafeRwLock::new(InterruptConfig {
preferred_mode,
configured_mode,
@ -201,11 +260,7 @@ impl PciDeviceInfo {
address: self.address,
pin,
};
let route = self
.segment
.irq_translation_map
.get(&src)
.ok_or(Error::InvalidOperation)?;
let route = self.segment.irq_translation_map.map_interrupt(&src)?;
log::debug!(
"PCI {} pin {:?} -> system IRQ #{}",

View File

@ -0,0 +1,63 @@
use alloc::collections::btree_map::BTreeMap;
use libk::error::Error;
use crate::device::{PciInterrupt, PciInterruptRoute};
#[derive(Debug)]
pub enum PciInterruptMap {
Fixed(BTreeMap<PciInterrupt, PciInterruptRoute>),
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
Acpi(alloc::string::String),
Legacy,
}
impl PciInterruptMap {
pub fn map_interrupt(&self, interrupt: &PciInterrupt) -> Result<PciInterruptRoute, Error> {
match self {
Self::Fixed(map) => map.get(interrupt).cloned().ok_or(Error::DoesNotExist),
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
Self::Acpi(aml_object_name) => {
use device_api::interrupt::{IrqLevel, IrqOptions, IrqTrigger};
use crate::device::PciInterruptPin;
let aml_pin = match interrupt.pin {
PciInterruptPin::A => ygg_driver_acpi::PciPin::IntA,
PciInterruptPin::B => ygg_driver_acpi::PciPin::IntB,
PciInterruptPin::C => ygg_driver_acpi::PciPin::IntC,
PciInterruptPin::D => ygg_driver_acpi::PciPin::IntD,
};
let aml_route = ygg_driver_acpi::get_pci_route(
aml_object_name.as_str(),
interrupt.address.device as u16,
interrupt.address.function as u16,
aml_pin,
)
.or_else(|| {
ygg_driver_acpi::get_pci_route(
aml_object_name.as_str(),
interrupt.address.device as u16,
0xFFFF,
aml_pin,
)
})
.ok_or(Error::DoesNotExist)?;
let trigger = match aml_route.trigger {
ygg_driver_acpi::InterruptTrigger::Edge => IrqTrigger::Edge,
ygg_driver_acpi::InterruptTrigger::Level => IrqTrigger::Level,
};
let level = match aml_route.polarity {
ygg_driver_acpi::InterruptPolarity::ActiveLow => IrqLevel::ActiveLow,
ygg_driver_acpi::InterruptPolarity::ActiveHigh => IrqLevel::ActiveHigh,
};
Ok(PciInterruptRoute {
options: IrqOptions { trigger, level },
number: aml_route.irq,
})
}
Self::Legacy => todo!(),
}
}
}

View File

@ -1,24 +1,30 @@
//! PCI/PCIe bus interfaces
#![no_std]
#![feature(let_chains)]
#![allow(clippy::missing_transmute_annotations)]
extern crate alloc;
use core::fmt;
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
use acpi::mcfg::McfgEntry;
use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use alloc::{format, sync::Arc, vec::Vec};
use bitflags::bitflags;
use device::{PciBusDevice, PciDeviceInfo, PciDriver, PciInterrupt, PciInterruptRoute, PciMatch};
use device::{PciBusDevice, PciDeviceInfo, PciDriver, PciMatch};
use device_api::device::Device;
use interrupt::PciInterruptMap;
use libk::fs::sysfs::{self, object::KObject};
use libk_mm::address::PhysicalAddress;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use space::legacy;
use yggdrasil_abi::error::Error;
use yggdrasil_abi::{error::Error, primitive_enum};
pub mod capability;
pub mod device;
pub mod interrupt;
mod nodes;
mod space;
pub use space::{
@ -29,6 +35,7 @@ pub use space::{
bitflags! {
/// Command register of the PCI configuration space
#[derive(PartialEq, Clone, Copy)]
pub struct PciCommandRegister: u16 {
/// If set, I/O access to the device is enabled
const ENABLE_IO = 1 << 0;
@ -73,20 +80,28 @@ pub enum PciBaseAddress {
Io(u16),
}
/// Unique ID assigned to PCI capability structures
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
#[repr(u8)]
pub enum PciCapabilityId {
/// MSI (32-bit or 64-bit)
primitive_enum! {
pub enum PciCapabilityId: u8 {
PowerManagement = 0x01,
Msi = 0x05,
/// Vendor-specific capability
VendorSpecific = 0x09,
/// MSI-X
MsiX = 0x11,
/// Unknown capability missing from this list
Unknown,
}
}
// /// Unique ID assigned to PCI capability structures
// #[derive(Clone, Copy, PartialEq, Eq, Debug)]
// #[non_exhaustive]
// #[repr(u8)]
// pub enum PciCapabilityId {
// /// MSI (32-bit or 64-bit)
// Msi = 0x05,
// /// Vendor-specific capability
// VendorSpecific = 0x09,
// /// MSI-X
// MsiX = 0x11,
// /// Unknown capability missing from this list
// Unknown,
// }
/// Interface used for querying PCI capabilities
#[allow(unused)]
@ -200,7 +215,7 @@ pub struct PciSegmentInfo {
pub bus_number_end: u8,
pub ecam_phys_base: Option<PhysicalAddress>,
pub irq_translation_map: BTreeMap<PciInterrupt, PciInterruptRoute>,
pub irq_translation_map: PciInterruptMap,
pub has_msi: bool,
}
@ -208,7 +223,7 @@ pub struct PciSegmentInfo {
pub struct PciBusSegment {
allocator: Option<BusAddressAllocator>,
info: Arc<PciSegmentInfo>,
devices: Vec<PciBusDevice>,
devices: Vec<Arc<KObject<IrqSafeSpinlock<PciBusDevice>>>>,
}
#[derive(Debug)]
@ -240,6 +255,14 @@ impl PciBaseAddress {
_ => None,
}
}
pub fn is_zero(&self) -> bool {
match *self {
Self::Memory32(base) => base == 0,
Self::Memory64(base) => base == 0,
Self::Io(base) => base == 0,
}
}
}
impl PciBusSegment {
@ -348,13 +371,39 @@ impl PciBusSegment {
}
}
let vendor_id = config.vendor_id();
let device_id = config.device_id();
let class = config.class_code();
let subclass = config.subclass();
let prog_if = config.prog_if();
let info = PciDeviceInfo {
address,
vendor_id,
device_id,
class,
subclass,
prog_if,
segment: self.info.clone(),
config_space: config,
interrupt_config: Arc::new(OneTimeInit::new()),
};
self.devices.push(PciBusDevice { info, driver: None });
let object = nodes::make_sysfs_object(PciBusDevice {
info,
driver_name: None,
device: None,
});
let pci_object = PCI_SYSFS_NODE.or_init_with(|| {
let bus_object = sysfs::bus().unwrap();
let pci_object = KObject::new(());
bus_object.add_object("pci", pci_object.clone()).ok();
pci_object
});
let name = format!("{address}");
pci_object.add_object(name, object.clone()).ok();
self.devices.push(object);
Ok(())
}
@ -390,9 +439,9 @@ impl PciBusManager {
/// Walks the bus device list and calls init/init_irq functions on any devices with associated
/// drivers
pub fn setup_bus_devices() -> Result<(), Error> {
log::info!("Setting up bus devices");
// log::info!("Setting up bus devices");
Self::walk_bus_devices(|device| {
log::info!("Set up {}", device.info.address);
// log::info!("Set up {}", device.info.address);
setup_bus_device(device)?;
Ok(true)
})
@ -407,7 +456,8 @@ impl PciBusManager {
for segment in this.segments.iter_mut() {
for device in segment.devices.iter_mut() {
if !f(device)? {
let mut device = device.lock();
if !f(&mut *device)? {
return Ok(());
}
}
@ -425,7 +475,7 @@ impl PciBusManager {
bus_number_start: 0,
bus_number_end: 255,
ecam_phys_base: None,
irq_translation_map: BTreeMap::new(),
irq_translation_map: PciInterruptMap::Legacy,
has_msi: false,
}),
allocator: None,
@ -449,8 +499,8 @@ impl PciBusManager {
bus_number_end: entry.bus_number_end,
ecam_phys_base: Some(PhysicalAddress::from_u64(entry.base_address)),
// TODO obtain this from ACPI SSDT
irq_translation_map: BTreeMap::new(),
// TODO get the segment's PCI root bridge AML name
irq_translation_map: PciInterruptMap::Acpi("\\_SB.PCI0._PRT".into()),
has_msi: true,
}),
// Firmware done this for us
@ -555,39 +605,29 @@ impl PciConfigurationSpace for PciConfigSpace {
}
fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
if device.driver.is_some() {
if device.device.is_some() {
return Ok(());
}
let config = &device.info.config_space;
log::debug!(
"{}: {:04x}:{:04x}",
device.info.address,
config.vendor_id(),
config.device_id()
device.info.vendor_id,
device.info.device_id
);
let class = config.class_code();
let subclass = config.subclass();
let prog_if = config.prog_if();
let drivers = PCI_DRIVERS.lock();
for driver in drivers.iter() {
if driver
.check
.check_device(&device.info, class, subclass, prog_if)
{
if driver.check.check_device(&device.info) {
// TODO add the device to the bus
log::debug!(" -> {:?}", driver.name);
// log::debug!(" -> {:?}", driver.name);
let instance = (driver.probe)(&device.info)?;
unsafe { instance.clone().init() }?;
device.driver.replace(instance);
device.device.replace(instance);
device.driver_name.replace(driver.name);
break;
} else {
log::debug!(" -> No driver");
}
}
@ -595,17 +635,19 @@ fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
}
impl PciMatch {
pub fn check_device(&self, info: &PciDeviceInfo, class: u8, subclass: u8, prog_if: u8) -> bool {
pub fn check_device(&self, info: &PciDeviceInfo) -> bool {
match self {
Self::Generic(f) => f(info),
&Self::Vendor(vendor_, device_) => {
info.config_space.vendor_id() == vendor_ && info.config_space.device_id() == device_
info.vendor_id == vendor_ && info.device_id == device_
}
&Self::Class(class_, Some(subclass_), Some(prog_if_)) => {
class_ == class && subclass_ == subclass && prog_if_ == prog_if
class_ == info.class && subclass_ == info.subclass && prog_if_ == info.prog_if
}
&Self::Class(class_, Some(subclass_), _) => class_ == class && subclass_ == subclass,
&Self::Class(class_, _, _) => class_ == class,
&Self::Class(class_, Some(subclass_), _) => {
class_ == info.class && subclass_ == info.subclass
}
&Self::Class(class_, _, _) => class_ == info.class,
}
}
}
@ -651,3 +693,4 @@ pub fn register_generic_driver(
static PCI_DRIVERS: IrqSafeSpinlock<Vec<PciDriver>> = IrqSafeSpinlock::new(Vec::new());
static PCI_MANAGER: IrqSafeSpinlock<PciBusManager> = IrqSafeSpinlock::new(PciBusManager::new());
static PCI_SYSFS_NODE: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();

View File

@ -0,0 +1,138 @@
use alloc::{format, string::String, sync::Arc};
use libk::{
error::Error,
fs::sysfs::{
attribute::{StringAttribute, StringAttributeOps},
object::KObject,
},
};
use libk_util::sync::IrqSafeSpinlock;
use crate::{device::PciBusDevice, PciBaseAddress, PciCapabilityId, PciConfigurationSpace};
pub(crate) fn make_sysfs_object(
device: PciBusDevice,
) -> Arc<KObject<IrqSafeSpinlock<PciBusDevice>>> {
struct Resources;
struct Capabilities;
struct Driver;
struct Class;
struct Id;
impl StringAttributeOps for Driver {
type Data = IrqSafeSpinlock<PciBusDevice>;
const NAME: &'static str = "driver";
fn read(state: &Self::Data) -> Result<String, Error> {
let state = state.lock();
if let Some(driver) = state.driver_name {
Ok(driver.into())
} else {
Ok("".into())
}
}
}
impl StringAttributeOps for Id {
type Data = IrqSafeSpinlock<PciBusDevice>;
const NAME: &'static str = "id";
fn read(state: &Self::Data) -> Result<String, Error> {
let state = state.lock();
Ok(format!(
"{:04x}:{:04x}",
state.info.vendor_id, state.info.device_id
))
}
}
impl StringAttributeOps for Class {
type Data = IrqSafeSpinlock<PciBusDevice>;
const NAME: &'static str = "class";
fn read(state: &Self::Data) -> Result<String, Error> {
let state = state.lock();
Ok(format!(
"{:02x}:{:02x}:{:02x}",
state.info.class, state.info.subclass, state.info.prog_if
))
}
}
impl StringAttributeOps for Resources {
type Data = IrqSafeSpinlock<PciBusDevice>;
const NAME: &'static str = "resources";
const NEWLINE: bool = false;
fn read(state: &Self::Data) -> Result<String, Error> {
use core::fmt::Write;
let state = state.lock();
let mut output = String::new();
for i in 0..6 {
if let Some(bar) = state.info.config_space.bar(i) {
if bar.is_zero() {
continue;
}
match bar {
PciBaseAddress::Io(base) => {
writeln!(output, "{i}:pio:{base:#06x}").ok();
}
PciBaseAddress::Memory32(base) => {
writeln!(output, "{i}:m32:{base:#010x}").ok();
}
PciBaseAddress::Memory64(base) => {
writeln!(output, "{i}:m64:{base:#018x}").ok();
}
}
}
}
if output.is_empty() {
output.push('\n');
}
Ok(output)
}
}
impl StringAttributeOps for Capabilities {
type Data = IrqSafeSpinlock<PciBusDevice>;
const NAME: &'static str = "capabilities";
const NEWLINE: bool = false;
fn read(state: &Self::Data) -> Result<String, Error> {
use core::fmt::Write;
let state = state.lock();
let mut output = String::new();
for (capability, offset, _) in state.info.config_space.capability_iter() {
write!(output, "{offset:04x}:").ok();
match capability {
Some(PciCapabilityId::Msi) => write!(output, "msi").ok(),
Some(PciCapabilityId::MsiX) => write!(output, "msix").ok(),
Some(PciCapabilityId::VendorSpecific) => write!(output, "vendor-specific").ok(),
Some(PciCapabilityId::PowerManagement) => {
write!(output, "power-management").ok()
}
None => write!(output, "unknown").ok(),
};
writeln!(output).ok();
}
if output.is_empty() {
output.push('\n');
}
Ok(output)
}
}
let object = KObject::new(IrqSafeSpinlock::new(device));
object
.add_attribute(StringAttribute::from(Capabilities))
.ok();
object.add_attribute(StringAttribute::from(Resources)).ok();
object.add_attribute(StringAttribute::from(Driver)).ok();
object.add_attribute(StringAttribute::from(Class)).ok();
object.add_attribute(StringAttribute::from(Id)).ok();
object
}

View File

@ -75,12 +75,12 @@ pub struct CapabilityIterator<'s, S: PciConfigurationSpace + ?Sized> {
}
impl<S: PciConfigurationSpace + ?Sized> Iterator for CapabilityIterator<'_, S> {
type Item = (PciCapabilityId, usize, usize);
type Item = (Option<PciCapabilityId>, usize, usize);
fn next(&mut self) -> Option<Self::Item> {
let offset = self.current? & !0x3;
let id = unsafe { core::mem::transmute(self.space.read_u8(offset)) };
let id = PciCapabilityId::try_from(self.space.read_u8(offset)).ok();
let len = self.space.read_u8(offset + 2);
let next_pointer = self.space.read_u8(offset + 1);
@ -374,7 +374,7 @@ pub trait PciConfigurationSpace {
/// Locates a capability within this configuration space
fn capability<C: PciCapability>(&self) -> Option<C::CapabilityData<'_, Self>> {
self.capability_iter().find_map(|(id, offset, len)| {
if id == C::ID && C::check(self, offset, len) {
if id.map_or(false, |id| id == C::ID) && C::check(self, offset, len) {
Some(C::data(self, offset, len))
} else {
None

View File

@ -1,4 +1,4 @@
use core::mem::size_of;
use core::{fmt, mem::size_of};
use alloc::sync::Arc;
use bytemuck::Pod;
@ -27,6 +27,37 @@ pub struct L2Packet {
pub data: Arc<PageBox<[u8]>>,
}
/// Defines an Ethernet link speed
#[derive(Debug, Clone, Copy)]
pub enum EthernetSpeed {
/// 1Gbps link
Speed1000,
/// 100Mbps link
Speed100,
/// 10Mbps link
Speed10,
/// Link speed not available/unknown
Unknown,
}
/// Defines whether an Ethernet link is capable of transmiting data both ways simultaneously
#[derive(Debug, Clone, Copy)]
pub enum Duplex {
/// Half-duplex link with multiplexed Tx and Rx
Half,
/// Full-duplex link capable of simultaneous Tx and Rx
Full,
/// Duplex mode not available/unknown
Unknown,
}
/// Represents the state of an Ethernet link
#[derive(Debug, Clone, Copy)]
pub enum EthernetLinkState {
Up(EthernetSpeed, Duplex),
Down,
}
impl L2Packet {
pub fn ethernet_frame(&self) -> &EthernetFrame {
bytemuck::from_bytes(
@ -78,3 +109,39 @@ pub fn handle(packet: L2Packet) {
}
}
}
impl fmt::Display for EthernetSpeed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let text = match self {
Self::Speed10 => "10Mbps",
Self::Speed100 => "100Mbps",
Self::Speed1000 => "1Gbps",
Self::Unknown => "N/A",
};
f.write_str(text)
}
}
impl fmt::Display for Duplex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let text = match self {
Self::Half => "half-duplex",
Self::Full => "full-duplex",
Self::Unknown => "N/A duplex mode",
};
f.write_str(text)
}
}
impl fmt::Display for EthernetLinkState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Up(speed, duplex) => {
write!(f, "up, speed {speed}, {duplex}")
}
Self::Down => {
write!(f, "down")
}
}
}
}

View File

@ -0,0 +1,17 @@
[package]
name = "ygg_driver_net_rtl81xx"
version = "0.1.0"
edition = "2024"
[dependencies]
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
ygg_driver_pci.path = "../../bus/pci"
ygg_driver_net_core.path = "../core"
tock-registers.workspace = true
log.workspace = true

View File

@ -0,0 +1,53 @@
#![no_std]
use alloc::sync::Arc;
use device_api::device::Device;
use libk::error::Error;
use rtl8139::Rtl8139;
use rtl8168::Rtl8168;
use ygg_driver_pci::{
device::{PciDeviceInfo, PreferredInterruptMode},
PciBaseAddress, PciCommandRegister, PciConfigurationSpace,
};
extern crate alloc;
pub mod rtl8139;
pub mod rtl8168;
pub fn probe_8168(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
let base = info
.config_space
.bar(2)
.and_then(PciBaseAddress::as_memory)
.ok_or(Error::InvalidArgument)?;
// if let Some(power) = info.config_space.capability::<PowerManagementCapability>() {
// power.set_device_power_state(DevicePowerState::D0);
// }
// Enable MMIO + interrupts + bus mastering
info.set_command(true, true, false, true);
let device = Rtl8168::new(base, info.clone())?;
Ok(Arc::new(device))
}
pub fn probe_8139(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
info.init_interrupts(PreferredInterruptMode::Msi(false))?;
// Enable MMIO + interrupts + bus mastering
let mut command = info.config_space.command();
command |= (PciCommandRegister::BUS_MASTER | PciCommandRegister::ENABLE_MEMORY).bits();
command &= !(PciCommandRegister::ENABLE_IO | PciCommandRegister::DISABLE_INTERRUPTS).bits();
info.config_space.set_command(command);
let base = info
.config_space
.bar(1)
.and_then(PciBaseAddress::as_memory)
.ok_or(Error::InvalidArgument)?;
let device = Rtl8139::new(base, info.clone())?;
Ok(Arc::new(device))
}

View File

@ -0,0 +1,422 @@
use core::mem::MaybeUninit;
use alloc::sync::Arc;
use device_api::{device::Device, interrupt::InterruptHandler};
use libk::error::Error;
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
device::DeviceMemoryIo,
PageBox,
};
use libk_util::{queue::BoundedQueue, sync::IrqSafeSpinlock, OneTimeInit};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use ygg_driver_net_core::{
interface::{NetworkDevice, NetworkInterfaceType},
Packet,
};
use ygg_driver_pci::device::PciDeviceInfo;
use yggdrasil_abi::net::MacAddress;
register_bitfields! {
u8,
pub CR [
/// Reset. Setting this bit forces the RTL8139D(L) to perform a software reset. This bit is
/// cleared by the RTL8139D(L) after the reset completes.
RST OFFSET(4) NUMBITS(1) [],
/// Receiver enable
RE OFFSET(3) NUMBITS(1) [],
/// Transmitter enable
TE OFFSET(2) NUMBITS(1) [],
/// Rx buffer empty
BUFE OFFSET(0) NUMBITS(1) [],
],
pub CONFIG1 [
/// LED pin control
LEDS1 OFFSET(7) NUMBITS(1) [],
/// LED pin control
LEDS0 OFFSET(6) NUMBITS(1) [],
/// Driver load. Software may use this bit to make sure that the driver has been loaded.
DVRLOAD OFFSET(5) NUMBITS(1) [],
/// LWAKE active mode. The LWACT bit and LWPTN bit in CONFIG4 are used to program the LWAKE
/// pin's output signal.
LWACT OFFSET(4) NUMBITS(1) [],
/// The operational registers are mapped into PCI memory space
MEMMAP OFFSET(3) NUMBITS(1) [],
/// The operational registers are mapped into PCI I/O space
IOMAP OFFSET(2) NUMBITS(1) [],
/// Set to enable Vital Product Data
VPD OFFSET(1) NUMBITS(1) [],
/// Power management enable
PMEn OFFSET(0) NUMBITS(1) [],
],
}
register_bitfields! {
u16,
pub IMR_ISR [
/// System error interrupt
SERR OFFSET(15) NUMBITS(1) [],
/// Time out interrupt
TIMEOUT OFFSET(14) NUMBITS(1) [],
/// Cable length change interrupt
LENCHG OFFSET(13) NUMBITS(1) [],
/// Rx FIFO overflow interrupt
FOVW OFFSET(6) NUMBITS(1) [],
/// Packet underrun/Link change interrupt
PUN_LINKCHG OFFSET(5) NUMBITS(1) [],
/// Rx buffer overflow
RXOVW OFFSET(4) NUMBITS(1) [],
/// Tx error interrupt
TER OFFSET(3) NUMBITS(1) [],
/// Tx OK interrupt
TOK OFFSET(2) NUMBITS(1) [],
/// Rx error interrupt
RER OFFSET(1) NUMBITS(1) [],
/// Rx OK interrupt
ROK OFFSET(0) NUMBITS(1) [],
],
}
register_bitfields! {
u32,
pub TSDn [
/// Carrier sense lost
CRS OFFSET(31) NUMBITS(1) [],
/// Tx aborted
TABT OFFSET(30) NUMBITS(1) [],
/// Out of window collision
OWC OFFSET(29) NUMBITS(1) [],
/// CD heart beat
CDH OFFSET(28) NUMBITS(1) [],
/// Number of collision count
NCC OFFSET(24) NUMBITS(4) [],
/// Early Tx threshold
ERTXTH OFFSET(16) NUMBITS(6) [],
/// Tx OK
TOK OFFSET(15) NUMBITS(1) [],
/// Tx FIFO underrun
TUN OFFSET(14) NUMBITS(1) [],
/// Tx descriptor is owned by the DMA. Set to 0 by the driver to initiate a send
OWN OFFSET(13) NUMBITS(1) [],
/// Tx size
SIZE OFFSET(0) NUMBITS(13) [],
],
pub RCR [
/// Early Rx threshold bits
ERTH OFFSET(24) NUMBITS(4) [],
/// Multiple early interrupt select
MULERINT OFFSET(17) NUMBITS(1) [],
/// TBD, didn't really understand the docs, lmao
RER8 OFFSET(16) NUMBITS(1) [],
/// Rx FIFO threshold
RXFTH OFFSET(13) NUMBITS(3) [],
/// Rx buffer length (+16 bytes)
RBLEN OFFSET(11) NUMBITS(2) [
Len8K = 0,
Len16K = 1,
Len32K = 2,
Len64K = 3,
],
/// Max DMA burst size per Rx DMA burst
MXDMA OFFSET(8) NUMBITS(3) [],
/// When 0: the packet will get split if crossing the Rx buffer end
/// When 1: the packet will get placed contiguously
WRAP OFFSET(7) NUMBITS(1) [],
/// Accept error packet
AER OFFSET(5) NUMBITS(1) [],
/// Accept runt (packets smaller than 64 bytes)
AR OFFSET(4) NUMBITS(1) [],
/// Accept broadcast packets
AB OFFSET(3) NUMBITS(1) [],
/// Accept multicast packets
AM OFFSET(2) NUMBITS(1) [],
/// Accept physical match
APM OFFSET(1) NUMBITS(1) [],
/// Accept all packets
AAP OFFSET(0) NUMBITS(1) [],
],
}
register_structs! {
#[allow(non_snake_case)]
pub Regs {
(0x0000 => pub IDRn: [ReadWrite<u32>; 2]),
(0x0008 => pub MARn: [ReadWrite<u8>; 8]),
(0x0010 => pub TSDn: [ReadWrite<u32, TSDn::Register>; 4]),
(0x0020 => pub TSADn: [ReadWrite<u32>; 4]),
(0x0030 => pub RBSTART: ReadWrite<u32>),
(0x0034 => pub ERBCR: ReadOnly<u16>),
(0x0036 => pub ERSR: ReadOnly<u8>),
(0x0037 => pub CR: ReadWrite<u8, CR::Register>),
(0x0038 => pub CAPR: ReadWrite<u16>),
(0x003A => pub CBR: ReadOnly<u16>),
(0x003C => pub IMR: ReadWrite<u16, IMR_ISR::Register>),
(0x003E => pub ISR: ReadWrite<u16, IMR_ISR::Register>),
(0x0040 => pub TCR: ReadWrite<u32>),
(0x0044 => pub RCR: ReadWrite<u32, RCR::Register>),
(0x0048 => pub TCTR: ReadWrite<u32>),
(0x004C => pub MPC: ReadWrite<u32>),
(0x0050 => pub r9346CR: ReadWrite<u8>),
(0x0051 => pub CONFIG0: ReadOnly<u8>),
(0x0052 => pub CONFIG1: ReadWrite<u8, CONFIG1::Register>),
(0x0053 => _1),
(0x0054 => pub TIMERINT: ReadWrite<u32>),
(0x0058 => pub MSR: ReadWrite<u8>),
(0x0059 => pub CONFIG3: ReadWrite<u8>),
(0x005A => pub CONFIG4: ReadWrite<u8>),
(0x005B => _2),
(0x005C => pub MULINT: ReadWrite<u16>),
(0x005E => pub RERID: ReadOnly<u8>),
(0x005F => _3),
(0x0060 => pub TSAD: ReadOnly<u16>),
(0x0062 => pub BMCR: ReadWrite<u16>),
(0x0064 => pub BMSR: ReadOnly<u16>),
(0x0066 => pub ANAR: ReadWrite<u16>),
(0x0068 => pub ANLPAR: ReadOnly<u16>),
(0x006A => pub ANER: ReadOnly<u16>),
(0x006C => pub DIS: ReadOnly<u16>),
(0x006E => pub FCSC: ReadOnly<u16>),
(0x0070 => pub NWAYTR: ReadWrite<u16>),
(0x0072 => pub REC: ReadOnly<u16>),
(0x0074 => pub CSCR: ReadWrite<u16>),
(0x0076 => _4),
(0x0078 => pub PHY1_PARM: ReadWrite<u32>),
(0x007C => pub TW_PARM: ReadWrite<u32>),
(0x0080 => pub PHY2_PARM: ReadWrite<u8>),
(0x0081 => _5),
(0x0084 => pub CRCn: [ReadWrite<u8>; 8]),
(0x008C => pub WAKEUPn: [ReadWrite<u32>; 16]),
(0x00CC => pub LSBCRCn: [ReadWrite<u8>; 8]),
(0x00D4 => _6),
(0x00D8 => pub CONFIG5: ReadWrite<u8>),
(0x00D9 => _7),
(0x0100 => @END),
}
}
struct Rx {
buffer: PageBox<[MaybeUninit<u8>]>,
rd: usize,
}
// TODO place a secondary Tx queue here, to send the queued packets when more slots become
// available
struct Tx {
buffers: [Option<PageBox<[u8]>>; 4],
wr: usize,
rd: usize,
queue: BoundedQueue<PageBox<[u8]>>,
}
pub struct Rtl8139 {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
mac: MacAddress,
pci: PciDeviceInfo,
nic: OneTimeInit<u32>,
rx: OneTimeInit<IrqSafeSpinlock<Rx>>,
tx: IrqSafeSpinlock<Tx>,
}
impl Tx {
pub fn tx_now(&mut self, regs: &Regs, packet: PageBox<[u8]>) -> Result<(), Error> {
let packet_address = unsafe { packet.as_physical_address() }
.try_into_u32()
.map_err(|_| Error::InvalidArgument)?;
let packet_len = packet.len();
if packet_len > 1500 {
return Err(Error::InvalidArgument);
}
self.buffers[self.wr] = Some(packet);
regs.TSADn[self.wr].set(packet_address);
regs.TSDn[self.wr].write(TSDn::SIZE.val(packet_len as u32));
self.wr = (self.wr + 1) % 4;
Ok(())
}
pub fn is_full(&self) -> bool {
(self.wr + 1) % 4 == self.rd
}
}
impl Rtl8139 {
// 8K + overflow space
const RX_BUFFER_LEN: usize = 8192;
const RX_BUFFER_OVERFLOW: usize = 4096;
pub fn new(base: PhysicalAddress, info: PciDeviceInfo) -> Result<Self, Error> {
let regs = unsafe { DeviceMemoryIo::<Regs>::map(base, Default::default()) }?;
let mac0 = regs.IDRn[0].get().to_le_bytes();
let mac1 = regs.IDRn[1].get().to_le_bytes();
let mac = MacAddress::from([mac0[0], mac0[1], mac0[2], mac0[3], mac1[0], mac1[1]]);
Ok(Self {
mac,
regs: IrqSafeSpinlock::new(regs),
pci: info,
nic: OneTimeInit::new(),
rx: OneTimeInit::new(),
tx: IrqSafeSpinlock::new(Tx {
buffers: [const { None }; 4],
wr: 0,
rd: 0,
queue: BoundedQueue::new(64),
}),
})
}
}
impl InterruptHandler for Rtl8139 {
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
let regs = self.regs.lock();
let status = regs.ISR.extract();
// Clear ISR bits
regs.ISR.write(IMR_ISR::TOK::SET + IMR_ISR::ROK::SET);
let mut any = false;
if status.matches_all(IMR_ISR::TOK::SET) {
let mut tx = self.tx.lock();
tx.rd = (tx.rd + 1) % 4;
if let Some(packet) = tx.queue.pop() {
// Should not fail, we just freed a single descriptor
tx.tx_now(&regs, packet).unwrap();
}
any = true;
}
if status.matches_all(IMR_ISR::ROK::SET) {
let nic = *self.nic.get();
let mut rx = self.rx.get().lock();
let cbr = regs.CBR.get();
loop {
let rx_pos = rx.rd % Self::RX_BUFFER_LEN;
if rx_pos == cbr as usize {
break;
}
// 4-byte preamble
let rx_len_0 = unsafe { rx.buffer[rx_pos + 2].assume_init() };
let rx_len_1 = unsafe { rx.buffer[rx_pos + 3].assume_init() };
let rx_len = u16::from_le_bytes([rx_len_0, rx_len_1]) as usize;
if rx_len >= 16 {
if let Ok(mut packet_buf) = PageBox::new_uninit_slice(rx_len) {
packet_buf.copy_from_slice(&rx.buffer[rx_pos + 4..rx_pos + rx_len + 4]);
let packet_buf = unsafe { packet_buf.assume_init_slice() };
let packet = Packet::new(packet_buf, 0, nic);
ygg_driver_net_core::receive_packet(packet).ok();
}
}
// rx_len + 4, aligned to 4 bytes
let total_len = (rx_len + 7) & !3;
rx.rd = rx.rd.wrapping_add(total_len);
}
regs.CAPR.set((rx.rd as u16).max(cbr).wrapping_sub(0x10));
rx.rd %= Self::RX_BUFFER_LEN;
any = true;
}
any
}
}
impl Device for Rtl8139 {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
log::info!("Initialize rtl8139 driver");
log::info!("MAC: {}", self.mac);
// Setup the initial Rx buffer
let rx_buffer = PageBox::new_uninit_slice(Self::RX_BUFFER_LEN + Self::RX_BUFFER_OVERFLOW)?;
let rx_buffer_address = unsafe { rx_buffer.as_physical_address() }
.try_into_u32()
.map_err(|_| Error::InvalidArgument)?;
self.pci.map_interrupt(Default::default(), self.clone())?;
let regs = self.regs.lock();
// Power up the device
regs.CONFIG1.set(0);
// Reset the device
let mut timeout = 1000000;
regs.CR.write(CR::RST::SET);
while timeout > 0 && regs.CR.matches_all(CR::RST::SET) {
core::hint::spin_loop();
timeout -= 1;
}
if timeout == 0 {
log::error!("rtl8139: reset timed out");
return Err(Error::TimedOut);
}
// Configure Rx
regs.RBSTART.set(rx_buffer_address);
regs.RCR
.write(RCR::WRAP::SET + RCR::AB::SET + RCR::APM::SET + RCR::AR::SET + RCR::AM::SET);
// Enable Rx/Tx interrupts
regs.IMR.write(IMR_ISR::ROK::SET + IMR_ISR::TOK::SET);
// Start Rx/Tx
regs.CR.modify(CR::TE::SET + CR::RE::SET);
self.rx.init(IrqSafeSpinlock::new(Rx {
buffer: rx_buffer,
rd: 0,
}));
let nic =
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
self.nic.init(nic.id());
Ok(())
}
fn display_name(&self) -> &str {
"Realtek RTL8139 10/100Mbps Ethernet Controller"
}
}
impl NetworkDevice for Rtl8139 {
fn transmit(&self, packet: PageBox<[u8]>) -> Result<(), Error> {
let mut tx = self.tx.lock();
// Buffer still in Tx, cannot send
if tx.is_full() {
if tx.queue.push(packet).is_err() {
log::warn!("rtl8139: out of tx buffers + queue full");
return Err(Error::WouldBlock);
} else {
return Ok(());
}
}
let regs = self.regs.lock();
tx.tx_now(&regs, packet)
}
fn packet_prefix_size(&self) -> usize {
0
}
fn read_hardware_address(&self) -> MacAddress {
self.mac
}
}

View File

@ -0,0 +1,722 @@
use core::{
mem::{self, MaybeUninit},
sync::atomic::{self, Ordering},
};
use alloc::{sync::Arc, vec::Vec};
use device_api::{device::Device, interrupt::InterruptHandler};
use libk::error::Error;
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
device::DeviceMemoryIo,
PageBox,
};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use ygg_driver_net_core::{
ethernet::{Duplex, EthernetLinkState, EthernetSpeed},
interface::{NetworkDevice, NetworkInterfaceType},
Packet,
};
use ygg_driver_pci::device::{PciDeviceInfo, PreferredInterruptMode};
use yggdrasil_abi::{bitflags, net::MacAddress};
register_bitfields! {
u8,
CR [
/// Software reset bit. Set by driver, cleared by device
RST OFFSET(4) NUMBITS(1) [],
/// Rx enable
RE OFFSET(3) NUMBITS(1) [],
/// Tx enable
TE OFFSET(2) NUMBITS(1) [],
],
TPPOLL [
HPQ OFFSET(7) NUMBITS(1) [],
NPQ OFFSET(6) NUMBITS(1) [],
FSWInt OFFSET(0) NUMBITS(1) [],
],
PHYSTATUS [
TXFLOW OFFSET(6) NUMBITS(1) [],
RXFLOW OFFSET(5) NUMBITS(1) [],
MF1000 OFFSET(4) NUMBITS(1) [],
M100 OFFSET(3) NUMBITS(1) [],
M10 OFFSET(2) NUMBITS(1) [],
LINKSTS OFFSET(1) NUMBITS(1) [],
FULLDUP OFFSET(0) NUMBITS(1) [],
],
}
register_bitfields! {
u16,
IMR_ISR [
TIMEOUT OFFSET(14) NUMBITS(1) [],
FEMP OFFSET(9) NUMBITS(1) [],
SWINT OFFSET(8) NUMBITS(1) [],
TDU OFFSET(7) NUMBITS(1) [],
FOVW OFFSET(6) NUMBITS(1) [],
LINKCHG OFFSET(5) NUMBITS(1) [],
RDU OFFSET(4) NUMBITS(1) [],
TER OFFSET(3) NUMBITS(1) [],
TOK OFFSET(2) NUMBITS(1) [],
RER OFFSET(1) NUMBITS(1) [],
ROK OFFSET(0) NUMBITS(1) [],
],
CPCR [
/// (rtl8169sc) PCI endianness mode
ENDIAN OFFSET(9) NUMBITS(1) [
Little = 0,
Big = 1,
],
/// (no docs found) TBD
MACSTATDIS OFFSET(7) NUMBITS(1) [],
/// Rx VLAN de-tagging enable
RXVLAN OFFSET(6) NUMBITS(1) [],
/// Rx checksum offload enable
RXCHKSUM OFFSET(5) NUMBITS(1) [],
/// PCI dual address cycle enable
DAC OFFSET(4) NUMBITS(1) [],
/// PCI multiple read/write enable
MULRW OFFSET(3) NUMBITS(1) [],
// Not documented, found in OpenBSD's re driver
/// C+ Rx mode enable
RXENB OFFSET(1) NUMBITS(1) [],
/// C+ Tx mode enable
TXENB OFFSET(0) NUMBITS(1) [],
],
}
register_bitfields! {
u32,
TCR [
HWVERID0 OFFSET(28) NUMBITS(2) [],
HWVERID1 OFFSET(26) NUMBITS(1) [],
IFG OFFSET(24) NUMBITS(2) [],
HWVERID2 OFFSET(23) NUMBITS(1) [],
TX_NOCRC OFFSET(16) NUMBITS(1) [],
MXDMA OFFSET(8) NUMBITS(1) [
Burst16 = 0b000,
Burst32 = 0b001,
Burst64 = 0b010,
Burst128 = 0b011,
Burst256 = 0b100,
Burst512 = 0b101,
Unlimited = 0b111,
],
],
RCR [
RXFTH OFFSET(13) NUMBITS(3) [
Threshold64 = 0b010,
Threshold128 = 0b011,
Threshold256 = 0b100,
Threshold512 = 0b101,
Threshold1024 = 0b110,
NoThreshold = 0b111,
],
EARLYOFFV2 OFFSET(11) NUMBITS(1) [],
MXDMA OFFSET(8) NUMBITS(3) [
Burst64 = 0b010,
Burst128 = 0b011,
Burst256 = 0b100,
Burst512 = 0b101,
Burst1024 = 0b110,
Unlimited = 0b111
],
R9356SEL OFFSET(6) NUMBITS(1) [],
AER OFFSET(5) NUMBITS(1) [],
AR OFFSET(4) NUMBITS(1) [],
AB OFFSET(3) NUMBITS(1) [],
AM OFFSET(2) NUMBITS(1) [],
APM OFFSET(1) NUMBITS(1) [],
AAP OFFSET(0) NUMBITS(1) [],
],
PHYAR [
FLAG OFFSET(31) NUMBITS(1) [
Read = 0,
Write = 1,
],
REGADDR OFFSET(16) NUMBITS(5) [],
DATA OFFSET(0) NUMBITS(16) [],
],
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x0000 => IDRn: [ReadWrite<u32>; 2]),
(0x0008 => MARn: [ReadWrite<u32>; 2]),
(0x0010 => DTCCR: ReadWrite<u64>),
(0x0018 => _0),
(0x0020 => TNPDS: [ReadWrite<u32>; 2]),
(0x0028 => THPDS: [ReadWrite<u32>; 2]),
(0x0030 => _1),
(0x0037 => CR: ReadWrite<u8, CR::Register>),
(0x0038 => TPPOLL: WriteOnly<u8, TPPOLL::Register>),
(0x0039 => _2),
(0x003C => IMR: ReadWrite<u16, IMR_ISR::Register>),
(0x003E => ISR: ReadWrite<u16, IMR_ISR::Register>),
(0x0040 => TCR: ReadWrite<u32, TCR::Register>),
(0x0044 => RCR: ReadWrite<u32, RCR::Register>),
(0x0048 => TCTR: ReadWrite<u32>),
(0x004C => MPKT: ReadWrite<u32>),
(0x0050 => R9346CR: ReadWrite<u8>),
(0x0051 => CONFIG0: ReadWrite<u8>),
(0x0052 => CONFIG1: ReadWrite<u8>),
(0x0053 => CONFIG2: ReadWrite<u8>),
(0x0054 => CONFIG3: ReadWrite<u8>),
(0x0055 => CONFIG4: ReadWrite<u8>),
(0x0056 => CONFIG5: ReadWrite<u8>),
(0x0057 => _4),
(0x0058 => TIMERINT: ReadWrite<u32>),
(0x005C => _5),
(0x0060 => PHYAR: ReadWrite<u32, PHYAR::Register>),
(0x0064 => _6),
(0x006C => PHYSTATUS: ReadOnly<u8, PHYSTATUS::Register>),
(0x006D => _7),
(0x0084 => WAKEUPn: [ReadWrite<u32>; 16]),
(0x00C4 => CRCn: [ReadWrite<u16>; 5]),
(0x00CE => _8),
(0x00DA => RMS: ReadWrite<u16>),
(0x00DC => _9),
(0x00E0 => CPCR: ReadWrite<u16, CPCR::Register>),
(0x00E2 => _10),
(0x00E4 => RDSAR: [ReadWrite<u32>; 2]),
(0x00EC => MTPS: ReadWrite<u8>),
(0x00ED => _11),
(0x00F0 => MISC: ReadWrite<u32>),
(0x00F4 => _12),
(0x0100 => @END),
}
}
bitflags! {
struct ChipFlags: u32 {
const MACSTAT: bit 0;
const NEED_PHY_RESET: bit 1;
const RXDV_GATED: bit 2;
const EARLYOFFV2: bit 3;
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[allow(non_camel_case_types)]
enum Revision {
rtl8168gu,
rtl8168h,
other,
}
#[repr(C)]
struct Descriptor {
cmd: u32,
vlan_cmd: u32,
address: PhysicalAddress,
}
struct RxRing {
entries: PageBox<[Descriptor]>,
buffers: Vec<PageBox<[MaybeUninit<u8>]>>,
rd: usize,
}
struct TxRing {
entries: PageBox<[Descriptor]>,
buffers: Vec<Option<PageBox<[u8]>>>,
wr: usize,
rd: usize,
}
pub struct Rtl8168 {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
mac: MacAddress,
pci: PciDeviceInfo,
nic: OneTimeInit<u32>,
rx: OneTimeInit<IrqSafeSpinlock<RxRing>>,
tx: OneTimeInit<IrqSafeSpinlock<TxRing>>,
}
impl RxRing {
pub fn with_capacity(capacity: usize) -> Result<Self, Error> {
let buffers = (0..capacity)
.map(|_| PageBox::new_uninit_slice(4096))
.collect::<Result<Vec<_>, _>>()?;
let entries = PageBox::new_slice_with(
|i| {
Descriptor::new_rx(
unsafe { buffers[i].as_physical_address() },
i == capacity - 1,
)
},
capacity,
)?;
unsafe { core::arch::asm!("wbinvd") };
Ok(Self {
entries,
buffers,
rd: 0,
})
}
pub fn consume<F: Fn(PageBox<[u8]>)>(&mut self, handler: F) -> Result<usize, Error> {
let mut count = 0;
loop {
let index = self.rd % self.entries.len();
let entry = &self.entries[index];
if entry.is_host_owned() {
let new_buffer = PageBox::new_uninit_slice(4096)?;
let new_buffer_address = unsafe { new_buffer.as_physical_address() };
let buffer = mem::replace(&mut self.buffers[index], new_buffer);
let buffer = unsafe { buffer.assume_init_slice() };
handler(buffer);
self.entries[index].setup_rx(new_buffer_address);
self.rd = self.rd.wrapping_add(1);
count += 1;
} else {
break;
}
}
Ok(count)
}
pub fn base_address(&self) -> PhysicalAddress {
unsafe { self.entries.as_physical_address() }
}
}
impl TxRing {
pub fn with_capacity(capacity: usize) -> Result<Self, Error> {
let entries =
PageBox::new_slice_with(|i| Descriptor::empty_tx(i == capacity - 1), capacity)?;
let buffers = (0..capacity).map(|_| None).collect();
Ok(Self {
entries,
buffers,
wr: 0,
rd: 0,
})
}
pub fn is_full(&self) -> bool {
self.wr.wrapping_add(1) % self.entries.len() == self.rd % self.entries.len()
}
pub fn push(&mut self, packet: PageBox<[u8]>) -> Result<(), Error> {
let packet_base = unsafe { packet.as_physical_address() };
let packet_size = packet.len();
// TODO packet size checks
if self.is_full() {
log::warn!("rtl81xx: tx ring full: wr={}, rd={}", self.wr, self.rd);
return Err(Error::WouldBlock);
}
let index = self.wr % self.entries.len();
self.buffers[index] = Some(packet);
self.entries[index].setup_tx(packet_base, packet_size);
self.wr = self.wr.wrapping_add(1);
Ok(())
}
pub fn consume(&mut self) -> Result<usize, Error> {
let mut count = 0;
loop {
let index = self.rd % self.entries.len();
let entry = &mut self.entries[index];
if self.rd != self.wr && entry.is_host_owned() {
self.rd = self.rd.wrapping_add(1);
count += 1;
} else {
break;
}
}
Ok(count)
}
pub fn base_address(&self) -> PhysicalAddress {
unsafe { self.entries.as_physical_address() }
}
}
impl Descriptor {
// Descriptor owned by DMA
const CMD_OWN: u32 = 1 << 31;
// Last descriptor in the ring
const CMD_END: u32 = 1 << 30;
// Last segment of a packet
const CMD_LS: u32 = 1 << 28;
// First segment of a packet
const CMD_FS: u32 = 1 << 29;
pub fn new_rx(buffer: PhysicalAddress, last: bool) -> Self {
let mut cmd = Self::CMD_OWN | 0x1000;
if last {
cmd |= Self::CMD_END;
}
Self {
cmd,
vlan_cmd: 0,
address: buffer,
}
}
pub fn empty_tx(last: bool) -> Self {
let cmd = if last { Self::CMD_END } else { 0 };
Self {
cmd,
vlan_cmd: 0,
address: PhysicalAddress::ZERO,
}
}
pub fn setup_rx(&mut self, buffer: PhysicalAddress) {
let cmd = self.cmd;
self.address = buffer;
self.vlan_cmd = 0;
unsafe {
atomic::fence(Ordering::Release);
core::arch::asm!("wbinvd");
core::ptr::write_volatile(&mut self.cmd, cmd | Self::CMD_OWN);
}
}
pub fn setup_tx(&mut self, buffer: PhysicalAddress, size: usize) {
let mut cmd = self.cmd;
cmd |= Self::CMD_OWN | Self::CMD_FS | Self::CMD_LS | (size as u32);
self.address = buffer;
self.vlan_cmd = 0;
unsafe {
atomic::fence(Ordering::Release);
core::arch::asm!("wbinvd");
core::ptr::write_volatile(&mut self.cmd, cmd);
}
}
pub fn is_host_owned(&self) -> bool {
self.cmd & Self::CMD_OWN == 0
}
}
impl Regs {
const TCR_REVISION_MASK: u32 = 0x7C800000;
pub fn gmii_write(&self, reg: u8, value: u16, mut timeout: u64) -> Result<(), Error> {
self.PHYAR.write(
PHYAR::REGADDR.val(reg as u32) + PHYAR::DATA.val(value as u32) + PHYAR::FLAG::Write,
);
while timeout > 0 && self.PHYAR.matches_all(PHYAR::FLAG::SET) {
core::hint::spin_loop();
timeout -= 1;
}
if timeout == 0 {
Err(Error::TimedOut)
} else {
Ok(())
}
}
#[allow(unused)]
pub fn gmii_read(&self, reg: u8, mut timeout: u64) -> Result<u16, Error> {
self.PHYAR
.write(PHYAR::REGADDR.val(reg as u32) + PHYAR::FLAG::Read);
loop {
if timeout == 0 {
return Err(Error::TimedOut);
}
let status = self.PHYAR.extract();
if status.matches_all(PHYAR::FLAG::SET) {
return Ok(status.read(PHYAR::DATA) as u16);
}
core::hint::spin_loop();
timeout -= 1;
}
}
pub fn get_link_state(&self) -> EthernetLinkState {
let phystatus = self.PHYSTATUS.extract();
if phystatus.matches_all(PHYSTATUS::LINKSTS::SET) {
let duplex = if phystatus.matches_all(PHYSTATUS::FULLDUP::SET) {
Duplex::Full
} else {
Duplex::Half
};
let (speed, duplex) = if phystatus.matches_all(PHYSTATUS::MF1000::SET) {
// 1000MF is always Full-Duplex
(EthernetSpeed::Speed1000, Duplex::Full)
} else if phystatus.matches_all(PHYSTATUS::M100::SET) {
(EthernetSpeed::Speed100, duplex)
} else if phystatus.matches_all(PHYSTATUS::M10::SET) {
(EthernetSpeed::Speed10, duplex)
} else {
(EthernetSpeed::Unknown, duplex)
};
EthernetLinkState::Up(speed, duplex)
} else {
EthernetLinkState::Down
}
}
pub fn get_chip_revision(&self) -> Revision {
let rev = self.TCR.get() & Self::TCR_REVISION_MASK;
match rev {
0x50800000 => Revision::rtl8168gu,
0x54000000 => Revision::rtl8168h,
_ => Revision::other,
}
}
pub fn reset_device(&self, mut timeout: u64) -> Result<(), Error> {
self.CR.write(CR::RST::SET);
while timeout > 0 && self.CR.matches_all(CR::RST::SET) {
core::hint::spin_loop();
timeout -= 1;
}
if timeout == 0 {
Err(Error::TimedOut)
} else {
Ok(())
}
}
pub fn reset_phy(&self, timeout: u64) -> Result<(), Error> {
// Set the reset bit in bmcr
self.gmii_write(0x00, 1 << 15, timeout)?;
for _ in 0..timeout {
core::hint::spin_loop();
}
// Disable power off mode + start auto-negotiation
self.gmii_write(0x00, 1 << 12, timeout)?;
for _ in 0..timeout {
core::hint::spin_loop();
}
self.gmii_write(0x1F, 0x00, timeout)?;
self.gmii_write(0x0E, 0x00, timeout)?;
Ok(())
}
// #[inline]
// pub fn lock_config(&self) {
// self.R9346CR.set(0x00);
// }
// #[inline]
// pub fn unlock_config(&self) {
// self.R9346CR.set(0xC0);
// }
}
impl Rtl8168 {
pub fn new(base: PhysicalAddress, info: PciDeviceInfo) -> Result<Self, Error> {
let regs = unsafe { DeviceMemoryIo::<Regs>::map(base, Default::default()) }?;
let mac0 = regs.IDRn[0].get().to_le_bytes();
let mac1 = regs.IDRn[1].get().to_le_bytes();
let mac = MacAddress::from([mac0[0], mac0[1], mac0[2], mac0[3], mac1[0], mac1[1]]);
Ok(Self {
regs: IrqSafeSpinlock::new(regs),
mac,
pci: info,
nic: OneTimeInit::new(),
rx: OneTimeInit::new(),
tx: OneTimeInit::new(),
})
}
}
impl InterruptHandler for Rtl8168 {
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
let regs = self.regs.lock();
let status = regs.ISR.extract();
if status.get() == 0 {
return false;
}
regs.ISR.set(status.get());
let mut any = false;
if status.matches_all(IMR_ISR::LINKCHG::SET) {
let state = regs.get_link_state();
log::info!("rtl8168: link is {state}");
any = true;
}
if status.matches_all(IMR_ISR::ROK::SET) {
let mut rx = self.rx.get().lock();
let nic = *self.nic.get();
let count = rx
.consume(|buffer| {
// TODO add packet len hint to packets
let packet = Packet::new(buffer, 0, nic);
ygg_driver_net_core::receive_packet(packet).ok();
})
.unwrap_or(0);
any |= count != 0;
}
if status.matches_all(IMR_ISR::TOK::SET) {
let mut tx = self.tx.get().lock();
let count = tx.consume().unwrap_or(0);
any |= count != 0;
}
if status.matches_all(IMR_ISR::RDU::SET) {
log::info!("rtl8168: rdu");
any |= true;
}
if !any {
log::warn!("rtl8168: possibly unhandled IRQ, ISR={:#x}", status.get());
}
any
}
}
impl Device for Rtl8168 {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
log::info!("Initialize rtl8168");
log::info!("MAC: {}", self.mac);
let rx_ring = RxRing::with_capacity(256)?;
let tx_ring = TxRing::with_capacity(256)?;
let rx_ring_base = rx_ring.base_address().into_u64();
let tx_ring_base = tx_ring.base_address().into_u64();
let regs = self.regs.lock();
let hwrev = regs.get_chip_revision();
log::info!("Revision: {:?}", hwrev);
let flags = hwrev.flags();
// Perform software reset
regs.reset_device(10000000)
.inspect_err(|_| log::warn!("rtl81xx: reset timed out"))?;
// Enable C+ mode for Rx/Tx
let mut cpcr = CPCR::TXENB::SET + CPCR::MULRW::SET + CPCR::RXCHKSUM::SET;
if flags.contains(ChipFlags::MACSTAT) {
cpcr += CPCR::MACSTATDIS::SET;
} else {
cpcr += CPCR::RXENB::SET;
}
regs.CPCR.write(cpcr);
// Reset the PHY
if flags.contains(ChipFlags::NEED_PHY_RESET) {
regs.reset_phy(10000000)
.inspect_err(|_| log::warn!("rtl81xx: PHY reset timed out"))?;
}
regs.RDSAR[1].set((rx_ring_base >> 32) as u32);
regs.RDSAR[0].set(rx_ring_base as u32);
regs.TNPDS[1].set((tx_ring_base >> 32) as u32);
regs.TNPDS[0].set(tx_ring_base as u32);
if flags.contains(ChipFlags::RXDV_GATED) {
regs.MISC.set(regs.MISC.get() & !0x80000);
}
log::info!("rx_ring_base = {rx_ring_base:#x}");
unsafe { core::arch::asm!("wbinvd") };
// Setup Tx
regs.TCR.modify(TCR::MXDMA::Unlimited);
// Setup Rx
let mut rcr = RCR::MXDMA::Unlimited
+ RCR::RXFTH::NoThreshold
+ RCR::AB::SET
+ RCR::AM::SET
+ RCR::APM::SET;
if flags.contains(ChipFlags::EARLYOFFV2) {
rcr += RCR::EARLYOFFV2::SET;
}
regs.RCR.write(rcr);
// Enable Rx/Tx
regs.CR.write(CR::RE::SET + CR::TE::SET);
// Setup interrupts
regs.IMR.set(0xFFFF);
regs.ISR.set(0xFFFF);
// Clear missed packet counter (?)
regs.MPKT.set(0);
regs.MTPS.set(31);
regs.RMS.set(0x1FFF);
regs.CONFIG1.set(regs.CONFIG1.get() | 0x20);
self.rx.init(IrqSafeSpinlock::new(rx_ring));
self.tx.init(IrqSafeSpinlock::new(tx_ring));
// Bind IRQ
self.pci
.init_interrupts(PreferredInterruptMode::Msi(false))?;
self.pci.map_interrupt(Default::default(), self.clone())?;
let interface =
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
self.nic.init(interface.id());
Ok(())
}
fn display_name(&self) -> &str {
"Realtek RTL8111/8168 Gigabit Ethernet Controller"
}
}
impl NetworkDevice for Rtl8168 {
fn transmit(&self, packet: PageBox<[u8]>) -> Result<(), Error> {
let mut tx = self.tx.get().lock();
let regs = self.regs.lock();
tx.push(packet)?;
regs.TPPOLL.write(TPPOLL::NPQ::SET);
Ok(())
}
fn packet_prefix_size(&self) -> usize {
0
}
fn read_hardware_address(&self) -> MacAddress {
self.mac
}
}
impl Revision {
fn flags(&self) -> ChipFlags {
match self {
Self::rtl8168h | Self::rtl8168gu => {
ChipFlags::NEED_PHY_RESET
| ChipFlags::MACSTAT
| ChipFlags::RXDV_GATED
| ChipFlags::EARLYOFFV2
}
Self::other => ChipFlags::empty(),
}
}
}

View File

@ -103,7 +103,7 @@ pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
let regs = unsafe { xhci_lib::Registers::new(bar0.try_into_usize().unwrap(), Mapper::new()) };
let xhci = Arc::new(Xhci::new(regs)?);
info.init_interrupts(PreferredInterruptMode::Msi)?;
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
info.map_interrupt(InterruptAffinity::Any, xhci.clone())?;
Ok(xhci)

View File

@ -176,7 +176,7 @@ impl<T: Transport + 'static> VirtioNet<T> {
transmit_count: usize,
) -> Result<(), Error> {
let receive_vector = if let Some(pci) = self.pci_device_info.as_ref() {
pci.init_interrupts(PreferredInterruptMode::Msi)?;
pci.init_interrupts(PreferredInterruptMode::Msi(true))?;
let info = pci.map_interrupt(InterruptAffinity::Any, self.clone())?;
info.map(|info| info.vector as u16)

View File

@ -25,6 +25,10 @@ pub fn device() -> Option<&'static Arc<KObject<()>>> {
object::DEVICE_OBJECT.try_get()
}
pub fn bus() -> Option<&'static Arc<KObject<()>>> {
object::BUS_OBJECT.try_get()
}
pub fn init() {
ROOT.init(object::setup_fixed_objects());
}

View File

@ -1,3 +1,5 @@
use core::ops::{Deref, DerefMut};
use alloc::sync::Arc;
use libk_util::OneTimeInit;
use yggdrasil_abi::{error::Error, io::FileMode};
@ -42,6 +44,20 @@ impl<D> KObject<D> {
}
}
impl<D> Deref for KObject<D> {
type Target = D;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<D> DerefMut for KObject<D> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
unsafe impl<D: Send> Send for KObject<D> {}
unsafe impl<D: Send> Sync for KObject<D> {}
@ -54,6 +70,8 @@ pub static KERNEL_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
pub static DEVICE_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
// `/debug`
pub static DEBUG_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
// `/bus`
pub static BUS_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
fn setup_fixed_object(root: &Arc<KObject<()>>, obj: &OneTimeInit<Arc<KObject<()>>>, name: &str) {
let obj = obj.init(KObject::new(()));
@ -66,6 +84,7 @@ pub fn setup_fixed_objects() -> Arc<Node> {
setup_fixed_object(root, &KERNEL_OBJECT, "kernel");
setup_fixed_object(root, &DEVICE_OBJECT, "device");
setup_fixed_object(root, &DEBUG_OBJECT, "debug");
setup_fixed_object(root, &BUS_OBJECT, "bus");
root.node.clone()
}

View File

@ -11,7 +11,7 @@ use libk_mm::{
};
use libk_util::sync::IrqSafeSpinlock;
use crate::arch::x86::intrinsics::{io_wait, IoPort, IoPortAccess};
use kernel_arch_x86::intrinsics::{io_wait, IoPort, IoPortAccess};
#[derive(Clone, Copy)]
#[repr(C)]

View File

@ -5,6 +5,7 @@
use abi::error::Error;
use alloc::{sync::Arc, vec::Vec};
use device_api::{device::Device, interrupt::Irq};
use kernel_arch_x86::ISA_IRQ_OFFSET;
use libk::{
config, debug,
fs::{devfs, sysfs},
@ -22,13 +23,6 @@ use crate::fs::{Initrd, INITRD_DATA};
use super::L3;
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
pub const ISA_IRQ_OFFSET: u32 = crate::arch::x86_64::ISA_IRQ_OFFSET;
#[cfg(any(target_arch = "x86", rust_analyzer))]
pub const ISA_IRQ_OFFSET: u32 = 0;
pub mod intrinsics;
mod pci;
pub mod peripherals;
@ -95,6 +89,18 @@ pub fn register_pci_drivers() {
0x1000,
ygg_driver_virtio_net::probe,
);
ygg_driver_pci::register_vendor_driver(
"Realtek RTL8139 10/100Mbps Ethernet",
0x10EC,
0x8139,
ygg_driver_net_rtl81xx::probe_8139,
);
ygg_driver_pci::register_vendor_driver(
"Realtek RTL8168/8111 Gigabit Ethernet",
0x10EC,
0x8168,
ygg_driver_net_rtl81xx::probe_8168,
);
}
// Initialize the bare minimum required to:

View File

@ -1,10 +1,7 @@
use kernel_arch_x86::intrinsics::{IoPort, IoPortAccess};
use libk_util::sync::IrqSafeSpinlock;
use ygg_driver_pci::LegacyPciAccess;
use crate::arch::x86::intrinsics::IoPortAccess;
use super::intrinsics::IoPort;
struct LegacyPciInner {
address: IoPort<u32>,
data: IoPort<u32>,

View File

@ -4,13 +4,12 @@ use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq},
};
use libk::{device::external_interrupt_controller, task::runtime, time};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use crate::arch::x86::{
use kernel_arch_x86::{
intrinsics::{IoPort, IoPortAccess},
ISA_IRQ_OFFSET,
};
use libk::{device::external_interrupt_controller, task::runtime, time};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
const FREQUENCY: u32 = 1193180;

View File

@ -9,10 +9,9 @@ use device_api::{
IrqOptions,
},
};
use kernel_arch_x86::intrinsics::{IoPort, IoPortAccess};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use crate::arch::x86::intrinsics::{IoPort, IoPortAccess};
#[cfg(any(target_arch = "x86", rust_analyzer))]
use crate::arch::i686::exception;
#[cfg(any(target_arch = "x86", rust_analyzer))]

View File

@ -8,14 +8,14 @@ use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq},
};
use kernel_arch_x86::{
intrinsics::{IoPort, IoPortAccess},
ISA_IRQ_OFFSET,
};
use libk::device::external_interrupt_controller;
use libk_util::sync::IrqSafeSpinlock;
use crate::arch::x86::{
intrinsics::{IoPort, IoPortAccess},
peripherals::ps2::codeset::{CODE_SET_1_00, CODE_SET_1_E0},
ISA_IRQ_OFFSET,
};
use codeset::{CODE_SET_1_00, CODE_SET_1_E0};
mod codeset;

View File

@ -5,13 +5,12 @@ use device_api::{
interrupt::{InterruptHandler, Irq},
};
use kernel_arch::{Architecture, ArchitectureImpl};
use libk::{device::external_interrupt_controller, time};
use libk_util::sync::IrqSafeSpinlock;
use crate::arch::x86::{
use kernel_arch_x86::{
intrinsics::{io_wait, IoPort, IoPortAccess},
ISA_IRQ_OFFSET,
};
use libk::{device::external_interrupt_controller, time};
use libk_util::sync::IrqSafeSpinlock;
const NMI_DISABLE: u8 = 1 << 7;
const CMOS_REG_SEC: u8 = 0x00;

View File

@ -5,6 +5,7 @@ use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq},
};
use kernel_arch_x86::intrinsics::{IoPort, IoPortAccess};
use libk::{
debug::DebugSink,
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
@ -12,8 +13,6 @@ use libk::{
};
use libk_util::sync::IrqSafeSpinlock;
use crate::arch::x86::intrinsics::{IoPort, IoPortAccess};
// Single port
struct Regs {
dr: IoPort<u8>,

View File

@ -1,390 +0,0 @@
//! x86-64 implementation of ACPI management interfaces
use core::{
alloc::{AllocError, Allocator, GlobalAlloc, Layout},
ptr::NonNull,
sync::atomic::Ordering,
time::Duration,
};
use ::acpi::{AcpiHandler, AcpiTables, PhysicalMapping};
use acpi_system::{
AcpiInterruptMethod, AcpiSleepState, AcpiSystem, AcpiSystemError, EventAction, FixedEvent,
};
use alloc::{boxed::Box, sync::Arc};
use device_api::{
device::Device,
interrupt::{InterruptHandler, IpiDeliveryTarget, IpiMessage, Irq},
};
use kernel_arch_x86_64::CPU_COUNT;
use libk::device::external_interrupt_controller;
use libk_mm::{
address::{PhysicalAddress, Virtualize},
heap::GLOBAL_HEAP,
pointer::PhysicalRef,
};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use yggdrasil_abi::error::Error;
use crate::{
arch::{
x86_64::{apic::ioapic::ISA_IRQ_OFFSET, SHUTDOWN_FENCE},
Platform, PLATFORM,
},
mem::{read_memory, write_memory},
};
use super::intrinsics;
#[derive(Clone, Copy)]
#[doc(hidden)]
pub struct AcpiAllocator;
#[derive(Clone, Copy)]
#[doc(hidden)]
pub struct AcpiHandlerImpl;
struct SciHandler;
static ACPI_SYSTEM: OneTimeInit<IrqSafeSpinlock<AcpiSystem<AcpiHandlerImpl>>> = OneTimeInit::new();
// impl Device for SciHandler {
// fn display_name(&self) -> &'static str {
// "ACPI interrupt handler"
// }
// }
impl Device for SciHandler {
fn display_name(&self) -> &str {
"ACPI SCI Handler"
}
}
impl InterruptHandler for SciHandler {
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
log::trace!("ACPI SCI received");
ACPI_SYSTEM.get().lock().handle_sci();
true
}
}
unsafe impl Allocator for AcpiAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let ptr = unsafe { GLOBAL_HEAP.alloc(layout) };
log::trace!("ACPI alloc: {:?} -> {:p}", layout, ptr);
if ptr.is_null() {
Err(AllocError)
} else {
unsafe {
Ok(NonNull::slice_from_raw_parts(
NonNull::new_unchecked(ptr),
layout.size(),
))
}
}
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
log::trace!("ACPI dealloc: {:?}, {:?}", ptr, layout);
GLOBAL_HEAP.dealloc(ptr.as_ptr(), layout);
}
}
impl acpi_system::Handler for AcpiHandlerImpl {
type MappedSlice = PhysicalRef<'static, [u8]>;
unsafe fn map_slice(address: u64, length: u64) -> Self::MappedSlice {
PhysicalRef::map_slice(
PhysicalAddress::from_u64(address),
length.try_into().unwrap(),
)
}
fn io_read_u8(port: u16) -> u8 {
let value = unsafe { intrinsics::inb(port) };
log::trace!("io_read_u8 {:#x} <- {:#x}", port, value);
value
}
fn io_read_u16(port: u16) -> u16 {
let value = unsafe { intrinsics::inw(port) };
log::trace!("io_read_u16 {:#x} <- {:#x}", port, value);
value
}
fn io_read_u32(port: u16) -> u32 {
let value = unsafe { intrinsics::inl(port) };
log::trace!("io_read_u32 {:#x} <- {:#x}", port, value);
value
}
fn io_write_u8(port: u16, value: u8) {
log::trace!("io_write_u8 {:#x}, {:#x}", port, value);
unsafe { intrinsics::outb(port, value) }
}
fn io_write_u16(port: u16, value: u16) {
log::trace!("io_write_u16 {:#x}, {:#x}", port, value);
unsafe { intrinsics::outw(port, value) }
}
fn io_write_u32(port: u16, value: u32) {
log::trace!("io_write_u32 {:#x}, {:#x}", port, value);
unsafe { intrinsics::outl(port, value) }
}
fn mem_read_u8(address: u64) -> u8 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u8 {:#x} -> {:#x}", address, value);
value
}
fn mem_read_u16(address: u64) -> u16 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u16 {:#x} -> {:#x}", address, value);
value
}
fn mem_read_u32(address: u64) -> u32 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u32 {:#x} -> {:#x}", address, value);
value
}
fn mem_read_u64(address: u64) -> u64 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u64 {:#x} -> {:#x}", address, value);
value
}
fn mem_write_u8(address: u64, value: u8) {
log::trace!("mem_write_u8 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn mem_write_u16(address: u64, value: u16) {
log::trace!("mem_write_u16 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn mem_write_u32(address: u64, value: u32) {
log::trace!("mem_write_u32 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn mem_write_u64(address: u64, value: u64) {
log::trace!("mem_write_u64 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn install_interrupt_handler(irq: u32) -> Result<(), AcpiSystemError> {
log::info!("Installing ACPI SCI handler at IRQ #{}", irq);
let intc = external_interrupt_controller().expect("No external intc");
let handler = Arc::new(SciHandler);
let irq = Irq::External(irq + ISA_IRQ_OFFSET);
intc.register_irq(irq, Default::default(), handler).unwrap();
intc.enable_irq(irq).unwrap();
Ok(())
}
fn stall(_duration: Duration) {
// TODO polling_sleep is not yet implemented properly
todo!()
// util::polling_sleep(duration).ok();
}
}
impl aml::Handler for AcpiHandlerImpl {
fn read_io_u8(&self, port: u16) -> u8 {
<Self as acpi_system::Handler>::io_read_u8(port)
}
fn read_io_u16(&self, port: u16) -> u16 {
<Self as acpi_system::Handler>::io_read_u16(port)
}
fn read_io_u32(&self, port: u16) -> u32 {
<Self as acpi_system::Handler>::io_read_u32(port)
}
fn write_io_u8(&self, port: u16, value: u8) {
<Self as acpi_system::Handler>::io_write_u8(port, value)
}
fn write_io_u16(&self, port: u16, value: u16) {
<Self as acpi_system::Handler>::io_write_u16(port, value)
}
fn write_io_u32(&self, port: u16, value: u32) {
<Self as acpi_system::Handler>::io_write_u32(port, value)
}
fn read_u8(&self, address: usize) -> u8 {
<Self as acpi_system::Handler>::mem_read_u8(address as u64)
}
fn read_u16(&self, address: usize) -> u16 {
<Self as acpi_system::Handler>::mem_read_u16(address as u64)
}
fn read_u32(&self, address: usize) -> u32 {
<Self as acpi_system::Handler>::mem_read_u32(address as u64)
}
fn read_u64(&self, address: usize) -> u64 {
<Self as acpi_system::Handler>::mem_read_u64(address as u64)
}
fn write_u8(&self, address: usize, value: u8) {
<Self as acpi_system::Handler>::mem_write_u8(address as u64, value)
}
fn write_u16(&self, address: usize, value: u16) {
<Self as acpi_system::Handler>::mem_write_u16(address as u64, value)
}
fn write_u32(&self, address: usize, value: u32) {
<Self as acpi_system::Handler>::mem_write_u32(address as u64, value)
}
fn write_u64(&self, address: usize, value: u64) {
<Self as acpi_system::Handler>::mem_write_u64(address as u64, value)
}
fn read_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u8 {
0xFF
}
fn read_pci_u16(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
) -> u16 {
0xFFFF
}
fn read_pci_u32(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
) -> u32 {
0xFFFFFFFF
}
fn write_pci_u8(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
_value: u8,
) {
}
fn write_pci_u16(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
_value: u16,
) {
}
fn write_pci_u32(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
_value: u32,
) {
}
fn read_ec_u8(&self, _address: u64) -> u8 {
0x00
}
fn write_ec_u8(&self, _address: u64, _value: u8) {}
fn sleep(&self, _duration: Duration) {
todo!()
// util::polling_sleep(duration).unwrap();
}
}
impl AcpiHandler for AcpiHandlerImpl {
// No actual address space modification is performed
unsafe fn map_physical_region<T>(
&self,
physical_address: usize,
size: usize,
) -> PhysicalMapping<Self, T> {
PhysicalMapping::new(
physical_address,
NonNull::new_unchecked(
PhysicalAddress::from_usize(physical_address).virtualize() as *mut T
),
size,
size,
*self,
)
}
// Unmap nothing, these addresses are "virtualized" to high address space
fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
}
/// Initializes ACPI management
#[allow(unused)]
pub fn init_acpi(tables: &'static AcpiTables<AcpiHandlerImpl>) -> Result<(), Error> {
// TODO currently broken for real HW
let mut system = AcpiSystem::new(tables, Box::new(AcpiHandlerImpl)).unwrap();
system.initialize(AcpiInterruptMethod::Apic).unwrap();
system
.enable_fixed_event(
&FixedEvent::POWER_BUTTON,
Box::new(|_| {
log::info!("Power button was pressed");
// TODO the correct way would be to
// 1. Nicely ask all the processes to quit
// 2. Wait for some time
// 3. Kill the remaining ones
// 4. Halt other cores
// 5. Sync filesystem
// 6. Do something with the devices
// 7. Actually enter the S5 state
unsafe {
PLATFORM
.send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)
.unwrap();
}
SHUTDOWN_FENCE.signal();
SHUTDOWN_FENCE.wait_all(CPU_COUNT.load(Ordering::Acquire));
log::info!("CPUs are parked, can shutdown now");
EventAction::EnterSleepState(AcpiSleepState::S5)
}),
)
.unwrap();
ACPI_SYSTEM.init(IrqSafeSpinlock::new(system));
Ok(())
}

View File

@ -9,6 +9,7 @@ use device_api::{
IrqLevel, IrqOptions, IrqTrigger,
},
};
use kernel_arch_x86::ISA_IRQ_OFFSET;
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock};
use tock_registers::{
@ -16,13 +17,12 @@ use tock_registers::{
register_structs,
registers::{ReadWrite, WriteOnly},
};
use ygg_driver_acpi::AcpiAllocator;
use crate::arch::x86_64::{acpi::AcpiAllocator, apic::local::BSP_APIC_ID};
use crate::arch::x86_64::apic::local::BSP_APIC_ID;
use super::{APIC_EXTERNAL_OFFSET, POPULATED_EXTERNAL_VECTORS};
pub const ISA_IRQ_OFFSET: u32 = 1024;
// IRQ 0 is timer, IRQ 1 reserved (for now?), +32 offset for exception entries
const IO_APIC_VECTOR_OFFSET: u32 = 32 + APIC_EXTERNAL_OFFSET;
@ -64,7 +64,7 @@ struct Inner {
pub struct IoApic {
inner: IrqSafeSpinlock<Inner>,
isa_redirections: [Option<IsaRedirection>; 16],
table: IrqSafeRwLock<FixedInterruptTable<{ POPULATED_EXTERNAL_VECTORS as usize }>>, // table: IrqSafeSpinlock<FixedInterruptTable<{ POPULATED_EXTERNAL_VECTORS as usize }>>,
table: IrqSafeRwLock<FixedInterruptTable<{ POPULATED_EXTERNAL_VECTORS as usize }>>,
}
impl Regs {
@ -83,7 +83,7 @@ impl Regs {
impl Inner {
fn map_gsi(&mut self, gsi: u32, vector: u32, apic_id: u32) -> Result<(), Error> {
assert!(gsi < self.max_gsi);
assert!(gsi <= self.max_gsi);
assert!(vector < 0x100);
log::info!("map_irq gsi{}, vec{}, apic{}", gsi, vector, apic_id);
@ -110,7 +110,7 @@ impl Inner {
}
fn configure_gsi(&mut self, gsi: u32, level: IrqLevel, trigger: IrqTrigger) {
assert!(gsi < self.max_gsi);
assert!(gsi <= self.max_gsi);
let mut low = self.regs.read(REG_REDIRECTION_BASE + gsi * 2);
@ -138,7 +138,7 @@ impl Inner {
}
fn set_gsi_enabled(&mut self, gsi: u32, enabled: bool) {
assert!(gsi < self.max_gsi);
assert!(gsi <= self.max_gsi);
let low = self.regs.read(REG_REDIRECTION_BASE + gsi * 2);
if enabled {

View File

@ -259,7 +259,7 @@ impl MessageInterruptController for LocalApic {
);
let value = 32 + APIC_MSI_OFFSET + i as u32;
let address = Self::base().into_usize() | ((self.id as usize) << 12);
let address = 0xFEE00000 | ((self.id as usize) << 12);
*msi = MsiInfo {
address,

View File

@ -3,13 +3,12 @@ use core::{mem::size_of, ops::DerefMut, ptr::null_mut, sync::atomic::Ordering};
use ::acpi::{mcfg::Mcfg, AcpiTables, HpetInfo, InterruptModel};
use abi::error::Error;
use acpi::{AcpiAllocator, AcpiHandlerImpl};
use alloc::{boxed::Box, sync::Arc};
use apic::{ioapic::IoApic, local::LocalApic};
use device_api::device::Device;
use kernel_arch_x86::{
cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures, ExtEdxFeatures},
gdt,
gdt, intrinsics,
};
use kernel_arch_x86_64::{
mem::{
@ -34,14 +33,14 @@ use libk_mm::{
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
table::{EntryLevel, EntryLevelExt},
};
use libk_util::{sync::SpinFence, OneTimeInit};
use libk_util::OneTimeInit;
use yboot_proto::{
v1::{self, AvailableMemoryRegion},
LoadProtocolV1,
};
use ygg_driver_acpi::{AcpiAllocator, AcpiHandlerImpl, EventAction, FixedEvent};
use ygg_driver_pci::PciBusManager;
mod acpi;
mod apic;
mod boot;
mod exception;
@ -55,13 +54,7 @@ use crate::{
use self::boot::BootData;
use super::{
x86::{intrinsics, InitrdSource},
Platform,
};
/// Offset where legacy ISA IRQs are remapped
pub const ISA_IRQ_OFFSET: u32 = apic::ioapic::ISA_IRQ_OFFSET;
use super::{x86::InitrdSource, Platform};
/// x86-64 architecture implementation
pub struct X86_64 {
@ -71,8 +64,6 @@ pub struct X86_64 {
fbconsole: OneTimeInit<Arc<FramebufferConsole>>,
}
static SHUTDOWN_FENCE: SpinFence = SpinFence::new();
/// Global x86-64 architecture value
pub static PLATFORM: X86_64 = X86_64 {
boot_data: OneTimeInit::new(),
@ -385,7 +376,18 @@ impl X86_64 {
let ioapic = IoApic::from_acpi(&apic_info)?;
register_external_interrupt_controller(ioapic);
// acpi::init_acpi(acpi).unwrap();
if let Err(error) = ygg_driver_acpi::switch_to_acpi(acpi) {
log::error!("ACPI initialization error: {error:?}");
} else {
if let Err(error) =
ygg_driver_acpi::add_event_handler(&FixedEvent::POWER_BUTTON, |_| {
log::info!("Power button pressed!");
EventAction::Nothing
})
{
log::error!("Couldn't set ACPI power button handler: {error:?}");
}
}
if let Ok(mcfg) = acpi.find_table::<Mcfg>() {
for entry in mcfg.entries() {

View File

@ -18,11 +18,10 @@ use libk_mm::{
pointer::PhysicalRefMut,
TableAllocatorImpl,
};
use ygg_driver_acpi::AcpiAllocator;
use crate::arch::x86_64::boot::__x86_64_ap_entry;
use super::acpi::AcpiAllocator;
static AP_BOOTSTRAP_BIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/__x86_64_ap_boot.bin"));
const AP_STACK_PAGES: usize = 8;

View File

@ -5,6 +5,7 @@ use crate::IntoArgs;
#[derive(Debug)]
pub enum QemuNic {
VirtioPci { mac: Option<String> },
Rtl8139 { mac: Option<String> },
}
#[derive(Debug, PartialEq, Eq)]
@ -46,6 +47,15 @@ impl IntoArgs for QemuNic {
}
command.arg(val);
}
Self::Rtl8139 { mac } => {
command.arg("-device");
let mut val = "rtl8139,netdev=net0".to_owned();
if let Some(mac) = mac {
val.push_str(",mac=");
val.push_str(mac);
}
command.arg(val);
}
}
}
}

73
userspace/Cargo.lock generated
View File

@ -331,7 +331,7 @@ version = "0.1.0"
dependencies = [
"clap",
"ed25519-dalek",
"rand 0.8.5",
"rand 0.8.5 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
"rsa",
"thiserror",
]
@ -862,7 +862,7 @@ dependencies = [
"num-integer",
"num-iter",
"num-traits",
"rand 0.8.5",
"rand 0.8.5 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
"smallvec",
"zeroize",
]
@ -941,6 +941,19 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "pci-ids"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d88ae3281b415d856e9c2ddbcdd5961e71c1a3e90138512c04d720241853a6af"
dependencies = [
"nom",
"phf",
"phf_codegen",
"proc-macro2",
"quote",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
@ -956,6 +969,44 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "phf"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared",
"rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "phf_shared"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project-lite"
version = "0.2.15"
@ -1045,6 +1096,15 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.8.5"
@ -1225,7 +1285,7 @@ dependencies = [
"env_logger",
"libterm",
"log",
"rand 0.8.5",
"rand 0.8.5 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
"sha2",
"thiserror",
"x25519-dalek",
@ -1393,6 +1453,12 @@ dependencies = [
"rand_core 0.6.4 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
]
[[package]]
name = "siphasher"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "slab"
version = "0.4.9"
@ -1485,6 +1551,7 @@ dependencies = [
"humansize",
"init",
"libterm",
"pci-ids",
"rand 0.9.0-alpha.1",
"serde",
"serde_json",

View File

@ -21,6 +21,7 @@ chrono.workspace = true
# TODO own impl
humansize = { version = "2.1.3", features = ["impl_style"] }
pci-ids = { version = "0.2.5" }
init = { path = "../init" }
@ -117,9 +118,14 @@ path = "src/sync.rs"
name = "sleep"
path = "src/sleep.rs"
[[bin]]
name = "lspci"
path = "src/lspci.rs"
[[bin]]
name = "tst"
path = "src/tst.rs"
[lints]
workspace = true

View File

@ -0,0 +1,248 @@
use std::{fmt, fs, io, path::PathBuf, process::ExitCode, str::FromStr};
use clap::Parser;
use pci_ids::FromId;
#[derive(Debug, Parser)]
struct Args {
#[clap(short, help = "Display numeric IDs only")]
numeric: bool,
#[clap(short, help = "Print all the information about the devices")]
verbose: bool,
}
struct PciAddress {
bus: u8,
device: u8,
function: u8,
}
struct DeviceId {
vendor: u16,
device: u16,
}
struct Device {
path: PathBuf,
address: PciAddress,
}
enum Resource {
Io(u16),
Memory32(u32),
Memory64(u64),
}
struct PciRegion {
index: usize,
resource: Resource
}
#[derive(Debug, thiserror::Error)]
enum Error {
#[error("{0}")]
Io(#[from] io::Error),
#[error("Invalid PCI ID")]
InvalidId,
}
impl FromStr for PciAddress {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut elements = s.split(':');
let bus = elements
.next()
.and_then(|p| u8::from_str_radix(p, 16).ok())
.ok_or(Error::InvalidId)?;
let device = elements
.next()
.and_then(|p| u8::from_str_radix(p, 16).ok())
.ok_or(Error::InvalidId)?;
let function = elements
.next()
.and_then(|p| u8::from_str_radix(p, 16).ok())
.ok_or(Error::InvalidId)?;
if elements.next().is_some() {
return Err(Error::InvalidId);
}
Ok(Self {
bus,
device,
function,
})
}
}
impl FromStr for DeviceId {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (vendor, device) = s.split_once(':').ok_or(Error::InvalidId)?;
let vendor = u16::from_str_radix(vendor, 16).map_err(|_| Error::InvalidId)?;
let device = u16::from_str_radix(device, 16).map_err(|_| Error::InvalidId)?;
Ok(Self { vendor, device })
}
}
impl FromStr for Resource {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (ty, addr) = s.split_once(':').ok_or(Error::InvalidId)?;
let addr = addr.strip_prefix("0x").ok_or(Error::InvalidId)?;
match ty {
"pio" => {
let addr = u16::from_str_radix(addr, 16).map_err(|_| Error::InvalidId)?;
Ok(Self::Io(addr))
},
"m32" => {
let addr = u32::from_str_radix(addr, 16).map_err(|_| Error::InvalidId)?;
Ok(Self::Memory32(addr))
},
"m64" => {
let addr = u64::from_str_radix(addr, 16).map_err(|_| Error::InvalidId)?;
Ok(Self::Memory64(addr))
},
_ => Err(Error::InvalidId)
}
}
}
impl FromStr for PciRegion {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (bar, resource) = s.split_once(':').ok_or(Error::InvalidId)?;
let index = usize::from_str(bar).map_err(|_| Error::InvalidId)?;
let resource = Resource::from_str(resource)?;
Ok(Self {
index, resource
})
}
}
impl fmt::Display for PciAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:02x}:{:02x}.{:x}",
self.bus, self.device, self.function
)
}
}
impl fmt::Display for DeviceId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04x}:{:04x}", self.vendor, self.device)
}
}
impl fmt::Display for Resource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::Io(addr) => write!(f, "I/O at {addr:#x}"),
Self::Memory32(addr) => write!(f, "Memory (32-bit) at {addr:#x}"),
Self::Memory64(addr) => write!(f, "Memory (64-bit) at {addr:#x}")
}
}
}
fn list_devices() -> Result<Vec<Device>, Error> {
let mut entries = Vec::new();
for entry in fs::read_dir("/sys/bus/pci")? {
let Ok(entry) = entry else { continue };
let file_name = entry.file_name();
let Some(name) = file_name.to_str() else {
continue;
};
if let Ok(address) = PciAddress::from_str(name) {
entries.push(Device {
path: entry.path(),
address,
});
}
}
Ok(entries)
}
fn read_device_id(device: &Device) -> Result<DeviceId, Error> {
let str = fs::read_to_string(device.path.join("id"))?;
DeviceId::from_str(str.trim())
}
fn read_device_regions(device: &Device) -> Result<Vec<PciRegion>, Error> {
let str = fs::read_to_string(device.path.join("resources"))?;
let mut regions = Vec::new();
for line in str.split('\n') {
let line = line.trim();
let Ok(region) = PciRegion::from_str(line) else { continue };
regions.push(region);
}
Ok(regions)
}
fn vendor_id(args: &Args, id: u16) -> String {
if args.numeric {
format!("{id:04x}")
} else {
pci_ids::Vendor::from_id(id).map(|v| v.name().into()).unwrap_or_else(|| format!("{id:04x}"))
}
}
fn device_id(args: &Args, vid: u16, pid: u16) -> String {
if args.numeric {
format!("{pid:04x}")
} else {
pci_ids::Device::from_vid_pid(vid, pid).map(|v| v.name().into()).unwrap_or_else(|| format!("{pid:04x}"))
}
}
fn print_device(args: &Args, device: &Device) -> Result<(), Error> {
let id = read_device_id(device)?;
print!("{}: ", device.address);
let vid = vendor_id(args, id.vendor);
let pid = device_id(args, id.vendor, id.device);
let device_name = if args.numeric {
format!("{vid}:{pid}")
}else {
format!("[{vid}] {pid}")
};
println!("{device_name}");
if args.verbose {
for region in read_device_regions(device)? {
println!(" BAR {}: {}", region.index, region.resource);
}
}
Ok(())
}
fn run(args: &Args) -> Result<(), Error> {
let devices = list_devices()?;
for device in devices {
if let Err(error) = print_device(args, &device) {
eprintln!("{}: {error}", device.address);
}
}
Ok(())
}
fn main() -> ExitCode {
let args = Args::parse();
match run(&args) {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("{error}");
ExitCode::FAILURE
}
}
}

View File

@ -45,6 +45,7 @@ const PROGRAMS: &[(&str, &str)] = &[
("date", "bin/date"),
("sync", "bin/sync"),
("sleep", "bin/sleep"),
("lspci", "bin/lspci"),
("tst", "bin/tst"),
// netutils
("netconf", "sbin/netconf"),

View File

@ -17,12 +17,21 @@ use crate::{
util::run_external_command,
};
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "kebab-case")]
enum QemuNetworkInterface {
#[default]
VirtioNet,
Rtl8139,
}
#[derive(Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "kebab-case", default)]
struct QemuNetworkConfig {
enable: bool,
interface_name: String,
mac: String,
interface: QemuNetworkInterface,
}
#[derive(Debug, Default, Clone, Copy, serde::Deserialize, serde::Serialize)]
@ -119,6 +128,7 @@ impl Default for QemuNetworkConfig {
enable: true,
interface_name: "qemu-tap0".into(),
mac: "12:34:56:65:43:21".into(),
interface: QemuNetworkInterface::default(),
}
}
}
@ -307,10 +317,13 @@ fn add_devices_from_config(
config: &QemuConfig,
) -> Result<(), Error> {
if config.network.enable {
let mac = Some(config.network.mac.clone());
let nic = match config.network.interface {
QemuNetworkInterface::VirtioNet => QemuNic::VirtioPci { mac },
QemuNetworkInterface::Rtl8139 => QemuNic::Rtl8139 { mac },
};
devices.push(QemuDevice::NetworkTap {
nic: QemuNic::VirtioPci {
mac: Some(config.network.mac.clone()),
},
nic,
script: Some("xtask/scripts/qemu-ifup".into()),
ifname: Some(config.network.interface_name.clone()),
});