| /* |
| * Copyright (c) 2014, 2016-2017, 2021 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. |
| */ |
| |
| #ifndef __DEV_VIRTIO_BASE_HH__ |
| #define __DEV_VIRTIO_BASE_HH__ |
| |
| #include <cstdint> |
| #include <functional> |
| #include <vector> |
| |
| #include "base/bitunion.hh" |
| #include "base/compiler.hh" |
| #include "base/types.hh" |
| #include "dev/virtio/virtio_ring.h" |
| #include "mem/port_proxy.hh" |
| #include "sim/serialize.hh" |
| #include "sim/sim_object.hh" |
| |
| namespace gem5 |
| { |
| |
| struct VirtIODeviceBaseParams; |
| struct VirtIODummyDeviceParams; |
| |
| class VirtQueue; |
| |
| /** @{ |
| * @name VirtIO endian conversion helpers |
| * |
| * VirtIO prior to version 1.0 (legacy versions) normally send values |
| * to the host in the guest systems native byte order. This is going |
| * to change in version 1.0 which mandates little endian. We currently |
| * only support the legacy version of VirtIO (the new and shiny |
| * standard is still in a draft state and not implemented by the |
| * kernel). Once we support the new standard, we should negotiate the |
| * VirtIO version with the guest and automatically use the right type |
| * of byte swapping. |
| */ |
| |
| template <typename T> |
| inline std::enable_if_t<std::is_same_v<T, vring_used_elem>, T> |
| swap_byte(T v) |
| { |
| v.id = swap_byte(v.id); |
| v.len = swap_byte(v.len); |
| return v; |
| } |
| |
| template <typename T> |
| inline std::enable_if_t<std::is_same_v<T, vring_desc>, T> |
| swap_byte(T v) |
| { |
| v.addr = swap_byte(v.addr); |
| v.len = swap_byte(v.len); |
| v.flags = swap_byte(v.flags); |
| v.next = swap_byte(v.next); |
| return v; |
| } |
| |
| /** @} */ |
| |
| /** |
| * VirtIO descriptor (chain) wrapper |
| * |
| * Communication in VirtIO takes place by sending and receiving chains |
| * of so called descriptors using device queues. The queue is |
| * responsible for sending a descriptor chain from the guest to the |
| * host and later sending it back to the guest. The descriptor chain |
| * itself can be thought of as a linked list of buffers (descriptors) |
| * that are read only (isIncoming() is true) or write only |
| * (isOutgoing() is true). A single chain may contain any mix of input |
| * and output buffers. |
| * |
| * The descriptor wrapper is normally <i>only</i> instantiated by the |
| * virtqueue wrapper (VirtQueue) and should never be instantiated in |
| * device models. The VirtQueue also ensures that the descriptor |
| * wrapper is re-populated with new data from the guest by calling |
| * updateChain() whenever a new descriptor chain is passed to the host |
| * (VirtQueue::consumeDescriptor()). The updateChain() method |
| * automatically does some sanity checks on the descriptor chain to |
| * detect loops. |
| */ |
| class VirtDescriptor |
| { |
| public: |
| /** Descriptor index in virtqueue */ |
| typedef uint16_t Index; |
| |
| /** @{ |
| * @name VirtIO Descriptor <-> Queue Interface |
| */ |
| /** |
| * Create a descriptor wrapper. |
| * |
| * @param memProxy Proxy to the guest physical memory. |
| * @param queue Queue owning this descriptor. |
| * @param index Index within the queue. |
| */ |
| VirtDescriptor(PortProxy &memProxy, ByteOrder bo, |
| VirtQueue &queue, Index index); |
| // WORKAROUND: The noexcept declaration works around a bug where |
| // gcc 4.7 tries to call the wrong constructor when emplacing |
| // something into a vector. |
| VirtDescriptor(VirtDescriptor &&other) noexcept; |
| ~VirtDescriptor() noexcept; |
| |
| VirtDescriptor &operator=(VirtDescriptor &&rhs) noexcept; |
| |
| /** Get the descriptor's index into the virtqueue. */ |
| Index index() const { return _index; } |
| |
| /** Populate this descriptor with data from the guest. */ |
| void update(); |
| |
| /** Populate this descriptor chain with data from the guest. */ |
| void updateChain(); |
| /** @} */ |
| |
| /** @{ |
| * @name Debug interfaces |
| */ |
| /** |
| * Dump the contents of a descriptor |
| */ |
| void dump() const; |
| /** |
| * Dump the contents of a descriptor chain starting at this |
| * descriptor. |
| */ |
| void dumpChain() const; |
| /** @} */ |
| |
| |
| /** @{ |
| * @name Device Model Interfaces |
| */ |
| /** |
| * Read the contents of a descriptor. |
| * |
| * This method copies the contents of a descriptor into a buffer |
| * within gem5. Devices should typically use chainRead() instead |
| * as it automatically follows the descriptor chain to read the |
| * desired number of bytes. |
| * |
| * @see chainRead |
| * |
| * @param offset Offset into the descriptor. |
| * @param dst Destination buffer. |
| * @param size Amount of data to read (in bytes). |
| */ |
| void read(size_t offset, uint8_t *dst, size_t size) const; |
| /** |
| * Write to the contents of a descriptor. |
| * |
| * This method copies the contents of a descriptor into a buffer |
| * within gem5. Devices should typically use chainWrite() instead |
| * as it automatically follows the descriptor chain to read the |
| * desired number of bytes. |
| * |
| * @see chainWrite |
| * |
| * @param offset Offset into the descriptor. |
| * @param src Source buffer. |
| * @param size Amount of data to read (in bytes). |
| */ |
| void write(size_t offset, const uint8_t *src, size_t size); |
| /** |
| * Retrieve the size of this descriptor. |
| * |
| * This method gets the size of a single descriptor. For incoming |
| * data, it corresponds to the amount of data that can be read |
| * from the descriptor. For outgoing data, it corresponds to the |
| * amount of data that can be written to it. |
| * |
| * @see chainSize |
| * |
| * @return Size of descriptor in bytes. |
| */ |
| size_t size() const { return desc.len; } |
| |
| /** |
| * Is this descriptor chained to another descriptor? |
| * |
| * @return true if there is a next pointer, false otherwise. |
| */ |
| bool hasNext() const { return desc.flags & VRING_DESC_F_NEXT; } |
| /** |
| * Get the pointer to the next descriptor in a chain. |
| * |
| * @return Pointer to the next descriptor or NULL if this is the |
| * last element in a chain. |
| */ |
| VirtDescriptor *next() const; |
| |
| /** Check if this is a read-only descriptor (incoming data). */ |
| bool isIncoming() const { return !isOutgoing(); } |
| /** Check if this is a write-only descriptor (outgoing data). */ |
| bool isOutgoing() const { return desc.flags & VRING_DESC_F_WRITE; } |
| |
| |
| /** |
| * Read the contents of a descriptor chain. |
| * |
| * This method reads the specified number of bytes from a |
| * descriptor chain starting at the this descriptor plus an offset |
| * in bytes. The method automatically follows the links in the |
| * descriptor chain. |
| * |
| * @param offset Offset into the chain (in bytes). |
| * @param dst Pointer to destination buffer. |
| * @param size Size (in bytes). |
| */ |
| void chainRead(size_t offset, uint8_t *dst, size_t size) const; |
| /** |
| * Write to a descriptor chain. |
| * |
| * This method writes the specified number of bytes to a |
| * descriptor chain starting at the this descriptor plus an offset |
| * in bytes. The method automatically follows the links in the |
| * descriptor chain. |
| * |
| * @param offset Offset into the chain (in bytes). |
| * @param src Pointer to source buffer. |
| * @param size Size (in bytes). |
| */ |
| void chainWrite(size_t offset, const uint8_t *src, size_t size); |
| /** |
| * Retrieve the size of this descriptor chain. |
| * |
| * This method gets the size of a descriptor chain starting at |
| * this descriptor. |
| * |
| * @return Size of descriptor chain in bytes. |
| */ |
| size_t chainSize() const; |
| /** @} */ |
| |
| private: |
| // Remove default constructor |
| VirtDescriptor(); |
| // Prevent copying |
| VirtDescriptor(const VirtDescriptor &other); |
| |
| /** Pointer to memory proxy */ |
| PortProxy *memProxy; |
| /** Pointer to virtqueue owning this descriptor */ |
| VirtQueue *queue; |
| |
| /** The byte order the descriptor is stored in. */ |
| ByteOrder byteOrder; |
| |
| /** Index in virtqueue */ |
| Index _index; |
| |
| /** Underlying descriptor */ |
| vring_desc desc; |
| }; |
| |
| /** |
| * Base wrapper around a virtqueue. |
| * |
| * VirtIO device models typically need to extend this class to |
| * implement their own device queues. |
| * |
| * @note Queues must be registered with |
| * VirtIODeviceBase::registerQueue() to be active. |
| */ |
| class VirtQueue : public Serializable |
| { |
| public: |
| virtual ~VirtQueue() {}; |
| |
| /** @{ |
| * @name Checkpointing Interface |
| */ |
| void serialize(CheckpointOut &cp) const override; |
| void unserialize(CheckpointIn &cp) override; |
| |
| /** @{ |
| * @name Low-level Device Interface |
| */ |
| |
| /** |
| * Reset cached state in this queue and in the associated |
| * ring buffers. A client of this method should be the |
| * VirtIODeviceBase::reset. |
| */ |
| void reset(); |
| |
| /** |
| * Set the base address of this queue. |
| * |
| * @param address Guest physical base address of the queue. |
| */ |
| void setAddress(Addr address); |
| /** |
| * Get the guest physical address of this queue. |
| * |
| * @return Physical address in guest where this queue resides. |
| */ |
| Addr getAddress() const { return _address; } |
| |
| /** |
| * Get the number of descriptors available in this queue. |
| * |
| * @return Size of queue in descriptors. |
| */ |
| uint16_t getSize() const { return _size; } |
| |
| /** |
| * Get a pointer to a specific descriptor in the queue. |
| * |
| * @note This interfaces is normally only used by VirtDescriptor |
| * to follow descriptor chains. Device models typically don't need |
| * to use it. |
| * |
| * @return Pointer to a VirtDescriptor. |
| */ |
| VirtDescriptor *getDescriptor(VirtDescriptor::Index index) { |
| return &descriptors[index]; |
| } |
| /** @} */ |
| |
| /** @{ |
| * @name Device Model Interfaces |
| */ |
| /** |
| * Get an incoming descriptor chain from the queue. |
| * |
| * @return Pointer to descriptor on success, NULL if no pending |
| * descriptors are available. |
| */ |
| VirtDescriptor *consumeDescriptor(); |
| /** |
| * Send a descriptor chain to the guest. |
| * |
| * This method posts a descriptor chain to the guest after a |
| * device model has finished processing it. The device model |
| * typically needs to call VirtIODeviceBase::kick() to deliver |
| * notify tell the guest that the queue has been updated. |
| * |
| * @note The desc parameter must refer to the first descriptor in |
| * a chain that has been retrieved using consumeDescriptor(). |
| * |
| * @note The len parameter specified the amount of data produced |
| * by the device model. It seems to be ignored by Linux and it is |
| * not well defined. |
| * |
| * @param desc Start of descriptor chain. |
| * @param len Length of the produced data. |
| */ |
| void produceDescriptor(VirtDescriptor *desc, uint32_t len); |
| /** @} */ |
| |
| /** @{ |
| * @name Device Model Callbacks |
| */ |
| /** |
| * Notify queue of pending events. |
| * |
| * This method is called by VirtIODeviceBase::onNotify() to notify |
| * the device model of pending data in a virtqueue. The default |
| * implementation of this method iterates over the available |
| * descriptor chains and calls onNotifyDescriptor() for every new |
| * incoming chain. |
| * |
| * Device models should normally overload one of onNotify() and |
| * onNotifyDescriptor(). |
| */ |
| virtual void onNotify(); |
| /** |
| * Notify queue of pending incoming descriptor. |
| * |
| * This method is called by the default implementation of |
| * onNotify() to notify the device model of pending data in a |
| * descriptor chain. |
| * |
| * Device models should normally overload one of onNotify() and |
| * onNotifyDescriptor(). |
| */ |
| virtual void onNotifyDescriptor(VirtDescriptor *desc) {}; |
| /** @} */ |
| |
| /** @{ |
| * @name Debug interfaces |
| */ |
| /** Dump the contents of a queue */ |
| void dump() const; |
| /** @} */ |
| |
| /** @{ */ |
| /** |
| * Page size used by VirtIO.\ It's hard-coded to 4096 bytes in |
| * the spec for historical reasons. |
| */ |
| static const Addr ALIGN_BITS = 12; |
| static const Addr ALIGN_SIZE = 1 << ALIGN_BITS; |
| /** @} */ |
| |
| protected: |
| /** |
| * Instantiate a new virtqueue. |
| * |
| * Instantiate a virtqueue with a fixed size. The size is |
| * specified in descriptors which are defined as 4096 bytes each. |
| * |
| * @param proxy Proxy to the guest physical memory. |
| * @param size Size in descriptors/pages. |
| */ |
| VirtQueue(PortProxy &proxy, ByteOrder bo, uint16_t size); |
| |
| /** Byte order in this queue */ |
| ByteOrder byteOrder; |
| |
| private: |
| VirtQueue(); |
| |
| /** Queue size in terms of number of descriptors */ |
| const uint16_t _size; |
| /** Base address of the queue */ |
| Addr _address; |
| /** Guest physical memory proxy */ |
| PortProxy &memProxy; |
| |
| private: |
| /** |
| * VirtIO ring buffer wrapper. |
| * |
| * This class wraps a VirtIO ring buffer. The template parameter T |
| * is used to select the data type for the items in the ring (used |
| * or available descriptors). |
| */ |
| template<typename T> |
| class VirtRing |
| { |
| public: |
| typedef uint16_t Flags; |
| typedef uint16_t Index; |
| |
| struct GEM5_PACKED Header |
| { |
| Flags flags; |
| Index index; |
| }; |
| |
| VirtRing<T>(PortProxy &proxy, ByteOrder bo, uint16_t size) : |
| header{0, 0}, ring(size), _proxy(proxy), _base(0), byteOrder(bo) |
| {} |
| |
| /** Reset any state in the ring buffer. */ |
| void |
| reset() |
| { |
| header = {0, 0}; |
| _base = 0; |
| }; |
| |
| /** |
| * Set the base address of the VirtIO ring buffer. |
| * |
| * @param addr New host physical address |
| */ |
| void setAddress(Addr addr) { _base = addr; } |
| |
| /** Update the ring buffer header with data from the guest. */ |
| void |
| readHeader() |
| { |
| assert(_base != 0); |
| _proxy.readBlob(_base, &header, sizeof(header)); |
| header.flags = gtoh(header.flags, byteOrder); |
| header.index = gtoh(header.index, byteOrder); |
| } |
| |
| void |
| writeHeader() |
| { |
| Header out; |
| assert(_base != 0); |
| out.flags = htog(header.flags, byteOrder); |
| out.index = htog(header.index, byteOrder); |
| _proxy.writeBlob(_base, &out, sizeof(out)); |
| } |
| |
| void |
| read() |
| { |
| readHeader(); |
| |
| /* Read and byte-swap the elements in the ring */ |
| T temp[ring.size()]; |
| _proxy.readBlob(_base + sizeof(header), |
| temp, sizeof(T) * ring.size()); |
| for (int i = 0; i < ring.size(); ++i) |
| ring[i] = gtoh(temp[i], byteOrder); |
| } |
| |
| void |
| write() |
| { |
| assert(_base != 0); |
| /* Create a byte-swapped copy of the ring and write it to |
| * guest memory. */ |
| T temp[ring.size()]; |
| for (int i = 0; i < ring.size(); ++i) |
| temp[i] = htog(ring[i], byteOrder); |
| _proxy.writeBlob(_base + sizeof(header), |
| temp, sizeof(T) * ring.size()); |
| writeHeader(); |
| } |
| |
| /** Ring buffer header in host byte order */ |
| Header header; |
| /** Elements in ring in host byte order */ |
| std::vector<T> ring; |
| |
| private: |
| // Remove default constructor |
| VirtRing<T>(); |
| |
| /** Guest physical memory proxy */ |
| PortProxy &_proxy; |
| /** Guest physical base address of the ring buffer */ |
| Addr _base; |
| /** Byte order in the ring */ |
| ByteOrder byteOrder; |
| }; |
| |
| /** Ring of available (incoming) descriptors */ |
| VirtRing<VirtDescriptor::Index> avail; |
| /** Ring of used (outgoing) descriptors */ |
| VirtRing<struct vring_used_elem> used; |
| |
| /** Offset of last consumed descriptor in the VirtQueue::avail |
| * ring */ |
| uint16_t _last_avail; |
| |
| /** Vector of pre-created descriptors indexed by their index into |
| * the queue. */ |
| std::vector<VirtDescriptor> descriptors; |
| }; |
| |
| /** |
| * Base class for all VirtIO-based devices. |
| * |
| * This class implements the functionality of the VirtIO 0.9.5 |
| * specification. This version of VirtIO is also known as "legacy" in |
| * the VirtIO 1.0 specification from OASIS. |
| * |
| * @see https://github.com/rustyrussell/virtio-spec |
| * @see http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html |
| */ |
| class VirtIODeviceBase : public SimObject |
| { |
| public: |
| typedef uint16_t QueueID; |
| typedef uint32_t FeatureBits; |
| /** This is a VirtQueue address as exposed through the low-level |
| * interface.\ The address needs to be multiplied by the page size |
| * (seems to be hardcoded to 4096 in the spec) to get the real |
| * physical address. |
| */ |
| typedef uint16_t VirtAddress; |
| /** Device Type (sometimes known as subsystem ID) */ |
| typedef uint16_t DeviceId; |
| |
| BitUnion8(DeviceStatus) |
| Bitfield<7> failed; |
| Bitfield<2> driver_ok; |
| Bitfield<1> driver; |
| Bitfield<0> acknowledge; |
| EndBitUnion(DeviceStatus) |
| |
| typedef VirtIODeviceBaseParams Params; |
| VirtIODeviceBase(const Params ¶ms, DeviceId id, size_t config_size, |
| FeatureBits features); |
| virtual ~VirtIODeviceBase(); |
| |
| public: |
| /** @{ |
| * @name SimObject Interfaces |
| */ |
| void serialize(CheckpointOut &cp) const override; |
| void unserialize(CheckpointIn &cp) override; |
| /** @} */ |
| |
| |
| protected: |
| /** @{ |
| * @name Device Model Interfaces |
| */ |
| |
| /** |
| * Inform the guest of available buffers. |
| * |
| * When a device model has finished processing incoming buffers |
| * (after onNotify has been called), it typically needs to inform |
| * the guest that there are new pending outgoing buffers. The |
| * method used to inform the guest is transport dependent, but is |
| * typically through an interrupt. Device models call this method |
| * to tell the transport interface to notify the guest. |
| */ |
| void |
| kick() |
| { |
| assert(transKick); |
| transKick(); |
| }; |
| |
| /** |
| * Register a new VirtQueue with the device model. |
| * |
| * Devices typically register at least one VirtQueue to use for |
| * communication with the guest. This <i>must</i> be done from the |
| * constructor since the number of queues are assumed to be |
| * constant throughout the lifetime of the device. |
| * |
| * @warning This method may only be called from the device model |
| * constructor. |
| */ |
| void registerQueue(VirtQueue &queue); |
| |
| |
| /** |
| * Feature set accepted by the guest. |
| * |
| * When the guest starts the driver for the device, it starts by |
| * negotiating features. The device first offers a set of features |
| * (see deviceFeatures), the driver then notifies the device of |
| * which features it accepted. The base class will automatically |
| * accept any feature set that is a subset of the features offered |
| * by the device. |
| */ |
| FeatureBits guestFeatures; |
| /** @} */ |
| |
| public: |
| /** @{ |
| * @name Optional VirtIO Interfaces |
| */ |
| /** |
| * Read from the configuration space of a device. |
| * |
| * This method is called by the transport interface to read data |
| * from a device model's configuration space. The device model |
| * should use the cfgOffset parameter as the offset into its |
| * configuration space. |
| * |
| * @warning The address in the packet should not be used to |
| * determine the offset into a device's configuration space. |
| * |
| * @param pkt Read request packet. |
| * @param cfgOffset Offset into the device's configuration space. |
| */ |
| virtual void readConfig(PacketPtr pkt, Addr cfgOffset); |
| /** |
| * Write to the configuration space of a device. |
| * |
| * This method is called by the transport interface to write data |
| * into a device model's configuration space. The device model |
| * should use the cfgOffset parameter as the offset into its |
| * configuration space. |
| * |
| * @warning The address in the packet should not be used to |
| * determine the offset into a device's configuration space. |
| * |
| * @param pkt Write request packet. |
| * @param cfgOffset Offset into the device's configuration space. |
| */ |
| virtual void writeConfig(PacketPtr pkt, Addr cfgOffset); |
| |
| /** |
| * Driver-request device reset. |
| * |
| * The device driver may reset a device by writing zero to the |
| * device status register (using setDeviceStatus()), which causes |
| * this method to be called. Device models overriding this method |
| * <i>must</i> ensure that the reset method of the base class is |
| * called when the device is reset. |
| * |
| * @note Always call the reset method of the base class from |
| * device-specific reset methods. |
| */ |
| virtual void reset(); |
| /** @} */ |
| |
| protected: |
| /** @{ |
| * @name Device Model Helpers |
| */ |
| |
| /** |
| * Read configuration data from a device structure. |
| * |
| * @param pkt Read request packet. |
| * @param cfgOffset Offset into the device's configuration space. |
| * @param cfg Device configuration |
| */ |
| void readConfigBlob(PacketPtr pkt, Addr cfgOffset, const uint8_t *cfg); |
| |
| /** |
| * Write configuration data to a device structure. |
| * |
| * @param pkt Write request packet. |
| * @param cfgOffset Offset into the device's configuration space. |
| * @param cfg Device configuration |
| */ |
| void writeConfigBlob(PacketPtr pkt, Addr cfgOffset, uint8_t *cfg); |
| |
| /** |
| * The byte order of the queues, descriptors, etc. |
| */ |
| ByteOrder byteOrder; |
| |
| /** @} */ |
| |
| public: |
| /** @{ |
| * @name VirtIO Transport Interfaces |
| */ |
| /** |
| * Register a callback to kick the guest through the transport |
| * interface. |
| * |
| * @param callback Callback into transport interface. |
| */ |
| void |
| registerKickCallback(const std::function<void()> &callback) |
| { |
| assert(!transKick); |
| transKick = callback; |
| } |
| |
| |
| /** |
| * Driver is requesting service. |
| * |
| * This method is called by the underlying hardware interface |
| * (e.g., PciVirtIO or MmmioVirtIO) to notify a device of pending |
| * incoming descriptors. |
| * |
| * @param index ID of the queue with pending actions. |
| */ |
| void onNotify(QueueID index); |
| |
| |
| /** |
| * Change currently active queue. |
| * |
| * The transport interface works on a queue at a time. The |
| * currently active queue is decided by the value of the queue |
| * select field in a device. |
| * |
| * @param idx ID of the queue to select. |
| */ |
| void setQueueSelect(QueueID idx) { _queueSelect = idx; } |
| /** |
| * Get the currently active queue. |
| * |
| * The transport interface works on a queue at a time. The |
| * currently active queue is decided by the value of the queue |
| * select field in a device. |
| * |
| * @return The ID of the currently active queue. |
| */ |
| QueueID getQueueSelect() const { return _queueSelect; } |
| |
| /** |
| * Change the host physical address of the currently active queue. |
| * |
| * @note The new address is specified in multiples of the page |
| * size (fixed to 4096 bytes in the standard). For example, if the |
| * address 10 is selected, the actual host physical address will |
| * be 40960. |
| * |
| * @see setQueueSelect |
| * @see getQueueSelect |
| * |
| * @param address New address of the currently active queue (in |
| * pages). |
| */ |
| void setQueueAddress(uint32_t address); |
| /** |
| * Get the host physical address of the currently active queue. |
| * |
| * @note The new address is specified in multiples of the page |
| * size (fixed to 4096 bytes in the standard). For example, if the |
| * address 10 is selected, the actual host physical address will |
| * be 40960. |
| * |
| * @see setQueueSelect |
| * @see getQueueSelect |
| * |
| * @return Address of the currently active queue (in pages). |
| */ |
| uint32_t getQueueAddress() const; |
| |
| /** |
| * Get the size (descriptors) of the currently active queue. |
| * |
| * @return Size of the currently active queue in number of |
| * descriptors. |
| */ |
| uint16_t getQueueSize() const { return getCurrentQueue().getSize(); } |
| |
| /** |
| * Update device status and optionally reset device. |
| * |
| * The special device status of 0 is used to reset the device by |
| * calling reset(). |
| * |
| * @param status New device status. |
| */ |
| void setDeviceStatus(DeviceStatus status); |
| |
| /** |
| * Retrieve the device status. |
| * |
| * @return Device status. |
| */ |
| DeviceStatus getDeviceStatus() const { return _deviceStatus; } |
| |
| /** |
| * Set feature bits accepted by the guest driver. |
| * |
| * This enables a subset of the features offered by the device |
| * model through the getGuestFeatures() interface. |
| */ |
| void setGuestFeatures(FeatureBits features); |
| |
| /** |
| * Get features accepted by the guest driver. |
| * |
| * @return Currently active features. |
| */ |
| FeatureBits getGuestFeatures() const { return guestFeatures; } |
| |
| /** Device ID (sometimes known as subsystem ID) */ |
| const DeviceId deviceId; |
| |
| /** Size of the device's configuration space */ |
| const size_t configSize; |
| |
| /** Feature set offered by the device */ |
| const FeatureBits deviceFeatures; |
| /** @} */ |
| |
| private: |
| /** Convenience method to get the currently selected queue */ |
| const VirtQueue &getCurrentQueue() const; |
| /** Convenience method to get the currently selected queue */ |
| VirtQueue &getCurrentQueue(); |
| |
| /** |
| * Status of the device |
| * |
| * @see getDeviceStatus |
| * @see setDeviceStatus |
| */ |
| DeviceStatus _deviceStatus; |
| |
| /** Queue select register (set by guest) */ |
| QueueID _queueSelect; |
| |
| /** List of virtual queues supported by this device */ |
| std::vector<VirtQueue *> _queues; |
| |
| /** Callbacks to kick the guest through the transport layer */ |
| std::function<void()> transKick; |
| }; |
| |
| class VirtIODummyDevice : public VirtIODeviceBase |
| { |
| public: |
| VirtIODummyDevice(const VirtIODummyDeviceParams ¶ms); |
| |
| protected: |
| /** VirtIO device ID */ |
| static const DeviceId ID_INVALID = 0x00; |
| }; |
| |
| } // namespace gem5 |
| |
| #endif // __DEV_VIRTIO_BASE_HH__ |