|  | /* | 
|  | * Copyright (c) 2014 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_VIRTIO_BASE_HH__ | 
|  | #define __DEV_VIRTIO_BASE_HH__ | 
|  |  | 
|  | #include "arch/isa_traits.hh" | 
|  | #include "base/bitunion.hh" | 
|  | #include "base/callback.hh" | 
|  | #include "dev/virtio/virtio_ring.h" | 
|  | #include "mem/port_proxy.hh" | 
|  | #include "sim/sim_object.hh" | 
|  |  | 
|  | struct VirtIODeviceBaseParams; | 
|  | 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. | 
|  | */ | 
|  |  | 
|  | /** Convert legacy VirtIO endianness to host endianness. */ | 
|  | template <typename T> inline T | 
|  | vtoh_legacy(T v) { | 
|  | return TheISA::gtoh(v); | 
|  | } | 
|  |  | 
|  | /** Convert host endianness to legacy VirtIO endianness. */ | 
|  | template <typename T> inline T | 
|  | htov_legacy(T v) { | 
|  | return TheISA::htog(v); | 
|  | } | 
|  |  | 
|  |  | 
|  | template <> inline vring_used_elem | 
|  | vtoh_legacy(vring_used_elem v) { | 
|  | v.id = vtoh_legacy(v.id); | 
|  | v.len = vtoh_legacy(v.len); | 
|  | return v; | 
|  | } | 
|  |  | 
|  | template <> inline vring_used_elem | 
|  | htov_legacy(vring_used_elem v) { | 
|  | v.id = htov_legacy(v.id); | 
|  | v.len = htov_legacy(v.len); | 
|  | return v; | 
|  | } | 
|  |  | 
|  | template <> inline vring_desc | 
|  | vtoh_legacy(vring_desc v) { | 
|  | v.addr = vtoh_legacy(v.addr); | 
|  | v.len = vtoh_legacy(v.len); | 
|  | v.flags = vtoh_legacy(v.flags); | 
|  | v.next = vtoh_legacy(v.next); | 
|  | return v; | 
|  | } | 
|  |  | 
|  | template <> inline vring_desc | 
|  | htov_legacy(vring_desc v) { | 
|  | v.addr = htov_legacy(v.addr); | 
|  | v.len = htov_legacy(v.len); | 
|  | v.flags = htov_legacy(v.flags); | 
|  | v.next = htov_legacy(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, 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; | 
|  |  | 
|  | /** 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: | 
|  | virtual ~VirtQueue() {}; | 
|  |  | 
|  | /** @{ | 
|  | * @name Checkpointing Interface | 
|  | */ | 
|  | virtual void serialize(std::ostream &os); | 
|  | virtual void unserialize(Checkpoint *cp, const std::string §ion); | 
|  | /** @} */ | 
|  |  | 
|  | /** @{ | 
|  | * @name Low-level Device Interface | 
|  | */ | 
|  | /** | 
|  | * 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 unsigned ALIGN_BITS = 12; | 
|  | static const unsigned 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, uint16_t size); | 
|  |  | 
|  | 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 Header { | 
|  | Flags flags; | 
|  | Index index; | 
|  | } M5_ATTR_PACKED; | 
|  |  | 
|  | VirtRing<T>(PortProxy &proxy, uint16_t size) | 
|  | : header{0, 0}, ring(size), _proxy(proxy), _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, (uint8_t *)&header, sizeof(header)); | 
|  | header.flags = vtoh_legacy(header.flags); | 
|  | header.index = vtoh_legacy(header.index); | 
|  | } | 
|  |  | 
|  | void writeHeader() { | 
|  | Header out; | 
|  | assert(_base != 0); | 
|  | out.flags = htov_legacy(header.flags); | 
|  | out.index = htov_legacy(header.index); | 
|  | _proxy.writeBlob(_base, (uint8_t *)&out, sizeof(out)); | 
|  | } | 
|  |  | 
|  | void read() { | 
|  | readHeader(); | 
|  |  | 
|  | /* Read and byte-swap the elements in the ring */ | 
|  | T temp[ring.size()]; | 
|  | _proxy.readBlob(_base + sizeof(header), | 
|  | (uint8_t *)temp, sizeof(T) * ring.size()); | 
|  | for (int i = 0; i < ring.size(); ++i) | 
|  | ring[i] = vtoh_legacy(temp[i]); | 
|  | } | 
|  |  | 
|  | 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] = htov_legacy(ring[i]); | 
|  | _proxy.writeBlob(_base + sizeof(header), | 
|  | (uint8_t *)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; | 
|  | }; | 
|  |  | 
|  | /** 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(Params *params, DeviceId id, size_t config_size, | 
|  | FeatureBits features); | 
|  | virtual ~VirtIODeviceBase(); | 
|  |  | 
|  | public: | 
|  | /** @{ | 
|  | * @name SimObject Interfaces | 
|  | */ | 
|  |  | 
|  | void serialize(std::ostream &os); | 
|  | void unserialize(Checkpoint *cp, const std::string §ion); | 
|  |  | 
|  | /** @} */ | 
|  |  | 
|  |  | 
|  | 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->process(); | 
|  | }; | 
|  |  | 
|  | /** | 
|  | * 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); | 
|  |  | 
|  | /** @} */ | 
|  |  | 
|  | public: | 
|  | /** @{ | 
|  | * @name VirtIO Transport Interfaces | 
|  | */ | 
|  | /** | 
|  | * Register a callback to kick the guest through the transport | 
|  | * interface. | 
|  | * | 
|  | * @param c Callback into transport interface. | 
|  | */ | 
|  | void registerKickCallback(Callback *c) { | 
|  | assert(!transKick); | 
|  | transKick = c; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * 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  */ | 
|  | Callback *transKick; | 
|  | }; | 
|  |  | 
|  | #endif // __DEV_VIRTIO_BASE_HH__ |