Compare commits

..

7 Commits

80 changed files with 5539 additions and 1748 deletions

758
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -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" }

@ -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 = []

@ -35,6 +35,8 @@ pub struct DeviceMemoryAttributes {
/// Describes a single device memory mapping
#[derive(Debug)]
pub struct RawDeviceMemoryMapping<A: KernelTableManager> {
/// Physical base address of the object
pub physical_base: u64,
/// Virtual address of the mapped object
pub address: usize,
/// Base address of the mapping start
@ -98,7 +100,8 @@ impl<A: KernelTableManager> RawDeviceMemoryMapping<A> {
address
}
pub fn into_raw_parts(self) -> (usize, usize, usize, usize) {
pub fn into_raw_parts(self) -> (u64, usize, usize, usize, usize) {
let physical_base = self.physical_base;
let address = self.address;
let base_address = self.base_address;
let page_count = self.page_count;
@ -106,7 +109,7 @@ impl<A: KernelTableManager> RawDeviceMemoryMapping<A> {
core::mem::forget(self);
(address, base_address, page_count, page_size)
(physical_base, address, base_address, page_count, page_size)
}
/// # Safety
@ -114,12 +117,14 @@ impl<A: KernelTableManager> RawDeviceMemoryMapping<A> {
/// Preconditions: all the fields must come from a [RawDeviceMemoryMapping::into_raw_parts]
/// call.
pub unsafe fn from_raw_parts(
physical_base: u64,
address: usize,
base_address: usize,
page_count: usize,
page_size: usize,
) -> Self {
Self {
physical_base,
address,
base_address,
page_count,

@ -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;

@ -222,6 +222,7 @@ unsafe fn map_device_memory(
let address = base_address + l2_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
l2_aligned.into_u64(),
address,
base_address,
page_count,
@ -233,6 +234,7 @@ unsafe fn map_device_memory(
let address = base_address + l3_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
l3_aligned.into_u64(),
address,
base_address,
page_count,

@ -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

@ -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();
}
}

@ -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"
}
}

@ -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(())
}

@ -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)
}
}
}

@ -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 {

@ -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);

@ -0,0 +1,14 @@
[package]
name = "ygg_driver_scsi"
version = "0.1.0"
edition = "2024"
[dependencies]
yggdrasil-abi.workspace = true
device-api.workspace = true
libk-util.workspace = true
libk-mm.workspace = true
libk.workspace = true
async-trait.workspace = true
log.workspace = true

@ -0,0 +1,102 @@
use libk::error::Error;
use crate::device::ScsiDeviceType;
pub trait ScsiCommand {
type Response;
const REQUEST_LEN: usize;
const RESPONSE_LEN: usize;
fn into_bytes(self) -> [u8; Self::REQUEST_LEN];
fn parse_response(bytes: &[u8]) -> Result<Self::Response, Error>;
}
// Add more info when needed
pub struct ScsiInquiry;
#[derive(Debug)]
pub struct ScsiInquiryResponse {
pub device_type: ScsiDeviceType,
}
impl ScsiCommand for ScsiInquiry {
type Response = ScsiInquiryResponse;
const REQUEST_LEN: usize = 6;
const RESPONSE_LEN: usize = 36;
fn into_bytes(self) -> [u8; Self::REQUEST_LEN] {
[0x12, 0x00, 0x00, 0x00, 0x00, 0x00]
}
fn parse_response(bytes: &[u8]) -> Result<Self::Response, Error> {
if bytes.len() != 36 {
return Err(Error::InvalidArgument);
}
let device_type = ScsiDeviceType::try_from(bytes[0] & 0x1F).unwrap_or_default();
Ok(ScsiInquiryResponse { device_type })
}
}
pub struct ScsiTestUnitReady;
#[derive(Debug)]
pub struct ScsiTestUnitReadyResponse;
impl ScsiCommand for ScsiTestUnitReady {
type Response = ScsiTestUnitReadyResponse;
const RESPONSE_LEN: usize = 0;
const REQUEST_LEN: usize = 6;
fn into_bytes(self) -> [u8; Self::REQUEST_LEN] {
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
}
fn parse_response(_bytes: &[u8]) -> Result<Self::Response, Error> {
Ok(ScsiTestUnitReadyResponse)
}
}
pub struct ScsiRequestSense;
#[derive(Debug)]
pub struct ScsiRequestSenseResponse;
impl ScsiCommand for ScsiRequestSense {
type Response = ScsiRequestSenseResponse;
const RESPONSE_LEN: usize = 0;
const REQUEST_LEN: usize = 6;
fn into_bytes(self) -> [u8; Self::REQUEST_LEN] {
[0x03, 0x00, 0x00, 0x00, 0x00, 0x00]
}
fn parse_response(_bytes: &[u8]) -> Result<Self::Response, Error> {
Ok(ScsiRequestSenseResponse)
}
}
pub struct ScsiReadCapacity;
#[derive(Debug)]
pub struct ScsiReadCapacityResponse {
pub block_size: u32,
pub block_count: u32,
}
impl ScsiCommand for ScsiReadCapacity {
type Response = ScsiReadCapacityResponse;
const REQUEST_LEN: usize = 10;
const RESPONSE_LEN: usize = 8;
fn into_bytes(self) -> [u8; Self::REQUEST_LEN] {
[0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
}
fn parse_response(bytes: &[u8]) -> Result<Self::Response, Error> {
if bytes.len() != 8 {
return Err(Error::InvalidArgument);
}
let block_count = u32::from_be_bytes(bytes[0..4].try_into().unwrap());
let block_size = u32::from_be_bytes(bytes[4..8].try_into().unwrap());
Ok(ScsiReadCapacityResponse {
block_size,
block_count,
})
}
}

@ -0,0 +1,24 @@
use yggdrasil_abi::primitive_enum;
primitive_enum! {
#[derive(Default)]
pub enum ScsiDeviceType: u8 {
DirectAccessBlock = 0x00,
SequentialAccess = 0x01,
Printer = 0x02,
Processor = 0x03,
WriteOnce = 0x04,
CdDvd = 0x05,
OpticalMemory = 0x07,
MediumChanger = 0x08,
StorageArrayController = 0x0C,
EnclosureServices = 0x0D,
SimplifiedDirectAccess = 0x0E,
OpticalCard = 0x0F,
BridgeController = 0x10,
ObjectBasedStorage = 0x11,
AutomationDriveInterface = 0x12,
#[default]
Other = 0x1F,
}
}

@ -0,0 +1,245 @@
#![feature(generic_const_exprs, maybe_uninit_slice)]
#![allow(incomplete_features)]
#![no_std]
use core::{mem::MaybeUninit, time::Duration};
use alloc::{
boxed::Box, collections::btree_map::BTreeMap, format, string::String, sync::Arc, vec::Vec,
};
use async_trait::async_trait;
use command::{ScsiInquiry, ScsiReadCapacity, ScsiRequestSense, ScsiTestUnitReady};
use device_api::device::Device;
use libk::{
device::{block::BlockDevice, manager::probe_partitions},
error::Error,
fs::devfs,
task::runtime,
};
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider, PageSlice};
use libk_util::{
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
OneTimeInit,
};
use transport::{ScsiTransport, ScsiTransportWrapper};
use yggdrasil_abi::io::FileMode;
extern crate alloc;
pub mod command;
pub mod device;
pub mod transport;
// TODO SCSI detach
pub struct ScsiDevice {
transport: IrqSafeSpinlock<ScsiTransportWrapper>,
lba_count: u64,
lba_size: usize,
index: OneTimeInit<u32>,
names: IrqSafeRwLock<Vec<String>>,
}
impl ScsiDevice {
// TODO support LUNs other than 0
pub async fn setup<T: ScsiTransport + 'static>(transport: T) -> Result<Arc<Self>, Error> {
let mut transport = ScsiTransportWrapper::new(transport);
transport.perform_command(0, ScsiInquiry).await?;
let mut attempts = 5;
let mut timeout = 100;
while attempts > 0 {
// TEST UNIT READY (6)
if transport
.perform_command(0, ScsiTestUnitReady)
.await
.is_ok()
{
break;
}
// If not, send a REQUEST SENSE (6)
if transport.perform_command(0, ScsiRequestSense).await.is_ok() {
break;
}
runtime::sleep(Duration::from_millis(timeout)).await;
timeout *= 2;
attempts -= 1;
}
if attempts == 0 {
log::error!("scsi: unit not ready");
return Err(Error::DoesNotExist);
}
let capacity_info = transport.perform_command(0, ScsiReadCapacity).await?;
log::info!(
"scsi: lba_size={}, lba_count={}",
capacity_info.block_size,
capacity_info.block_count
);
Ok(Arc::new(Self {
transport: IrqSafeSpinlock::new(transport),
lba_count: capacity_info.block_count.into(),
lba_size: capacity_info.block_size as usize,
index: OneTimeInit::new(),
names: IrqSafeRwLock::new(Vec::new()),
}))
}
pub fn detach(&self) {
if let Some(&index) = self.index.try_get() {
detach(index);
}
}
}
#[async_trait]
impl BlockDevice for ScsiDevice {
async fn read_aligned(
&self,
position: u64,
buffer: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), Error> {
if buffer.len() % self.lba_size != 0 {
return Err(Error::InvalidArgument);
}
let lba_start = position / self.lba_size as u64;
let lba_count = buffer.len() / self.lba_size;
if lba_start.saturating_add(lba_count as u64) >= self.lba_count {
return Err(Error::InvalidArgument);
}
log::info!("scsi: read lba={lba_start}, count={lba_count}");
let mut transport = self.transport.lock();
for i in 0..lba_count {
let lba = lba_start + i as u64;
let offset = self.lba_size * i;
let slice = unsafe {
MaybeUninit::slice_assume_init_mut(&mut buffer[offset..offset + self.lba_size])
};
let len = transport.read(0, lba, slice).await?;
if len != self.lba_size {
log::warn!("scsi: truncated read received at lba {lba}");
return Err(Error::InvalidOperation);
}
}
Ok(())
}
async fn write_aligned(&self, _position: u64, _buffer: &PageSlice<u8>) -> Result<(), Error> {
// TODO AtaWriteDmaEx
Err(Error::NotImplemented)
}
fn block_size(&self) -> usize {
self.lba_size
}
fn block_count(&self) -> u64 {
self.lba_count
}
fn max_blocks_per_request(&self) -> usize {
8
}
}
impl PageProvider for ScsiDevice {
fn get_page(&self, _offset: u64) -> Result<PhysicalAddress, Error> {
Err(Error::NotImplemented)
}
fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> {
Err(Error::NotImplemented)
}
fn clone_page(
&self,
_offset: u64,
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
Err(Error::NotImplemented)
}
}
impl Device for ScsiDevice {
fn display_name(&self) -> &str {
"SCSI Storage Device"
}
}
impl Drop for ScsiDevice {
fn drop(&mut self) {
if let Some(index) = self.index.try_get() {
log::info!("scsi{index} dropped");
}
}
}
// TODO this is crap
static SCSI_DEVICES: IrqSafeSpinlock<BTreeMap<u32, Arc<ScsiDevice>>> =
IrqSafeSpinlock::new(BTreeMap::new());
static SCSI_BITMAP: IrqSafeSpinlock<u32> = IrqSafeSpinlock::new(0);
pub fn attach(device: Arc<ScsiDevice>) -> Result<(), Error> {
let index = {
let mut bitmap = SCSI_BITMAP.lock();
let index = (0..8)
.position(|p| *bitmap & (1 << p) == 0)
.ok_or(Error::InvalidOperation)
.inspect_err(|_| log::warn!("Cannot attach SCSI device: too many of them"))?
as u32;
let mut devices = SCSI_DEVICES.lock();
*bitmap |= 1 << index;
assert!(!devices.contains_key(&index));
devices.insert(index, device.clone());
index
};
let name = format!("scsi{index}");
device.index.init(index);
device.names.write().push(name.clone());
devfs::add_named_block_device(device.clone(), name.clone(), FileMode::new(0o600)).ok();
log::info!("{name} attached");
// TODO this code is repeated everywhere
runtime::spawn(async move {
let name = name;
probe_partitions(device.clone(), |index, partition| {
let partition_name = format!("{name}p{}", index + 1);
log::info!("{name}: partition {partition_name}");
device.names.write().push(partition_name.clone());
devfs::add_named_block_device(
Arc::new(partition),
partition_name,
FileMode::new(0o600),
)
.ok();
})
.await
.ok();
})
.ok();
Ok(())
}
pub fn detach(index: u32) {
let mut devices = SCSI_DEVICES.lock();
let mut bitmap = SCSI_BITMAP.lock();
if let Some(device) = devices.remove(&index) {
{
let names = device.names.read();
for name in names.iter() {
devfs::remove_node(name).ok();
}
}
*bitmap &= !(1 << index);
log::info!("scsi{index} detached");
}
}

@ -0,0 +1,72 @@
use alloc::boxed::Box;
use async_trait::async_trait;
use libk::error::Error;
use crate::command::ScsiCommand;
#[async_trait]
pub trait ScsiTransport: Send + Sync {
/// Perform a no-data request
async fn perform_request_raw(
&mut self,
lun: u8,
request_data: &[u8],
response_buffer: &mut [u8],
) -> Result<usize, Error>;
}
pub struct ScsiTransportWrapper {
inner: Box<dyn ScsiTransport>,
}
impl ScsiTransportWrapper {
pub fn new<T: ScsiTransport + 'static>(inner: T) -> Self {
Self {
inner: Box::new(inner),
}
}
pub async fn read(&mut self, lun: u8, lba: u64, buffer: &mut [u8]) -> Result<usize, Error> {
if lba >= u32::MAX as u64 || buffer.len() > u16::MAX as usize {
return Err(Error::InvalidArgument);
}
let lba_bytes = (lba as u32).to_be_bytes();
// Issue a READ (10) command
let request_buffer = [
0x28,
0x00,
lba_bytes[0],
lba_bytes[1],
lba_bytes[2],
lba_bytes[3],
0x00,
0x00,
0x01,
0x00,
];
self.inner
.perform_request_raw(lun, &request_buffer, buffer)
.await
}
pub async fn perform_command<R: ScsiCommand>(
&mut self,
lun: u8,
request: R,
) -> Result<R::Response, Error>
where
[u8; R::RESPONSE_LEN]: Sized,
[u8; R::REQUEST_LEN]: Sized,
{
let mut response_buffer = [0; R::RESPONSE_LEN];
let request_buffer = request.into_bytes();
let response_len = self
.inner
.perform_request_raw(lun, &request_buffer, &mut response_buffer)
.await?;
R::parse_response(&response_buffer[..response_len])
}
}

@ -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

@ -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,56 @@ 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 set_pme_en(&self, state: bool) {
let pmcsr = self.space.read_u16(self.offset + 4);
let new = if state {
pmcsr | (1 << 8)
} else {
pmcsr & !(1 << 8)
};
if pmcsr == new {
return;
}
log::info!("Set PMCSR.PME_En = {state}");
self.space.write_u16(self.offset + 4, new);
}
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 +352,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 +398,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 +494,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,

@ -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
@ -53,7 +53,7 @@ pub enum PciInterruptPin {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PreferredInterruptMode {
Msi,
Msi(bool),
Legacy,
}
@ -85,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>,
@ -98,22 +99,70 @@ pub struct PciBusDevice {
}
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()
}
@ -121,7 +170,6 @@ impl PciDeviceInfo {
// Ignore preferred_mode, the only supported is Legacy
self.legacy_interrupt_mode()
};
IrqSafeRwLock::new(InterruptConfig {
preferred_mode,
configured_mode,
@ -212,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 #{}",

@ -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!(),
}
}
}

@ -1,25 +1,29 @@
//! 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, format, 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;
@ -31,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;
@ -75,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)]
@ -202,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,
}
@ -426,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)
})
@ -462,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,
@ -486,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
@ -607,7 +620,7 @@ fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
for driver in drivers.iter() {
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() }?;

