110388SAndreas.Sandberg@ARM.com/*
212114Ssascha.bischoff@arm.com * Copyright (c) 2014, 2016-2017 ARM Limited
310388SAndreas.Sandberg@ARM.com * All rights reserved
410388SAndreas.Sandberg@ARM.com *
510388SAndreas.Sandberg@ARM.com * The license below extends only to copyright in the software and shall
610388SAndreas.Sandberg@ARM.com * not be construed as granting a license to any other intellectual
710388SAndreas.Sandberg@ARM.com * property including but not limited to intellectual property relating
810388SAndreas.Sandberg@ARM.com * to a hardware implementation of the functionality of the software
910388SAndreas.Sandberg@ARM.com * licensed hereunder.  You may use the software subject to the license
1010388SAndreas.Sandberg@ARM.com * terms below provided that you ensure that this notice is replicated
1110388SAndreas.Sandberg@ARM.com * unmodified and in its entirety in all distributions of the software,
1210388SAndreas.Sandberg@ARM.com * modified or unmodified, in source code or in binary form.
1310388SAndreas.Sandberg@ARM.com *
1410388SAndreas.Sandberg@ARM.com * Redistribution and use in source and binary forms, with or without
1510388SAndreas.Sandberg@ARM.com * modification, are permitted provided that the following conditions are
1610388SAndreas.Sandberg@ARM.com * met: redistributions of source code must retain the above copyright
1710388SAndreas.Sandberg@ARM.com * notice, this list of conditions and the following disclaimer;
1810388SAndreas.Sandberg@ARM.com * redistributions in binary form must reproduce the above copyright
1910388SAndreas.Sandberg@ARM.com * notice, this list of conditions and the following disclaimer in the
2010388SAndreas.Sandberg@ARM.com * documentation and/or other materials provided with the distribution;
2110388SAndreas.Sandberg@ARM.com * neither the name of the copyright holders nor the names of its
2210388SAndreas.Sandberg@ARM.com * contributors may be used to endorse or promote products derived from
2310388SAndreas.Sandberg@ARM.com * this software without specific prior written permission.
2410388SAndreas.Sandberg@ARM.com *
2510388SAndreas.Sandberg@ARM.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2610388SAndreas.Sandberg@ARM.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2710388SAndreas.Sandberg@ARM.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2810388SAndreas.Sandberg@ARM.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2910388SAndreas.Sandberg@ARM.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3010388SAndreas.Sandberg@ARM.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3110388SAndreas.Sandberg@ARM.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3210388SAndreas.Sandberg@ARM.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3310388SAndreas.Sandberg@ARM.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3410388SAndreas.Sandberg@ARM.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3510388SAndreas.Sandberg@ARM.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3610388SAndreas.Sandberg@ARM.com *
3710388SAndreas.Sandberg@ARM.com * Authors: Andreas Sandberg
3810388SAndreas.Sandberg@ARM.com */
3910388SAndreas.Sandberg@ARM.com
4010388SAndreas.Sandberg@ARM.com#ifndef __DEV_VIRTIO_BASE_HH__
4110388SAndreas.Sandberg@ARM.com#define __DEV_VIRTIO_BASE_HH__
4210388SAndreas.Sandberg@ARM.com
4310388SAndreas.Sandberg@ARM.com#include "arch/isa_traits.hh"
4410388SAndreas.Sandberg@ARM.com#include "base/bitunion.hh"
4510388SAndreas.Sandberg@ARM.com#include "base/callback.hh"
4610388SAndreas.Sandberg@ARM.com#include "dev/virtio/virtio_ring.h"
4710388SAndreas.Sandberg@ARM.com#include "mem/port_proxy.hh"
4810388SAndreas.Sandberg@ARM.com#include "sim/sim_object.hh"
4910388SAndreas.Sandberg@ARM.com
5010388SAndreas.Sandberg@ARM.comstruct VirtIODeviceBaseParams;
5111931Sandreas.sandberg@arm.comstruct VirtIODummyDeviceParams;
5211931Sandreas.sandberg@arm.com
5310388SAndreas.Sandberg@ARM.comclass VirtQueue;
5410388SAndreas.Sandberg@ARM.com
5510388SAndreas.Sandberg@ARM.com/** @{
5610388SAndreas.Sandberg@ARM.com * @name VirtIO endian conversion helpers
5710388SAndreas.Sandberg@ARM.com *
5810388SAndreas.Sandberg@ARM.com * VirtIO prior to version 1.0 (legacy versions) normally send values
5910388SAndreas.Sandberg@ARM.com * to the host in the guest systems native byte order. This is going
6010388SAndreas.Sandberg@ARM.com * to change in version 1.0 which mandates little endian. We currently
6110388SAndreas.Sandberg@ARM.com * only support the legacy version of VirtIO (the new and shiny
6210388SAndreas.Sandberg@ARM.com * standard is still in a draft state and not implemented by the
6310388SAndreas.Sandberg@ARM.com * kernel). Once we support the new standard, we should negotiate the
6410388SAndreas.Sandberg@ARM.com * VirtIO version with the guest and automatically use the right type
6510388SAndreas.Sandberg@ARM.com * of byte swapping.
6610388SAndreas.Sandberg@ARM.com */
6710388SAndreas.Sandberg@ARM.com
6810388SAndreas.Sandberg@ARM.com/** Convert legacy VirtIO endianness to host endianness. */
6910388SAndreas.Sandberg@ARM.comtemplate <typename T> inline T
7010388SAndreas.Sandberg@ARM.comvtoh_legacy(T v) {
7110388SAndreas.Sandberg@ARM.com    return TheISA::gtoh(v);
7210388SAndreas.Sandberg@ARM.com}
7310388SAndreas.Sandberg@ARM.com
7410388SAndreas.Sandberg@ARM.com/** Convert host endianness to legacy VirtIO endianness. */
7510388SAndreas.Sandberg@ARM.comtemplate <typename T> inline T
7610388SAndreas.Sandberg@ARM.comhtov_legacy(T v) {
7710388SAndreas.Sandberg@ARM.com    return TheISA::htog(v);
7810388SAndreas.Sandberg@ARM.com}
7910388SAndreas.Sandberg@ARM.com
8010388SAndreas.Sandberg@ARM.com
8110388SAndreas.Sandberg@ARM.comtemplate <> inline vring_used_elem
8210388SAndreas.Sandberg@ARM.comvtoh_legacy(vring_used_elem v) {
8310388SAndreas.Sandberg@ARM.com    v.id = vtoh_legacy(v.id);
8410388SAndreas.Sandberg@ARM.com    v.len = vtoh_legacy(v.len);
8510388SAndreas.Sandberg@ARM.com    return v;
8610388SAndreas.Sandberg@ARM.com}
8710388SAndreas.Sandberg@ARM.com
8810388SAndreas.Sandberg@ARM.comtemplate <> inline vring_used_elem
8910388SAndreas.Sandberg@ARM.comhtov_legacy(vring_used_elem v) {
9010388SAndreas.Sandberg@ARM.com    v.id = htov_legacy(v.id);
9110388SAndreas.Sandberg@ARM.com    v.len = htov_legacy(v.len);
9210388SAndreas.Sandberg@ARM.com    return v;
9310388SAndreas.Sandberg@ARM.com}
9410388SAndreas.Sandberg@ARM.com
9510388SAndreas.Sandberg@ARM.comtemplate <> inline vring_desc
9610388SAndreas.Sandberg@ARM.comvtoh_legacy(vring_desc v) {
9710388SAndreas.Sandberg@ARM.com    v.addr = vtoh_legacy(v.addr);
9810388SAndreas.Sandberg@ARM.com    v.len = vtoh_legacy(v.len);
9910388SAndreas.Sandberg@ARM.com    v.flags = vtoh_legacy(v.flags);
10010388SAndreas.Sandberg@ARM.com    v.next = vtoh_legacy(v.next);
10110388SAndreas.Sandberg@ARM.com    return v;
10210388SAndreas.Sandberg@ARM.com}
10310388SAndreas.Sandberg@ARM.com
10410388SAndreas.Sandberg@ARM.comtemplate <> inline vring_desc
10510388SAndreas.Sandberg@ARM.comhtov_legacy(vring_desc v) {
10610388SAndreas.Sandberg@ARM.com    v.addr = htov_legacy(v.addr);
10710388SAndreas.Sandberg@ARM.com    v.len = htov_legacy(v.len);
10810388SAndreas.Sandberg@ARM.com    v.flags = htov_legacy(v.flags);
10910388SAndreas.Sandberg@ARM.com    v.next = htov_legacy(v.next);
11010388SAndreas.Sandberg@ARM.com    return v;
11110388SAndreas.Sandberg@ARM.com}
11210388SAndreas.Sandberg@ARM.com
11310388SAndreas.Sandberg@ARM.com/** @} */
11410388SAndreas.Sandberg@ARM.com
11510388SAndreas.Sandberg@ARM.com/**
11610388SAndreas.Sandberg@ARM.com * VirtIO descriptor (chain) wrapper
11710388SAndreas.Sandberg@ARM.com *
11810388SAndreas.Sandberg@ARM.com * Communication in VirtIO takes place by sending and receiving chains
11910388SAndreas.Sandberg@ARM.com * of so called descriptors using device queues. The queue is
12010388SAndreas.Sandberg@ARM.com * responsible for sending a descriptor chain from the guest to the
12110388SAndreas.Sandberg@ARM.com * host and later sending it back to the guest. The descriptor chain
12210388SAndreas.Sandberg@ARM.com * itself can be thought of as a linked list of buffers (descriptors)
12310388SAndreas.Sandberg@ARM.com * that are read only (isIncoming() is true) or write only
12410388SAndreas.Sandberg@ARM.com * (isOutgoing() is true). A single chain may contain any mix of input
12510388SAndreas.Sandberg@ARM.com * and output buffers.
12610388SAndreas.Sandberg@ARM.com *
12710388SAndreas.Sandberg@ARM.com * The descriptor wrapper is normally <i>only</i> instantiated by the
12810388SAndreas.Sandberg@ARM.com * virtqueue wrapper (VirtQueue) and should never be instantiated in
12910388SAndreas.Sandberg@ARM.com * device models. The VirtQueue also ensures that the descriptor
13010388SAndreas.Sandberg@ARM.com * wrapper is re-populated with new data from the guest by calling
13110388SAndreas.Sandberg@ARM.com * updateChain() whenever a new descriptor chain is passed to the host
13210388SAndreas.Sandberg@ARM.com * (VirtQueue::consumeDescriptor()). The updateChain() method
13310388SAndreas.Sandberg@ARM.com * automatically does some sanity checks on the descriptor chain to
13410388SAndreas.Sandberg@ARM.com * detect loops.
13510388SAndreas.Sandberg@ARM.com */
13610388SAndreas.Sandberg@ARM.comclass VirtDescriptor
13710388SAndreas.Sandberg@ARM.com{
13810388SAndreas.Sandberg@ARM.com  public:
13910388SAndreas.Sandberg@ARM.com    /** Descriptor index in virtqueue */
14010388SAndreas.Sandberg@ARM.com    typedef uint16_t Index;
14110388SAndreas.Sandberg@ARM.com
14210388SAndreas.Sandberg@ARM.com    /** @{
14310388SAndreas.Sandberg@ARM.com     * @name VirtIO Descriptor <-> Queue Interface
14410388SAndreas.Sandberg@ARM.com     */
14510388SAndreas.Sandberg@ARM.com    /**
14610388SAndreas.Sandberg@ARM.com     * Create a descriptor wrapper.
14710388SAndreas.Sandberg@ARM.com     *
14810388SAndreas.Sandberg@ARM.com     * @param memProxy Proxy to the guest physical memory.
14910388SAndreas.Sandberg@ARM.com     * @param queue Queue owning this descriptor.
15010388SAndreas.Sandberg@ARM.com     * @param index Index within the queue.
15110388SAndreas.Sandberg@ARM.com     */
15210388SAndreas.Sandberg@ARM.com    VirtDescriptor(PortProxy &memProxy, VirtQueue &queue, Index index);
15310388SAndreas.Sandberg@ARM.com    // WORKAROUND: The noexcept declaration works around a bug where
15410388SAndreas.Sandberg@ARM.com    // gcc 4.7 tries to call the wrong constructor when emplacing
15510388SAndreas.Sandberg@ARM.com    // something into a vector.
15610388SAndreas.Sandberg@ARM.com    VirtDescriptor(VirtDescriptor &&other) noexcept;
15710388SAndreas.Sandberg@ARM.com    ~VirtDescriptor() noexcept;
15810388SAndreas.Sandberg@ARM.com
15910388SAndreas.Sandberg@ARM.com    VirtDescriptor &operator=(VirtDescriptor &&rhs) noexcept;
16010388SAndreas.Sandberg@ARM.com
16110388SAndreas.Sandberg@ARM.com    /** Get the descriptor's index into the virtqueue. */
16210388SAndreas.Sandberg@ARM.com    Index index() const { return _index; }
16310388SAndreas.Sandberg@ARM.com
16410388SAndreas.Sandberg@ARM.com    /** Populate this descriptor with data from the guest. */
16510388SAndreas.Sandberg@ARM.com    void update();
16610388SAndreas.Sandberg@ARM.com
16710388SAndreas.Sandberg@ARM.com    /** Populate this descriptor chain with data from the guest. */
16810388SAndreas.Sandberg@ARM.com    void updateChain();
16910388SAndreas.Sandberg@ARM.com    /** @} */
17010388SAndreas.Sandberg@ARM.com
17110388SAndreas.Sandberg@ARM.com    /** @{
17210388SAndreas.Sandberg@ARM.com     * @name Debug interfaces
17310388SAndreas.Sandberg@ARM.com     */
17410388SAndreas.Sandberg@ARM.com    /**
17510388SAndreas.Sandberg@ARM.com     * Dump the contents of a descriptor
17610388SAndreas.Sandberg@ARM.com     */
17710388SAndreas.Sandberg@ARM.com    void dump() const;
17810388SAndreas.Sandberg@ARM.com    /**
17910388SAndreas.Sandberg@ARM.com     * Dump the contents of a descriptor chain starting at this
18010388SAndreas.Sandberg@ARM.com     * descriptor.
18110388SAndreas.Sandberg@ARM.com     */
18210388SAndreas.Sandberg@ARM.com    void dumpChain() const;
18310388SAndreas.Sandberg@ARM.com    /** @} */
18410388SAndreas.Sandberg@ARM.com
18510388SAndreas.Sandberg@ARM.com
18610388SAndreas.Sandberg@ARM.com    /** @{
18710388SAndreas.Sandberg@ARM.com     * @name Device Model Interfaces
18810388SAndreas.Sandberg@ARM.com     */
18910388SAndreas.Sandberg@ARM.com    /**
19010388SAndreas.Sandberg@ARM.com     * Read the contents of a descriptor.
19110388SAndreas.Sandberg@ARM.com     *
19210388SAndreas.Sandberg@ARM.com     * This method copies the contents of a descriptor into a buffer
19310388SAndreas.Sandberg@ARM.com     * within gem5. Devices should typically use chainRead() instead
19410388SAndreas.Sandberg@ARM.com     * as it automatically follows the descriptor chain to read the
19510388SAndreas.Sandberg@ARM.com     * desired number of bytes.
19610388SAndreas.Sandberg@ARM.com     *
19710388SAndreas.Sandberg@ARM.com     * @see chainRead
19810388SAndreas.Sandberg@ARM.com     *
19910388SAndreas.Sandberg@ARM.com     * @param offset Offset into the descriptor.
20010388SAndreas.Sandberg@ARM.com     * @param dst Destination buffer.
20110388SAndreas.Sandberg@ARM.com     * @param size Amount of data to read (in bytes).
20210388SAndreas.Sandberg@ARM.com     */
20310388SAndreas.Sandberg@ARM.com    void read(size_t offset, uint8_t *dst, size_t size) const;
20410388SAndreas.Sandberg@ARM.com    /**
20510388SAndreas.Sandberg@ARM.com     * Write to the contents of a descriptor.
20610388SAndreas.Sandberg@ARM.com     *
20710388SAndreas.Sandberg@ARM.com     * This method copies the contents of a descriptor into a buffer
20810388SAndreas.Sandberg@ARM.com     * within gem5. Devices should typically use chainWrite() instead
20910388SAndreas.Sandberg@ARM.com     * as it automatically follows the descriptor chain to read the
21010388SAndreas.Sandberg@ARM.com     * desired number of bytes.
21110388SAndreas.Sandberg@ARM.com     *
21210388SAndreas.Sandberg@ARM.com     * @see chainWrite
21310388SAndreas.Sandberg@ARM.com     *
21410388SAndreas.Sandberg@ARM.com     * @param offset Offset into the descriptor.
21510388SAndreas.Sandberg@ARM.com     * @param src Source buffer.
21610388SAndreas.Sandberg@ARM.com     * @param size Amount of data to read (in bytes).
21710388SAndreas.Sandberg@ARM.com     */
21810388SAndreas.Sandberg@ARM.com    void write(size_t offset, const uint8_t *src, size_t size);
21910388SAndreas.Sandberg@ARM.com    /**
22010388SAndreas.Sandberg@ARM.com     * Retrieve the size of this descriptor.
22110388SAndreas.Sandberg@ARM.com     *
22210388SAndreas.Sandberg@ARM.com     * This method gets the size of a single descriptor. For incoming
22310388SAndreas.Sandberg@ARM.com     * data, it corresponds to the amount of data that can be read
22410388SAndreas.Sandberg@ARM.com     * from the descriptor. For outgoing data, it corresponds to the
22510388SAndreas.Sandberg@ARM.com     * amount of data that can be written to it.
22610388SAndreas.Sandberg@ARM.com     *
22710388SAndreas.Sandberg@ARM.com     * @see chainSize
22810388SAndreas.Sandberg@ARM.com     *
22910388SAndreas.Sandberg@ARM.com     * @return Size of descriptor in bytes.
23010388SAndreas.Sandberg@ARM.com     */
23110388SAndreas.Sandberg@ARM.com    size_t size() const { return desc.len; }
23210388SAndreas.Sandberg@ARM.com
23310388SAndreas.Sandberg@ARM.com    /**
23410388SAndreas.Sandberg@ARM.com     * Is this descriptor chained to another descriptor?
23510388SAndreas.Sandberg@ARM.com     *
23610388SAndreas.Sandberg@ARM.com     * @return true if there is a next pointer, false otherwise.
23710388SAndreas.Sandberg@ARM.com     */
23810388SAndreas.Sandberg@ARM.com    bool hasNext() const { return desc.flags & VRING_DESC_F_NEXT; }
23910388SAndreas.Sandberg@ARM.com    /**
24010388SAndreas.Sandberg@ARM.com     * Get the pointer to the next descriptor in a chain.
24110388SAndreas.Sandberg@ARM.com     *
24210388SAndreas.Sandberg@ARM.com     * @return Pointer to the next descriptor or NULL if this is the
24310388SAndreas.Sandberg@ARM.com     * last element in a chain.
24410388SAndreas.Sandberg@ARM.com     */
24510388SAndreas.Sandberg@ARM.com    VirtDescriptor *next() const;
24610388SAndreas.Sandberg@ARM.com
24710388SAndreas.Sandberg@ARM.com    /** Check if this is a read-only descriptor (incoming data). */
24810388SAndreas.Sandberg@ARM.com    bool isIncoming() const { return !isOutgoing(); }
24910388SAndreas.Sandberg@ARM.com    /** Check if this is a write-only descriptor (outgoing data). */
25010388SAndreas.Sandberg@ARM.com    bool isOutgoing() const { return desc.flags & VRING_DESC_F_WRITE; }
25110388SAndreas.Sandberg@ARM.com
25210388SAndreas.Sandberg@ARM.com
25310388SAndreas.Sandberg@ARM.com    /**
25410388SAndreas.Sandberg@ARM.com     * Read the contents of a descriptor chain.
25510388SAndreas.Sandberg@ARM.com     *
25610388SAndreas.Sandberg@ARM.com     * This method reads the specified number of bytes from a
25710388SAndreas.Sandberg@ARM.com     * descriptor chain starting at the this descriptor plus an offset
25810388SAndreas.Sandberg@ARM.com     * in bytes. The method automatically follows the links in the
25910388SAndreas.Sandberg@ARM.com     * descriptor chain.
26010388SAndreas.Sandberg@ARM.com     *
26110388SAndreas.Sandberg@ARM.com     * @param offset Offset into the chain (in bytes).
26210388SAndreas.Sandberg@ARM.com     * @param dst Pointer to destination buffer.
26310388SAndreas.Sandberg@ARM.com     * @param size Size (in bytes).
26410388SAndreas.Sandberg@ARM.com     */
26510388SAndreas.Sandberg@ARM.com    void chainRead(size_t offset, uint8_t *dst, size_t size) const;
26610388SAndreas.Sandberg@ARM.com    /**
26710388SAndreas.Sandberg@ARM.com     * Write to a descriptor chain.
26810388SAndreas.Sandberg@ARM.com     *
26910388SAndreas.Sandberg@ARM.com     * This method writes the specified number of bytes to a
27010388SAndreas.Sandberg@ARM.com     * descriptor chain starting at the this descriptor plus an offset
27110388SAndreas.Sandberg@ARM.com     * in bytes. The method automatically follows the links in the
27210388SAndreas.Sandberg@ARM.com     * descriptor chain.
27310388SAndreas.Sandberg@ARM.com     *
27410388SAndreas.Sandberg@ARM.com     * @param offset Offset into the chain (in bytes).
27510388SAndreas.Sandberg@ARM.com     * @param src Pointer to source buffer.
27610388SAndreas.Sandberg@ARM.com     * @param size Size (in bytes).
27710388SAndreas.Sandberg@ARM.com     */
27810388SAndreas.Sandberg@ARM.com    void chainWrite(size_t offset, const uint8_t *src, size_t size);
27910388SAndreas.Sandberg@ARM.com    /**
28010388SAndreas.Sandberg@ARM.com     * Retrieve the size of this descriptor chain.
28110388SAndreas.Sandberg@ARM.com     *
28210388SAndreas.Sandberg@ARM.com     * This method gets the size of a descriptor chain starting at
28310388SAndreas.Sandberg@ARM.com     * this descriptor.
28410388SAndreas.Sandberg@ARM.com     *
28510388SAndreas.Sandberg@ARM.com     * @return Size of descriptor chain in bytes.
28610388SAndreas.Sandberg@ARM.com     */
28710388SAndreas.Sandberg@ARM.com    size_t chainSize() const;
28810388SAndreas.Sandberg@ARM.com    /** @} */
28910388SAndreas.Sandberg@ARM.com
29010388SAndreas.Sandberg@ARM.com  private:
29110388SAndreas.Sandberg@ARM.com    // Remove default constructor
29210388SAndreas.Sandberg@ARM.com    VirtDescriptor();
29310388SAndreas.Sandberg@ARM.com    // Prevent copying
29410388SAndreas.Sandberg@ARM.com    VirtDescriptor(const VirtDescriptor &other);
29510388SAndreas.Sandberg@ARM.com
29610388SAndreas.Sandberg@ARM.com    /** Pointer to memory proxy */
29710388SAndreas.Sandberg@ARM.com    PortProxy *memProxy;
29810388SAndreas.Sandberg@ARM.com    /** Pointer to virtqueue owning this descriptor */
29910388SAndreas.Sandberg@ARM.com    VirtQueue *queue;
30010388SAndreas.Sandberg@ARM.com
30110388SAndreas.Sandberg@ARM.com    /** Index in virtqueue */
30210388SAndreas.Sandberg@ARM.com    Index _index;
30310388SAndreas.Sandberg@ARM.com
30410388SAndreas.Sandberg@ARM.com    /** Underlying descriptor */
30510388SAndreas.Sandberg@ARM.com    vring_desc desc;
30610388SAndreas.Sandberg@ARM.com};
30710388SAndreas.Sandberg@ARM.com
30810388SAndreas.Sandberg@ARM.com/**
30910388SAndreas.Sandberg@ARM.com * Base wrapper around a virtqueue.
31010388SAndreas.Sandberg@ARM.com *
31110388SAndreas.Sandberg@ARM.com * VirtIO device models typically need to extend this class to
31210388SAndreas.Sandberg@ARM.com * implement their own device queues.
31310388SAndreas.Sandberg@ARM.com *
31410388SAndreas.Sandberg@ARM.com * @note Queues must be registered with
31510388SAndreas.Sandberg@ARM.com * VirtIODeviceBase::registerQueue() to be active.
31610388SAndreas.Sandberg@ARM.com */
31710905Sandreas.sandberg@arm.comclass VirtQueue : public Serializable {
31810388SAndreas.Sandberg@ARM.compublic:
31910388SAndreas.Sandberg@ARM.com    virtual ~VirtQueue() {};
32010388SAndreas.Sandberg@ARM.com
32110388SAndreas.Sandberg@ARM.com    /** @{
32210388SAndreas.Sandberg@ARM.com     * @name Checkpointing Interface
32310388SAndreas.Sandberg@ARM.com     */
32411168Sandreas.hansson@arm.com    void serialize(CheckpointOut &cp) const override;
32511168Sandreas.hansson@arm.com    void unserialize(CheckpointIn &cp) override;
32610388SAndreas.Sandberg@ARM.com
32710388SAndreas.Sandberg@ARM.com    /** @{
32810388SAndreas.Sandberg@ARM.com     * @name Low-level Device Interface
32910388SAndreas.Sandberg@ARM.com     */
33010388SAndreas.Sandberg@ARM.com    /**
33110388SAndreas.Sandberg@ARM.com     * Set the base address of this queue.
33210388SAndreas.Sandberg@ARM.com     *
33310388SAndreas.Sandberg@ARM.com     * @param address Guest physical base address of the queue.
33410388SAndreas.Sandberg@ARM.com     */
33510388SAndreas.Sandberg@ARM.com    void setAddress(Addr address);
33610388SAndreas.Sandberg@ARM.com    /**
33710388SAndreas.Sandberg@ARM.com     * Get the guest physical address of this queue.
33810388SAndreas.Sandberg@ARM.com     *
33910388SAndreas.Sandberg@ARM.com     * @return Physical address in guest where this queue resides.
34010388SAndreas.Sandberg@ARM.com     */
34110388SAndreas.Sandberg@ARM.com    Addr getAddress() const { return _address; }
34210388SAndreas.Sandberg@ARM.com
34310388SAndreas.Sandberg@ARM.com    /**
34410388SAndreas.Sandberg@ARM.com     * Get the number of descriptors available in this queue.
34510388SAndreas.Sandberg@ARM.com     *
34610388SAndreas.Sandberg@ARM.com     * @return Size of queue in descriptors.
34710388SAndreas.Sandberg@ARM.com     */
34810388SAndreas.Sandberg@ARM.com     uint16_t getSize() const { return _size; }
34910388SAndreas.Sandberg@ARM.com
35010388SAndreas.Sandberg@ARM.com    /**
35110388SAndreas.Sandberg@ARM.com     * Get a pointer to a specific descriptor in the queue.
35210388SAndreas.Sandberg@ARM.com     *
35310388SAndreas.Sandberg@ARM.com     * @note This interfaces is normally only used by VirtDescriptor
35410388SAndreas.Sandberg@ARM.com     * to follow descriptor chains. Device models typically don't need
35510388SAndreas.Sandberg@ARM.com     * to use it.
35610388SAndreas.Sandberg@ARM.com     *
35710388SAndreas.Sandberg@ARM.com     * @return Pointer to a VirtDescriptor.
35810388SAndreas.Sandberg@ARM.com     */
35910388SAndreas.Sandberg@ARM.com    VirtDescriptor *getDescriptor(VirtDescriptor::Index index) {
36010388SAndreas.Sandberg@ARM.com        return &descriptors[index];
36110388SAndreas.Sandberg@ARM.com    }
36210388SAndreas.Sandberg@ARM.com    /** @} */
36310388SAndreas.Sandberg@ARM.com
36410388SAndreas.Sandberg@ARM.com    /** @{
36510388SAndreas.Sandberg@ARM.com     * @name Device Model Interfaces
36610388SAndreas.Sandberg@ARM.com     */
36710388SAndreas.Sandberg@ARM.com    /**
36810388SAndreas.Sandberg@ARM.com     * Get an incoming descriptor chain from the queue.
36910388SAndreas.Sandberg@ARM.com     *
37010388SAndreas.Sandberg@ARM.com     * @return Pointer to descriptor on success, NULL if no pending
37110388SAndreas.Sandberg@ARM.com     * descriptors are available.
37210388SAndreas.Sandberg@ARM.com     */
37310388SAndreas.Sandberg@ARM.com    VirtDescriptor *consumeDescriptor();
37410388SAndreas.Sandberg@ARM.com    /**
37510388SAndreas.Sandberg@ARM.com     * Send a descriptor chain to the guest.
37610388SAndreas.Sandberg@ARM.com     *
37710388SAndreas.Sandberg@ARM.com     * This method posts a descriptor chain to the guest after a
37810388SAndreas.Sandberg@ARM.com     * device model has finished processing it. The device model
37910388SAndreas.Sandberg@ARM.com     * typically needs to call VirtIODeviceBase::kick() to deliver
38010388SAndreas.Sandberg@ARM.com     * notify tell the guest that the queue has been updated.
38110388SAndreas.Sandberg@ARM.com     *
38210388SAndreas.Sandberg@ARM.com     * @note The desc parameter must refer to the first descriptor in
38310388SAndreas.Sandberg@ARM.com     * a chain that has been retrieved using consumeDescriptor().
38410388SAndreas.Sandberg@ARM.com     *
38510388SAndreas.Sandberg@ARM.com     * @note The len parameter specified the amount of data produced
38610388SAndreas.Sandberg@ARM.com     * by the device model. It seems to be ignored by Linux and it is
38710388SAndreas.Sandberg@ARM.com     * not well defined.
38810388SAndreas.Sandberg@ARM.com     *
38910388SAndreas.Sandberg@ARM.com     * @param desc Start of descriptor chain.
39010388SAndreas.Sandberg@ARM.com     * @param len Length of the produced data.
39110388SAndreas.Sandberg@ARM.com     */
39210388SAndreas.Sandberg@ARM.com    void produceDescriptor(VirtDescriptor *desc, uint32_t len);
39310388SAndreas.Sandberg@ARM.com    /** @} */
39410388SAndreas.Sandberg@ARM.com
39510388SAndreas.Sandberg@ARM.com    /** @{
39610388SAndreas.Sandberg@ARM.com     * @name Device Model Callbacks
39710388SAndreas.Sandberg@ARM.com     */
39810388SAndreas.Sandberg@ARM.com    /**
39910388SAndreas.Sandberg@ARM.com     * Notify queue of pending events.
40010388SAndreas.Sandberg@ARM.com     *
40110388SAndreas.Sandberg@ARM.com     * This method is called by VirtIODeviceBase::onNotify() to notify
40210388SAndreas.Sandberg@ARM.com     * the device model of pending data in a virtqueue. The default
40310388SAndreas.Sandberg@ARM.com     * implementation of this method iterates over the available
40410388SAndreas.Sandberg@ARM.com     * descriptor chains and calls onNotifyDescriptor() for every new
40510388SAndreas.Sandberg@ARM.com     * incoming chain.
40610388SAndreas.Sandberg@ARM.com     *
40710388SAndreas.Sandberg@ARM.com     * Device models should normally overload one of onNotify() and
40810388SAndreas.Sandberg@ARM.com     * onNotifyDescriptor().
40910388SAndreas.Sandberg@ARM.com     */
41010388SAndreas.Sandberg@ARM.com    virtual void onNotify();
41110388SAndreas.Sandberg@ARM.com    /**
41210388SAndreas.Sandberg@ARM.com     * Notify queue of pending incoming descriptor.
41310388SAndreas.Sandberg@ARM.com     *
41410388SAndreas.Sandberg@ARM.com     * This method is called by the default implementation of
41510388SAndreas.Sandberg@ARM.com     * onNotify() to notify the device model of pending data in a
41610388SAndreas.Sandberg@ARM.com     * descriptor chain.
41710388SAndreas.Sandberg@ARM.com     *
41810388SAndreas.Sandberg@ARM.com     * Device models should normally overload one of onNotify() and
41910388SAndreas.Sandberg@ARM.com     * onNotifyDescriptor().
42010388SAndreas.Sandberg@ARM.com     */
42110388SAndreas.Sandberg@ARM.com    virtual void onNotifyDescriptor(VirtDescriptor *desc) {};
42210388SAndreas.Sandberg@ARM.com    /** @} */
42310388SAndreas.Sandberg@ARM.com
42410388SAndreas.Sandberg@ARM.com    /** @{
42510388SAndreas.Sandberg@ARM.com     * @name Debug interfaces
42610388SAndreas.Sandberg@ARM.com     */
42710388SAndreas.Sandberg@ARM.com    /** Dump the contents of a queue */
42810388SAndreas.Sandberg@ARM.com    void dump() const;
42910388SAndreas.Sandberg@ARM.com    /** @} */
43010388SAndreas.Sandberg@ARM.com
43110388SAndreas.Sandberg@ARM.com    /** @{ */
43210388SAndreas.Sandberg@ARM.com    /**
43310388SAndreas.Sandberg@ARM.com     * Page size used by VirtIO.\ It's hard-coded to 4096 bytes in
43410388SAndreas.Sandberg@ARM.com     * the spec for historical reasons.
43510388SAndreas.Sandberg@ARM.com     */
43612114Ssascha.bischoff@arm.com    static const Addr ALIGN_BITS = 12;
43712114Ssascha.bischoff@arm.com    static const Addr ALIGN_SIZE = 1 << ALIGN_BITS;
43810388SAndreas.Sandberg@ARM.com    /** @} */
43910388SAndreas.Sandberg@ARM.com
44010388SAndreas.Sandberg@ARM.com  protected:
44110388SAndreas.Sandberg@ARM.com    /**
44210388SAndreas.Sandberg@ARM.com     * Instantiate a new virtqueue.
44310388SAndreas.Sandberg@ARM.com     *
44410388SAndreas.Sandberg@ARM.com     * Instantiate a virtqueue with a fixed size. The size is
44510388SAndreas.Sandberg@ARM.com     * specified in descriptors which are defined as 4096 bytes each.
44610388SAndreas.Sandberg@ARM.com     *
44710388SAndreas.Sandberg@ARM.com     * @param proxy Proxy to the guest physical memory.
44810388SAndreas.Sandberg@ARM.com     * @param size Size in descriptors/pages.
44910388SAndreas.Sandberg@ARM.com     */
45010388SAndreas.Sandberg@ARM.com    VirtQueue(PortProxy &proxy, uint16_t size);
45110388SAndreas.Sandberg@ARM.com
45210388SAndreas.Sandberg@ARM.com  private:
45310388SAndreas.Sandberg@ARM.com    VirtQueue();
45410388SAndreas.Sandberg@ARM.com
45510388SAndreas.Sandberg@ARM.com    /** Queue size in terms of number of descriptors */
45610388SAndreas.Sandberg@ARM.com    const uint16_t _size;
45710388SAndreas.Sandberg@ARM.com    /** Base address of the queue */
45810388SAndreas.Sandberg@ARM.com    Addr _address;
45910388SAndreas.Sandberg@ARM.com    /** Guest physical memory proxy */
46010388SAndreas.Sandberg@ARM.com    PortProxy &memProxy;
46110388SAndreas.Sandberg@ARM.com
46210388SAndreas.Sandberg@ARM.com  private:
46310388SAndreas.Sandberg@ARM.com    /**
46410388SAndreas.Sandberg@ARM.com     * VirtIO ring buffer wrapper.
46510388SAndreas.Sandberg@ARM.com     *
46610388SAndreas.Sandberg@ARM.com     * This class wraps a VirtIO ring buffer. The template parameter T
46710388SAndreas.Sandberg@ARM.com     * is used to select the data type for the items in the ring (used
46810388SAndreas.Sandberg@ARM.com     * or available descriptors).
46910388SAndreas.Sandberg@ARM.com     */
47010388SAndreas.Sandberg@ARM.com    template<typename T>
47110388SAndreas.Sandberg@ARM.com    class VirtRing
47210388SAndreas.Sandberg@ARM.com    {
47310388SAndreas.Sandberg@ARM.com      public:
47410388SAndreas.Sandberg@ARM.com        typedef uint16_t Flags;
47510388SAndreas.Sandberg@ARM.com        typedef uint16_t Index;
47610388SAndreas.Sandberg@ARM.com
47710388SAndreas.Sandberg@ARM.com        struct Header {
47810388SAndreas.Sandberg@ARM.com            Flags flags;
47910388SAndreas.Sandberg@ARM.com            Index index;
48010388SAndreas.Sandberg@ARM.com        } M5_ATTR_PACKED;
48110388SAndreas.Sandberg@ARM.com
48210388SAndreas.Sandberg@ARM.com        VirtRing<T>(PortProxy &proxy, uint16_t size)
48310559Sandreas.hansson@arm.com        : header{0, 0}, ring(size), _proxy(proxy), _base(0) {}
48410388SAndreas.Sandberg@ARM.com
48510388SAndreas.Sandberg@ARM.com        /**
48610388SAndreas.Sandberg@ARM.com         * Set the base address of the VirtIO ring buffer.
48710388SAndreas.Sandberg@ARM.com         *
48810388SAndreas.Sandberg@ARM.com         * @param addr New host physical address
48910388SAndreas.Sandberg@ARM.com         */
49010388SAndreas.Sandberg@ARM.com        void setAddress(Addr addr) { _base = addr; }
49110388SAndreas.Sandberg@ARM.com
49210388SAndreas.Sandberg@ARM.com        /** Update the ring buffer header with data from the guest. */
49310388SAndreas.Sandberg@ARM.com        void readHeader() {
49410388SAndreas.Sandberg@ARM.com            assert(_base != 0);
49514010Sgabeblack@google.com            _proxy.readBlob(_base, &header, sizeof(header));
49610388SAndreas.Sandberg@ARM.com            header.flags = vtoh_legacy(header.flags);
49710388SAndreas.Sandberg@ARM.com            header.index = vtoh_legacy(header.index);
49810388SAndreas.Sandberg@ARM.com        }
49910388SAndreas.Sandberg@ARM.com
50010388SAndreas.Sandberg@ARM.com        void writeHeader() {
50110388SAndreas.Sandberg@ARM.com            Header out;
50210388SAndreas.Sandberg@ARM.com            assert(_base != 0);
50310388SAndreas.Sandberg@ARM.com            out.flags = htov_legacy(header.flags);
50410388SAndreas.Sandberg@ARM.com            out.index = htov_legacy(header.index);
50514010Sgabeblack@google.com            _proxy.writeBlob(_base, &out, sizeof(out));
50610388SAndreas.Sandberg@ARM.com        }
50710388SAndreas.Sandberg@ARM.com
50810388SAndreas.Sandberg@ARM.com        void read() {
50910388SAndreas.Sandberg@ARM.com            readHeader();
51010388SAndreas.Sandberg@ARM.com
51110388SAndreas.Sandberg@ARM.com            /* Read and byte-swap the elements in the ring */
51210388SAndreas.Sandberg@ARM.com            T temp[ring.size()];
51310388SAndreas.Sandberg@ARM.com            _proxy.readBlob(_base + sizeof(header),
51414010Sgabeblack@google.com                            temp, sizeof(T) * ring.size());
51510388SAndreas.Sandberg@ARM.com            for (int i = 0; i < ring.size(); ++i)
51610388SAndreas.Sandberg@ARM.com                ring[i] = vtoh_legacy(temp[i]);
51710388SAndreas.Sandberg@ARM.com        }
51810388SAndreas.Sandberg@ARM.com
51910388SAndreas.Sandberg@ARM.com        void write() {
52010388SAndreas.Sandberg@ARM.com            assert(_base != 0);
52110388SAndreas.Sandberg@ARM.com            /* Create a byte-swapped copy of the ring and write it to
52210388SAndreas.Sandberg@ARM.com             * guest memory. */
52310388SAndreas.Sandberg@ARM.com            T temp[ring.size()];
52410388SAndreas.Sandberg@ARM.com            for (int i = 0; i < ring.size(); ++i)
52510388SAndreas.Sandberg@ARM.com                temp[i] = htov_legacy(ring[i]);
52610388SAndreas.Sandberg@ARM.com            _proxy.writeBlob(_base + sizeof(header),
52714010Sgabeblack@google.com                             temp, sizeof(T) * ring.size());
52810388SAndreas.Sandberg@ARM.com            writeHeader();
52910388SAndreas.Sandberg@ARM.com        }
53010388SAndreas.Sandberg@ARM.com
53110388SAndreas.Sandberg@ARM.com        /** Ring buffer header in host byte order */
53210388SAndreas.Sandberg@ARM.com        Header header;
53310388SAndreas.Sandberg@ARM.com        /** Elements in ring in host byte order */
53410388SAndreas.Sandberg@ARM.com        std::vector<T> ring;
53510388SAndreas.Sandberg@ARM.com
53610388SAndreas.Sandberg@ARM.com      private:
53710388SAndreas.Sandberg@ARM.com        // Remove default constructor
53810388SAndreas.Sandberg@ARM.com        VirtRing<T>();
53910388SAndreas.Sandberg@ARM.com
54010388SAndreas.Sandberg@ARM.com        /** Guest physical memory proxy */
54110388SAndreas.Sandberg@ARM.com        PortProxy &_proxy;
54210388SAndreas.Sandberg@ARM.com        /** Guest physical base address of the ring buffer */
54310388SAndreas.Sandberg@ARM.com        Addr _base;
54410388SAndreas.Sandberg@ARM.com    };
54510388SAndreas.Sandberg@ARM.com
54610388SAndreas.Sandberg@ARM.com    /** Ring of available (incoming) descriptors */
54710388SAndreas.Sandberg@ARM.com    VirtRing<VirtDescriptor::Index> avail;
54810388SAndreas.Sandberg@ARM.com    /** Ring of used (outgoing) descriptors */
54910388SAndreas.Sandberg@ARM.com    VirtRing<struct vring_used_elem> used;
55010388SAndreas.Sandberg@ARM.com
55110388SAndreas.Sandberg@ARM.com    /** Offset of last consumed descriptor in the VirtQueue::avail
55210388SAndreas.Sandberg@ARM.com     * ring */
55310388SAndreas.Sandberg@ARM.com    uint16_t _last_avail;
55410388SAndreas.Sandberg@ARM.com
55510388SAndreas.Sandberg@ARM.com    /** Vector of pre-created descriptors indexed by their index into
55610388SAndreas.Sandberg@ARM.com     * the queue. */
55710388SAndreas.Sandberg@ARM.com    std::vector<VirtDescriptor> descriptors;
55810388SAndreas.Sandberg@ARM.com};
55910388SAndreas.Sandberg@ARM.com
56010388SAndreas.Sandberg@ARM.com/**
56110388SAndreas.Sandberg@ARM.com * Base class for all VirtIO-based devices.
56210388SAndreas.Sandberg@ARM.com *
56310388SAndreas.Sandberg@ARM.com * This class implements the functionality of the VirtIO 0.9.5
56410388SAndreas.Sandberg@ARM.com * specification. This version of VirtIO is also known as "legacy" in
56510388SAndreas.Sandberg@ARM.com * the VirtIO 1.0 specification from OASIS.
56610388SAndreas.Sandberg@ARM.com *
56710388SAndreas.Sandberg@ARM.com * @see https://github.com/rustyrussell/virtio-spec
56810388SAndreas.Sandberg@ARM.com * @see http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.html
56910388SAndreas.Sandberg@ARM.com */
57010388SAndreas.Sandberg@ARM.comclass VirtIODeviceBase : public SimObject
57110388SAndreas.Sandberg@ARM.com{
57210388SAndreas.Sandberg@ARM.com  public:
57310388SAndreas.Sandberg@ARM.com    typedef uint16_t QueueID;
57410388SAndreas.Sandberg@ARM.com    typedef uint32_t FeatureBits;
57510388SAndreas.Sandberg@ARM.com    /** This is a VirtQueue address as exposed through the low-level
57610388SAndreas.Sandberg@ARM.com     * interface.\ The address needs to be multiplied by the page size
57710388SAndreas.Sandberg@ARM.com     * (seems to be hardcoded to 4096 in the spec) to get the real
57810388SAndreas.Sandberg@ARM.com     * physical address.
57910388SAndreas.Sandberg@ARM.com     */
58010388SAndreas.Sandberg@ARM.com    typedef uint16_t VirtAddress;
58110388SAndreas.Sandberg@ARM.com    /** Device Type (sometimes known as subsystem ID) */
58210388SAndreas.Sandberg@ARM.com    typedef uint16_t DeviceId;
58310388SAndreas.Sandberg@ARM.com
58410388SAndreas.Sandberg@ARM.com    BitUnion8(DeviceStatus)
58510388SAndreas.Sandberg@ARM.com        Bitfield<7> failed;
58610388SAndreas.Sandberg@ARM.com        Bitfield<2> driver_ok;
58710388SAndreas.Sandberg@ARM.com        Bitfield<1> driver;
58810388SAndreas.Sandberg@ARM.com        Bitfield<0> acknowledge;
58910388SAndreas.Sandberg@ARM.com    EndBitUnion(DeviceStatus)
59010388SAndreas.Sandberg@ARM.com
59110388SAndreas.Sandberg@ARM.com    typedef VirtIODeviceBaseParams Params;
59210388SAndreas.Sandberg@ARM.com    VirtIODeviceBase(Params *params, DeviceId id, size_t config_size,
59310388SAndreas.Sandberg@ARM.com                     FeatureBits features);
59410388SAndreas.Sandberg@ARM.com    virtual ~VirtIODeviceBase();
59510388SAndreas.Sandberg@ARM.com
59610388SAndreas.Sandberg@ARM.com  public:
59710388SAndreas.Sandberg@ARM.com    /** @{
59810388SAndreas.Sandberg@ARM.com     * @name SimObject Interfaces
59910388SAndreas.Sandberg@ARM.com     */
60011168Sandreas.hansson@arm.com    void serialize(CheckpointOut &cp) const override;
60111168Sandreas.hansson@arm.com    void unserialize(CheckpointIn &cp) override;
60210388SAndreas.Sandberg@ARM.com    /** @} */
60310388SAndreas.Sandberg@ARM.com
60410388SAndreas.Sandberg@ARM.com
60510388SAndreas.Sandberg@ARM.com  protected:
60610388SAndreas.Sandberg@ARM.com    /** @{
60710388SAndreas.Sandberg@ARM.com     * @name Device Model Interfaces
60810388SAndreas.Sandberg@ARM.com     */
60910388SAndreas.Sandberg@ARM.com
61010388SAndreas.Sandberg@ARM.com    /**
61110388SAndreas.Sandberg@ARM.com     * Inform the guest of available buffers.
61210388SAndreas.Sandberg@ARM.com     *
61310388SAndreas.Sandberg@ARM.com     * When a device model has finished processing incoming buffers
61410388SAndreas.Sandberg@ARM.com     * (after onNotify has been called), it typically needs to inform
61510388SAndreas.Sandberg@ARM.com     * the guest that there are new pending outgoing buffers. The
61610388SAndreas.Sandberg@ARM.com     * method used to inform the guest is transport dependent, but is
61710388SAndreas.Sandberg@ARM.com     * typically through an interrupt. Device models call this method
61810388SAndreas.Sandberg@ARM.com     * to tell the transport interface to notify the guest.
61910388SAndreas.Sandberg@ARM.com     */
62010388SAndreas.Sandberg@ARM.com    void kick() {
62110388SAndreas.Sandberg@ARM.com        assert(transKick);
62210388SAndreas.Sandberg@ARM.com        transKick->process();
62310388SAndreas.Sandberg@ARM.com    };
62410388SAndreas.Sandberg@ARM.com
62510388SAndreas.Sandberg@ARM.com    /**
62610388SAndreas.Sandberg@ARM.com     * Register a new VirtQueue with the device model.
62710388SAndreas.Sandberg@ARM.com     *
62810388SAndreas.Sandberg@ARM.com     * Devices typically register at least one VirtQueue to use for
62910388SAndreas.Sandberg@ARM.com     * communication with the guest. This <i>must</i> be done from the
63010388SAndreas.Sandberg@ARM.com     * constructor since the number of queues are assumed to be
63110388SAndreas.Sandberg@ARM.com     * constant throughout the lifetime of the device.
63210388SAndreas.Sandberg@ARM.com     *
63310388SAndreas.Sandberg@ARM.com     * @warning This method may only be called from the device model
63410388SAndreas.Sandberg@ARM.com     * constructor.
63510388SAndreas.Sandberg@ARM.com     */
63610388SAndreas.Sandberg@ARM.com    void registerQueue(VirtQueue &queue);
63710388SAndreas.Sandberg@ARM.com
63810388SAndreas.Sandberg@ARM.com
63910388SAndreas.Sandberg@ARM.com    /**
64010388SAndreas.Sandberg@ARM.com     * Feature set accepted by the guest.
64110388SAndreas.Sandberg@ARM.com     *
64210388SAndreas.Sandberg@ARM.com     * When the guest starts the driver for the device, it starts by
64310388SAndreas.Sandberg@ARM.com     * negotiating features. The device first offers a set of features
64410388SAndreas.Sandberg@ARM.com     * (see deviceFeatures), the driver then notifies the device of
64510388SAndreas.Sandberg@ARM.com     * which features it accepted. The base class will automatically
64610388SAndreas.Sandberg@ARM.com     * accept any feature set that is a subset of the features offered
64710388SAndreas.Sandberg@ARM.com     * by the device.
64810388SAndreas.Sandberg@ARM.com     */
64910388SAndreas.Sandberg@ARM.com    FeatureBits guestFeatures;
65010388SAndreas.Sandberg@ARM.com    /** @} */
65110388SAndreas.Sandberg@ARM.com
65210388SAndreas.Sandberg@ARM.com  public:
65310388SAndreas.Sandberg@ARM.com    /** @{
65410388SAndreas.Sandberg@ARM.com     * @name Optional VirtIO Interfaces
65510388SAndreas.Sandberg@ARM.com     */
65610388SAndreas.Sandberg@ARM.com    /**
65710388SAndreas.Sandberg@ARM.com     * Read from the configuration space of a device.
65810388SAndreas.Sandberg@ARM.com     *
65910388SAndreas.Sandberg@ARM.com     * This method is called by the transport interface to read data
66010388SAndreas.Sandberg@ARM.com     * from a device model's configuration space. The device model
66110388SAndreas.Sandberg@ARM.com     * should use the cfgOffset parameter as the offset into its
66210388SAndreas.Sandberg@ARM.com     * configuration space.
66310388SAndreas.Sandberg@ARM.com     *
66410388SAndreas.Sandberg@ARM.com     * @warning The address in the packet should not be used to
66510388SAndreas.Sandberg@ARM.com     * determine the offset into a device's configuration space.
66610388SAndreas.Sandberg@ARM.com     *
66710388SAndreas.Sandberg@ARM.com     * @param pkt Read request packet.
66810388SAndreas.Sandberg@ARM.com     * @param cfgOffset Offset into the device's configuration space.
66910388SAndreas.Sandberg@ARM.com     */
67010388SAndreas.Sandberg@ARM.com    virtual void readConfig(PacketPtr pkt, Addr cfgOffset);
67110388SAndreas.Sandberg@ARM.com    /**
67210388SAndreas.Sandberg@ARM.com     * Write to the configuration space of a device.
67310388SAndreas.Sandberg@ARM.com     *
67410388SAndreas.Sandberg@ARM.com     * This method is called by the transport interface to write data
67510388SAndreas.Sandberg@ARM.com     * into a device model's configuration space. The device model
67610388SAndreas.Sandberg@ARM.com     * should use the cfgOffset parameter as the offset into its
67710388SAndreas.Sandberg@ARM.com     * configuration space.
67810388SAndreas.Sandberg@ARM.com     *
67910388SAndreas.Sandberg@ARM.com     * @warning The address in the packet should not be used to
68010388SAndreas.Sandberg@ARM.com     * determine the offset into a device's configuration space.
68110388SAndreas.Sandberg@ARM.com     *
68210388SAndreas.Sandberg@ARM.com     * @param pkt Write request packet.
68310388SAndreas.Sandberg@ARM.com     * @param cfgOffset Offset into the device's configuration space.
68410388SAndreas.Sandberg@ARM.com     */
68510388SAndreas.Sandberg@ARM.com    virtual void writeConfig(PacketPtr pkt, Addr cfgOffset);
68610388SAndreas.Sandberg@ARM.com
68710388SAndreas.Sandberg@ARM.com    /**
68810388SAndreas.Sandberg@ARM.com     * Driver-request device reset.
68910388SAndreas.Sandberg@ARM.com     *
69010388SAndreas.Sandberg@ARM.com     * The device driver may reset a device by writing zero to the
69110388SAndreas.Sandberg@ARM.com     * device status register (using setDeviceStatus()), which causes
69210388SAndreas.Sandberg@ARM.com     * this method to be called. Device models overriding this method
69310388SAndreas.Sandberg@ARM.com     * <i>must</i> ensure that the reset method of the base class is
69410388SAndreas.Sandberg@ARM.com     * called when the device is reset.
69510388SAndreas.Sandberg@ARM.com     *
69610388SAndreas.Sandberg@ARM.com     * @note Always call the reset method of the base class from
69710388SAndreas.Sandberg@ARM.com     * device-specific reset methods.
69810388SAndreas.Sandberg@ARM.com     */
69910388SAndreas.Sandberg@ARM.com    virtual void reset();
70010388SAndreas.Sandberg@ARM.com    /** @} */
70110388SAndreas.Sandberg@ARM.com
70210388SAndreas.Sandberg@ARM.com  protected:
70310388SAndreas.Sandberg@ARM.com    /** @{
70410388SAndreas.Sandberg@ARM.com     * @name Device Model Helpers
70510388SAndreas.Sandberg@ARM.com     */
70610388SAndreas.Sandberg@ARM.com
70710388SAndreas.Sandberg@ARM.com    /**
70810388SAndreas.Sandberg@ARM.com     * Read configuration data from a device structure.
70910388SAndreas.Sandberg@ARM.com     *
71010388SAndreas.Sandberg@ARM.com     * @param pkt Read request packet.
71110388SAndreas.Sandberg@ARM.com     * @param cfgOffset Offset into the device's configuration space.
71210388SAndreas.Sandberg@ARM.com     * @param cfg Device configuration
71310388SAndreas.Sandberg@ARM.com     */
71410388SAndreas.Sandberg@ARM.com    void readConfigBlob(PacketPtr pkt, Addr cfgOffset, const uint8_t *cfg);
71510388SAndreas.Sandberg@ARM.com
71610388SAndreas.Sandberg@ARM.com    /**
71710388SAndreas.Sandberg@ARM.com     * Write configuration data to a device structure.
71810388SAndreas.Sandberg@ARM.com     *
71910388SAndreas.Sandberg@ARM.com     * @param pkt Write request packet.
72010388SAndreas.Sandberg@ARM.com     * @param cfgOffset Offset into the device's configuration space.
72110388SAndreas.Sandberg@ARM.com     * @param cfg Device configuration
72210388SAndreas.Sandberg@ARM.com     */
72310388SAndreas.Sandberg@ARM.com    void writeConfigBlob(PacketPtr pkt, Addr cfgOffset, uint8_t *cfg);
72410388SAndreas.Sandberg@ARM.com
72510388SAndreas.Sandberg@ARM.com    /** @} */
72610388SAndreas.Sandberg@ARM.com
72710388SAndreas.Sandberg@ARM.com  public:
72810388SAndreas.Sandberg@ARM.com    /** @{
72910388SAndreas.Sandberg@ARM.com     * @name VirtIO Transport Interfaces
73010388SAndreas.Sandberg@ARM.com     */
73110388SAndreas.Sandberg@ARM.com     /**
73210388SAndreas.Sandberg@ARM.com      * Register a callback to kick the guest through the transport
73310388SAndreas.Sandberg@ARM.com      * interface.
73410388SAndreas.Sandberg@ARM.com      *
73510388SAndreas.Sandberg@ARM.com      * @param c Callback into transport interface.
73610388SAndreas.Sandberg@ARM.com      */
73710388SAndreas.Sandberg@ARM.com    void registerKickCallback(Callback *c) {
73810388SAndreas.Sandberg@ARM.com        assert(!transKick);
73910388SAndreas.Sandberg@ARM.com        transKick = c;
74010388SAndreas.Sandberg@ARM.com    }
74110388SAndreas.Sandberg@ARM.com
74210388SAndreas.Sandberg@ARM.com
74310388SAndreas.Sandberg@ARM.com    /**
74410388SAndreas.Sandberg@ARM.com     * Driver is requesting service.
74510388SAndreas.Sandberg@ARM.com     *
74610388SAndreas.Sandberg@ARM.com     * This method is called by the underlying hardware interface
74710388SAndreas.Sandberg@ARM.com     * (e.g., PciVirtIO or MmmioVirtIO) to notify a device of pending
74810388SAndreas.Sandberg@ARM.com     * incoming descriptors.
74910388SAndreas.Sandberg@ARM.com     *
75010388SAndreas.Sandberg@ARM.com     * @param index ID of the queue with pending actions.
75110388SAndreas.Sandberg@ARM.com     */
75210388SAndreas.Sandberg@ARM.com    void onNotify(QueueID index);
75310388SAndreas.Sandberg@ARM.com
75410388SAndreas.Sandberg@ARM.com
75510388SAndreas.Sandberg@ARM.com    /**
75610388SAndreas.Sandberg@ARM.com     * Change currently active queue.
75710388SAndreas.Sandberg@ARM.com     *
75810388SAndreas.Sandberg@ARM.com     * The transport interface works on a queue at a time. The
75910388SAndreas.Sandberg@ARM.com     * currently active queue is decided by the value of the queue
76010388SAndreas.Sandberg@ARM.com     * select field in a device.
76110388SAndreas.Sandberg@ARM.com     *
76210388SAndreas.Sandberg@ARM.com     * @param idx ID of the queue to select.
76310388SAndreas.Sandberg@ARM.com     */
76410388SAndreas.Sandberg@ARM.com    void setQueueSelect(QueueID idx) { _queueSelect = idx; }
76510388SAndreas.Sandberg@ARM.com    /**
76610388SAndreas.Sandberg@ARM.com     * Get the currently active queue.
76710388SAndreas.Sandberg@ARM.com     *
76810388SAndreas.Sandberg@ARM.com     * The transport interface works on a queue at a time. The
76910388SAndreas.Sandberg@ARM.com     * currently active queue is decided by the value of the queue
77010388SAndreas.Sandberg@ARM.com     * select field in a device.
77110388SAndreas.Sandberg@ARM.com     *
77210388SAndreas.Sandberg@ARM.com     * @return The ID of the currently active queue.
77310388SAndreas.Sandberg@ARM.com     */
77410388SAndreas.Sandberg@ARM.com    QueueID getQueueSelect() const { return _queueSelect; }
77510388SAndreas.Sandberg@ARM.com
77610388SAndreas.Sandberg@ARM.com    /**
77710388SAndreas.Sandberg@ARM.com     * Change the host physical address of the currently active queue.
77810388SAndreas.Sandberg@ARM.com     *
77910388SAndreas.Sandberg@ARM.com     * @note The new address is specified in multiples of the page
78010388SAndreas.Sandberg@ARM.com     * size (fixed to 4096 bytes in the standard). For example, if the
78110388SAndreas.Sandberg@ARM.com     * address 10 is selected, the actual host physical address will
78210388SAndreas.Sandberg@ARM.com     * be 40960.
78310388SAndreas.Sandberg@ARM.com     *
78410388SAndreas.Sandberg@ARM.com     * @see setQueueSelect
78510388SAndreas.Sandberg@ARM.com     * @see getQueueSelect
78610388SAndreas.Sandberg@ARM.com     *
78710388SAndreas.Sandberg@ARM.com     * @param address New address of the currently active queue (in
78810388SAndreas.Sandberg@ARM.com     * pages).
78910388SAndreas.Sandberg@ARM.com     */
79010388SAndreas.Sandberg@ARM.com    void setQueueAddress(uint32_t address);
79110388SAndreas.Sandberg@ARM.com    /**
79210388SAndreas.Sandberg@ARM.com     * Get the host physical address of the currently active queue.
79310388SAndreas.Sandberg@ARM.com     *
79410388SAndreas.Sandberg@ARM.com     * @note The new address is specified in multiples of the page
79510388SAndreas.Sandberg@ARM.com     * size (fixed to 4096 bytes in the standard). For example, if the
79610388SAndreas.Sandberg@ARM.com     * address 10 is selected, the actual host physical address will
79710388SAndreas.Sandberg@ARM.com     * be 40960.
79810388SAndreas.Sandberg@ARM.com     *
79910388SAndreas.Sandberg@ARM.com     * @see setQueueSelect
80010388SAndreas.Sandberg@ARM.com     * @see getQueueSelect
80110388SAndreas.Sandberg@ARM.com     *
80210388SAndreas.Sandberg@ARM.com     * @return Address of the currently active queue (in pages).
80310388SAndreas.Sandberg@ARM.com     */
80410388SAndreas.Sandberg@ARM.com    uint32_t getQueueAddress() const;
80510388SAndreas.Sandberg@ARM.com
80610388SAndreas.Sandberg@ARM.com    /**
80710388SAndreas.Sandberg@ARM.com     * Get the size (descriptors) of the currently active queue.
80810388SAndreas.Sandberg@ARM.com     *
80910388SAndreas.Sandberg@ARM.com     * @return Size of the currently active queue in number of
81010388SAndreas.Sandberg@ARM.com     * descriptors.
81110388SAndreas.Sandberg@ARM.com     */
81210388SAndreas.Sandberg@ARM.com    uint16_t getQueueSize() const { return getCurrentQueue().getSize(); }
81310388SAndreas.Sandberg@ARM.com
81410388SAndreas.Sandberg@ARM.com    /**
81510388SAndreas.Sandberg@ARM.com     * Update device status and optionally reset device.
81610388SAndreas.Sandberg@ARM.com     *
81710388SAndreas.Sandberg@ARM.com     * The special device status of 0 is used to reset the device by
81810388SAndreas.Sandberg@ARM.com     * calling reset().
81910388SAndreas.Sandberg@ARM.com     *
82010388SAndreas.Sandberg@ARM.com     * @param status New device status.
82110388SAndreas.Sandberg@ARM.com     */
82210388SAndreas.Sandberg@ARM.com    void setDeviceStatus(DeviceStatus status);
82310388SAndreas.Sandberg@ARM.com
82410388SAndreas.Sandberg@ARM.com    /**
82510388SAndreas.Sandberg@ARM.com     * Retrieve the device status.
82610388SAndreas.Sandberg@ARM.com     *
82710388SAndreas.Sandberg@ARM.com     * @return Device status.
82810388SAndreas.Sandberg@ARM.com     */
82910388SAndreas.Sandberg@ARM.com    DeviceStatus getDeviceStatus() const { return _deviceStatus; }
83010388SAndreas.Sandberg@ARM.com
83110388SAndreas.Sandberg@ARM.com    /**
83210388SAndreas.Sandberg@ARM.com     * Set feature bits accepted by the guest driver.
83310388SAndreas.Sandberg@ARM.com     *
83410388SAndreas.Sandberg@ARM.com     * This enables a subset of the features offered by the device
83510388SAndreas.Sandberg@ARM.com     * model through the getGuestFeatures() interface.
83610388SAndreas.Sandberg@ARM.com     */
83710388SAndreas.Sandberg@ARM.com    void setGuestFeatures(FeatureBits features);
83810388SAndreas.Sandberg@ARM.com
83910388SAndreas.Sandberg@ARM.com    /**
84010388SAndreas.Sandberg@ARM.com     * Get features accepted by the guest driver.
84110388SAndreas.Sandberg@ARM.com     *
84210388SAndreas.Sandberg@ARM.com     * @return Currently active features.
84310388SAndreas.Sandberg@ARM.com     */
84410388SAndreas.Sandberg@ARM.com    FeatureBits getGuestFeatures() const { return guestFeatures; }
84510388SAndreas.Sandberg@ARM.com
84610388SAndreas.Sandberg@ARM.com    /** Device ID (sometimes known as subsystem ID) */
84710388SAndreas.Sandberg@ARM.com    const DeviceId deviceId;
84810388SAndreas.Sandberg@ARM.com
84910388SAndreas.Sandberg@ARM.com    /** Size of the device's configuration space */
85010388SAndreas.Sandberg@ARM.com    const size_t configSize;
85110388SAndreas.Sandberg@ARM.com
85210388SAndreas.Sandberg@ARM.com    /** Feature set offered by the device */
85310388SAndreas.Sandberg@ARM.com    const FeatureBits deviceFeatures;
85410388SAndreas.Sandberg@ARM.com    /** @} */
85510388SAndreas.Sandberg@ARM.com
85610388SAndreas.Sandberg@ARM.com  private:
85710388SAndreas.Sandberg@ARM.com    /** Convenience method to get the currently selected queue */
85810388SAndreas.Sandberg@ARM.com    const VirtQueue &getCurrentQueue() const;
85910388SAndreas.Sandberg@ARM.com    /** Convenience method to get the currently selected queue */
86010388SAndreas.Sandberg@ARM.com    VirtQueue &getCurrentQueue();
86110388SAndreas.Sandberg@ARM.com
86210388SAndreas.Sandberg@ARM.com    /**
86310388SAndreas.Sandberg@ARM.com     * Status of the device
86410388SAndreas.Sandberg@ARM.com     *
86510388SAndreas.Sandberg@ARM.com     * @see getDeviceStatus
86610388SAndreas.Sandberg@ARM.com     * @see setDeviceStatus
86710388SAndreas.Sandberg@ARM.com     */
86810388SAndreas.Sandberg@ARM.com    DeviceStatus _deviceStatus;
86910388SAndreas.Sandberg@ARM.com
87010388SAndreas.Sandberg@ARM.com    /** Queue select register (set by guest) */
87110388SAndreas.Sandberg@ARM.com    QueueID _queueSelect;
87210388SAndreas.Sandberg@ARM.com
87310388SAndreas.Sandberg@ARM.com    /** List of virtual queues supported by this device */
87410388SAndreas.Sandberg@ARM.com    std::vector<VirtQueue *> _queues;
87510388SAndreas.Sandberg@ARM.com
87610388SAndreas.Sandberg@ARM.com    /** Callbacks to kick the guest through the transport layer  */
87710388SAndreas.Sandberg@ARM.com    Callback *transKick;
87810388SAndreas.Sandberg@ARM.com};
87910388SAndreas.Sandberg@ARM.com
88011931Sandreas.sandberg@arm.comclass VirtIODummyDevice : public VirtIODeviceBase
88111931Sandreas.sandberg@arm.com{
88211931Sandreas.sandberg@arm.com  public:
88311931Sandreas.sandberg@arm.com    VirtIODummyDevice(VirtIODummyDeviceParams *params);
88411931Sandreas.sandberg@arm.com
88511931Sandreas.sandberg@arm.com  protected:
88611931Sandreas.sandberg@arm.com    /** VirtIO device ID */
88711931Sandreas.sandberg@arm.com    static const DeviceId ID_INVALID = 0x00;
88811931Sandreas.sandberg@arm.com};
88911931Sandreas.sandberg@arm.com
89010388SAndreas.Sandberg@ARM.com#endif // __DEV_VIRTIO_BASE_HH__
891