You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
8.0 KiB
245 lines
8.0 KiB
use crate::consts::KERNEL_OFFSET;
|
|
use crate::drivers::block::*;
|
|
use crate::drivers::net::*;
|
|
use crate::drivers::{Driver, DRIVERS, NET_DRIVERS};
|
|
use crate::memory::active_table;
|
|
use alloc::collections::BTreeMap;
|
|
use alloc::string::String;
|
|
use alloc::sync::Arc;
|
|
use core::cmp::Ordering;
|
|
use pci::*;
|
|
use rcore_memory::{paging::PageTable, PAGE_SIZE};
|
|
use spin::Mutex;
|
|
|
|
const PCI_COMMAND: u16 = 0x04;
|
|
const PCI_CAP_PTR: u16 = 0x34;
|
|
const PCI_INTERRUPT_LINE: u16 = 0x3c;
|
|
const PCI_INTERRUPT_PIN: u16 = 0x3d;
|
|
|
|
const PCI_MSI_CTRL_CAP: u16 = 0x00;
|
|
const PCI_MSI_ADDR: u16 = 0x04;
|
|
const PCI_MSI_UPPER_ADDR: u16 = 0x08;
|
|
const PCI_MSI_DATA: u16 = 0x0C;
|
|
|
|
const PCI_CAP_ID_MSI: u8 = 0x05;
|
|
|
|
struct PortOpsImpl;
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
use x86_64::instructions::port::Port;
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
impl PortOps for PortOpsImpl {
|
|
|
|
unsafe fn read8(&self, port: u16) -> u8 {
|
|
Port::new(port).read()
|
|
}
|
|
unsafe fn read16(&self, port: u16) -> u16 {
|
|
Port::new(port).read()
|
|
}
|
|
unsafe fn read32(&self, port: u16) -> u32 {
|
|
Port::new(port).read()
|
|
}
|
|
unsafe fn write8(&self, port: u16, val: u8) {
|
|
Port::new(port).write(val);
|
|
}
|
|
unsafe fn write16(&self, port: u16, val: u16) {
|
|
Port::new(port).write(val);
|
|
}
|
|
unsafe fn write32(&self, port: u16, val: u32) {
|
|
Port::new(port).write(val);
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(target_arch = "mips")]
|
|
use crate::util::{read, write};
|
|
#[cfg(feature = "board_malta")]
|
|
const PCI_BASE: usize = 0xbbe00000;
|
|
|
|
#[cfg(target_arch = "mips")]
|
|
impl PortOps for PortOpsImpl {
|
|
|
|
unsafe fn read8(&self, port: u16) -> u8 {
|
|
read(PCI_BASE + port as usize)
|
|
}
|
|
unsafe fn read16(&self, port: u16) -> u16 {
|
|
read(PCI_BASE + port as usize)
|
|
}
|
|
unsafe fn read32(&self, port: u16) -> u32 {
|
|
read(PCI_BASE + port as usize)
|
|
}
|
|
unsafe fn write8(&self, port: u16, val: u8) {
|
|
write(PCI_BASE + port as usize, val);
|
|
}
|
|
unsafe fn write16(&self, port: u16, val: u16) {
|
|
write(PCI_BASE + port as usize, val);
|
|
}
|
|
unsafe fn write32(&self, port: u16, val: u32) {
|
|
write(PCI_BASE + port as usize, val);
|
|
}
|
|
}
|
|
|
|
/// Enable the pci device and its interrupt
|
|
/// Return assigned MSI interrupt number when applicable
|
|
unsafe fn enable(loc: Location) -> Option<u32> {
|
|
let ops = &PortOpsImpl;
|
|
let am = CSpaceAccessMethod::IO;
|
|
|
|
// 23 and lower are used
|
|
static mut MSI_IRQ: u32 = 23;
|
|
|
|
let orig = am.read16(ops, loc, PCI_COMMAND);
|
|
// IO Space | MEM Space | Bus Mastering | Special Cycles | PCI Interrupt Disable
|
|
am.write32(ops, loc, PCI_COMMAND, (orig | 0x40f) as u32);
|
|
|
|
// find MSI cap
|
|
let mut msi_found = false;
|
|
let mut cap_ptr = am.read8(ops, loc, PCI_CAP_PTR) as u16;
|
|
let mut assigned_irq = None;
|
|
while cap_ptr > 0 {
|
|
let cap_id = am.read8(ops, loc, cap_ptr);
|
|
if cap_id == PCI_CAP_ID_MSI {
|
|
// The manual Volume 3 Chapter 10.11 Message Signalled Interrupts
|
|
// 0 is (usually) the apic id of the bsp.
|
|
am.write32(ops, loc, cap_ptr + PCI_MSI_ADDR, 0xfee00000 | (0 << 12));
|
|
MSI_IRQ += 1;
|
|
let irq = MSI_IRQ;
|
|
assigned_irq = Some(irq);
|
|
// we offset all our irq numbers by 32
|
|
am.write32(ops, loc, cap_ptr + PCI_MSI_DATA, irq + 32);
|
|
|
|
// enable MSI interrupt, assuming 64bit for now
|
|
let orig_ctrl = am.read32(ops, loc, cap_ptr + PCI_MSI_CTRL_CAP);
|
|
am.write32(ops, loc, cap_ptr + PCI_MSI_CTRL_CAP, orig_ctrl | 0x10000);
|
|
debug!(
|
|
"MSI control {:#b}, enabling MSI interrupts",
|
|
orig_ctrl >> 16
|
|
);
|
|
msi_found = true;
|
|
break;
|
|
}
|
|
debug!("PCI device has cap id {} at {:#X}", cap_id, cap_ptr);
|
|
cap_ptr = am.read8(ops, loc, cap_ptr + 1) as u16;
|
|
}
|
|
|
|
if !msi_found {
|
|
// Use PCI legacy interrupt instead
|
|
// IO Space | MEM Space | Bus Mastering | Special Cycles
|
|
am.write32(ops, loc, PCI_COMMAND, (orig | 0xf) as u32);
|
|
debug!("MSI not found, using PCI interrupt");
|
|
}
|
|
|
|
assigned_irq
|
|
}
|
|
|
|
pub fn init_driver(dev: &PCIDevice) {
|
|
let name = format!("enp{}s{}f{}", dev.loc.bus, dev.loc.device, dev.loc.function);
|
|
match (dev.id.vendor_id, dev.id.device_id) {
|
|
(0x8086, 0x100e) | (0x8086, 0x100f) | (0x8086, 0x10d3) => {
|
|
// 0x100e
|
|
// 82540EM Gigabit Ethernet Controller
|
|
// 0x100f
|
|
// 82545EM Gigabit Ethernet Controller (Copper)
|
|
// 0x10d3
|
|
// 82574L Gigabit Network Connection
|
|
if let Some(BAR::Memory(addr, len, _, _)) = dev.bars[0] {
|
|
let irq = unsafe { enable(dev.loc) };
|
|
let vaddr = KERNEL_OFFSET + addr as usize;
|
|
let mut current_addr = addr as usize;
|
|
while current_addr < addr as usize + len as usize {
|
|
active_table().map_if_not_exists(KERNEL_OFFSET + current_addr, current_addr);
|
|
current_addr = current_addr + PAGE_SIZE;
|
|
}
|
|
e1000::e1000_init(name, irq, vaddr, len as usize);
|
|
}
|
|
}
|
|
(0x8086, 0x10fb) => {
|
|
// 82599ES 10-Gigabit SFI/SFP+ Network Connection
|
|
if let Some(BAR::Memory(addr, len, _, _)) = dev.bars[0] {
|
|
let irq = unsafe { enable(dev.loc) };
|
|
let vaddr = KERNEL_OFFSET + addr as usize;
|
|
let mut current_addr = addr as usize;
|
|
while current_addr < addr as usize + len as usize {
|
|
active_table().map_if_not_exists(KERNEL_OFFSET + current_addr, current_addr);
|
|
current_addr = current_addr + PAGE_SIZE;
|
|
}
|
|
PCI_DRIVERS
|
|
.lock()
|
|
.insert(dev.loc, ixgbe::ixgbe_init(name, irq, vaddr, len as usize));
|
|
}
|
|
}
|
|
(0x8086, 0x2922) => {
|
|
// 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA Controller [AHCI mode]
|
|
if let Some(BAR::Memory(addr, len, _, _)) = dev.bars[5] {
|
|
let irq = unsafe { enable(dev.loc) };
|
|
assert!(len as usize <= PAGE_SIZE);
|
|
let vaddr = KERNEL_OFFSET + addr as usize;
|
|
active_table().map(vaddr, addr as usize);
|
|
PCI_DRIVERS
|
|
.lock()
|
|
.insert(dev.loc, ahci::ahci_init(irq, vaddr, len as usize));
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
pub fn detach_driver(loc: &Location) -> bool {
|
|
match PCI_DRIVERS.lock().remove(loc) {
|
|
Some(driver) => {
|
|
DRIVERS
|
|
.write()
|
|
.retain(|dri| dri.get_id() != driver.get_id());
|
|
NET_DRIVERS
|
|
.write()
|
|
.retain(|dri| dri.get_id() != driver.get_id());
|
|
true
|
|
}
|
|
None => false,
|
|
}
|
|
}
|
|
|
|
pub fn init() {
|
|
let mut pci_iter = unsafe { scan_bus(&PortOpsImpl, CSpaceAccessMethod::IO) };
|
|
for dev in pci_iter {
|
|
info!(
|
|
"pci: {:02x}:{:02x}.{} {:#x} {:#x} ({} {}) irq: {}:{:?}",
|
|
dev.loc.bus,
|
|
dev.loc.device,
|
|
dev.loc.function,
|
|
dev.id.vendor_id,
|
|
dev.id.device_id,
|
|
dev.id.class,
|
|
dev.id.subclass,
|
|
dev.pic_interrupt_line,
|
|
dev.interrupt_pin,
|
|
);
|
|
init_driver(&dev);
|
|
}
|
|
}
|
|
|
|
pub fn find_device(vendor: u16, product: u16) -> Option<Location> {
|
|
let mut pci_iter = unsafe { scan_bus(&PortOpsImpl, CSpaceAccessMethod::IO) };
|
|
for dev in pci_iter {
|
|
if dev.id.vendor_id == vendor && dev.id.device_id == product {
|
|
return Some(dev.loc);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
pub fn get_bar0_mem(loc: Location) -> Option<(usize, usize)> {
|
|
unsafe { probe_function(&PortOpsImpl, loc, CSpaceAccessMethod::IO) }
|
|
.and_then(|dev| dev.bars[0])
|
|
.map(|bar| match bar {
|
|
BAR::Memory(addr, len, _, _) => (addr as usize, len as usize),
|
|
_ => unimplemented!(),
|
|
})
|
|
}
|
|
|
|
lazy_static! {
|
|
pub static ref PCI_DRIVERS: Mutex<BTreeMap<Location, Arc<Driver>>> =
|
|
Mutex::new(BTreeMap::new());
|
|
}
|