/*
* Copyright (c) 2015 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* not be construed as granting a license to any other intellectual
* property including but not limited to intellectual property relating
* to a hardware implementation of the functionality of the software
* licensed hereunder. You may use the software subject to the license
* terms below provided that you ensure that this notice is replicated
* unmodified and in its entirety in all distributions of the software,
* modified or unmodified, in source code or in binary form.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors: Andreas Sandberg
*/
#ifndef __DEV_PCI_HOST_HH__
#define __DEV_PCI_HOST_HH__
#include "dev/io_device.hh"
#include "dev/pci/types.hh"
struct PciHostParams;
struct GenericPciHostParams;
class PciDevice;
class Platform;
/**
* The PCI host describes the interface between PCI devices and a
* simulated system.
*
* The PCI host controller has three main responsibilities:
*
* - Expose a configuration memory space that allows devices to
* be discovered and configured.
*
- Map and deliver interrupts to the CPU.
*
- Map memory addresses from the PCI bus's various memory
* spaces (Legacy IO, non-prefetchable memory, and
* prefetchable memory) to physical memory.
*
*
* PCI devices need to register themselves with a PCI host using the
* PciHost::registerDevice() call. This call returns a
* PciHost::DeviceInterface that provides for common functionality
* such as interrupt delivery and memory mapping.
*
* The PciHost class itself provides very little functionality. Simple
* PciHost functionality is implemented by the GenericPciHost class.
*/
class PciHost : public PioDevice
{
public:
PciHost(const PciHostParams *p);
virtual ~PciHost();
public:
/**
* @{
* @name Device interface
*/
/**
* Callback interface from PCI devices to the host.
*
* Devices get an instance of this object when they register
* themselves with the host using the PciHost::registerDevice()
* call.
*/
class DeviceInterface
{
friend class ::PciHost;
protected:
/**
* Instantiate a device interface
*
* @param host PCI host that this device belongs to.
* @param bus_addr The device's position on the PCI bus
* @param pin Interrupt pin
*/
DeviceInterface(PciHost &host, PciBusAddr &bus_addr, PciIntPin pin);
public:
DeviceInterface() = delete;
void operator=(const DeviceInterface &) = delete;
const std::string name() const;
/**
* Post a PCI interrupt to the CPU.
*/
void postInt();
/**
* Clear a posted PCI interrupt
*/
void clearInt();
/**
* Calculate the physical address of an IO location on the PCI
* bus.
*
* @param addr Address in the PCI IO address space
* @return Address in the system's physical address space.
*/
Addr pioAddr(Addr addr) const { return host.pioAddr(busAddr, addr); }
/**
* Calculate the physical address of a non-prefetchable memory
* location in the PCI address space.
*
* @param addr Address in the PCI memory address space
* @return Address in the system's physical address space.
*/
Addr memAddr(Addr addr) const { return host.memAddr(busAddr, addr); }
/**
* Calculate the physical address of a prefetchable memory
* location in the PCI address space.
*
* @param addr Address in the PCI DMA memory address space
* @return Address in the system's physical address space.
*/
Addr dmaAddr(Addr addr) const { return host.dmaAddr(busAddr, addr); }
protected:
PciHost &host;
const PciBusAddr busAddr;
const PciIntPin interruptPin;
};
/**
* Register a PCI device with the host.
*
* @param device Device to register
* @param bus_addr The device's position on the PCI bus
* @param pin Interrupt pin
* @return A device-specific DeviceInterface instance.
*/
virtual DeviceInterface registerDevice(PciDevice *device,
PciBusAddr bus_addr, PciIntPin pin);
/** @} */
protected:
/**
* @{
* @name PciHost controller interface
*/
/**
* Post an interrupt to the CPU.
*
* @param bus_addr The device's position on the PCI bus
* @param pin PCI interrupt pin
*/
virtual void postInt(const PciBusAddr &bus_addr, PciIntPin pin) = 0;
/**
* Post an interrupt to the CPU.
*
* @param bus_addr The device's position on the PCI bus
* @param pin PCI interrupt pin
*/
virtual void clearInt(const PciBusAddr &bus_addr, PciIntPin pin) = 0;
/**
* Calculate the physical address of an IO location on the PCI
* bus.
*
* @param bus_addr The device's position on the PCI bus
* @param pci_addr Address in the PCI IO address space
* @return Address in the system's physical address space.
*/
virtual Addr pioAddr(const PciBusAddr &bus_addr, Addr pci_addr) const = 0;
/**
* Calculate the physical address of a non-prefetchable memory
* location in the PCI address space.
*
* @param bus_addr The device's position on the PCI bus
* @param pci_addr Address in the PCI memory address space
* @return Address in the system's physical address space.
*/
virtual Addr memAddr(const PciBusAddr &bus_addr, Addr pci_addr) const = 0;
/**
* Calculate the physical address of a prefetchable memory
* location in the PCI address space.
*
* @param bus_addr The device's position on the PCI bus
* @param pci_addr Address in the PCI DMA memory address space
* @return Address in the system's physical address space.
*/
virtual Addr dmaAddr(const PciBusAddr &bus_addr, Addr pci_addr) const = 0;
/** @} */
protected:
/**
* Retrieve a PCI device from its bus address.
*
* @return Pointer to a PciDevice instance or nullptr if the
* device doesn't exist.
*/
PciDevice *getDevice(const PciBusAddr &addr);
/**
* Retrieve a PCI device from its bus address.
*
* @return Pointer to a constant PciDevice instance or nullptr if
* the device doesn't exist.
*/
const PciDevice *getDevice(const PciBusAddr &addr) const;
private:
/** Currently registered PCI devices */
std::map devices;
};
/**
* Configurable generic PCI host interface
*
* The GenericPciHost provides a configurable generic PCI host
* implementation.
*
* The generic controller binds to one range of physical addresses to
* implement the PCI subsystem's configuraiton space. The base
* address, size and mapping between memory addresses and PCI devices
* are all configurable as simulation parameters. The basic
* implementation supports both the Configuration Access Mechanism
* (CAM) and Enhanced Configuration Access Mechanism (ECAM)
* configuration space layout. The layouts can be configured by
* changing the number of bits allocated to each device in the
* configuration space. ECAM uses 12 bits per device, while CAM uses 8
* bits per device.
*
* Interrupts are delivered via the Platform::postInt() and
* Platform::clearInt() calls. Interrupt numbers are mapped statically
* using the interrupt line (PciDevice::interruptLine()) returned from
* the device. Implementations may override mapPciInterrupt() to
* dynamically map a PciBusAddr and PciIntPin to a platform-specific
* interrupt.
*
* All PCI memory spaces (IO, prefetchable, and non-prefetchable)
* support a simple base+offset mapping that can be configured using
* simulation parameters. The base defaults to 0 for all of them.
*/
class GenericPciHost : public PciHost
{
public:
GenericPciHost(const GenericPciHostParams *p);
virtual ~GenericPciHost();
public: // PioDevice
Tick read(PacketPtr pkt) override;
Tick write(PacketPtr pkt) override;
AddrRangeList getAddrRanges() const override;
protected: // PciHost
Addr pioAddr(const PciBusAddr &bus_addr, Addr pci_addr) const override {
return pciPioBase + pci_addr;
}
Addr memAddr(const PciBusAddr &bus_addr, Addr pci_addr) const override {
return pciMemBase + pci_addr;
}
Addr dmaAddr(const PciBusAddr &bus_addr, Addr pci_addr) const override {
return pciDmaBase + pci_addr;
}
protected: // Configuration address space handling
/**
* Decode a configuration space address.
*
*
* @param addr Offset into the configuration space
* @return Tuple containing the PCI bus address and an offset into
* the device's configuration space.
*/
virtual std::pair decodeAddress(Addr address);
protected: // Interrupt handling
void postInt(const PciBusAddr &addr, PciIntPin pin) override;
void clearInt(const PciBusAddr &addr, PciIntPin pin) override;
virtual uint32_t mapPciInterrupt(const PciBusAddr &bus_addr,
PciIntPin pin) const;
protected:
Platform &platform;
const Addr confBase;
const Addr confSize;
const uint8_t confDeviceBits;
const Addr pciPioBase;
const Addr pciMemBase;
const Addr pciDmaBase;
};
#endif // __DEV_PCI_HOST_HH__