From ff205ad65d6336aab43418da4c2d08dfbd536350 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sat, 19 Nov 2022 08:18:51 -0700 Subject: [PATCH] ihda: Enable MSI and use pcid channel --- Cargo.lock | 1 + ihdad/Cargo.toml | 2 + ihdad/config.toml | 3 +- ihdad/src/main.rs | 345 ++++++++++++++++++++++++++-------------------- 4 files changed, 204 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e448276b87..09ed04dbaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -484,6 +484,7 @@ version = "0.1.0" dependencies = [ "bitflags", "log", + "pcid", "redox-daemon", "redox-log", "redox_event", diff --git a/ihdad/Cargo.toml b/ihdad/Cargo.toml index f30d2fae70..7ef0407d69 100755 --- a/ihdad/Cargo.toml +++ b/ihdad/Cargo.toml @@ -11,3 +11,5 @@ redox-log = "0.1" redox_event = { git = "https://gitlab.redox-os.org/redox-os/event.git" } redox_syscall = "0.3" spin = "0.9" + +pcid = { path = "../pcid" } diff --git a/ihdad/config.toml b/ihdad/config.toml index 11fc35efb1..b4d8a132d7 100644 --- a/ihdad/config.toml +++ b/ihdad/config.toml @@ -2,4 +2,5 @@ name = "Intel HD Audio" class = 4 subclass = 3 -command = ["ihdad", "$NAME", "$BAR0", "$BARSIZE0", "$IRQ", "$VENID", "$DEVID"] +command = ["ihdad"] +use_channel = true diff --git a/ihdad/src/main.rs b/ihdad/src/main.rs index 30d1ffcbcd..9ef7efa711 100755 --- a/ihdad/src/main.rs +++ b/ihdad/src/main.rs @@ -5,7 +5,8 @@ extern crate spin; extern crate syscall; extern crate event; -use std::{env, usize}; +use std::convert::TryFrom; +use std::usize; use std::fs::File; use std::io::{ErrorKind, Read, Write, Result}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; @@ -14,6 +15,8 @@ use std::cell::RefCell; use std::sync::Arc; use event::EventQueue; +use pcid_interface::{MsiSetFeatureInfo, PcidServerHandle, PciFeature, PciFeatureInfo, SetFeatureInfo}; +use pcid_interface::irq_helpers::{read_bsp_apic_id, allocate_single_interrupt_vector}; use redox_log::{OutputBuilder, RedoxLogger}; pub mod hda; @@ -69,187 +72,237 @@ fn setup_logging() -> Option<&'static RedoxLogger> { } } -fn main() { - let mut args = env::args().skip(1); +#[cfg(target_arch = "x86_64")] +fn get_int_method(pcid_handle: &mut PcidServerHandle) -> Option { + let pci_config = pcid_handle.fetch_config().expect("ihdad: failed to fetch config"); - let mut name = args.next().expect("ihda: no name provided"); - name.push_str("_ihda"); + let irq = pci_config.func.legacy_interrupt_line; - let bar_str = args.next().expect("ihda: no address provided"); - let bar = usize::from_str_radix(&bar_str, 16).expect("ihda: failed to parse address"); + let all_pci_features = pcid_handle.fetch_all_features().expect("ihdad: failed to fetch pci features"); + log::info!("PCI FEATURES: {:?}", all_pci_features); - let bar_size_str = args.next().expect("ihda: no address size provided"); - let bar_size = usize::from_str_radix(&bar_size_str, 16).expect("ihda: failed to parse address size"); + let (has_msi, mut msi_enabled) = all_pci_features.iter().map(|(feature, status)| (feature.is_msi(), status.is_enabled())).find(|&(f, _)| f).unwrap_or((false, false)); + let (has_msix, mut msix_enabled) = all_pci_features.iter().map(|(feature, status)| (feature.is_msix(), status.is_enabled())).find(|&(f, _)| f).unwrap_or((false, false)); - let irq_str = args.next().expect("ihda: no irq provided"); - let irq = irq_str.parse::().expect("ihda: failed to parse irq"); + if has_msi && !msi_enabled && !has_msix { + msi_enabled = true; + } + if has_msix && !msix_enabled { + msix_enabled = true; + } - let vend_str = args.next().expect("ihda: no vendor id provided"); - let vend = usize::from_str_radix(&vend_str, 16).expect("ihda: failed to parse vendor id"); + if msi_enabled && !msix_enabled { + use pcid_interface::msi::x86_64::{DeliveryMode, self as x86_64_msix}; - let prod_str = args.next().expect("ihda: no product id provided"); - let prod = usize::from_str_radix(&prod_str, 16).expect("ihda: failed to parse product id"); + let capability = match pcid_handle.feature_info(PciFeature::Msi).expect("ihdad: failed to retrieve the MSI capability structure from pcid") { + PciFeatureInfo::Msi(s) => s, + PciFeatureInfo::MsiX(_) => panic!(), + }; + // TODO: Allow allocation of up to 32 vectors. - print!("{}", format!(" + ihda {} on: {:X} size: {} IRQ: {}\n", name, bar, bar_size, irq)); + // TODO: Find a way to abstract this away, potantially as a helper module for + // pcid_interface, so that this can be shared between nvmed, xhcid, ixgebd, etc.. - // Daemonize - redox_daemon::Daemon::new(move |daemon| { - let _logger_ref = setup_logging(); + let destination_id = read_bsp_apic_id().expect("ihdad: failed to read BSP apic id"); + let lapic_id = u8::try_from(destination_id).expect("CPU id didn't fit inside u8"); + let msg_addr = x86_64_msix::message_address(lapic_id, false, false); - let address = unsafe { - syscall::physmap(bar, bar_size, PHYSMAP_WRITE | PHYSMAP_NO_CACHE) - .expect("ihdad: failed to map address") - }; - { - let mut time_file = File::open(format!("time:{}", syscall::CLOCK_MONOTONIC)).expect("ihdad: failed to open time file"); + let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id).expect("ihdad: failed to allocate interrupt vector").expect("ihdad: no interrupt vectors left"); + let msg_data = x86_64_msix::message_data_edge_triggered(DeliveryMode::Fixed, vector); - let mut irq_file = File::open(format!("irq:{}", irq)).expect("ihdad: failed to open IRQ file"); + let set_feature_info = MsiSetFeatureInfo { + multi_message_enable: Some(0), + message_address: Some(msg_addr), + message_upper_address: Some(0), + message_data: Some(msg_data as u16), + mask_bits: None, + }; + pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info)).expect("ihdad: failed to set feature info"); - let vend_prod:u32 = ((vend as u32) << 16) | (prod as u32); + pcid_handle.enable_feature(PciFeature::Msi).expect("ihdad: failed to enable MSI"); + log::info!("Enabled MSI"); - let device = Arc::new(RefCell::new(unsafe { hda::IntelHDA::new(address, vend_prod).expect("ihdad: failed to allocate device") })); - let socket_fd = syscall::open(":audiohw", syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("ihdad: failed to create hda scheme"); - let socket = Arc::new(RefCell::new(unsafe { File::from_raw_fd(socket_fd as RawFd) })); - daemon.ready().expect("ihdad: failed to signal readiness"); + Some(interrupt_handle) + } else if pci_config.func.legacy_interrupt_pin.is_some() { + // legacy INTx# interrupt pins. + Some(File::open(format!("irq:{}", irq)).expect("ihdad: failed to open legacy IRQ file")) + } else { + // no interrupts at all + None + } +} - let mut event_queue = EventQueue::::new().expect("ihdad: Could not create event queue."); +//TODO: MSI on non-x86_64? +#[cfg(not(target_arch = "x86_64"))] +fn get_int_method(pcid_handle: &mut PcidServerHandle, address: usize) -> Option { + let pci_config = pcid_handle.fetch_config().expect("ihdad: failed to fetch config"); + let irq = pci_config.func.legacy_interrupt_line; - syscall::setrens(0, 0).expect("ihdad: failed to enter null namespace"); + if pci_config.func.legacy_interrupt_pin.is_some() { + // legacy INTx# interrupt pins. + Some(File::open(format!("irq:{}", irq)).expect("ihdad: failed to open legacy IRQ file")) + } else { + // no interrupts at all + None + } +} - let todo = Arc::new(RefCell::new(Vec::::new())); +fn daemon(daemon: redox_daemon::Daemon) -> ! { + let _logger_ref = setup_logging(); - let todo_time = todo.clone(); - let device_time = device.clone(); - let socket_time = socket.clone(); - event_queue.add(time_file.as_raw_fd(), move |_event| -> Result> { - let mut todo = todo_time.borrow_mut(); + let mut pcid_handle = PcidServerHandle::connect_default().expect("rtl8168d: failed to setup channel to pcid"); + + let pci_config = pcid_handle.fetch_config().expect("rtl8168d: failed to fetch config"); + + let mut name = pci_config.func.name(); + name.push_str("_rtl8168"); + + let bar = pci_config.func.bars[0]; + let bar_size = pci_config.func.bar_sizes[0]; + let bar_ptr = match bar { + pcid_interface::PciBar::Memory32(ptr) => match ptr { + 0 => panic!("BAR 0 is mapped to address 0"), + _ => ptr as u64, + }, + pcid_interface::PciBar::Memory64(ptr) => match ptr { + 0 => panic!("BAR 0 is mapped to address 0"), + _ => ptr, + }, + other => panic!("Expected memory bar, found {}", other), + }; + + eprintln!(" + IHDA {} on: {:#X} size: {}", name, bar_ptr, bar_size); + + let address = unsafe { + syscall::physmap(bar_ptr as usize, bar_size as usize, PHYSMAP_WRITE | PHYSMAP_NO_CACHE) + .expect("ihdad: failed to map address") + }; + + //TODO: MSI-X + let mut irq_file = get_int_method(&mut pcid_handle).expect("rtl8168d: no interrupt file"); + + { + let vend_prod:u32 = ((pci_config.func.venid as u32) << 16) | (pci_config.func.devid as u32); + + let device = Arc::new(RefCell::new(unsafe { hda::IntelHDA::new(address, vend_prod).expect("ihdad: failed to allocate device") })); + let socket_fd = syscall::open(":audiohw", syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("ihdad: failed to create hda scheme"); + let socket = Arc::new(RefCell::new(unsafe { File::from_raw_fd(socket_fd as RawFd) })); + daemon.ready().expect("ihdad: failed to signal readiness"); + + let mut event_queue = EventQueue::::new().expect("ihdad: Could not create event queue."); + + syscall::setrens(0, 0).expect("ihdad: failed to enter null namespace"); + + let todo = Arc::new(RefCell::new(Vec::::new())); + + let todo_irq = todo.clone(); + let device_irq = device.clone(); + let socket_irq = socket.clone(); + event_queue.add(irq_file.as_raw_fd(), move |_event| -> Result> { + let mut irq = [0; 8]; + irq_file.read(&mut irq)?; + + if device_irq.borrow_mut().irq() { + irq_file.write(&mut irq)?; + + let mut todo = todo_irq.borrow_mut(); let mut i = 0; while i < todo.len() { - if let Some(a) = device_time.borrow_mut().handle(&mut todo[i]) { + if let Some(a) = device_irq.borrow_mut().handle(&mut todo[i]) { let mut packet = todo.remove(i); packet.a = a; - socket_time.borrow_mut().write(&packet)?; + socket_irq.borrow_mut().write(&packet)?; } else { i += 1; } } - let mut time = syscall::TimeSpec::default(); - time_file.read(&mut time)?; - time.tv_nsec += 1_000_000; // 1 ms - while time.tv_nsec >= 1_000_000_000 { - time.tv_sec += 1; - time.tv_nsec -= 1_000_000_000; - } - time_file.write(&time)?; - - Ok(None) - }).expect("ihdad: failed to catch events on time file"); - - let todo_irq = todo.clone(); - let device_irq = device.clone(); - let socket_irq = socket.clone(); - event_queue.add(irq_file.as_raw_fd(), move |_event| -> Result> { - let mut irq = [0; 8]; - irq_file.read(&mut irq)?; - - if device_irq.borrow_mut().irq() { - irq_file.write(&mut irq)?; - - let mut todo = todo_irq.borrow_mut(); - let mut i = 0; - while i < todo.len() { - if let Some(a) = device_irq.borrow_mut().handle(&mut todo[i]) { - let mut packet = todo.remove(i); - packet.a = a; - socket_irq.borrow_mut().write(&packet)?; - } else { - i += 1; - } - } - - /* - let next_read = device_irq.next_read(); - if next_read > 0 { - return Ok(Some(next_read)); - } - */ - } - Ok(None) - }).expect("ihdad: failed to catch events on IRQ file"); - let socket_fd = socket.borrow().as_raw_fd(); - let socket_packet = socket.clone(); - event_queue.add(socket_fd, move |_event| -> Result> { - loop { - let mut packet = Packet::default(); - match socket_packet.borrow_mut().read(&mut packet) { - Ok(0) => return Ok(Some(0)), - Ok(_) => (), - Err(err) => if err.kind() == ErrorKind::WouldBlock { - break; - } else { - return Err(err); - } - } - - if let Some(a) = device.borrow_mut().handle(&mut packet) { - packet.a = a; - socket_packet.borrow_mut().write(&packet)?; - } else { - todo.borrow_mut().push(packet); - } - } - /* - let next_read = device.borrow().next_read(); + let next_read = device_irq.next_read(); if next_read > 0 { return Ok(Some(next_read)); } */ - - Ok(None) - }).expect("ihdad: failed to catch events on IRQ file"); - - for event_count in event_queue.trigger_all(event::Event { - fd: 0, - flags: EventFlags::empty(), - }).expect("ihdad: failed to trigger events") { - socket.borrow_mut().write(&Packet { - id: 0, - pid: 0, - uid: 0, - gid: 0, - a: syscall::number::SYS_FEVENT, - b: 0, - c: syscall::flag::EVENT_READ.bits(), - d: event_count - }).expect("ihdad: failed to write event"); } - + Ok(None) + }).expect("ihdad: failed to catch events on IRQ file"); + let socket_fd = socket.borrow().as_raw_fd(); + let socket_packet = socket.clone(); + event_queue.add(socket_fd, move |_event| -> Result> { loop { - { - //device_loop.borrow_mut().handle_interrupts(); - } - let event_count = event_queue.run().expect("ihdad: failed to handle events"); - if event_count == 0 { - //TODO: Handle todo - break; + let mut packet = Packet::default(); + match socket_packet.borrow_mut().read(&mut packet) { + Ok(0) => return Ok(Some(0)), + Ok(_) => (), + Err(err) => if err.kind() == ErrorKind::WouldBlock { + break; + } else { + return Err(err); + } } - socket.borrow_mut().write(&Packet { - id: 0, - pid: 0, - uid: 0, - gid: 0, - a: syscall::number::SYS_FEVENT, - b: 0, - c: syscall::flag::EVENT_READ.bits(), - d: event_count - }).expect("ihdad: failed to write event"); + if let Some(a) = device.borrow_mut().handle(&mut packet) { + packet.a = a; + socket_packet.borrow_mut().write(&packet)?; + } else { + todo.borrow_mut().push(packet); + } } + + /* + let next_read = device.borrow().next_read(); + if next_read > 0 { + return Ok(Some(next_read)); + } + */ + + Ok(None) + }).expect("ihdad: failed to catch events on IRQ file"); + + for event_count in event_queue.trigger_all(event::Event { + fd: 0, + flags: EventFlags::empty(), + }).expect("ihdad: failed to trigger events") { + socket.borrow_mut().write(&Packet { + id: 0, + pid: 0, + uid: 0, + gid: 0, + a: syscall::number::SYS_FEVENT, + b: 0, + c: syscall::flag::EVENT_READ.bits(), + d: event_count + }).expect("ihdad: failed to write event"); } - unsafe { let _ = syscall::physunmap(address); } + loop { + { + //device_loop.borrow_mut().handle_interrupts(); + } + let event_count = event_queue.run().expect("ihdad: failed to handle events"); + if event_count == 0 { + //TODO: Handle todo + break; + } + + socket.borrow_mut().write(&Packet { + id: 0, + pid: 0, + uid: 0, + gid: 0, + a: syscall::number::SYS_FEVENT, + b: 0, + c: syscall::flag::EVENT_READ.bits(), + d: event_count + }).expect("ihdad: failed to write event"); + } + } + + unsafe { let _ = syscall::physunmap(address); } std::process::exit(0); - }).expect("ihdad: failed to daemonize"); +} + +fn main() { + // Daemonize + redox_daemon::Daemon::new(daemon).expect("ihdad: failed to daemonize"); }