@ -107,10 +107,13 @@ pub(crate) fn make_sysfs_object(
for (capability, offset, _) in state.info.config_space.capability_iter() {
write!(output, "{offset:04x}:").ok();
match capability {
PciCapabilityId::Msi => write!(output, "msi").ok(),
PciCapabilityId::MsiX => write!(output, "msix").ok(),
PciCapabilityId::VendorSpecific => write!(output, "vendor-specific").ok(),
PciCapabilityId::Unknown => write!(output, "unknown").ok(),
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();
}

@ -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

@ -12,7 +12,10 @@ libk-mm.workspace = true
libk.workspace = true
ygg_driver_input = { path = "../../input" }
# For mass storage
ygg_driver_scsi = { path = "../../block/scsi" }
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
async-trait.workspace = true

@ -24,6 +24,7 @@ impl UsbBusManager {
}
pub fn register_device(device: Arc<UsbDeviceAccess>) {
log::info!("usb: register device {}", device.bus_address());
BUS_MANAGER
.devices
.write()

@ -0,0 +1,173 @@
use core::mem::MaybeUninit;
use alloc::sync::Arc;
use futures_util::{future::BoxFuture, FutureExt};
use libk_mm::PageBox;
use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
use crate::{device::UsbDeviceAccess, error::UsbError, info::UsbDeviceClass};
use super::{UsbClassInfo, UsbDriver};
pub struct UsbHidKeyboardDriver;
const MODIFIER_MAP: &[KeyboardKey] = &[
KeyboardKey::LControl,
KeyboardKey::LShift,
KeyboardKey::LAlt,
KeyboardKey::Unknown,
KeyboardKey::RControl,
KeyboardKey::RShift,
KeyboardKey::RAlt,
KeyboardKey::Unknown,
];
#[derive(Default)]
struct KeyboardState {
state: [u64; 4],
mods: u8,
}
impl KeyboardState {
pub fn new() -> Self {
Self::default()
}
pub fn translate_key(k: u8) -> KeyboardKey {
match k {
4..=29 => KeyboardKey::Char(k - 4 + b'a'),
30..=38 => KeyboardKey::Char(k - 30 + b'1'),
39 => KeyboardKey::Char(b'0'),
40 => KeyboardKey::Enter,
41 => KeyboardKey::Escape,
42 => KeyboardKey::Backspace,
43 => KeyboardKey::Tab,
44 => KeyboardKey::Char(b' '),
45 => KeyboardKey::Char(b'-'),
46 => KeyboardKey::Char(b'='),
47 => KeyboardKey::Char(b'['),
48 => KeyboardKey::Char(b']'),
49 => KeyboardKey::Char(b'\\'),
51 => KeyboardKey::Char(b';'),
52 => KeyboardKey::Char(b'\''),
53 => KeyboardKey::Char(b'`'),
54 => KeyboardKey::Char(b','),
55 => KeyboardKey::Char(b'.'),
56 => KeyboardKey::Char(b'/'),
58..=69 => KeyboardKey::F(k - 58),
_ => {
log::debug!("Unknown key: {}", k);
KeyboardKey::Unknown
}
}
}
pub fn retain_modifiers(
&mut self,
m: u8,
events: &mut [MaybeUninit<KeyboardKeyEvent>],
) -> usize {
let mut count = 0;
let released = self.mods & !m;
for (i, modifier) in MODIFIER_MAP.iter().enumerate().take(8) {
if released & (1 << i) != 0 {
events[count].write(KeyboardKeyEvent::Released(*modifier));
count += 1;
}
}
self.mods &= m;
count
}
pub fn press_modifiers(
&mut self,
m: u8,
events: &mut [MaybeUninit<KeyboardKeyEvent>],
) -> usize {
let mut count = 0;
let pressed = m & !self.mods;
for (i, modifier) in MODIFIER_MAP.iter().enumerate().take(8) {
if pressed & (1 << i) != 0 {
events[count].write(KeyboardKeyEvent::Pressed(*modifier));
count += 1;
}
}
self.mods = m;
count
}
pub fn retain(&mut self, keys: &[u8], events: &mut [MaybeUninit<KeyboardKeyEvent>]) -> usize {
let mut count = 0;
for i in 1..256 {
if self.state[i / 64] & (1 << (i % 64)) != 0 && !keys.contains(&(i as u8)) {
events[count].write(KeyboardKeyEvent::Released(Self::translate_key(i as u8)));
self.state[i / 64] &= !(1 << (i % 64));
count += 1;
}
}
count
}
pub fn press(&mut self, keys: &[u8], events: &mut [MaybeUninit<KeyboardKeyEvent>]) -> usize {
let mut count = 0;
for &k in keys {
let index = (k as usize) / 64;
if self.state[index] & (1 << (k % 64)) == 0 {
self.state[index] |= 1 << (k % 64);
events[count].write(KeyboardKeyEvent::Pressed(Self::translate_key(k)));
count += 1;
}
}
count
}
}
impl UsbDriver for UsbHidKeyboardDriver {
fn run(
self: Arc<Self>,
device: Arc<UsbDeviceAccess>,
) -> BoxFuture<'static, Result<(), UsbError>> {
async move {
// TODO not sure whether to use boot protocol (easy) or GetReport
let config = device.select_configuration(|_| true).await?.unwrap();
assert_eq!(config.endpoints.len(), 1);
let pipe = device.open_interrupt_in_pipe(1).await?;
let mut buffer = PageBox::new_slice(0, 8).map_err(UsbError::MemoryError)?;
let mut state = KeyboardState::new();
let mut events = [MaybeUninit::uninit(); 16];
loop {
let mut event_count = 0;
let data = pipe.read(&mut buffer).await?;
event_count += state.retain_modifiers(data[0], &mut events);
event_count += state.press_modifiers(data[0], &mut events[event_count..]);
event_count += state.retain(&data[2..], &mut events[event_count..]);
event_count += state.press(&data[2..], &mut events[event_count..]);
let events = unsafe { MaybeUninit::slice_assume_init_ref(&events[..event_count]) };
for &event in events {
log::trace!("Generic Keyboard: {:?}", event);
ygg_driver_input::send_event(event);
}
}
}
.boxed()
}
fn name(&self) -> &'static str {
"USB HID Keyboard"
}
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
class.class == UsbDeviceClass::Hid && class.subclass == 0x01
}
}

@ -0,0 +1,227 @@
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use bytemuck::{Pod, Zeroable};
use futures_util::{future::BoxFuture, FutureExt};
use libk::error::Error;
use libk_mm::PageBox;
use ygg_driver_scsi::{transport::ScsiTransport, ScsiDevice};
use crate::{
device::{UsbDeviceAccess, UsbDeviceDetachHandler},
error::UsbError,
info::UsbDeviceClass,
pipe::{
bulk::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
control::UsbClassSpecificRequest,
},
};
use super::{UsbClassInfo, UsbDriver};
pub struct UsbMassStorageDriverBulkOnly;
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
struct Cbw {
signature: u32, // 0x00
tag: u32, // 0x04
transfer_length: u32, // 0x08
flags: u8, // 0x0C
lun: u8, // 0x0D
cb_length: u8, // 0x0E
cb_data: [u8; 16], // 0x0F
// Not sent
_0: u8,
}
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
struct Csw {
signature: u32,
tag: u32,
data_residue: u32,
status: u8,
_0: [u8; 3],
}
struct Bbb {
#[allow(unused)]
device: Arc<UsbDeviceAccess>,
in_pipe: UsbBulkInPipeAccess,
out_pipe: UsbBulkOutPipeAccess,
buffer: PageBox<[u8]>,
last_tag: u32,
}
struct DetachHandler(Arc<ScsiDevice>);
impl Bbb {
pub fn new(
device: Arc<UsbDeviceAccess>,
in_pipe: UsbBulkInPipeAccess,
out_pipe: UsbBulkOutPipeAccess,
) -> Result<Self, UsbError> {
let buffer = PageBox::new_slice(0, 4096).map_err(UsbError::MemoryError)?;
Ok(Self {
device,
in_pipe,
out_pipe,
buffer,
last_tag: 0,
})
}
}
impl Bbb {
async fn send_cbw(
&mut self,
lun: u8,
host_to_dev: bool,
command: &[u8],
response_len: usize,
) -> Result<u32, Error> {
self.last_tag = self.last_tag.wrapping_add(1);
self.buffer[..size_of::<Cbw>()].fill(0);
let cbw = bytemuck::from_bytes_mut::<Cbw>(&mut self.buffer[..size_of::<Cbw>()]);
let tag = self.last_tag;
cbw.signature = 0x43425355;
cbw.tag = tag;
cbw.transfer_length = response_len as u32;
if !host_to_dev {
cbw.flags = 1 << 7;
}
cbw.lun = lun;
cbw.cb_length = command.len() as u8;
cbw.cb_data[..command.len()].copy_from_slice(command);
self.out_pipe
.write(self.buffer.as_slice().subslice(..31))
.await?;
Ok(tag)
}
async fn read_csw(&mut self, tag: u32) -> Result<(), Error> {
self.in_pipe
.read(self.buffer.as_slice_mut().subslice_mut(..13))
.await?;
let csw = bytemuck::from_bytes::<Csw>(&self.buffer[..size_of::<Csw>()]);
if csw.signature != 0x53425355 {
log::warn!("msc: invalid csw signature");
return Err(Error::InvalidArgument);
}
if csw.tag != tag {
let csw_tag = csw.tag;
log::warn!("msc: invalid csw tag (got {}, expected {tag})", csw_tag);
return Err(Error::InvalidArgument);
}
if csw.status != 0x00 {
log::warn!("msc: csw error status {:#02x}", csw.status);
return Err(Error::InvalidArgument);
}
Ok(())
}
async fn read_response_data(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
// TODO limit by max_packet_size
let bytes = self
.in_pipe
.read(self.buffer.as_slice_mut().subslice_mut(..buffer.len()))
.await?;
buffer[..bytes.len()].copy_from_slice(bytes);
Ok(bytes.len())
}
}
#[async_trait]
impl ScsiTransport for Bbb {
async fn perform_request_raw(
&mut self,
lun: u8,
request_data: &[u8],
response_buffer: &mut [u8],
) -> Result<usize, Error> {
if request_data.len() > 16 || response_buffer.len() > self.buffer.len() {
return Err(Error::InvalidArgument);
}
let tag = self
.send_cbw(lun, false, request_data, response_buffer.len())
.await?;
let response_len = if response_buffer.is_empty() {
0
} else {
self.read_response_data(response_buffer).await?
};
self.read_csw(tag).await?;
Ok(response_len)
}
}
impl UsbDeviceDetachHandler for DetachHandler {
fn handle_device_detach(&self) {
log::info!("Mass storage detached");
self.0.detach();
}
}
#[derive(Debug, Pod, Zeroable, Clone, Copy)]
#[repr(C)]
pub struct BulkOnlyMassStorageReset;
impl UsbClassSpecificRequest for BulkOnlyMassStorageReset {
const BM_REQUEST_TYPE: u8 = 0b00100001;
const B_REQUEST: u8 = 0b11111111;
}
impl UsbDriver for UsbMassStorageDriverBulkOnly {
fn run(
self: Arc<Self>,
device: Arc<UsbDeviceAccess>,
) -> BoxFuture<'static, Result<(), UsbError>> {
async move {
// TODO filter to only accept BBB config
let config = device.select_configuration(|_| true).await?.unwrap();
// Bulk-in, bulk-out
assert_eq!(config.endpoints.len(), 2);
// TODO those indices may be different
let control_pipe = device.control_pipe();
let in_pipe = device.open_bulk_in_pipe(1).await?;
let out_pipe = device.open_bulk_out_pipe(2).await?;
// Perform a Bulk-Only Mass Storage Reset
// TODO interface id?
control_pipe
.class_specific_request::<BulkOnlyMassStorageReset>(0, 0)
.await?;
// TODO get max LUN
let bbb = Bbb::new(device.clone(), in_pipe, out_pipe)?;
let scsi = ScsiDevice::setup(bbb)
.await
.inspect_err(|error| log::error!("msc: scsi error {error:?}"))
.map_err(|_| UsbError::DriverError)?;
let detach = DetachHandler(scsi.clone());
device.set_detach_handler(Arc::new(detach));
ygg_driver_scsi::attach(scsi).ok();
Ok(())
}
.boxed()
}
fn name(&self) -> &'static str {
"USB Mass Storage"
}
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
// TODO support other protocols
class.class == UsbDeviceClass::MassStorage && class.interface_protocol_number == 0x50
}
}

@ -9,10 +9,16 @@ use crate::{
info::{UsbDeviceClass, UsbDeviceProtocol},
};
pub mod hid_keyboard;
pub mod mass_storage;
#[derive(Debug)]
pub struct UsbClassInfo {
pub class: UsbDeviceClass,
pub subclass: u8,
pub protocol: UsbDeviceProtocol,
pub device_protocol_number: u8,
pub interface_protocol_number: u8,
}
pub trait UsbDriver: Send + Sync {
@ -55,6 +61,8 @@ async fn extract_class_info(device: &UsbDeviceAccess) -> Result<Option<UsbClassI
class,
subclass,
protocol,
interface_protocol_number: if_info.interface_protocol_number,
device_protocol_number: device_info.device_protocol_number,
}))
} else {
Ok(None)
@ -104,191 +112,8 @@ pub fn register_driver(driver: Arc<dyn UsbDriver + 'static>) {
pub fn register_default_class_drivers() {
register_driver(Arc::new(hid_keyboard::UsbHidKeyboardDriver));
register_driver(Arc::new(mass_storage::UsbMassStorageDriverBulkOnly));
}
static USB_DEVICE_DRIVERS: IrqSafeRwLock<Vec<Arc<dyn UsbDriver + 'static>>> =
IrqSafeRwLock::new(Vec::new());
pub mod hid_keyboard {
use core::mem::MaybeUninit;
use alloc::sync::Arc;
use futures_util::{future::BoxFuture, FutureExt};
use libk_mm::PageBox;
use yggdrasil_abi::io::{KeyboardKey, KeyboardKeyEvent};
use crate::{device::UsbDeviceAccess, error::UsbError, info::UsbDeviceClass};
use super::{UsbClassInfo, UsbDriver};
pub struct UsbHidKeyboardDriver;
const MODIFIER_MAP: &[KeyboardKey] = &[
KeyboardKey::LControl,
KeyboardKey::LShift,
KeyboardKey::LAlt,
KeyboardKey::Unknown,
KeyboardKey::RControl,
KeyboardKey::RShift,
KeyboardKey::RAlt,
KeyboardKey::Unknown,
];
#[derive(Default)]
struct KeyboardState {
state: [u64; 4],
mods: u8,
}
impl KeyboardState {
pub fn new() -> Self {
Self::default()
}
pub fn translate_key(k: u8) -> KeyboardKey {
match k {
4..=29 => KeyboardKey::Char(k - 4 + b'a'),
30..=38 => KeyboardKey::Char(k - 30 + b'1'),
39 => KeyboardKey::Char(b'0'),
40 => KeyboardKey::Enter,
41 => KeyboardKey::Escape,
42 => KeyboardKey::Backspace,
43 => KeyboardKey::Tab,
44 => KeyboardKey::Char(b' '),
45 => KeyboardKey::Char(b'-'),
46 => KeyboardKey::Char(b'='),
47 => KeyboardKey::Char(b'['),
48 => KeyboardKey::Char(b']'),
49 => KeyboardKey::Char(b'\\'),
51 => KeyboardKey::Char(b';'),
52 => KeyboardKey::Char(b'\''),
53 => KeyboardKey::Char(b'`'),
54 => KeyboardKey::Char(b','),
55 => KeyboardKey::Char(b'.'),
56 => KeyboardKey::Char(b'/'),
58..=69 => KeyboardKey::F(k - 58),
_ => {
log::debug!("Unknown key: {}", k);
KeyboardKey::Unknown
}
}
}
pub fn retain_modifiers(
&mut self,
m: u8,
events: &mut [MaybeUninit<KeyboardKeyEvent>],
) -> usize {
let mut count = 0;
let released = self.mods & !m;
for (i, modifier) in MODIFIER_MAP.iter().enumerate().take(8) {
if released & (1 << i) != 0 {
events[count].write(KeyboardKeyEvent::Released(*modifier));
count += 1;
}
}
self.mods &= m;
count
}
pub fn press_modifiers(
&mut self,
m: u8,
events: &mut [MaybeUninit<KeyboardKeyEvent>],
) -> usize {
let mut count = 0;
let pressed = m & !self.mods;
for (i, modifier) in MODIFIER_MAP.iter().enumerate().take(8) {
if pressed & (1 << i) != 0 {
events[count].write(KeyboardKeyEvent::Pressed(*modifier));
count += 1;
}
}
self.mods = m;
count
}
pub fn retain(
&mut self,
keys: &[u8],
events: &mut [MaybeUninit<KeyboardKeyEvent>],
) -> usize {
let mut count = 0;
for i in 1..256 {
if self.state[i / 64] & (1 << (i % 64)) != 0 && !keys.contains(&(i as u8)) {
events[count].write(KeyboardKeyEvent::Released(Self::translate_key(i as u8)));
self.state[i / 64] &= !(1 << (i % 64));
count += 1;
}
}
count
}
pub fn press(
&mut self,
keys: &[u8],
events: &mut [MaybeUninit<KeyboardKeyEvent>],
) -> usize {
let mut count = 0;
for &k in keys {
let index = (k as usize) / 64;
if self.state[index] & (1 << (k % 64)) == 0 {
self.state[index] |= 1 << (k % 64);
events[count].write(KeyboardKeyEvent::Pressed(Self::translate_key(k)));
count += 1;
}
}
count
}
}
impl UsbDriver for UsbHidKeyboardDriver {
fn run(
self: Arc<Self>,
device: Arc<UsbDeviceAccess>,
) -> BoxFuture<'static, Result<(), UsbError>> {
async move {
// TODO not sure whether to use boot protocol (easy) or GetReport
let config = device.select_configuration(|_| true).await?.unwrap();
assert_eq!(config.endpoints.len(), 1);
let pipe = device.open_interrupt_in_pipe(1).await?;
let mut buffer = PageBox::new_slice(0, 8).map_err(UsbError::MemoryError)?;
let mut state = KeyboardState::new();
let mut events = [MaybeUninit::uninit(); 16];
loop {
let mut event_count = 0;
let data = pipe.read(&mut buffer).await?;
event_count += state.retain_modifiers(data[0], &mut events);
event_count += state.press_modifiers(data[0], &mut events[event_count..]);
event_count += state.retain(&data[2..], &mut events[event_count..]);
event_count += state.press(&data[2..], &mut events[event_count..]);
let events =
unsafe { MaybeUninit::slice_assume_init_ref(&events[..event_count]) };
for &event in events {
log::trace!("Generic Keyboard: {:?}", event);
ygg_driver_input::send_event(event);
}
}
}
.boxed()
}
fn name(&self) -> &'static str {
"USB HID Keyboard"
}
fn probe(&self, class: &UsbClassInfo, _device: &UsbDeviceAccess) -> bool {
class.class == UsbDeviceClass::Hid && class.subclass == 0x01
}
}
}

