Package org.jnode.driver.bus.pci

Source Code of org.jnode.driver.bus.pci.PCIDriver

/*
* $Id$
*
* Copyright (C) 2003-2014 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));
        }
    }
}
TOP

Related Classes of org.jnode.driver.bus.pci.PCIDriver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.