Program Listing for File virtio.hpp

Return to documentation for file (dev/virtio/virtio.hpp)

/* SPDX-License-Identifier: BSD-2-Clause */
#pragma once
#include <dev/devkit/service.hpp>
#include <dev/pci/device.hpp>
#include <hal/int.hpp>
#include <vm/heap.hpp>

#ifdef __amd64__
#include <amd64/idt.hpp>
#endif

namespace Gaia::Dev {

struct VirtQueueDesc {
  /* Address (guest-physical). */
  uint64_t addr;
  /* Length. */
  uint32_t len;
  /* The flags as indicated above. */
  uint16_t flags;
  /* We chain unused descriptors via this, too */
  uint16_t next;
};

struct VirtQueueAvail {
  uint16_t flags;
  uint16_t idx;
  uint16_t ring[];
};

struct VirtQueueUsedElem {
  /* Index of start of used descriptor chain. */
  uint32_t id;

  /* Total length of the descriptor chain which was written to. */
  uint32_t len;
};

struct VirtQueueUsed {
  uint32_t flags;
  uint32_t idx;
  VirtQueueUsedElem ring[];
  /* Only if VIRTIO_F_EVENT_IDX: le16 avail_event; */
};

struct VirtQueue {
  uint16_t num;
  uint16_t num_free, num_max;
  uint16_t notify_off;

  uint16_t last_free_desc;

  VirtQueueDesc *desc;
  VirtQueueAvail *avail;
  VirtQueueUsed *used;

  uint16_t allocate_desc();
  void free_desc(uint16_t desc);
};

class VirtioDevice : public Service {
public:
  VirtioDevice();
  static Vm::UniquePtr<Service> create();
  static void int_handler(Hal::InterruptFrame *frame, void *arg);

  void start(Service *provider) override;

  bool match_properties(Properties &props) override;

  const char *class_name() override { return "VirtioDevice"; }
  const char *name() override { return name_str.data(); }

  void enable();

  void setup_queue(VirtQueue &queue, uint16_t index);

  void notify_queue(VirtQueue &queue);

  void *device_cfg;

protected:
  PciDevice *device;
  frg::string<Gaia::Vm::HeapAllocator> name_str = "";
  uint16_t device_id;
  Hal::InterruptEntry int_entry;

  struct [[gnu::packed]] PciCommonCfg {
    /* About the whole device. */
    uint32_t device_feature_select; /* read-write */
    uint32_t device_feature;        /* read-only for driver */
    uint32_t driver_feature_select; /* read-write */
    uint32_t driver_feature;        /* read-write */
    uint16_t msix_config;           /* read-write */
    uint16_t num_queues;            /* read-only for driver */
    uint8_t device_status;          /* read-write */
    uint8_t config_generation;      /* read-only for driver */

    /* About a specific virtqueue. */
    uint16_t queue_select;      /* read-write */
    uint16_t queue_size;        /* read-write */
    uint16_t queue_msix_vector; /* read-write */
    uint16_t queue_enable;      /* read-write */
    uint16_t queue_notify_off;  /* read-only for driver */
    uint64_t queue_desc;        /* read-write */
    uint64_t queue_avail;       /* read-write */
    uint64_t queue_used;        /* read-write */
  };

  struct [[gnu::packed]] PciCap {
    uint8_t cap_vndr;   /* Generic PCI field: PCI_CAP_ID_VNDR */
    uint8_t cap_next;   /* Generic PCI field: next ptr. */
    uint8_t cap_len;    /* Generic PCI field: capability length */
    uint8_t cfg_type;   /* Identifies the structure. */
    uint8_t bar;        /* Where to find it. */
    uint8_t padding[3]; /* Pad to full dword. */
    uint32_t offset;    /* Offset within bar. */
    uint32_t length;    /* Length of the structure, in bytes. */
  };

  enum class PciCapType {
    COMMON_CFG = 1,
    NOTIFY_CFG = 2,
    ISR_CFG = 3,
    DEVICE_CFG = 4,
    PCI_CFG = 5,
  };

  void handle_capability(PciCap cap, uint32_t cap_off);

  PciCommonCfg *common_cfg;
  uintptr_t notify_base, notify_off_multiplier;

  Vm::UniquePtr<Service> driver;
};

void virtiodevice_driver_register();

} // namespace Gaia::Dev