@ -8,7 +8,7 @@ use alloc::{sync::Arc, vec::Vec};
use futures_util::task::AtomicWaker;
use libk_mm::address::PhysicalAddress;
use crate::error::UsbError;
use crate::error::{TransferError, UsbError};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum UsbDirection {
@ -44,6 +44,13 @@ pub struct UsbInterruptTransfer {
pub status: Arc<UsbTransferStatus>,
}
pub struct UsbBulkTransfer {
pub address: PhysicalAddress,
pub length: usize,
pub direction: UsbDirection,
pub status: Arc<UsbTransferStatus>,
}
impl UsbDirection {
pub const fn is_device_to_host(self) -> bool {
matches!(self, UsbDirection::In)
@ -63,6 +70,17 @@ impl UsbTransferResult {
pub fn sub_length(&self) -> usize {
(self.0 & 0xFFFFFF) as _
}
pub fn error_code(&self) -> TransferError {
match self.0 >> 24 {
0 => TransferError::InvalidTransfer,
2 => TransferError::BufferError,
4 => TransferError::UsbTransactionError,
6 => TransferError::Stall,
13 => TransferError::ShortPacket(self.0 & 0xFFFFFF),
_ => TransferError::Other,
}
}
}
impl UsbControlTransfer {
@ -79,6 +97,13 @@ impl UsbInterruptTransfer {
}
}
impl UsbBulkTransfer {
pub async fn wait(&self) -> Result<usize, UsbError> {
let sub_length = self.status.wait().await?;
Ok(self.length.saturating_sub(sub_length))
}
}
impl UsbTransferStatus {
pub fn new() -> Self {
Self {
@ -95,7 +120,7 @@ impl UsbTransferStatus {
} else if v.is_aborted() {
Err(UsbError::DeviceDisconnected)
} else {
Err(UsbError::TransferFailed)
Err(UsbError::TransferFailed(v.error_code()))
}
})
})

@ -1,8 +1,9 @@
use bytemuck::{Pod, Zeroable};
use crate::{
device::UsbSpeed,
error::UsbError,
info::{UsbDeviceClass, UsbDeviceProtocol, UsbEndpointType},
info::{UsbDeviceClass, UsbDeviceProtocol, UsbEndpointType, UsbVersion},
UsbDirection,
};
@ -134,12 +135,14 @@ impl UsbDeviceDescriptor {
UsbDeviceProtocol::try_from(self.device_protocol).unwrap_or(UsbDeviceProtocol::Unknown)
}
pub fn max_packet_size(&self) -> Result<usize, UsbError> {
match self.max_packet_size_0 {
8 => Ok(8),
16 => Ok(16),
32 => Ok(32),
64 => Ok(64),
pub fn max_packet_size(&self, version: UsbVersion, speed: UsbSpeed) -> Result<usize, UsbError> {
match (version.is_version_3(), speed, self.max_packet_size_0) {
(true, UsbSpeed::Super, 9) => Ok(1 << 9),
(true, _, _) => todo!("Non-GenX speed USB3+ maxpacketsize0"),
(false, _, 8) => Ok(8),
(false, _, 16) => Ok(16),
(false, _, 32) => Ok(32),
(false, _, 64) => Ok(64),
_ => Err(UsbError::InvalidDescriptorField),
}
}

@ -1,14 +1,15 @@
use core::{fmt, ops::Deref};
use alloc::{boxed::Box, vec::Vec};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use futures_util::future::BoxFuture;
use libk_mm::PageBox;
use libk_util::sync::spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard};
use crate::{
error::UsbError,
info::{UsbConfigurationInfo, UsbDeviceInfo, UsbEndpointInfo, UsbInterfaceInfo},
info::{UsbConfigurationInfo, UsbDeviceInfo, UsbEndpointInfo, UsbInterfaceInfo, UsbVersion},
pipe::{
bulk::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
control::{ConfigurationDescriptorEntry, UsbControlPipeAccess},
interrupt::UsbInterruptInPipeAccess,
},
@ -38,6 +39,10 @@ pub enum UsbSpeed {
Super,
}
pub trait UsbDeviceDetachHandler: Send + Sync {
fn handle_device_detach(&self);
}
#[allow(unused)]
pub trait UsbDevice: Send + Sync {
// Endpoint "0"
@ -49,12 +54,19 @@ pub trait UsbDevice: Send + Sync {
) -> BoxFuture<Result<UsbInterruptInPipeAccess, UsbError>> {
unimplemented!()
}
fn open_bulk_in_pipe(&self, number: u8) -> BoxFuture<Result<UsbBulkInPipeAccess, UsbError>> {
unimplemented!()
}
fn open_bulk_out_pipe(&self, number: u8) -> BoxFuture<Result<UsbBulkOutPipeAccess, UsbError>> {
unimplemented!()
}
fn port_number(&self) -> u8;
fn bus_address(&self) -> UsbBusAddress;
fn speed(&self) -> UsbSpeed;
fn controller_ref(&self) -> &dyn UsbHostController;
fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>);
fn handle_detach(&self);
fn debug(&self) {}
@ -73,6 +85,17 @@ impl UsbDeviceAccess {
let device_desc = control.query_device_descriptor().await?;
let bcd_usb = device_desc.bcd_usb;
let usb_version = UsbVersion::from_bcd_usb(device_desc.bcd_usb)
.ok_or(UsbError::InvalidDescriptorField)
.inspect_err(|_| {
log::error!(
"{}: unsupported/invalid USB version: {:#x}",
raw.bus_address(),
bcd_usb
)
})?;
let manufacturer = control
.query_string(device_desc.manufacturer_str, &mut string_buffer)
.await?;
@ -83,6 +106,7 @@ impl UsbDeviceAccess {
let info = UsbDeviceInfo {
manufacturer,
product,
usb_version,
id_vendor: device_desc.id_vendor,
id_product: device_desc.id_product,
@ -90,8 +114,9 @@ impl UsbDeviceAccess {
device_class: device_desc.class(),
device_subclass: device_desc.device_subclass,
device_protocol: device_desc.protocol(),
device_protocol_number: device_desc.device_protocol,
max_packet_size: device_desc.max_packet_size()?,
max_packet_size: device_desc.max_packet_size(usb_version, raw.speed())?,
};
Ok(Self {
@ -172,6 +197,7 @@ impl UsbDeviceAccess {
interface_class: iface.class(),
interface_subclass: iface.interface_subclass,
interface_protocol: iface.protocol(),
interface_protocol_number: iface.interface_protocol,
});
}
_ => (),
@ -187,6 +213,10 @@ impl UsbDeviceAccess {
Ok(info)
}
pub fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>) {
self.device.set_detach_handler(handler);
}
}
impl Deref for UsbDeviceAccess {

@ -1,5 +1,15 @@
use yggdrasil_abi::error::Error;
#[derive(Debug)]
pub enum TransferError {
InvalidTransfer,
ShortPacket(u32),
BufferError,
UsbTransactionError,
Stall,
Other,
}
#[derive(Debug)]
pub enum UsbError {
/// Could not allocate memory for some device structure
@ -8,7 +18,7 @@ pub enum UsbError {
SystemError(Error),
// HC-side init stage errors
OutOfAddresses,
HostControllerCommandFailed(u8),
HostControllerCommandFailed(u8, u32),
PortResetFailed,
PortInitFailed,
// Setup stage errors
@ -17,7 +27,9 @@ pub enum UsbError {
// Runtime errors
DeviceBusy,
DeviceDisconnected,
TransferFailed,
TransferFailed(TransferError),
// Driver errors
DriverError,
}
impl From<UsbError> for Error {

@ -1,3 +1,5 @@
use core::fmt;
use alloc::{string::String, vec::Vec};
use yggdrasil_abi::primitive_enum;
@ -27,10 +29,20 @@ pub enum UsbUsageType {
Reserved,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum UsbVersion {
Usb11,
Usb20,
Usb30,
Usb31,
Usb32,
}
primitive_enum! {
pub enum UsbDeviceClass: u8 {
FromInterface = 0x00,
Hid = 0x03,
MassStorage = 0x08,
Unknown = 0xFF,
}
}
@ -50,6 +62,7 @@ pub struct UsbInterfaceInfo {
pub interface_class: UsbDeviceClass,
pub interface_subclass: u8,
pub interface_protocol: UsbDeviceProtocol,
pub interface_protocol_number: u8,
}
#[derive(Debug, Clone)]
@ -73,12 +86,46 @@ pub struct UsbDeviceInfo {
pub manufacturer: String,
pub product: String,
pub usb_version: UsbVersion,
pub id_vendor: u16,
pub id_product: u16,
pub device_class: UsbDeviceClass,
pub device_subclass: u8,
pub device_protocol: UsbDeviceProtocol,
pub device_protocol_number: u8,
/// Max packet size for endpoint zero
pub max_packet_size: usize,
}
impl UsbVersion {
pub fn is_version_3(&self) -> bool {
matches!(self, Self::Usb30 | Self::Usb31 | Self::Usb32)
}
pub fn from_bcd_usb(value: u16) -> Option<Self> {
match value {
0x110 => Some(UsbVersion::Usb11),
0x200 => Some(UsbVersion::Usb20),
0x300 => Some(UsbVersion::Usb30),
0x310 => Some(UsbVersion::Usb31),
0x320 => Some(UsbVersion::Usb32),
_ => None,
}
}
}
impl fmt::Display for UsbVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let string = match self {
Self::Usb11 => "USB1.1",
Self::Usb20 => "USB2.0",
Self::Usb30 => "USB3.0",
Self::Usb31 => "USB3.1",
Self::Usb32 => "USB3.2",
};
f.write_str(string)
}
}

@ -0,0 +1,55 @@
use core::ops::Deref;
use alloc::boxed::Box;
use libk_mm::PageSlice;
use crate::{communication::UsbBulkTransfer, error::UsbError};
use super::UsbGenericPipe;
pub trait UsbBulkInPipe: UsbGenericPipe + Send + Sync {
fn start_read(&self, buffer: &mut PageSlice<u8>) -> Result<UsbBulkTransfer, UsbError>;
fn complete_transfer(&self, transfer: UsbBulkTransfer);
}
pub trait UsbBulkOutPipe: UsbGenericPipe + Send + Sync {
fn start_write(&self, buffer: &PageSlice<u8>) -> Result<UsbBulkTransfer, UsbError>;
fn complete_transfer(&self, transfer: UsbBulkTransfer);
}
pub struct UsbBulkInPipeAccess(pub Box<dyn UsbBulkInPipe>);
pub struct UsbBulkOutPipeAccess(pub Box<dyn UsbBulkOutPipe>);
impl UsbBulkInPipeAccess {
pub async fn read<'a>(&self, buffer: &'a mut PageSlice<u8>) -> Result<&'a [u8], UsbError> {
let transfer = self.start_read(buffer)?;
let len = transfer.wait().await?;
self.complete_transfer(transfer);
Ok(&buffer[..len])
}
}
impl Deref for UsbBulkInPipeAccess {
type Target = dyn UsbBulkInPipe;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl UsbBulkOutPipeAccess {
pub async fn write<'a>(&self, buffer: &'a PageSlice<u8>) -> Result<(), UsbError> {
let transfer = self.start_write(buffer)?;
transfer.wait().await?;
self.complete_transfer(transfer);
Ok(())
}
}
impl Deref for UsbBulkOutPipeAccess {
type Target = dyn UsbBulkOutPipe;
fn deref(&self) -> &Self::Target {
&*self.0
}
}

@ -40,6 +40,11 @@ pub trait UsbDeviceRequest: Sized + Pod {
const B_REQUEST: u8;
}
pub trait UsbClassSpecificRequest: Sized + Pod {
const BM_REQUEST_TYPE: u8;
const B_REQUEST: u8;
}
pub trait UsbDescriptorRequest: UsbDeviceRequest {
const DESCRIPTOR_TYPE: u8;
}
@ -312,6 +317,24 @@ impl UsbControlPipeAccess {
.await
}
pub async fn class_specific_request<D: UsbClassSpecificRequest>(
&self,
w_value: u16,
w_index: u16,
) -> Result<(), UsbError> {
self.perform_value_control(
ControlTransferSetup {
bm_request_type: D::BM_REQUEST_TYPE,
b_request: D::B_REQUEST,
w_value,
w_index,
w_length: 0,
},
None,
)
.await
}
pub async fn set_configuration(&self, value: u16) -> Result<(), UsbError> {
self.perform_action::<SetConfiguration>(value, 0).await
}

@ -1,3 +1,4 @@
pub mod bulk;
pub mod control;
pub mod interrupt;

@ -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")
}
}
}
}

@ -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

@ -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))
}

@ -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
}
}

@ -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(),
}
}
}

@ -0,0 +1,134 @@
use core::ops::{Deref, DerefMut};
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
PageBox,
};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use xhci_lib::context::{self, DeviceHandler, InputHandler};
use ygg_driver_usb::error::UsbError;
use crate::{
controller::{ContextSize, PortNumber},
regs::PortSpeed,
};
pub enum XhciDeviceContext {
Context32(IrqSafeRwLock<PageBox<context::Device32Byte>>),
Context64(IrqSafeRwLock<PageBox<context::Device64Byte>>),
}
pub enum XhciInputContext {
Context32(PageBox<context::Input32Byte>),
Context64(PageBox<context::Input64Byte>),
}
impl XhciDeviceContext {
pub fn new(size: ContextSize) -> Result<Self, UsbError> {
match size {
ContextSize::Context32 => PageBox::new(context::Device::new_32byte())
.map(IrqSafeRwLock::new)
.map(Self::Context32),
ContextSize::Context64 => PageBox::new(context::Device::new_64byte())
.map(IrqSafeRwLock::new)
.map(Self::Context64),
}
.map_err(UsbError::MemoryError)
}
pub fn map<T, F: FnOnce(&dyn DeviceHandler) -> T>(&self, mapper: F) -> T {
match self {
Self::Context32(cx) => mapper(&**cx.read()),
Self::Context64(cx) => mapper(&**cx.read()),
}
}
pub fn physical_address(&self) -> PhysicalAddress {
match self {
Self::Context32(cx) => unsafe { cx.read().as_physical_address() },
Self::Context64(cx) => unsafe { cx.read().as_physical_address() },
}
}
}
impl XhciInputContext {
pub fn new(size: ContextSize) -> Result<Self, UsbError> {
match size {
ContextSize::Context32 => {
PageBox::new(context::Input::new_32byte()).map(Self::Context32)
}
ContextSize::Context64 => {
PageBox::new(context::Input::new_64byte()).map(Self::Context64)
}
}
.map_err(UsbError::MemoryError)
}
pub fn new_address_device(
size: ContextSize,
bus_address: u8,
root_hub_port_number: PortNumber,
speed: u8,
max_packet_size: usize,
dequeue_pointer: PhysicalAddress,
) -> Result<Self, UsbError> {
let mut cx = Self::new(size)?;
{
let control = cx.control_mut();
control.set_add_context_flag(0); // Enable slot context
control.set_add_context_flag(1); // Enable endpoint 0 context
}
{
let slot = cx.device_mut().slot_mut();
slot.set_context_entries(1);
slot.set_interrupter_target(0);
slot.set_usb_device_address(bus_address);
slot.set_root_hub_port_number(root_hub_port_number.into());
slot.set_speed(speed);
}
{
let ep0 = cx.device_mut().endpoint_mut(1);
ep0.set_endpoint_type(context::EndpointType::Control);
ep0.set_error_count(3);
// Use the provided max_packet_size, or the default one for the given speed
ep0.set_max_packet_size(max_packet_size as u16);
ep0.set_tr_dequeue_pointer(dequeue_pointer.into_u64());
ep0.set_dequeue_cycle_state();
}
Ok(cx)
}
pub fn physical_address(&self) -> PhysicalAddress {
match self {
Self::Context32(cx) => unsafe { cx.as_physical_address() },
Self::Context64(cx) => unsafe { cx.as_physical_address() },
}
}
}
impl Deref for XhciInputContext {
type Target = dyn InputHandler;
fn deref(&self) -> &Self::Target {
match self {
Self::Context32(cx) => &**cx,
Self::Context64(cx) => &**cx,
}
}
}
impl DerefMut for XhciInputContext {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Self::Context32(cx) => &mut **cx,
Self::Context64(cx) => &mut **cx,
}
}
}

