The specialized version of byte_swap cannot be found by the compiler. As a temporary workaround to get the major patch going, move the specialization to the base header file. Change-Id: I7d2bfc1c29b70042860ae06cdc043c0490cd8916 Signed-off-by: Daniel R. Carvalho <odanrc@yahoo.com.br> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/46322 Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Jason Lowe-Power <power.jg@gmail.com> Maintainer: Jason Lowe-Power <power.jg@gmail.com>
914 lines
28 KiB
C++
914 lines
28 KiB
C++
/*
|
|
* 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"
|
|
|
|
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<T, vring_used_elem>::value, 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<T, vring_desc>::value, 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;
|
|
};
|
|
|
|
#endif // __DEV_VIRTIO_BASE_HH__
|