/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.driver.bus.pci; import java.io.PrintWriter; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.List; import javax.naming.NameNotFoundException; import org.apache.log4j.Logger; import org.jnode.driver.Device; import org.jnode.driver.DeviceAlreadyRegisteredException; import org.jnode.driver.DeviceInfoAPI; import org.jnode.driver.DeviceManager; import org.jnode.driver.Driver; import org.jnode.driver.DriverException; import org.jnode.naming.InitialNaming; import org.jnode.system.resource.IOResource; import org.jnode.system.resource.ResourceManager; import org.jnode.system.resource.ResourceNotFreeException; import org.jnode.system.resource.ResourceOwner; import org.jnode.util.AccessControllerUtils; import org.jnode.util.NumberUtils; import org.jnode.vm.VirtualMemoryRegion; import org.jnode.vm.facade.VmArchitecture; import org.jnode.vm.facade.VmUtils; import org.jnode.work.Work; import org.jnode.work.WorkUtils; import org.vmmagic.unboxed.Address; /** * Driver for the PCI bus itself. * * @author epr */ final class PCIDriver extends Driver implements DeviceInfoAPI, PCIBusAPI, PCIConstants { /** * My logger */ private static final Logger log = Logger.getLogger(PCIDriver.class); /** * IO space of the PCI configuration registers */ private IOResource pciConfigIO; /** * All pci devices */ private List<PCIDevice> devices; /** * Global lock used to protected access to the configuration space */ private static final Object CONFIG_LOCK = new Object(); private final PCIBus rootBus; /** * Create a new instance */ protected PCIDriver(Device pciDevice) throws DriverException { this.rootBus = new PCIBus(pciDevice.getBus(), this); } /** * Register all PCI devices with the device manager. */ public void startDevice() throws DriverException { try { final Device pciBusDevice = getDevice(); final ResourceManager rm = InitialNaming.lookup(ResourceManager.NAME); // Claim the resources pciConfigIO = claimPorts(rm, pciBusDevice); // Register the API's pciBusDevice.registerAPI(PCIBusAPI.class, this); pciBusDevice.registerAPI(DeviceInfoAPI.class, this); // Find the PCI devices devices = probeDevices(); // Start the PCI devices WorkUtils.add(new Work("Starting PCI devices") { public void execute() { startDevices(devices); } }); } catch (ResourceNotFreeException ex) { throw new DriverException("Cannot claim IO ports", ex); } catch (DriverException ex) { throw new DriverException("Driver exception during register", ex); } catch (NameNotFoundException ex) { throw new DriverException("Cannot find resource or device manager", ex); } } /** * Unregister all PCI devices from the device manager. */ public void stopDevice() throws DriverException { // Stop & unregister all PCI devices DeviceManager devMan; try { devMan = InitialNaming.lookup(DeviceManager.NAME); } catch (NameNotFoundException ex) { throw new DriverException("Cannot find device manager", ex); } for (PCIDevice dev : devices) { log.debug("Stopping and unregistering device " + dev.getId()); try { devMan.unregister(dev); } catch (DriverException ex) { throw new DriverException("Driver exception during unregister", ex); } } // Remove the API final Device pciBusDevice = getDevice(); pciBusDevice.unregisterAPI(DeviceInfoAPI.class); pciBusDevice.unregisterAPI(PCIBusAPI.class); // Release the resources pciConfigIO.release(); pciConfigIO = null; } /** * Return the list of connection PCI devices. * * @return A List containing all connected devices as instanceof PCIDevice. */ public List getDevices() { return devices; } /** * Find a device with a given vendor and device id. * * @param vendorId * @param deviceId * @return The found device, of null if not found. */ public PCIDevice findDevice(int vendorId, int deviceId) { for (PCIDevice dev : devices) { final PCIDeviceConfig cfg = dev.getConfig(); if (cfg.getVendorID() == vendorId) { if (cfg.getDeviceID() == deviceId) { return dev; } } } return null; } /** * Probe the PCI bus for a list of all connected devices. * * @return A List containing all connected devices as instanceof PCIDevice. */ protected List<PCIDevice> probeDevices() { final ArrayList<PCIDevice> result = new ArrayList<PCIDevice>(); rootBus.probeDevices(result); return result; } /** * Start all PCI devices. All bridges are started in a sequence. After that * all non-bridges are started in parallel (goal, not implemented yet). */ final void startDevices(List<PCIDevice> devices) { // List all devices for (PCIDevice dev : devices) { final PCIDeviceConfig cfg = dev.getConfig(); log.debug("PCI " + dev.getPCIName() + '\t' + NumberUtils.hex(cfg.getVendorID(), 4) + ':' + NumberUtils.hex(cfg.getDeviceID(), 4) + ':' + NumberUtils.hex(cfg.getRevision(), 2) + ' ' + NumberUtils.hex(cfg.getBaseClass(), 2) + ':' + NumberUtils.hex(cfg.getSubClass(), 2) + ':' + NumberUtils.hex(cfg.getMinorClass(), 2) // + "\tIRQ" + cfg.getInterruptLine() + ":" + cfg.getInterruptPin() + "\tCMD " + NumberUtils.hex(cfg.getCommand(), 4)); } // Remap all devices remapDeviceAddresses(devices); // Register all bridges final DeviceManager devMan = getDevice().getManager(); for (PCIDevice dev : devices) { if (dev.isBridge()) { try { devMan.register(dev); } catch (DeviceAlreadyRegisteredException ex) { log.error("Cannot start " + dev.getId(), ex); } catch (DriverException ex) { log.error("Cannot start " + dev.getId(), ex); } } } // Register all non-bridges for (final PCIDevice dev : devices) { if (!dev.isBridge()) { WorkUtils.add(new Work(dev.getId()) { public void execute() { try { devMan.register(dev); } catch (DeviceAlreadyRegisteredException ex) { log.error("Cannot start " + dev.getId(), ex); } catch (DriverException ex) { log.error("Cannot start " + dev.getId(), ex); } } }); } } } /** * Remap the addresses of the given pci devices. * Currently we only verify if the memory addresses are in the * DEVICE space. * * @param devices */ protected void remapDeviceAddresses(List<PCIDevice> devices) { log.debug("Remapping pci devices"); final VmArchitecture arch = VmUtils.getVm().getArch(); final Address start = arch.getStart(VirtualMemoryRegion.DEVICE); final Address end = arch.getEnd(VirtualMemoryRegion.DEVICE); for (PCIDevice dev : devices) { final PCIDeviceConfig cfg = dev.getConfig(); if (cfg.isHeaderType0()) { int addrIdx = 0; for (PCIBaseAddress addr : cfg.asHeaderType0().getBaseAddresses()) { if (addr.isBelow1Mb()) { // Ignore } else if (addr.isMemorySpace()) { final Address memStart = Address.fromLong(addr .getMemoryBase()); final Address memEnd = memStart.add(addr.getSize()); if (memStart.LT(start) || memEnd.GE(end)) { log.error("Base address[" + addrIdx + "] of " + dev + " out of device space"); } else { log.debug("Base address[" + addrIdx + "] of " + dev + " in device space"); } } else if (addr.isIOSpace()) { // Ignore for now } addrIdx++; } } } } /** * Read an 8-bit int from the PCI configuration space of the given device. * * @param bus 0..255 * @param unit 0..31 * @param func 0..7 * @param offset 0..255 (byte offset) */ protected int readConfigByte(int bus, int unit, int func, int offset) { if ((bus < 0) || (bus > 255)) { throw new IllegalArgumentException( "Invalid bus value"); } if ((unit < 0) || (unit > 31)) { throw new IllegalArgumentException( "Invalid unit value"); } if ((func < 0) || (func > 7)) { throw new IllegalArgumentException( "Invalid func value"); } if ((offset < 0) || (offset > 255)) { throw new IllegalArgumentException( "Invalid offset value"); } int address = 0x80000000 | (bus << 16) | (unit << 11) | (func << 8) | (offset & ~3); synchronized (CONFIG_LOCK) { pciConfigIO.outPortDword(PW32_CONFIG_ADDRESS, address); return pciConfigIO.inPortByte(PRW32_CONFIG_DATA + (offset & 3)) & 0xFF; } } /** * Read a 32-bit int from the PCI configuration space of the given device. * * @param bus 0..255 * @param unit 0..31 * @param func 0..7 * @param offset 0..255 (byte offset) */ protected int readConfigDword(int bus, int unit, int func, int offset) { if ((bus < 0) || (bus > 255)) { throw new IllegalArgumentException( "Invalid bus value"); } if ((unit < 0) || (unit > 31)) { throw new IllegalArgumentException( "Invalid unit value"); } if ((func < 0) || (func > 7)) { throw new IllegalArgumentException( "Invalid func value"); } if ((offset < 0) || (offset > 255)) { throw new IllegalArgumentException( "Invalid offset value"); } int address = 0x80000000 | (bus << 16) | (unit << 11) | (func << 8) | offset; synchronized (CONFIG_LOCK) { pciConfigIO.outPortDword(PW32_CONFIG_ADDRESS, address); return pciConfigIO.inPortDword(PRW32_CONFIG_DATA); } } /** * Read a 16-bit int from the PCI configuration space of the given device. * * @param bus 0..255 * @param unit 0..31 * @param func 0..7 * @param offset 0..255 (byte offset) */ protected int readConfigWord(int bus, int unit, int func, int offset) { if ((bus < 0) || (bus > 255)) { throw new IllegalArgumentException( "Invalid bus value"); } if ((unit < 0) || (unit > 31)) { throw new IllegalArgumentException( "Invalid unit value"); } if ((func < 0) || (func > 7)) { throw new IllegalArgumentException( "Invalid func value"); } if ((offset < 0) || (offset > 255)) { throw new IllegalArgumentException( "Invalid offset value"); } int address = 0x80000000 | (bus << 16) | (unit << 11) | (func << 8) | (offset & ~3); synchronized (CONFIG_LOCK) { pciConfigIO.outPortDword(PW32_CONFIG_ADDRESS, address); return pciConfigIO.inPortWord(PRW32_CONFIG_DATA + (offset & 2)); } } /** * Write a 32-bit int into the PCI configuration space of the given device. * * @param bus 0..255 * @param unit 0..31 * @param func 0..7 * @param offset 0..255 (byte offset) * @param value */ protected void writeConfigDword(int bus, int unit, int func, int offset, int value) { if ((bus < 0) || (bus > 255)) { throw new IllegalArgumentException( "Invalid bus value"); } if ((unit < 0) || (unit > 31)) { throw new IllegalArgumentException( "Invalid unit value"); } if ((func < 0) || (func > 7)) { throw new IllegalArgumentException( "Invalid func value"); } if ((offset < 0) || (offset > 255)) { throw new IllegalArgumentException( "Invalid register value"); } int address = 0x80000000 | (bus << 16) | (unit << 11) | (func << 8) | (offset & ~3); synchronized (CONFIG_LOCK) { pciConfigIO.outPortDword(PW32_CONFIG_ADDRESS, address); pciConfigIO.outPortDword(PRW32_CONFIG_DATA, value); } } /** * Write a 16-bit int into the PCI configuration space of the given device. * * @param bus 0..255 * @param unit 0..31 * @param func 0..7 * @param offset 0..255 (byte offset) * @param value */ protected void writeConfigWord(int bus, int unit, int func, int offset, int value) { if ((bus < 0) || (bus > 255)) { throw new IllegalArgumentException( "Invalid bus value"); } if ((unit < 0) || (unit > 31)) { throw new IllegalArgumentException( "Invalid unit value"); } if ((func < 0) || (func > 7)) { throw new IllegalArgumentException( "Invalid func value"); } if ((offset < 0) || (offset > 255)) { throw new IllegalArgumentException( "Invalid register value"); } int address = 0x80000000 | (bus << 16) | (unit << 11) | (func << 8) | (offset & ~3); synchronized (CONFIG_LOCK) { pciConfigIO.outPortDword(PW32_CONFIG_ADDRESS, address); pciConfigIO.outPortWord(PRW32_CONFIG_DATA + (offset & 2), value); } } /** * Write a 8-bit int into the PCI configuration space of the given device. * * @param bus 0..255 * @param unit 0..31 * @param func 0..7 * @param offset 0..255 (byte offset) * @param value */ protected void writeConfigByte(int bus, int unit, int func, int offset, int value) { if ((bus < 0) || (bus > 255)) { throw new IllegalArgumentException( "Invalid bus value"); } if ((unit < 0) || (unit > 31)) { throw new IllegalArgumentException( "Invalid unit value"); } if ((func < 0) || (func > 7)) { throw new IllegalArgumentException( "Invalid func value"); } if ((offset < 0) || (offset > 255)) { throw new IllegalArgumentException( "Invalid register value"); } int address = 0x80000000 | (bus << 16) | (unit << 11) | (func << 8) | (offset & ~3); synchronized (CONFIG_LOCK) { pciConfigIO.outPortDword(PW32_CONFIG_ADDRESS, address); pciConfigIO.outPortByte(PRW32_CONFIG_DATA + (offset & 3), value); } } private IOResource claimPorts(final ResourceManager rm, final ResourceOwner owner) throws ResourceNotFreeException, DriverException { try { return AccessControllerUtils .doPrivileged(new PrivilegedExceptionAction<IOResource>() { public IOResource run() throws ResourceNotFreeException { return rm.claimIOResource(owner, PCI_FIRST_PORT, PCI_LAST_PORT - PCI_FIRST_PORT + 1); } }); } catch (ResourceNotFreeException ex) { throw ex; } catch (Exception ex) { throw new DriverException("Unknown exception", ex); } } /** * @see org.jnode.driver.DeviceInfoAPI#showInfo(java.io.PrintWriter) */ public void showInfo(PrintWriter out) { final ArrayList<PCIDevice> devices = new ArrayList<PCIDevice>(this.devices); // List all devices for (PCIDevice dev : devices) { final PCIDeviceConfig cfg = dev.getConfig(); out.println("PCI " + dev.getPCIName() + '\t' + NumberUtils.hex(cfg.getVendorID(), 4) + ':' + NumberUtils.hex(cfg.getDeviceID(), 4) + ':' + NumberUtils.hex(cfg.getRevision(), 2) + ' ' + NumberUtils.hex(cfg.getBaseClass(), 2) + ':' + NumberUtils.hex(cfg.getSubClass(), 2) + ':' + NumberUtils.hex(cfg.getMinorClass(), 2) // + "\tIRQ" // + cfg.getInterruptLine() + ":" + cfg.getInterruptPin() + "\tCMD " + NumberUtils.hex(cfg.getCommand(), 4)); } } }