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