@ -1,384 +1,192 @@
use core::{future::poll_fn, sync::atomic::Ordering, task::Poll, time::Duration};
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec, vec::Vec};
use atomic_enum::atomic_enum;
use alloc::{sync::Arc, vec::Vec};
use device_api::{device::Device, interrupt::InterruptHandler};
use futures_util::task::AtomicWaker;
use libk::task::runtime;
use libk::error::Error;
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
device::DeviceMemoryIo,
PageBox,
};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use ygg_driver_usb::{
bus::UsbBusManager,
device::{UsbBusAddress, UsbDevice, UsbDeviceAccess},
error::UsbError,
pipe::control::UsbControlPipeAccess,
util::UsbAddressAllocator,
UsbHostController,
};
use yggdrasil_abi::error::Error;
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use ygg_driver_pci::device::PciDeviceInfo;
use ygg_driver_usb::error::UsbError;
use crate::{
device::XhciBusDevice,
pipe::ControlPipe,
regs::{Mapper, PortSpeed, Regs},
regs::{ExtendedCapability, Regs, CONFIG, HCSPARAMS1, PORTLI, PORTSC, USBCMD, USBSTS},
ring::{
CommandExecutor, CommandRing, ControlTransferRing, Event, EventRing, EventRingSegmentTable,
GenericTransferRing,
command::CommandRing,
event::{Event, EventRing, EventRingSegmentTable},
GenericRing,
},
XhciContext,
};
#[atomic_enum]
#[derive(PartialEq, Eq)]
pub enum PortState {
Disconnected, // Default + set by "handle detach"
Init, // Set by "port task"
Running, // Set by "port task"
}
struct PortStruct {
state: AtomicPortState,
notify: AtomicWaker,
address: IrqSafeRwLock<Option<UsbBusAddress>>,
struct ScratchpadArray {
buffers: Vec<PageBox<[u8]>>,
array: PageBox<[PhysicalAddress]>,
}
pub struct Xhci {
regs: Regs,
bus_address: OneTimeInit<u16>,
address_allocator: UsbAddressAllocator,
port_count: usize,
// TODO use to allocate proper contexts
#[allow(unused)]
context_size: usize,
pci: PciDeviceInfo,
dcbaa: IrqSafeRwLock<PageBox<[PhysicalAddress]>>,
endpoints: IrqSafeRwLock<BTreeMap<(u8, u8), Arc<dyn GenericTransferRing>>>,
scratchpads: Option<ScratchpadArray>,
command_ring: CommandRing,
event_ring: EventRing,
pub(crate) command_ring: CommandRing,
erst: EventRingSegmentTable,
}
port_states: Vec<PortStruct>,
impl ScratchpadArray {
pub fn new(capacity: usize, element_size: usize) -> Result<Self, UsbError> {
let buffers = (0..capacity)
.map(|_| PageBox::new_slice(0, element_size))
.collect::<Result<Vec<_>, _>>()
.map_err(UsbError::MemoryError)?;
let array =
PageBox::new_slice_with(|i| unsafe { buffers[i].as_physical_address() }, capacity)
.map_err(UsbError::MemoryError)?;
Ok(Self { buffers, array })
}
}
impl Xhci {
pub fn new(regs: xhci_lib::Registers<Mapper>) -> Result<Self, UsbError> {
let event_ring = EventRing::new(128)?;
let command_ring = CommandRing::new(128)?;
let regs = Regs::from(regs);
let port_count = regs.port_count();
let slot_count = regs.max_slot_count();
let context_size = regs.context_size();
let dcbaa = PageBox::new_slice(PhysicalAddress::ZERO, slot_count + 1)
pub fn new(pci: PciDeviceInfo, regs: Regs) -> Result<Self, UsbError> {
let mut dcbaa = PageBox::new_slice(PhysicalAddress::ZERO, regs.slot_count + 1)
.map_err(UsbError::MemoryError)?;
let command_ring = CommandRing::new(128)?;
let event_ring = EventRing::new(128)?;
let erst = EventRingSegmentTable::for_event_rings(&[&event_ring])?;
// Setup scratch buffers
// TODO: Linux seems to just ignore the PAGESIZE, it's (1 << 0) everywhere
let scratchpads = if regs.scratch_count != 0 {
let array = ScratchpadArray::new(regs.scratch_count, 0x1000)?;
dcbaa[0] = unsafe { array.array.as_physical_address() };
Some(array)
} else {
None
};
for cap in regs.extended_capabilities.iter() {
match cap {
ExtendedCapability::ProtocolSupport(support) => {
let Some(version) = support.usb_revision() else {
continue;
};
for port in support.port_range() {
log::info!("* Port {port}: {version}");
}
}
ExtendedCapability::LegacySupport(legsup) => {
legsup.write().perform_bios_handoff(10000000)?;
}
}
}
Ok(Self {
regs,
bus_address: OneTimeInit::new(),
address_allocator: UsbAddressAllocator::new(),
port_count,
context_size,
event_ring,
command_ring,
pci,
dcbaa: IrqSafeRwLock::new(dcbaa),
endpoints: IrqSafeRwLock::new(BTreeMap::new()),
port_states: Vec::from_iter((0..port_count).map(|_| PortStruct {
state: AtomicPortState::new(PortState::Disconnected),
notify: AtomicWaker::new(),
address: IrqSafeRwLock::new(None),
})),
scratchpads,
command_ring,
event_ring,
erst,
})
}
pub fn register_device_context(&self, slot_id: u8, context: PhysicalAddress) {
self.dcbaa.write()[slot_id as usize] = context;
}
pub fn register_endpoint(
&self,
slot_id: u8,
endpoint_id: u8,
ring: Arc<dyn GenericTransferRing>,
) {
self.endpoints.write().insert((slot_id, endpoint_id), ring);
}
pub fn shutdown_endpoint(&self, slot_id: u8, endpoint_id: u8) {
if let Some(endpoint) = self.endpoints.write().remove(&(slot_id, endpoint_id)) {
endpoint.shutdown();
} else {
log::warn!(
"Endpoint {}:{} does not exist, maybe already shut down?",
slot_id,
endpoint_id
);
}
}
pub fn notify_transfer(
&self,
slot_id: u8,
endpoint_id: u8,
address: PhysicalAddress,
status: u32,
) {
if let Some(ep) = self.endpoints.read().get(&(slot_id, endpoint_id)) {
ep.notify(address, status);
}
}
async fn assign_device(
self: Arc<Self>,
speed: PortSpeed,
slot_id: u8,
root_hub_port_number: u8,
) -> Result<Box<dyn UsbDevice>, UsbError> {
let address = self.address_allocator.allocate().unwrap();
let ring = Arc::new(ControlTransferRing::new(slot_id, 1, 128)?);
let context =
XhciContext::new_32byte_address_device(&ring, speed, address, root_hub_port_number)?;
let mut input = context.input.write();
self.register_device_context(slot_id, unsafe { context.output.as_physical_address() });
self.command_ring
.address_device(&*self, slot_id, &mut input)
.await?;
self.register_endpoint(slot_id, 1, ring.clone());
let pipe = ControlPipe::new(self.clone(), slot_id, ring.clone());
// TODO: If the device is a Full-speed one, determine its max packet size for the control
// endpoint
if speed == PortSpeed::Full {
todo!()
}
drop(input);
let bus_address = UsbBusAddress {
bus: *self.bus_address.get(),
device: address,
};
let device = XhciBusDevice {
xhci: self.clone(),
slot_id,
port_id: root_hub_port_number,
bus_address,
speed,
context: Arc::new(context),
rings: IrqSafeRwLock::new(vec![ring]),
control_pipe: UsbControlPipeAccess(Box::new(pipe)),
};
Ok(Box::new(device))
}
async fn port_task(self: Arc<Self>, index: usize) -> Result<(), UsbError> {
let state = &self.port_states[index];
self.reset_port(index).await?;
let regs = self.regs.ports.read(index);
let speed =
PortSpeed::try_from(regs.portsc.port_speed()).map_err(|_| UsbError::PortInitFailed)?;
let slot_id = self.command_ring.enable_slot(self.as_ref()).await?;
let device = self
.clone()
.assign_device(speed, slot_id, (index + 1) as _)
.await?;
let device = UsbDeviceAccess::setup(device).await?;
let old = state.address.write().replace(device.bus_address());
assert!(old.is_none());
UsbBusManager::register_device(Arc::new(device));
state.state.store(PortState::Running, Ordering::Release);
Ok(())
}
fn handle_device_attached(self: Arc<Self>, port: usize) -> Result<(), UsbError> {
log::info!("Port {}: device attached", port);
if let Err(err) = self.port_states[port].state.compare_exchange(
PortState::Disconnected,
PortState::Init,
Ordering::Acquire,
Ordering::Relaxed,
) {
log::warn!("Could not start port init task: port state is {:?}", err);
return Err(UsbError::DeviceBusy);
}
runtime::spawn(async move { self.port_task(port).await }).map_err(UsbError::SystemError)?;
Ok(())
}
fn handle_device_detached(&self, port: usize) -> Result<(), UsbError> {
let state = &self.port_states[port];
match state.state.swap(PortState::Disconnected, Ordering::Release) {
PortState::Init => {
log::warn!("USB device detach received while in init state");
Ok(())
}
PortState::Running => {
log::info!("Port {}: device detached", port);
let address = state
.address
.write()
.take()
.expect("Port marked as Running, but has no address");
UsbBusManager::detach_device(address);
state.notify.wake();
Ok(())
}
// Already disconnected
PortState::Disconnected => Ok(()),
}
}
fn handle_port_event(self: Arc<Self>, port: usize) -> Result<(), UsbError> {
let state = &self.port_states[port];
let port_regs = self.regs.ports.read(port);
if port_regs.portsc.connect_status_change() {
if port_regs.portsc.current_connect_status() {
self.handle_device_attached(port)
} else {
self.handle_device_detached(port)
}
} else {
// Some other event
state.notify.wake();
Ok(())
}
}
async fn reset_port(&self, port: usize) -> Result<(), UsbError> {
log::debug!("Reset port {}", port);
self.regs.ports.update(port, |u| {
u.portsc.set_port_reset();
});
// Wait for port reset
// TODO handle disconnect during reset?
let result = runtime::with_timeout(
poll_fn(|cx| {
let state = &self.port_states[port];
state.notify.register(cx.waker());
if !self.regs.ports.read(port).portsc.port_reset() {
Poll::Ready(())
} else {
Poll::Pending
}
}),
Duration::from_secs(1),
)
.await;
match result {
Ok(()) => Ok(()),
Err(_) => Err(UsbError::PortResetFailed),
}
}
fn handle_event(self: Arc<Self>) {
fn handle_event(&self) {
while let Some(event) = self.event_ring.try_dequeue() {
match event {
Event::PortChange(port) => {
self.clone().handle_port_event(port - 1).ok();
Event::PortChange(index) => {
log::info!("Event Ring: port {index} changed");
let ports = self.regs.ports.write();
let port = &ports[index - 1];
let status = port.PORTSC.extract();
let portli = port.PORTLI.extract();
if status.matches_any(PORTSC::CSC::SET) {
let connected = status.matches_any(PORTSC::CCS::SET);
port.PORTSC.set(status.get());
let pp = status.read(PORTSC::PP);
let ccs = status.read(PORTSC::CCS);
let ped = status.read(PORTSC::PED);
let pls = status.read(PORTSC::PLS);
let ps = status.read(PORTSC::PS);
log::info!("port {index} PORTSC={:#x}", status.get());
log::info!("* xhci {}", self.pci.address);
log::info!("* PP={pp} CCS={ccs} PED={ped} PLS={pls} PS={ps}");
}
Event::CommandCompletion { address, reply } => {
self.command_ring.notify(address, reply);
}
Event::Transfer {
address,
slot_id,
endpoint_id,
status,
} => {
self.notify_transfer(slot_id, endpoint_id, address, status);
_ => {
log::info!("other event");
}
}
}
self.regs
.set_interrupter_0_dequeue_pointer(self.event_ring.dequeue_pointer());
}
}
impl UsbHostController for Xhci {}
impl CommandExecutor for Xhci {
fn ring_doorbell(&self, index: usize, target: u8) {
self.regs.ring_doorbell(index, target);
.runtime
.write()
.set_interrupter_dequeue_pointer(0, self.event_ring.dequeue_pointer());
}
}
impl Device for Xhci {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
log::info!("Init USB xHCI");
self.regs.hc_reset(10000000)?;
log::info!("xHC reset complete");
self.regs.reset();
self.regs.set_max_slot_count();
// Configure the HC
let dcbaap = unsafe { self.dcbaa.read().as_physical_address() };
let cr_base = self.command_ring.base();
let erst = EventRingSegmentTable::for_event_rings(&[&self.event_ring])?;
let dcbaa = self.dcbaa.read();
let op = self.regs.operational.write();
let rt = self.regs.runtime.write();
let ports = self.regs.ports.write();
self.regs
.configure(&dcbaa, &self.command_ring, &self.event_ring, &erst);
log::info!("xhci: configure HC");
op.CONFIG
.modify(CONFIG::MaxSlotsEn.val(self.regs.slot_count as u32));
op.set_dcbaap(dcbaap);
op.set_crcr(cr_base, true);
let bus = UsbBusManager::register_bus(self.clone());
self.bus_address.init(bus);
log::info!("xhci: configure interrupter");
rt.configure_interrupter(0, &self.event_ring, &self.erst);
for port in 0..self.port_count {
let p = self.regs.ports.read(port);
if p.portsc.current_connect_status() {
self.clone().handle_device_attached(port).ok();
}
}
// Enable interrupts and start the HC
log::info!("xhci: start HC");
op.USBCMD.modify(USBCMD::INTE::SET + USBCMD::HSEE::SET);
op.USBCMD.modify(USBCMD::RS::SET);
op.wait_usbsts_bit(USBSTS::CNR::CLEAR, 100000000)?;
Ok(())
}
fn display_name(&self) -> &str {
"USB xHCI"
"xHCI"
}
}
impl InterruptHandler for Xhci {
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
if let Some(status) = self.regs.handle_interrupt() {
if status.event_interrupt() {
let status = self.regs.handle_interrupt(0);
log::info!("IRQ started: {:#x}", status.get());
if status.matches_all(USBSTS::HSE::SET) {
log::error!("xhci: Host System Error occurred");
}
if status.matches_all(USBSTS::EINT::SET) {
self.handle_event();
}
log::info!("IRQ finished");
true
} else {
false
}
}
}

@ -1,23 +1,30 @@
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use futures_util::{future::BoxFuture, FutureExt};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use xhci_lib::context::{self, InputHandler};
use libk_util::sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock};
use xhci_lib::context;
use ygg_driver_usb::{
device::{UsbBusAddress, UsbDevice, UsbSpeed},
device::{UsbBusAddress, UsbDevice, UsbDeviceDetachHandler, UsbSpeed},
error::UsbError,
info::UsbEndpointType,
pipe::{control::UsbControlPipeAccess, interrupt::UsbInterruptInPipeAccess},
pipe::{
bulk::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
control::UsbControlPipeAccess,
interrupt::UsbInterruptInPipeAccess,
},
UsbDirection, UsbHostController,
};
use crate::{
pipe::InterruptInPipe,
context::{XhciDeviceContext, XhciInputContext},
pipe::{BulkInPipe, BulkOutPipe, InterruptInPipe},
regs::PortSpeed,
ring::{transfer::InterruptInTransferRing, GenericTransferRing},
Xhci, XhciContext,
ring::{
transfer::{BulkInTransferRing, BulkOutTransferRing, InterruptInTransferRing},
GenericTransferRing,
},
Xhci,
};
// TODO device context information
pub struct XhciBusDevice {
pub(crate) port_id: u8,
pub(crate) slot_id: u8,
@ -27,9 +34,11 @@ pub struct XhciBusDevice {
pub(crate) xhci: Arc<Xhci>,
pub(crate) context: Arc<XhciContext<8>>,
#[allow(unused)]
pub(crate) device_context: XhciDeviceContext,
pub(crate) rings: IrqSafeRwLock<Vec<Arc<dyn GenericTransferRing>>>,
pub(crate) control_pipe: UsbControlPipeAccess,
pub(crate) detach_handler: IrqSafeSpinlock<Option<Arc<dyn UsbDeviceDetachHandler>>>,
}
impl XhciBusDevice {
@ -56,42 +65,51 @@ impl XhciBusDevice {
) -> Result<(), UsbError> {
log::debug!("Setup endpoint dci #{}: {:?} {:?}", dci, ty, direction);
let mut input = self.context.input.write();
let ep_type = match (ty, direction) {
(UsbEndpointType::Interrupt, UsbDirection::In) => context::EndpointType::InterruptIn,
(UsbEndpointType::Interrupt, UsbDirection::Out) => context::EndpointType::InterruptOut,
(UsbEndpointType::Bulk, UsbDirection::In) => context::EndpointType::BulkIn,
(UsbEndpointType::Bulk, UsbDirection::Out) => context::EndpointType::BulkOut,
_ => todo!(),
};
let mut input_cx = XhciInputContext::new(self.xhci.context_size)?;
{
let control = input.control_mut();
let control = input_cx.control_mut();
control.set_add_context_flag(0);
control.clear_add_context_flag(1);
control.set_add_context_flag(dci as _);
}
{
let slot = input.device_mut().slot_mut();
let slot = input_cx.device_mut().slot_mut();
slot.set_context_entries(31);
slot.set_interrupter_target(0);
slot.set_usb_device_address(self.bus_address.device);
slot.set_root_hub_port_number(self.port_id);
slot.set_speed(self.speed.into());
}
{
let ep_cx = input.device_mut().endpoint_mut(dci as _);
let ep_cx = input_cx.device_mut().endpoint_mut(dci as _);
ep_cx.set_tr_dequeue_pointer(ring.dequeue_pointer().into());
ep_cx.set_dequeue_cycle_state();
ep_cx.set_endpoint_type(ep_type);
ep_cx.set_error_count(3);
// TODO get from endpoint info
// TODO
ep_cx.set_max_packet_size(8);
ep_cx.set_tr_dequeue_pointer(ring.dequeue_pointer().into_u64());
ep_cx.set_dequeue_cycle_state();
}
self.xhci
.command_ring
.configure_endpoint(self.xhci.as_ref(), self.slot_id, &mut input)
.configure_endpoint(
self.xhci.as_ref(),
self.slot_id,
input_cx.physical_address(),
)
.await?;
self.rings.write().push(ring.clone());
@ -141,12 +159,50 @@ impl UsbDevice for XhciBusDevice {
.boxed()
}
fn open_bulk_in_pipe(&self, number: u8) -> BoxFuture<Result<UsbBulkInPipeAccess, UsbError>> {
async move {
let dci = Self::dci(UsbEndpointType::Bulk, UsbDirection::In, number) as u8;
let ring = Arc::new(BulkInTransferRing::new(self.slot_id, dci as _, 128)?);
self.setup_endpoint_inner(ring.clone(), dci, UsbEndpointType::Bulk, UsbDirection::In)
.await?;
let pipe = BulkInPipe::new(self.xhci.clone(), self.slot_id, number, dci, ring);
Ok(UsbBulkInPipeAccess(Box::new(pipe)))
}
.boxed()
}
fn open_bulk_out_pipe(&self, number: u8) -> BoxFuture<Result<UsbBulkOutPipeAccess, UsbError>> {
async move {
let dci = Self::dci(UsbEndpointType::Bulk, UsbDirection::Out, number) as u8;
let ring = Arc::new(BulkOutTransferRing::new(self.slot_id, dci as _, 128)?);
self.setup_endpoint_inner(ring.clone(), dci, UsbEndpointType::Bulk, UsbDirection::Out)
.await?;
let pipe = BulkOutPipe::new(self.xhci.clone(), self.slot_id, number, dci, ring);
Ok(UsbBulkOutPipeAccess(Box::new(pipe)))
}
.boxed()
}
fn set_detach_handler(&self, handler: Arc<dyn UsbDeviceDetachHandler>) {
*self.detach_handler.lock() = Some(handler);
}
fn handle_detach(&self) {
log::info!("Device detach handler");
for ring in self.rings.write().drain(..) {
self.xhci
.shutdown_endpoint(ring.slot_id(), ring.endpoint_id());
}
if let Some(handler) = self.detach_handler.lock().take() {
handler.handle_device_detach();
}
}
fn speed(&self) -> UsbSpeed {

@ -4,87 +4,22 @@
extern crate alloc;
mod controller;
mod device;
mod pipe;
mod regs;
mod ring;
use alloc::sync::Arc;
pub use controller::Xhci;
use controller::Xhci;
use device_api::{device::Device, interrupt::InterruptAffinity};
use libk_mm::PageBox;
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use regs::{Mapper, PortSpeed};
use ring::{ControlTransferRing, GenericTransferRing};
use xhci_lib::context::{self, InputHandler};
use regs::Regs;
// use regs::Mapper;
// use xhci_lib::extended_capabilities;
use ygg_driver_pci::{
capability::{DevicePowerState, PowerManagementCapability},
device::{PciDeviceInfo, PreferredInterruptMode},
PciCommandRegister, PciConfigurationSpace,
};
use ygg_driver_usb::error::UsbError;
use yggdrasil_abi::error::Error;
pub struct XhciContext<const N: usize> {
pub(crate) input: IrqSafeRwLock<PageBox<context::Input<N>>>,
pub(crate) output: PageBox<context::Device<N>>,
}
impl XhciContext<8> {
pub fn new_32byte() -> Result<Self, UsbError> {
let input = PageBox::new(context::Input::new_32byte()).map_err(UsbError::MemoryError)?;
let output = PageBox::new(context::Device::new_32byte()).map_err(UsbError::MemoryError)?;
Ok(Self {
input: IrqSafeRwLock::new(input),
output,
})
}
pub fn new_32byte_address_device(
default_control_ring: &ControlTransferRing,
speed: PortSpeed,
address: u8,
root_hub_port_number: u8,
) -> Result<Self, UsbError> {
let mut input =
PageBox::new(context::Input::new_32byte()).map_err(UsbError::MemoryError)?;
let output = PageBox::new(context::Device::new_32byte()).map_err(UsbError::MemoryError)?;
// Setup input context
{
let control = input.control_mut();
control.set_add_context_flag(0);
control.set_add_context_flag(1);
}
{
let slot = input.device_mut().slot_mut();
slot.set_context_entries(1);
slot.set_interrupter_target(0);
slot.set_usb_device_address(address);
slot.set_root_hub_port_number(root_hub_port_number);
slot.set_speed(speed.into());
}
{
let ep0 = input.device_mut().endpoint_mut(1);
ep0.set_endpoint_type(context::EndpointType::Control);
ep0.set_tr_dequeue_pointer(default_control_ring.dequeue_pointer().into());
ep0.set_dequeue_cycle_state();
ep0.set_error_count(3);
ep0.set_max_packet_size(speed.default_max_packet_size() as _);
}
Ok(Self {
input: IrqSafeRwLock::new(input),
output,
})
}
}
mod controller;
mod regs;
mod ring;
pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
// TODO Chip Hardware Reset
@ -95,15 +30,27 @@ pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
.as_memory()
.expect("xHCI's BAR0 is not memory-type");
if let Some(power) = info.config_space.capability::<PowerManagementCapability>() {
log::info!("xHC has power management capability");
let power_state = power.get_device_power_state();
if power_state != DevicePowerState::D0 {
power.set_device_power_state(DevicePowerState::D0);
}
// Enable PME# signal generation
power.set_pme_en(true);
}
let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
info.config_space.set_command(cmd.bits());
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(true))?;
let regs = Regs::map(bar0)?;
let xhci = Arc::new(Xhci::new(info.clone(), regs)?);
info.init_interrupts(PreferredInterruptMode::Msi)?;
info.map_interrupt(InterruptAffinity::Any, xhci.clone())?;
Ok(xhci)

@ -1,9 +1,10 @@
use alloc::sync::Arc;
use libk_mm::{address::PhysicalAddress, PageBox};
use libk_mm::{address::PhysicalAddress, PageBox, PageSlice};
use ygg_driver_usb::{
communication::UsbInterruptTransfer,
communication::{UsbBulkTransfer, UsbInterruptTransfer},
error::UsbError,
pipe::{
bulk::{UsbBulkInPipe, UsbBulkOutPipe},
control::{ControlTransferSetup, UsbControlPipe},
interrupt::UsbInterruptInPipe,
UsbGenericPipe,
@ -12,7 +13,10 @@ use ygg_driver_usb::{
};
use crate::{
ring::{transfer::InterruptInTransferRing, ControlTransferRing},
ring::{
transfer::{BulkInTransferRing, BulkOutTransferRing, InterruptInTransferRing},
ControlTransferRing,
},
Xhci,
};
@ -32,6 +36,27 @@ pub struct InterruptInPipe {
ring: Arc<InterruptInTransferRing>,
}
#[allow(unused)]
pub struct BulkInPipe {
xhci: Arc<Xhci>,
slot_id: u8,
endpoint_id: u8,
dci: u8,
ring: Arc<BulkInTransferRing>,
}
#[allow(unused)]
pub struct BulkOutPipe {
xhci: Arc<Xhci>,
slot_id: u8,
endpoint_id: u8,
dci: u8,
ring: Arc<BulkOutTransferRing>,
}
impl UsbGenericPipe for ControlPipe {}
impl UsbControlPipe for ControlPipe {
@ -83,3 +108,63 @@ impl InterruptInPipe {
}
}
}
impl UsbGenericPipe for BulkInPipe {}
impl UsbBulkInPipe for BulkInPipe {
fn start_read(&self, buffer: &mut PageSlice<u8>) -> Result<UsbBulkTransfer, UsbError> {
self.ring.start_transfer(self.xhci.as_ref(), buffer)
}
fn complete_transfer(&self, transfer: UsbBulkTransfer) {
self.ring.complete_transfer(transfer)
}
}
impl BulkInPipe {
pub fn new(
xhci: Arc<Xhci>,
slot_id: u8,
endpoint_id: u8,
dci: u8,
ring: Arc<BulkInTransferRing>,
) -> Self {
Self {
xhci,
slot_id,
endpoint_id,
dci,
ring,
}
}
}
impl UsbGenericPipe for BulkOutPipe {}
impl UsbBulkOutPipe for BulkOutPipe {
fn start_write(&self, buffer: &PageSlice<u8>) -> Result<UsbBulkTransfer, UsbError> {
self.ring.start_transfer(self.xhci.as_ref(), buffer)
}
fn complete_transfer(&self, transfer: UsbBulkTransfer) {
self.ring.complete_transfer(transfer)
}
}
impl BulkOutPipe {
pub fn new(
xhci: Arc<Xhci>,
slot_id: u8,
endpoint_id: u8,
dci: u8,
ring: Arc<BulkOutTransferRing>,
) -> Self {
Self {
xhci,
slot_id,
endpoint_id,
dci,
ring,
}
}
}

@ -1,283 +0,0 @@
use core::{cell::UnsafeCell, num::NonZeroUsize};
use alloc::{sync::Arc, vec::Vec};
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
device::RawDeviceMemoryMapping,
PageBox,
};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use xhci_lib::{
accessor::{array, marker},
registers::{
operational::UsbStatusRegister, Capability, Doorbell, InterrupterRegisterSet, Operational,
PortRegisterSet,
},
};
use ygg_driver_usb::device::UsbSpeed;
use yggdrasil_abi::primitive_enum;
use crate::ring::{CommandRing, EventRing, EventRingSegmentTable, GenericRing};
#[derive(Clone)]
pub struct Mapper {
mappings: Vec<Arc<RawDeviceMemoryMapping>>,
}
pub struct LockedArray<T> {
array: UnsafeCell<array::Generic<T, Mapper, marker::ReadWrite>>,
locks: Vec<IrqSafeRwLock<()>>,
}
unsafe impl<T> Sync for LockedArray<T> {}
unsafe impl<T> Send for LockedArray<T> {}
pub struct Regs {
operational: IrqSafeRwLock<Operational<Mapper>>,
interrupters: IrqSafeRwLock<InterrupterRegisterSet<Mapper>>,
capability: Capability<Mapper>,
doorbells: LockedArray<Doorbell>,
pub ports: LockedArray<PortRegisterSet>,
}
impl<T> LockedArray<T> {
#[inline]
#[allow(clippy::mut_from_ref)]
unsafe fn get_mut(&self) -> &mut array::Generic<T, Mapper, marker::ReadWrite> {
&mut *self.array.get()
}
pub fn update<U: FnOnce(&mut T)>(&self, index: usize, f: U) {
let _guard = self.locks[index].write();
unsafe { self.get_mut() }.update_volatile_at(index, f);
}
pub fn read(&self, index: usize) -> T {
let _guard = self.locks[index].read();
unsafe { self.get_mut() }.read_volatile_at(index)
}
}
impl<T> From<array::Generic<T, Mapper, marker::ReadWrite>> for LockedArray<T> {
fn from(value: array::Generic<T, Mapper, marker::ReadWrite>) -> Self {
let locks = Vec::from_iter((0..value.len()).map(|_| IrqSafeRwLock::new(())));
Self {
array: UnsafeCell::new(value),
locks,
}
}
}
impl From<xhci_lib::Registers<Mapper>> for Regs {
fn from(value: xhci_lib::Registers<Mapper>) -> Self {
Self {
operational: IrqSafeRwLock::new(value.operational),
capability: value.capability,
interrupters: IrqSafeRwLock::new(value.interrupter_register_set),
doorbells: LockedArray::from(value.doorbell),
ports: LockedArray::from(value.port_register_set),
}
}
}
impl Regs {
pub fn reset(&self) {
let mut o = self.operational.write();
// TODO Get ownership from firmware
// Stop the controller
o.usbcmd.update_volatile(|u| {
u.clear_run_stop();
});
while !o.usbsts.read_volatile().hc_halted() {
core::hint::spin_loop();
}
// Reset the controller
o.usbcmd.update_volatile(|u| {
u.set_host_controller_reset();
});
while o.usbcmd.read_volatile().host_controller_reset()
|| o.usbsts.read_volatile().controller_not_ready()
{
core::hint::spin_loop();
}
}
pub fn max_slot_count(&self) -> usize {
self.capability
.hcsparams1
.read_volatile()
.number_of_device_slots() as _
}
pub fn set_max_slot_count(&self) -> usize {
let device_slot_count = self.max_slot_count();
let mut o = self.operational.write();
// Set max slots enabled
o.config.update_volatile(|u| {
u.set_max_device_slots_enabled(device_slot_count as _);
});
device_slot_count as _
}
pub fn context_size(&self) -> usize {
match self.capability.hccparams1.read_volatile().context_size() {
true => 64,
false => 32,
}
}
pub fn port_count(&self) -> usize {
self.capability.hcsparams1.read_volatile().number_of_ports() as _
}
pub fn configure(
&self,
dcbaa: &PageBox<[PhysicalAddress]>,
cmd_ring: &CommandRing,
evt_ring: &EventRing,
erst: &EventRingSegmentTable,
) {
let mut o = self.operational.write();
let mut i = self.interrupters.write();
o.dcbaap.update_volatile(|u| unsafe {
u.set(dcbaa.as_physical_address().into());
});
o.crcr.update_volatile(|u| {
u.set_command_ring_pointer(cmd_ring.base().into());
u.set_ring_cycle_state();
});
let mut intr0 = i.interrupter_mut(0);
intr0.erstsz.update_volatile(|u| {
u.set(erst.capacity().try_into().unwrap());
});
intr0.erdp.update_volatile(|u| {
log::debug!("::: Dequeue Pointer: {:#x}", evt_ring.dequeue_pointer());
u.set_event_ring_dequeue_pointer(evt_ring.dequeue_pointer().into());
});
intr0.erstba.update_volatile(|u| {
u.set(erst.physical_address().into());
});
// intr0.imod.update_volatile(|u| {
// u.set_interrupt_moderation_interval(0)
// .set_interrupt_moderation_counter(0);
// });
intr0.iman.update_volatile(|u| {
u.set_interrupt_enable();
});
o.usbcmd.update_volatile(|u| {
u.set_interrupter_enable().set_run_stop();
});
}
pub fn handle_interrupt(&self) -> Option<UsbStatusRegister> {
let mut o = self.operational.write();
let mut i = self.interrupters.write();
let status = o.usbsts.read_volatile();
if !status.event_interrupt() {
return None;
}
o.usbsts.write_volatile(status);
if status.host_system_error() {
return Some(status);
}
// Acknowledge interrupts
let mut intr0 = i.interrupter_mut(0);
intr0.iman.update_volatile(|u| {
u.clear_interrupt_pending();
});
Some(status)
}
pub fn set_interrupter_0_dequeue_pointer(&self, pointer: PhysicalAddress) {
let mut i = self.interrupters.write();
i.interrupter_mut(0).erdp.update_volatile(|u| {
u.set_event_ring_dequeue_pointer(pointer.into());
u.clear_event_handler_busy();
});
}
pub fn ring_doorbell(&self, index: usize, target: u8) {
self.doorbells.update(index, |u| {
u.set_doorbell_target(target);
});
}
}
impl Mapper {
pub fn new() -> Self {
Self {
mappings: Vec::new(),
}
}
}
impl xhci_lib::accessor::Mapper for Mapper {
unsafe fn map(&mut self, phys_start: usize, bytes: usize) -> NonZeroUsize {
let mapping = RawDeviceMemoryMapping::map(phys_start as u64, bytes, Default::default())
.expect("Could not map an USB xHCI region");
let address = mapping.address;
self.mappings.push(Arc::new(mapping));
NonZeroUsize::new_unchecked(address)
}
fn unmap(&mut self, _virt_start: usize, _bytes: usize) {
// TODO
}
}
// Register value definitions
primitive_enum! {
pub enum PortSpeed: u8 {
Full = 1,
Low = 2,
High = 3,
SuperGen1x1 = 4,
SuperGen2x1 = 5,
SuperGen1x2 = 6,
SuperGen2x2 = 7,
}
}
impl PortSpeed {
pub fn default_max_packet_size(&self) -> usize {
match self {
Self::Low => 8,
Self::High => 64,
Self::SuperGen1x1 | Self::SuperGen1x2 | Self::SuperGen2x1 | Self::SuperGen2x2 => 512,
// See Section 4.3., point 7. of the initialization list
Self::Full => 8,
}
}
}
impl From<PortSpeed> for UsbSpeed {
fn from(value: PortSpeed) -> Self {
match value {
PortSpeed::Low => UsbSpeed::Low,
PortSpeed::Full => UsbSpeed::Full,
PortSpeed::High => UsbSpeed::High,
PortSpeed::SuperGen1x1
| PortSpeed::SuperGen1x2
| PortSpeed::SuperGen2x1
| PortSpeed::SuperGen2x2 => UsbSpeed::Super,
}
}
}

@ -0,0 +1,814 @@
use core::{
ops::Range,
sync::atomic::{AtomicU32, Ordering},
};
use alloc::vec::Vec;
use device_api::interrupt::Irq;
use libk::error::Error;
use libk_mm::{
address::{PhysicalAddress, Virtualize},
device::DeviceMemoryIo,
pointer::PhysicalRef,
};
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use tock_registers::{
fields::FieldValue,
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{InMemoryRegister, ReadOnly, ReadWrite, WriteOnly},
LocalRegisterCopy,
};
use ygg_driver_usb::{error::UsbError, info::UsbVersion};
use crate::ring::{
event::{EventRing, EventRingSegmentTable},
GenericRing,
};
register_bitfields! {
u32,
pub HCSPARAMS1 [
MaxSlots OFFSET(0) NUMBITS(8) [],
MaxIntrs OFFSET(8) NUMBITS(11) [],
MaxPorts OFFSET(24) NUMBITS(8) [],
],
pub HCSPARAMS2 [
IST OFFSET(0) NUMBITS(4) [],
ERSTMax OFFSET(4) NUMBITS(4) [],
MaxScratchpadBufsHi OFFSET(21) NUMBITS(5) [],
ScratchpadRestore OFFSET(26) NUMBITS(1) [],
MaxScratchpadBufsLo OFFSET(27) NUMBITS(5) [],
],
pub HCSPARAMS3 [
U1ExitLatency OFFSET(0) NUMBITS(8) [],
U2ExitLatency OFFSET(16) NUMBITS(16) [],
],
pub HCCPARAMS1 [
AC64 OFFSET(0) NUMBITS(1) [],
BNC OFFSET(1) NUMBITS(1) [],
CSZ OFFSET(2) NUMBITS(1) [],
PPC OFFSET(3) NUMBITS(1) [],
PIND OFFSET(4) NUMBITS(1) [],
LHRC OFFSET(5) NUMBITS(1) [],
LTC OFFSET(6) NUMBITS(1) [],
NSS OFFSET(7) NUMBITS(1) [],
PAE OFFSET(8) NUMBITS(1) [],
SPC OFFSET(9) NUMBITS(1) [],
SEC OFFSET(10) NUMBITS(1) [],
CFC OFFSET(11) NUMBITS(1) [],
MaxPSASize OFFSET(12) NUMBITS(4) [],
XECP OFFSET(16) NUMBITS(16) [],
],
pub HCCPARAMS2 [
U3C OFFSET(0) NUMBITS(1) [],
CMC OFFSET(1) NUMBITS(1) [],
FSC OFFSET(2) NUMBITS(1) [],
CTC OFFSET(3) NUMBITS(1) [],
LEC OFFSET(4) NUMBITS(1) [],
CIC OFFSET(5) NUMBITS(1) [],
ETC OFFSET(6) NUMBITS(1) [],
ETC_TSC OFFSET(7) NUMBITS(1) [],
GSC OFFSET(8) NUMBITS(1) [],
VTC OFFSET(9) NUMBITS(1) [],
],
pub USBCMD [
RS OFFSET(0) NUMBITS(1) [],
HCRST OFFSET(1) NUMBITS(1) [],
INTE OFFSET(2) NUMBITS(1) [],
HSEE OFFSET(3) NUMBITS(1) [],
LHCRST OFFSET(7) NUMBITS(1) [],
CSS OFFSET(8) NUMBITS(1) [],
CRS OFFSET(9) NUMBITS(1) [],
EWE OFFSET(10) NUMBITS(1) [],
EU3S OFFSET(11) NUMBITS(1) [],
CME OFFSET(13) NUMBITS(1) [],
ETE OFFSET(14) NUMBITS(1) [],
TSC_EN OFFSET(15) NUMBITS(1) [],
VTIOE OFFSET(16) NUMBITS(1) [],
],
pub USBSTS [
HCH OFFSET(0) NUMBITS(1) [],
HSE OFFSET(2) NUMBITS(1) [],
EINT OFFSET(3) NUMBITS(1) [],
PCD OFFSET(4) NUMBITS(1) [],
SSS OFFSET(8) NUMBITS(1) [],
RSS OFFSET(9) NUMBITS(1) [],
SRE OFFSET(10) NUMBITS(1) [],
CNR OFFSET(11) NUMBITS(1) [],
HCE OFFSET(12) NUMBITS(1) [],
],
pub CONFIG [
MaxSlotsEn OFFSET(0) NUMBITS(8) [],
U3E OFFSET(8) NUMBITS(1) [],
CIE OFFSET(9) NUMBITS(1) [],
],
pub IMAN [
IP OFFSET(0) NUMBITS(1) [],
IE OFFSET(1) NUMBITS(1) [],
],
pub IMOD [
IMODI OFFSET(0) NUMBITS(16) [],
IMODC OFFSET(16) NUMBITS(16) [],
],
pub PORTSC [
CCS OFFSET(0) NUMBITS(1) [],
PED OFFSET(1) NUMBITS(1) [],
OCA OFFSET(3) NUMBITS(1) [],
PR OFFSET(4) NUMBITS(1) [],
PLS OFFSET(5) NUMBITS(4) [],
PP OFFSET(9) NUMBITS(1) [],
PS OFFSET(10) NUMBITS(4) [],
PIC OFFSET(14) NUMBITS(2) [],
LWS OFFSET(16) NUMBITS(1) [],
CSC OFFSET(17) NUMBITS(1) [],
PEC OFFSET(18) NUMBITS(1) [],
WRC OFFSET(19) NUMBITS(1) [],
OCC OFFSET(20) NUMBITS(1) [],
PRC OFFSET(21) NUMBITS(1) [],
],
pub PORTLI [
LEC OFFSET(0) NUMBITS(16) [],
RLC OFFSET(16) NUMBITS(4) [],
TLC OFFSET(20) NUMBITS(4) [],
],
}
register_structs! {
#[allow(non_snake_case)]
pub CapabilityRegs {
(0x00 => pub CAPLENGTH: ReadOnly<u8>),
(0x01 => _0),
(0x02 => pub HCIVERSION: ReadOnly<u16>),
(0x04 => pub HCSPARAMS1: ReadOnly<u32, HCSPARAMS1::Register>),
(0x08 => pub HCSPARAMS2: ReadOnly<u32, HCSPARAMS2::Register>),
(0x0C => pub HCSPARAMS3: ReadOnly<u32, HCSPARAMS3::Register>),
(0x10 => pub HCCPARAMS1: ReadOnly<u32, HCCPARAMS1::Register>),
(0x14 => pub DBOFF: ReadOnly<u32>),
(0x18 => pub RTSOFF: ReadOnly<u32>),
(0x1C => pub HCCPARAMS2: ReadOnly<u32, HCCPARAMS2::Register>),
(0x20 => @END),
}
}
register_structs! {
#[allow(non_snake_case)]
pub OperationalRegs {
(0x000 => pub USBCMD: ReadWrite<u32, USBCMD::Register>),
(0x004 => pub USBSTS: ReadWrite<u32, USBSTS::Register>),
(0x008 => pub PAGESIZE: ReadOnly<u32>),
(0x00C => _0),
(0x014 => pub DNCTRL: ReadWrite<u32>),
(0x018 => pub CRCR: ReadWrite<u64>),
// (0x018 => pub CRCR_LO: ReadWrite<u32>),
// (0x01C => pub CRCR_HI: ReadWrite<u32>),
(0x020 => _1),
(0x030 => pub DCBAAP: ReadWrite<u64>),
// (0x030 => pub DCBAAP_LO: ReadWrite<u32>),
// (0x034 => pub DCBAAP_HI: ReadWrite<u32>),
(0x038 => pub CONFIG: ReadWrite<u32, CONFIG::Register>),
(0x03C => _2),
(0x400 => @END),
}
}
register_structs! {
#[allow(non_snake_case)]
pub PortRegs {
(0x00 => pub PORTSC: ReadWrite<u32, PORTSC::Register>),
(0x04 => pub PORTPMSC: ReadWrite<u32>),
(0x08 => pub PORTLI: ReadWrite<u32, PORTLI::Register>),
(0x0C => pub PORTHLPMC: ReadWrite<u32>),
(0x10 => @END),
}
}
register_structs! {
#[allow(non_snake_case)]
pub InterrupterRegs {
(0x00 => IMAN: ReadWrite<u32, IMAN::Register>),
(0x04 => IMOD: ReadWrite<u32, IMOD::Register>),
(0x08 => ERSTSZ: ReadWrite<u32>),
(0x0C => _0),
(0x10 => ERSTBA: ReadWrite<u64>),
// (0x10 => ERSTBA_LO: ReadWrite<u32>),
// (0x14 => ERSTBA_HI: ReadWrite<u32>),
(0x18 => ERDP: ReadWrite<u64>),
// (0x18 => ERDP_LO: ReadWrite<u32>),
// (0x1C => ERDP_HI: ReadWrite<u32>),
(0x20 => @END),
}
}
register_structs! {
#[allow(non_snake_case)]
pub RuntimeRegs {
(0x0000 => MFINDEX: ReadWrite<u32>),
(0x0004 => _0),
// NOTE there're 1024 of them, but I'm not going to use all of those anyway, so use 1023
// for a nice round number
(0x0020 => IRn: [InterrupterRegs; 1023]),
(0x8000 => @END),
}
}
pub struct Regs {
pub(crate) capability: DeviceMemoryIo<'static, CapabilityRegs>,
pub(crate) operational: IrqSafeRwLock<DeviceMemoryIo<'static, OperationalRegs>>,
pub(crate) runtime: IrqSafeRwLock<DeviceMemoryIo<'static, RuntimeRegs>>,
pub(crate) doorbells: DeviceMemoryIo<'static, [AtomicU32]>,
pub(crate) extended_capabilities: Vec<ExtendedCapability>,
// TODO per-port locks
pub(crate) ports: IrqSafeRwLock<DeviceMemoryIo<'static, [PortRegs]>>,
pub(crate) port_count: usize,
pub(crate) slot_count: usize,
pub(crate) scratch_count: usize,
}
pub struct ProtocolSupport {
words: [u32; 4],
}
pub struct LegacySupport(DeviceMemoryIo<'static, [AtomicU32; 4]>);
pub enum ExtendedCapability {
LegacySupport(IrqSafeRwLock<LegacySupport>),
ProtocolSupport(ProtocolSupport),
}
impl Regs {
const PORT_REGS_OFFSET: usize = 0x400;
pub fn map(base: PhysicalAddress) -> Result<Self, Error> {
let capability =
unsafe { DeviceMemoryIo::<CapabilityRegs>::map(base, Default::default()) }?;
let operational_offset = capability.CAPLENGTH.get() as usize;
let doorbell_offset = capability.DBOFF.get() & !0x3;
let runtime_offset = capability.RTSOFF.get() & !0x1F;
let extended_capability_offset =
(capability.HCCPARAMS1.read(HCCPARAMS1::XECP) as usize) << 2;
let port_count = capability.HCSPARAMS1.read(HCSPARAMS1::MaxPorts) as usize;
let slot_count = capability.HCSPARAMS1.read(HCSPARAMS1::MaxSlots) as usize;
let operational_base = base.add(operational_offset);
let doorbell_base = base.add(doorbell_offset as usize);
let runtime_base = base.add(runtime_offset as usize);
let scratch_lo = capability.HCSPARAMS2.read(HCSPARAMS2::MaxScratchpadBufsLo);
let scratch_hi = capability.HCSPARAMS2.read(HCSPARAMS2::MaxScratchpadBufsHi);
let scratch_count = ((scratch_hi << 5) | scratch_lo) as usize;
let operational = unsafe {
DeviceMemoryIo::<OperationalRegs>::map(operational_base, Default::default())
}?;
let runtime =
unsafe { DeviceMemoryIo::<RuntimeRegs>::map(runtime_base, Default::default()) }?;
let ports = unsafe {
DeviceMemoryIo::map_slice(
operational_base.add(Self::PORT_REGS_OFFSET),
port_count,
Default::default(),
)
}?;
let doorbells =
unsafe { DeviceMemoryIo::map_slice(doorbell_base, 256, Default::default()) }?;
let mut extended_capabilities = Vec::new();
if extended_capability_offset != 0 {
let extended_capability_base = base.add(extended_capability_offset);
let mut current = extended_capability_base;
loop {
let header =
unsafe { DeviceMemoryIo::<[AtomicU32; 4]>::map(current, Default::default()) }?;
let word0 = header[0].load(Ordering::Acquire);
let id = word0 as u8;
let next_pointer = ((word0 >> 8) & 0xFF) as usize;
match id {
0x01 => extended_capabilities.push(ExtendedCapability::LegacySupport(
IrqSafeRwLock::new(LegacySupport(header)),
)),
0x02 => {
let words = [
word0,
header[1].load(Ordering::Acquire),
header[2].load(Ordering::Acquire),
header[3].load(Ordering::Acquire),
];
extended_capabilities.push(ExtendedCapability::ProtocolSupport(
ProtocolSupport { words },
))
}
_ => (),
}
if next_pointer == 0 {
break;
} else {
current = current.add(next_pointer << 2);
}
}
}
Ok(Self {
capability,
operational: IrqSafeRwLock::new(operational),
runtime: IrqSafeRwLock::new(runtime),
doorbells,
ports: IrqSafeRwLock::new(ports),
extended_capabilities,
port_count,
slot_count,
scratch_count,
})
}
pub fn hc_reset(&self, timeout_cycles: u64) -> Result<(), Error> {
let op = self.operational.write();
// Wait for CNR to get cleared
op.wait_usbsts_bit(USBSTS::CNR::CLEAR, timeout_cycles)?;
// Clear run/stop and wait for the HC to halt
op.USBCMD.modify(USBCMD::RS::CLEAR);
op.wait_usbsts_bit(USBSTS::HCH::SET, timeout_cycles)?;
// Reset the HC, wait for CNR to get cleared again
op.USBCMD.modify(USBCMD::HCRST::SET);
op.wait_usbsts_bit(USBSTS::CNR::CLEAR, timeout_cycles)?;
// Halt the HC again
op.USBCMD.modify(USBCMD::RS::CLEAR);
op.wait_usbsts_bit(USBSTS::HCH::SET, timeout_cycles)?;
Ok(())
}
pub fn handle_interrupt(&self, vector: usize) -> LocalRegisterCopy<u32, USBSTS::Register> {
let op = self.operational.write();
let rt = self.runtime.write();
let status = op.USBSTS.extract();
// Clear the RW1C bits
op.USBSTS.set(status.get());
// Acknowledge interrupts for given vector (vector == interrupter index)
let interrupter = &rt.IRn[vector];
interrupter.IMAN.modify(IMAN::IP::SET);
status
}
}
impl OperationalRegs {
pub fn wait_usbsts_bit(
&self,
bit: FieldValue<u32, USBSTS::Register>,
mut timeout: u64,
) -> Result<(), Error> {
while timeout > 0 && !self.USBSTS.matches_all(bit) {
core::hint::spin_loop();
timeout -= 1;
}
if timeout > 0 {
Ok(())
} else {
Err(Error::TimedOut)
}
}
pub fn set_dcbaap(&self, value: PhysicalAddress) {
let value = value.into_u64();
self.DCBAAP.set(value);
// self.DCBAAP_HI.set((value >> 32) as u32);
// self.DCBAAP_LO.set(value as u32);
}
pub fn set_crcr(&self, value: PhysicalAddress, dcs: bool) {
let mut value = value.into_u64();
if dcs {
value |= 1;
}
self.CRCR.set(value);
}
// let mut intr0 = i.interrupter_mut(0);
// intr0.erstsz.update_volatile(|u| {
// u.set(erst.capacity().try_into().unwrap());
// });
// intr0.erdp.update_volatile(|u| {
// log::debug!("::: Dequeue Pointer: {:#x}", evt_ring.dequeue_pointer());
// u.set_event_ring_dequeue_pointer(evt_ring.dequeue_pointer().into());
// });
// intr0.erstba.update_volatile(|u| {
// u.set(erst.physical_address().into());
// });
// // intr0.imod.update_volatile(|u| {
// // u.set_interrupt_moderation_interval(0)
// // .set_interrupt_moderation_counter(0);
// // });
// intr0.iman.update_volatile(|u| {
// u.set_interrupt_enable();
// });
//
// o.usbcmd.update_volatile(|u| {
// u.set_interrupter_enable().set_run_stop();
// });
//
}
impl RuntimeRegs {
pub fn configure_interrupter(
&self,
index: usize,
event_ring: &EventRing,
erst: &EventRingSegmentTable,
) {
let interrupter = &self.IRn[index];
let erdp = event_ring.dequeue_pointer().into_u64();
let erstba = erst.physical_address().into_u64();
interrupter.ERSTSZ.set(erst.capacity() as u32);
interrupter.ERSTBA.set(erstba);
interrupter.ERDP.set(erdp);
interrupter.IMAN.write(IMAN::IE::SET);
}
pub fn set_interrupter_dequeue_pointer(&self, index: usize, erdp: PhysicalAddress) {
let _ = self.IRn[index].ERDP.get();
self.IRn[index].ERDP.set(erdp.into_u64() | (1 << 3));
}
}
impl ProtocolSupport {
pub fn usb_revision(&self) -> Option<UsbVersion> {
UsbVersion::from_bcd_usb((self.words[0] >> 16) as u16)
}
pub fn port_range(&self) -> Range<u8> {
let port_offset = self.words[2] as u8;
let port_count = (self.words[2] >> 8) as u8;
port_offset.max(1)..port_offset.saturating_add(port_count)
}
}
impl LegacySupport {
fn is_bios_owned(&self) -> bool {
self.0[0].load(Ordering::Acquire) & (1 << 16) != 0
}
fn set_os_owned(&mut self) {
self.0[0].fetch_or(1 << 24, Ordering::Release);
}
pub fn perform_bios_handoff(&mut self, mut timeout_cycles: u64) -> Result<(), UsbError> {
if !self.is_bios_owned() {
return Ok(());
}
// Clear SMI events
self.0[1].fetch_and(
!((1 << 0) | (1 << 4) | (1 << 13) | (1 << 14) | (1 << 15)),
Ordering::Release,
);
// Set OS owned semaphore
self.set_os_owned();
while timeout_cycles > 0 && self.is_bios_owned() {
core::hint::spin_loop();
timeout_cycles -= 1;
}
if timeout_cycles > 0 {
Ok(())
} else {
log::error!("xhci: BIOS handoff timed out");
Err(UsbError::DeviceBusy)
}
}
}
// use core::{cell::UnsafeCell, num::NonZeroUsize};
//
// use alloc::{sync::Arc, vec::Vec};
// use libk_mm::{
// address::{AsPhysicalAddress, PhysicalAddress},
// device::RawDeviceMemoryMapping,
// PageBox,
// };
// use libk_util::sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock};
// use xhci_lib::{
// accessor::{array, marker},
// registers::{
// operational::UsbStatusRegister, Capability, Doorbell, InterrupterRegisterSet, Operational,
// PortRegisterSet,
// },
// };
// use ygg_driver_usb::device::UsbSpeed;
// use yggdrasil_abi::primitive_enum;
//
// use crate::ring::{CommandRing, EventRing, EventRingSegmentTable, GenericRing};
//
// struct MapperInner {
// mappings: Vec<(RawDeviceMemoryMapping, usize)>,
// }
//
// #[derive(Clone)]
// pub struct Mapper {
// mapper: Arc<IrqSafeSpinlock<MapperInner>>,
// }
//
// pub struct LockedArray<T> {
// array: UnsafeCell<array::Generic<T, Mapper, marker::ReadWrite>>,
// locks: Vec<IrqSafeRwLock<()>>,
// }
//
// unsafe impl<T> Sync for LockedArray<T> {}
// unsafe impl<T> Send for LockedArray<T> {}
//
// pub struct Regs {
// operational: IrqSafeRwLock<Operational<Mapper>>,
// interrupters: IrqSafeRwLock<InterrupterRegisterSet<Mapper>>,
// capability: Capability<Mapper>,
// doorbells: LockedArray<Doorbell>,
// pub ports: LockedArray<PortRegisterSet>,
// }
//
// impl<T> LockedArray<T> {
// #[inline]
// #[allow(clippy::mut_from_ref)]
// unsafe fn get_mut(&self) -> &mut array::Generic<T, Mapper, marker::ReadWrite> {
// &mut *self.array.get()
// }
//
// pub fn update<U: FnOnce(&mut T)>(&self, index: usize, f: U) {
// let _guard = self.locks[index].write();
// unsafe { self.get_mut() }.update_volatile_at(index, f);
// }
//
// pub fn read(&self, index: usize) -> T {
// let _guard = self.locks[index].read();
// unsafe { self.get_mut() }.read_volatile_at(index)
// }
// }
//
// impl<T> From<array::Generic<T, Mapper, marker::ReadWrite>> for LockedArray<T> {
// fn from(value: array::Generic<T, Mapper, marker::ReadWrite>) -> Self {
// let locks = Vec::from_iter((0..value.len()).map(|_| IrqSafeRwLock::new(())));
//
// Self {
// array: UnsafeCell::new(value),
// locks,
// }
// }
// }
//
// impl From<xhci_lib::Registers<Mapper>> for Regs {
// fn from(value: xhci_lib::Registers<Mapper>) -> Self {
// Self {
// operational: IrqSafeRwLock::new(value.operational),
// capability: value.capability,
// interrupters: IrqSafeRwLock::new(value.interrupter_register_set),
// doorbells: LockedArray::from(value.doorbell),
// ports: LockedArray::from(value.port_register_set),
// }
// }
// }
//
// impl Regs {
// pub fn reset(&self) {
// let mut o = self.operational.write();
//
// // TODO Get ownership from firmware
//
// // Stop the controller
// o.usbcmd.update_volatile(|u| {
// u.clear_run_stop();
// });
//
// while !o.usbsts.read_volatile().hc_halted() {
// core::hint::spin_loop();
// }
//
// // Reset the controller
// o.usbcmd.update_volatile(|u| {
// u.set_host_controller_reset();
// });
// while o.usbcmd.read_volatile().host_controller_reset()
// || o.usbsts.read_volatile().controller_not_ready()
// {
// core::hint::spin_loop();
// }
// }
//
// pub fn max_slot_count(&self) -> usize {
// self.capability
// .hcsparams1
// .read_volatile()
// .number_of_device_slots() as _
// }
//
// pub fn set_max_slot_count(&self) -> usize {
// let device_slot_count = self.max_slot_count();
// let mut o = self.operational.write();
// // Set max slots enabled
// o.config.update_volatile(|u| {
// u.set_max_device_slots_enabled(device_slot_count as _);
// });
//
// device_slot_count as _
// }
//
// pub fn context_size(&self) -> usize {
// match self.capability.hccparams1.read_volatile().context_size() {
// true => 64,
// false => 32,
// }
// }
//
// pub fn port_count(&self) -> usize {
// self.capability.hcsparams1.read_volatile().number_of_ports() as _
// }
//
// pub fn scratchpad_count(&self) -> usize {
// self.capability
// .hcsparams2
// .read_volatile()
// .max_scratchpad_buffers() as usize
// }
//
// pub fn configure(
// &self,
// dcbaa: &PageBox<[PhysicalAddress]>,
// cmd_ring: &CommandRing,
// evt_ring: &EventRing,
// erst: &EventRingSegmentTable,
// ) {
// let mut o = self.operational.write();
// let mut i = self.interrupters.write();
//
// o.dcbaap.update_volatile(|u| unsafe {
// u.set(dcbaa.as_physical_address().into());
// });
// o.crcr.update_volatile(|u| {
// u.set_command_ring_pointer(cmd_ring.base().into());
// u.set_ring_cycle_state();
// });
//
// // Wait for the controller to come out of "not ready" state
// while o.usbsts.read_volatile().controller_not_ready() {
// core::hint::spin_loop();
// }
// }
//
// pub fn handle_interrupt(&self) -> Option<UsbStatusRegister> {
// let mut o = self.operational.write();
// let mut i = self.interrupters.write();
//
// let status = o.usbsts.read_volatile();
//
// if !status.event_interrupt() {
// return None;
// }
//
// o.usbsts.write_volatile(status);
//
// if status.host_system_error() {
// return Some(status);
// }
//
// // Acknowledge interrupts
// let mut intr0 = i.interrupter_mut(0);
// intr0.iman.update_volatile(|u| {
// u.clear_interrupt_pending();
// });
//
// Some(status)
// }
//
// pub fn set_interrupter_0_dequeue_pointer(&self, pointer: PhysicalAddress) {
// let mut i = self.interrupters.write();
//
// i.interrupter_mut(0).erdp.update_volatile(|u| {
// u.set_event_ring_dequeue_pointer(pointer.into());
// u.clear_event_handler_busy();
// });
// }
//
// pub fn ring_doorbell(&self, index: usize, target: u8) {
// self.doorbells.update(index, |u| {
// u.set_doorbell_target(target);
// });
// }
// }
//
// impl Mapper {
// pub fn new() -> Self {
// Self {
// mapper: Arc::new(IrqSafeSpinlock::new(MapperInner {
// mappings: Vec::new(),
// })),
// }
// }
// }
//
// impl xhci_lib::accessor::Mapper for Mapper {
// // FIXME really slow, but at least reduces the number of unneeded mappings
// unsafe fn map(&mut self, phys_start: usize, bytes: usize) -> NonZeroUsize {
// let mut mapper = self.mapper.lock();
// for (mapping, refcount) in mapper.mappings.iter_mut() {
// if phys_start as u64 >= mapping.physical_base
// && ((phys_start + bytes) as u64)
// <= mapping.physical_base + (mapping.page_size * mapping.page_count) as u64
// {
// *refcount += 1;
// return NonZeroUsize::new_unchecked(
// mapping.base_address + (phys_start - mapping.physical_base as usize),
// );
// }
// }
// let mapping = RawDeviceMemoryMapping::map(phys_start as u64, bytes, Default::default())
// .inspect_err(|error| {
// log::error!(
// "Cannot map xHC MMIO region {:#x?}: {error:?}",
// phys_start..phys_start + bytes
// )
// })
// .expect("Could not map an USB xHCI region");
// let address = mapping.address;
// log::info!("xhci: map {:#x} -> {:#x}", mapping.base_address, address);
// mapper.mappings.push((mapping, 1));
// NonZeroUsize::new_unchecked(address)
// }
//
// fn unmap(&mut self, _virt_start: usize, _bytes: usize) {
// // let mut mapper = self.mapper.lock();
// // let index = mapper.mappings.iter().position(|(mapping, _)| {
// // virt_start >= mapping.base_address
// // && virt_start + bytes
// // <= mapping.base_address + mapping.page_count * mapping.page_size
// // });
//
// // if let Some(index) = index {
// // let (entry, refcount) = &mut mapper.mappings[index];
// // *refcount -= 1;
// // if *refcount == 0 {
// // log::info!("xhci: unmap {:#x}", entry.base_address);
// // mapper.mappings.remove(index);
// // }
// // }
// }
// }
//
// // Register value definitions
//
// primitive_enum! {
// pub enum PortSpeed: u8 {
// Full = 1,
// Low = 2,
// High = 3,
// SuperGen1x1 = 4,
// SuperGen2x1 = 5,
// SuperGen1x2 = 6,
// SuperGen2x2 = 7,
// }
// }
//
// impl PortSpeed {
// pub fn default_max_packet_size(&self) -> usize {
// match self {
// Self::Low => 8,
// Self::High => 64,
// Self::SuperGen1x1 | Self::SuperGen1x2 | Self::SuperGen2x1 | Self::SuperGen2x2 => 512,
//
// // See Section 4.3., point 7. of the initialization list
// Self::Full => 8,
// }
// }
// }
//
// impl From<PortSpeed> for UsbSpeed {
// fn from(value: PortSpeed) -> Self {
// match value {
// PortSpeed::Low => UsbSpeed::Low,
// PortSpeed::Full => UsbSpeed::Full,
// PortSpeed::High => UsbSpeed::High,
// PortSpeed::SuperGen1x1
// | PortSpeed::SuperGen1x2
// | PortSpeed::SuperGen2x1
// | PortSpeed::SuperGen2x2 => UsbSpeed::Super,
// }
// }
// }

@ -15,7 +15,6 @@ use libk_util::{
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
waker::QueueWaker,
};
use xhci_lib::context;
use ygg_driver_usb::error::UsbError;
use yggdrasil_abi::define_bitfields;
@ -110,13 +109,17 @@ impl CommandRing {
inner.enqueue(trb)
}
pub async fn address_device<E: CommandExecutor, const N: usize>(
pub async fn address_device<E: CommandExecutor>(
&self,
executor: &E,
slot_id: u8,
input: &mut PageBox<context::Input<N>>,
cx_physical_address: PhysicalAddress,
bsr: bool,
) -> Result<(), UsbError> {
self.submit_and_wait(executor, AddressDeviceCommandTrb::new(input, slot_id))
self.submit_and_wait(
executor,
AddressDeviceCommandTrb::new(cx_physical_address, slot_id, bsr),
)
.await?;
Ok(())
}
@ -125,15 +128,32 @@ impl CommandRing {
&self,
executor: &E,
slot_id: u8,
input: &mut PageBox<context::Input<8>>,
cx_physical_address: PhysicalAddress,
) -> Result<(), UsbError> {
self.submit_and_wait(executor, ConfigureEndpointCommandTrb::new(input, slot_id))
self.submit_and_wait(
executor,
ConfigureEndpointCommandTrb::new(cx_physical_address, slot_id),
)
.await?;
Ok(())
}
pub async fn enable_slot<E: CommandExecutor>(&self, executor: &E) -> Result<u8, UsbError> {
self.submit_and_wait(executor, EnableSlotCommandTrb::new())
pub async fn enable_slot<E: CommandExecutor>(
&self,
executor: &E,
slot_type: u8,
) -> Result<u8, UsbError> {
self.submit_and_wait(executor, EnableSlotCommandTrb::new(slot_type))
.await
}
#[allow(unused)]
pub async fn disable_slot<E: CommandExecutor>(
&self,
executor: &E,
slot_id: u8,
) -> Result<u8, UsbError> {
self.submit_and_wait(executor, DisableSlotCommandTrb::new(slot_id))
.await
}
@ -153,6 +173,7 @@ impl CommandRing {
} else {
Poll::Ready(Err(UsbError::HostControllerCommandFailed(
status.completion_code,
status.completion_parameter,
)))
}
} else {
@ -181,8 +202,15 @@ define_bitfields! {
}
}
define_bitfields! {
pub DisableSlotCommandFlags : u32 {
(24..32) => slot_id
}
}
define_bitfields! {
pub AddressDeviceCommandFlags : u32 {
9 => bsr + set_bsr,
(24..32) => slot_id
}
}
@ -207,6 +235,13 @@ pub struct EnableSlotCommandTrb {
pub flags: EnableSlotCommandFlags,
}
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
#[repr(C, align(16))]
pub struct DisableSlotCommandTrb {
_0: [u32; 3],
pub flags: DisableSlotCommandFlags,
}
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
#[repr(C, align(16))]
pub struct AddressDeviceCommandTrb {
@ -233,6 +268,7 @@ pub struct RawCommandTrb {
#[derive(Debug, Clone, Copy)]
pub struct CommandReply {
pub completion_code: u8,
pub completion_parameter: u32,
pub slot_id: u8,
}
@ -241,34 +277,37 @@ pub trait CommandTrb: Pod + fmt::Debug {
}
impl EnableSlotCommandTrb {
pub fn new() -> Self {
pub fn new(slot_type: u8) -> Self {
Self {
_0: [0; 3],
flags: EnableSlotCommandFlags::new(0),
flags: EnableSlotCommandFlags::new(slot_type as u32),
}
}
}
impl DisableSlotCommandTrb {
pub fn new(slot_id: u8) -> Self {
Self {
_0: [0; 3],
flags: DisableSlotCommandFlags::new(slot_id as u32),
}
}
}
impl AddressDeviceCommandTrb {
pub fn new<const N: usize>(
input_context: &mut PageBox<context::Input<N>>,
slot_id: u8,
) -> Self {
pub fn new(input_context_address: PhysicalAddress, slot_id: u8, bsr: bool) -> Self {
Self {
input_context_address: unsafe { input_context.as_physical_address() },
input_context_address,
_0: 0,
flags: AddressDeviceCommandFlags::new(slot_id as _),
flags: AddressDeviceCommandFlags::new(bsr, slot_id as _),
}
}
}
impl ConfigureEndpointCommandTrb {
pub fn new<const N: usize>(
input_context: &mut PageBox<context::Input<N>>,
slot_id: u8,
) -> Self {
pub fn new(input_context_address: PhysicalAddress, slot_id: u8) -> Self {
Self {
input_context_address: unsafe { input_context.as_physical_address() },
input_context_address,
_0: 0,
flags: ConfigureEndpointCommandFlags::new(slot_id as _),
}
@ -279,6 +318,10 @@ impl CommandTrb for EnableSlotCommandTrb {
const TRB_TYPE: u8 = 9;
}
impl CommandTrb for DisableSlotCommandTrb {
const TRB_TYPE: u8 = 10;
}
impl CommandTrb for AddressDeviceCommandTrb {
const TRB_TYPE: u8 = 11;
}

@ -218,6 +218,7 @@ impl RawEventTrb {
address: command.address,
reply: CommandReply {
completion_code: command.status.completion_code() as _,
completion_parameter: command.status.completion_parameter(),
slot_id: command.flags.slot_id() as _,
},
})

@ -1,15 +1,19 @@
//use bytemuck::{Pod, Zeroable};
//use libk_mm::address::PhysicalAddress;
//use yggdrasil_abi::define_bitfields;
use bytemuck::{Pod, Zeroable};
use libk_mm::address::PhysicalAddress;
use yggdrasil_abi::define_bitfields;
pub mod command;
pub mod event;
pub mod transfer;
pub use command::CommandRing;
pub use event::{Event, EventRing, EventRingSegmentTable};
pub use transfer::ControlTransferRing;
// pub mod transfer;
//
// pub use command::CommandRing;
// pub use event::{Event, EventRing, EventRingSegmentTable};
// pub use transfer::ControlTransferRing;
//
pub trait CommandExecutor {
fn ring_doorbell(&self, index: usize, target: u8);
}

@ -7,11 +7,13 @@ use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use bytemuck::{Pod, Zeroable};
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
PageBox,
PageBox, PageSlice,
};
use libk_util::sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock, IrqSafeSpinlockGuard};
use ygg_driver_usb::{
communication::UsbInterruptTransfer, error::UsbError, pipe::control::ControlTransferSetup,
communication::{UsbBulkTransfer, UsbInterruptTransfer},
error::UsbError,
pipe::control::ControlTransferSetup,
UsbControlTransfer, UsbDirection, UsbTransferStatus, UsbTransferToken,
};
use yggdrasil_abi::define_bitfields;
@ -55,6 +57,29 @@ pub struct InterruptInTransferRing {
shutdown: AtomicBool,
}
pub struct BulkInTransferRing {
inner: IrqSafeSpinlock<TransferRingInner>,
capacity: usize,
completions: IrqSafeRwLock<BTreeMap<PhysicalAddress, Arc<UsbTransferStatus>>>,
slot_id: u8,
ep_id: u8,
shutdown: AtomicBool,
}
pub struct BulkOutTransferRing {
inner: IrqSafeSpinlock<TransferRingInner>,
capacity: usize,
completions: IrqSafeRwLock<BTreeMap<PhysicalAddress, Arc<UsbTransferStatus>>>,
slot_id: u8,
ep_id: u8,
shutdown: AtomicBool,
}
struct TransferBuilder<'a> {
ring: &'a ControlTransferRing,
ring_inner: IrqSafeSpinlockGuard<'a, TransferRingInner>,
@ -248,6 +273,96 @@ impl GenericTransferRing for InterruptInTransferRing {
}
}
impl GenericRing for BulkInTransferRing {
fn base(&self) -> PhysicalAddress {
unsafe { self.inner.lock().trbs.as_physical_address() }
}
fn capacity(&self) -> usize {
self.capacity
}
}
impl GenericTransferRing for BulkInTransferRing {
fn dequeue_pointer(&self) -> PhysicalAddress {
let inner = self.inner.lock();
unsafe { inner.trbs.as_physical_address() }
.add(inner.dequeue_index * size_of::<RawTransferTrb>())
}
fn notify(&self, address: PhysicalAddress, value: u32) {
if value == 0 {
return;
}
let mut completions = self.completions.write();
if let Some(status) = completions.remove(&address) {
status.signal(value);
}
}
fn shutdown(&self) {
self.shutdown.store(true, Ordering::Release);
let mut completions = self.completions.write();
while let Some((_, status)) = completions.pop_first() {
status.abort();
}
}
fn slot_id(&self) -> u8 {
self.slot_id
}
fn endpoint_id(&self) -> u8 {
self.ep_id
}
}
impl GenericRing for BulkOutTransferRing {
fn capacity(&self) -> usize {
self.capacity
}
fn base(&self) -> PhysicalAddress {
unsafe { self.inner.lock().trbs.as_physical_address() }
}
}
impl GenericTransferRing for BulkOutTransferRing {
fn dequeue_pointer(&self) -> PhysicalAddress {
let inner = self.inner.lock();
unsafe { inner.trbs.as_physical_address() }
.add(inner.dequeue_index * size_of::<RawTransferTrb>())
}
fn shutdown(&self) {
self.shutdown.store(true, Ordering::Release);
let mut completions = self.completions.write();
while let Some((_, status)) = completions.pop_first() {
status.abort();
}
}
fn notify(&self, address: PhysicalAddress, value: u32) {
if value == 0 {
return;
}
let mut completions = self.completions.write();
if let Some(status) = completions.remove(&address) {
status.signal(value);
}
}
fn endpoint_id(&self) -> u8 {
self.ep_id
}
fn slot_id(&self) -> u8 {
self.slot_id
}
}
impl InterruptInTransferRing {
pub fn new(slot_id: u8, ep_id: u8, capacity: usize) -> Result<Self, UsbError> {
let trbs = PageBox::new_zeroed_slice(capacity).map_err(UsbError::MemoryError)?;
@ -305,6 +420,120 @@ impl InterruptInTransferRing {
}
}
impl BulkInTransferRing {
pub fn new(slot_id: u8, ep_id: u8, capacity: usize) -> Result<Self, UsbError> {
let trbs = PageBox::new_zeroed_slice(capacity).map_err(UsbError::MemoryError)?;
Ok(Self {
inner: IrqSafeSpinlock::new(TransferRingInner {
trbs,
enqueue_index: 0,
dequeue_index: 0,
cycle_bit: true,
}),
completions: IrqSafeRwLock::new(BTreeMap::new()),
slot_id,
ep_id,
capacity,
shutdown: AtomicBool::new(false),
})
}
pub fn start_transfer<E: CommandExecutor>(
&self,
executor: &E,
buffer: &mut PageSlice<u8>,
) -> Result<UsbBulkTransfer, UsbError> {
// Don't even try to start the transfer
if self.shutdown.load(Ordering::Acquire) {
return Err(UsbError::DeviceDisconnected);
}
let status = Arc::new(UsbTransferStatus::new());
let address = self.inner.lock().enqueue(NormalTransferTrb::new(
unsafe { buffer.as_physical_address() },
buffer.len(),
true,
));
self.completions.write().insert(address, status.clone());
executor.ring_doorbell(self.slot_id as _, self.ep_id);
Ok(UsbBulkTransfer {
length: buffer.len(),
direction: UsbDirection::In,
address,
status,
})
}
pub fn complete_transfer(&self, _transfer: UsbBulkTransfer) {
// Interrupt transfers consist of one TRB each
// TODO: Can two transfers happen simultaneously? e.g.
//
// [TRBa, TRBb] are queued in the ring, both are executing and
// TRBb finishes first
self.inner.lock().advance();
}
}
impl BulkOutTransferRing {
pub fn new(slot_id: u8, ep_id: u8, capacity: usize) -> Result<Self, UsbError> {
let trbs = PageBox::new_zeroed_slice(capacity).map_err(UsbError::MemoryError)?;
Ok(Self {
inner: IrqSafeSpinlock::new(TransferRingInner {
trbs,
enqueue_index: 0,
dequeue_index: 0,
cycle_bit: true,
}),
completions: IrqSafeRwLock::new(BTreeMap::new()),
slot_id,
ep_id,
capacity,
shutdown: AtomicBool::new(false),
})
}
pub fn start_transfer<E: CommandExecutor>(
&self,
executor: &E,
buffer: &PageSlice<u8>,
) -> Result<UsbBulkTransfer, UsbError> {
// Don't even try to start the transfer
if self.shutdown.load(Ordering::Acquire) {
return Err(UsbError::DeviceDisconnected);
}
let status = Arc::new(UsbTransferStatus::new());
let address = self.inner.lock().enqueue(NormalTransferTrb::new(
unsafe { buffer.as_physical_address() },
buffer.len(),
true,
));
self.completions.write().insert(address, status.clone());
executor.ring_doorbell(self.slot_id as _, self.ep_id);
Ok(UsbBulkTransfer {
direction: UsbDirection::Out,
length: buffer.len(),
address,
status,
})
}
pub fn complete_transfer(&self, _transfer: UsbBulkTransfer) {
// Interrupt transfers consist of one TRB each
// TODO: Can two transfers happen simultaneously? e.g.
//
// [TRBa, TRBb] are queued in the ring, both are executing and
// TRBb finishes first
self.inner.lock().advance();
}
}
impl ControlTransferRing {
pub fn new(slot_id: u8, ep_id: u8, capacity: usize) -> Result<Self, UsbError> {
let trbs = PageBox::new_zeroed_slice(capacity).map_err(UsbError::MemoryError)?;
@ -328,6 +557,14 @@ impl ControlTransferRing {
})
}
pub fn reset(&self) {
let mut inner = self.inner.lock();
self.transfer_id.store(0, Ordering::Release);
inner.enqueue_index = 0;
inner.dequeue_index = 0;
inner.cycle_bit = true;
}
pub fn start_transfer<E: CommandExecutor>(
&self,
executor: &E,

@ -0,0 +1,78 @@
use core::{
future::poll_fn,
sync::atomic::{AtomicU64, Ordering},
task::Poll,
};
use futures_util::task::AtomicWaker;
pub struct EventBitmap {
bitmap: AtomicU64,
waker: AtomicWaker,
}
struct BitIter {
mask: u64,
index: usize,
limit: usize,
}
impl Iterator for BitIter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
while self.index < self.limit {
let bit = self.index;
self.index += 1;
if self.mask & (1 << bit) != 0 {
return Some(bit);
}
}
None
}
}
impl EventBitmap {
pub fn new() -> Self {
Self {
bitmap: AtomicU64::new(0),
waker: AtomicWaker::new(),
}
}
pub fn signal(&self, bit: usize) {
self.bitmap.fetch_or(1 << bit, Ordering::Release);
self.waker.wake();
}
pub async fn wait_specific(&self, bit: usize) {
poll_fn(|cx| {
let state = self.bitmap.fetch_and(!(1 << bit), Ordering::Acquire);
if state & (1 << bit) != 0 {
Poll::Ready(())
} else {
self.waker.register(cx.waker());
Poll::Pending
}
})
.await
}
pub async fn wait_any(&self, max: usize) -> impl Iterator<Item = usize> {
poll_fn(|cx| {
let mask = self.bitmap.swap(0, Ordering::Acquire);
if mask == 0 {
self.waker.register(cx.waker());
return Poll::Pending;
}
Poll::Ready(BitIter {
mask,
index: 0,
limit: max,
})
})
.await
}
}

@ -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)

@ -158,8 +158,9 @@ impl<T: ?Sized> AsPhysicalAddress for DeviceMemoryIo<'_, T> {
}
}
unsafe impl<T: ?Sized> Send for DeviceMemoryIo<'_, T> {}
impl<T: ?Sized> !Sync for DeviceMemoryIo<'_, T> {}
unsafe impl<T: ?Sized + Send> Send for DeviceMemoryIo<'_, T> {}
unsafe impl<T: ?Sized + Sync> Sync for DeviceMemoryIo<'_, T> {}
//impl<T: ?Sized> !Sync for DeviceMemoryIo<'_, T> {}
impl<'a, T: Sized> DeviceMemoryIoMut<'a, T> {
/// Maps a physical address as device memory to a slice `[T; len]`

@ -33,6 +33,7 @@ use crate::{
object::KObject,
},
task::{process::Process, thread::Thread},
time::monotonic_time,
};
const MAX_DEBUG_SINKS: usize = 8;
@ -160,7 +161,6 @@ impl log::Log for DebugSinkWrapper {
let level = LogLevel::from(record.level());
let sink = self.sink();
let cpu = Cpu::try_local().map(|c| c.id());
let file = record.file().unwrap_or("<???>");
let line = record.line().unwrap_or(0);
let args = record.args();
@ -171,25 +171,30 @@ impl log::Log for DebugSinkWrapper {
};
let mut writer = SinkWriter { sink };
let now = monotonic_time();
let s = now.seconds();
match record.target() {
"program" => {
write!(writer, "{args}").ok();
}
"raw" => {
write!(writer, "{prefix}{args}{suffix}").ok();
}
"io" => {
writeln!(writer, "[io] {args}").ok();
}
_ => {
write!(writer, "{prefix}").ok();
":program" => {
write!(writer, "{prefix}{s:06}:").ok();
if let Some(cpu) = cpu {
write!(writer, "{cpu}:").ok();
} else {
write!(writer, "?:").ok();
}
writeln!(writer, "{file}:{line}: {args}{suffix}").ok();
write!(writer, "ptrace:{args}").ok();
}
":raw" => {
write!(writer, "{prefix}{args}{suffix}").ok();
}
target => {
write!(writer, "{prefix}{s:06}:").ok();
if let Some(cpu) = cpu {
write!(writer, "{cpu}:").ok();
} else {
write!(writer, "?:").ok();
}
writeln!(writer, "{target}:{line}: {args}{suffix}").ok();
}
}
}
@ -429,11 +434,10 @@ pub fn disable_early_sinks() {
/// Print a trace message coming from a process
pub fn program_trace(process: &Process, thread: &Thread, message: &str) {
log::debug!(
target: "program",
"[trace {}:{}:{:?}] {message}\n",
target: ":program",
"{} ({}) {message}\n",
process.name,
process.id,
thread.id,
*thread.name.read()
);
}

@ -9,7 +9,7 @@ use crate::{
vfs::{
impls::{fixed_path_symlink, MemoryDirectory},
path::OwnedFilename,
AccessToken, Metadata, Node, NodeFlags, NodeRef,
AccessToken, Filename, Metadata, Node, NodeFlags, NodeRef,
},
};
@ -41,6 +41,13 @@ pub fn redirect<S: AsRef<str>>(name: S, destination: &str) -> Result<(), Error>
root.add_child(filename, fixed_path_symlink(destination))
}
pub fn remove_node<S: AsRef<str>>(name: S) -> Result<(), Error> {
let name = name.as_ref();
let root = DEVFS_ROOT.get();
let filename = Filename::new(name)?;
root.remove_file(filename, unsafe { AccessToken::authorized() })
}
/// Adds a character device with a custom name
pub fn add_named_char_device<S: AsRef<str>>(
dev: Arc<dyn CharDevice>,

@ -1,4 +1,4 @@
use core::fmt;
use core::{fmt, sync::atomic::AtomicBool};
use alloc::sync::Arc;
use futures_util::{future::BoxFuture, task::ArcWake, Future, FutureExt};
@ -12,6 +12,7 @@ pub trait Termination {
pub struct Task {
pub(super) future: IrqSafeSpinlock<Option<BoxFuture<'static, ()>>>,
pub(super) enqueued: AtomicBool,
}
impl ArcWake for Task {
@ -28,7 +29,10 @@ impl Task {
}
.boxed(),
));
Arc::new(Self { future })
Arc::new(Self {
future,
enqueued: AtomicBool::new(false),
})
}
}

@ -1,3 +1,5 @@
use core::sync::atomic::Ordering;
use alloc::sync::Arc;
use crossbeam_queue::ArrayQueue;
use libk_util::{sync::IrqGuard, OneTimeInit};
@ -31,9 +33,13 @@ impl TaskQueue {
}
pub fn enqueue(&self, task: Arc<Task>) -> Result<(), Error> {
// Already enqueued
if task.enqueued.swap(true, Ordering::Acquire) {
return Ok(());
}
let _irq = IrqGuard::acquire();
if self.task_queue.push(task).is_err() {
todo!();
return Err(Error::WouldBlock);
}
self.wakeup_one();
Ok(())
@ -44,6 +50,7 @@ impl TaskQueue {
// assert!(PlatformImpl::interrupt_mask());
loop {
if let Some(task) = self.task_queue.pop() {
task.enqueued.store(false, Ordering::Release);
return Ok(task);
}
@ -59,7 +66,7 @@ impl TaskQueue {
/// Initializes the global async/await task queue
pub fn init_task_queue() {
TASK_QUEUE.init(TaskQueue::new(128));
TASK_QUEUE.init(TaskQueue::new(256));
}
pub(super) fn push_task(task: Arc<Task>) -> Result<(), Error> {

@ -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)]

@ -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;
@ -59,23 +53,23 @@ pub enum SelectedClockSource {
// TODO move this to some sort of .init_array-style implicit thing
pub fn register_pci_drivers() {
// XXX: Only works with MSI-X, so no i686
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
ygg_driver_pci::register_class_driver(
"NVMe Host Controller",
0x01,
Some(0x08),
Some(0x02),
ygg_driver_nvme::probe,
);
// #[cfg(any(target_arch = "x86_64", rust_analyzer))]
// ygg_driver_pci::register_class_driver(
// "NVMe Host Controller",
// 0x01,
// Some(0x08),
// Some(0x02),
// ygg_driver_nvme::probe,
// );
// XXX: i686 hangs in interrupt handler
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
ygg_driver_pci::register_class_driver(
"AHCI Controller",
0x01,
Some(0x06),
Some(0x01),
ygg_driver_ahci::probe,
);
// #[cfg(any(target_arch = "x86_64", rust_analyzer))]
// ygg_driver_pci::register_class_driver(
// "AHCI Controller",
// 0x01,
// Some(0x06),
// Some(0x01),
// ygg_driver_ahci::probe,
// );
ygg_driver_pci::register_class_driver(
"USB xHCI",
0x0C,
@ -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:

@ -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>,

@ -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;

@ -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))]

@ -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;

@ -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;

@ -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>,

@ -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(())
}

@ -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 {

@ -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,

@ -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() {

@ -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;

@ -33,6 +33,8 @@ pub fn kinit() -> Result<(), Error> {
runtime::spawn(ygg_driver_usb::bus::bus_handler())?;
runtime::spawn(console::flush_consoles_task()).ok();
loop {}
devfs::root()
.add_child(
OwnedFilename::new("tty").unwrap(),

@ -135,7 +135,7 @@ pub fn kernel_main() -> ! {
libk::panic::set_handler(panic::panic_handler);
unsafe {
PLATFORM.start_application_processors();
// PLATFORM.start_application_processors();
}
Cpu::init_ipi_queues(ArchitectureImpl::cpu_count());

@ -134,7 +134,7 @@ impl ProcessId {
impl fmt::Display for ProcessId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<Process {}>", self.0)
write!(f, "{}", self.0)
}
}

@ -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);
}
}
}
}

@ -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()),
});