blob: 60818a3e89b31637ee3a00e0fe40ffdc1a1b9c22 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_VIRTUALIZATION_BIN_VMM_VIRTIO_PCI_H_
#define SRC_VIRTUALIZATION_BIN_VMM_VIRTIO_PCI_H_
#include <lib/fit/function.h>
#include <virtio/virtio.h>
#include <zircon/types.h>
#include "src/virtualization/bin/vmm/pci.h"
// Virtio PCI Bar Layout.
//
// Expose all read/write fields on BAR0 using a strongly ordered mapping.
// Map the Queue notify region to BAR1 with a BELL type that does not require
// the guest to decode any instruction fields. The queue to notify can be
// inferred based on the address accessed alone.
//
// BAR0 BAR1
// ------------ 00h ------------ 00h
// | Virtio PCI | | Queue 0 |
// | Common | | Notify |
// | Config | |------------| 04h
// |------------| 38h | Queue 1 |
// | ISR Config | | Notify |
// |------------| 3ch |------------|
// | Device- | | ... |
// | Specific | |------------| 04 * N
// | Config | | Queue N |
// | | | Notify |
// ------------ ------------
// These structures are defined in Virtio 1.0 Section 4.1.4.
static constexpr uint8_t kVirtioPciBar = 0;
static constexpr uint8_t kVirtioPciNotifyBar = 1;
static_assert(kVirtioPciBar < kPciMaxBars && kVirtioPciNotifyBar < kPciMaxBars,
"Not enough BAR registers available");
static constexpr size_t kVirtioPciNumCapabilities = 4;
// We initialize Virtio devices with a ring size so that a sensible size is set,
// even if they do not configure one themselves.
static constexpr uint16_t kDefaultVirtioQueueSize = 128;
// Queue addresses as defined in Virtio 1.0 Section 4.1.4.3.
struct VirtioQueueConfig {
union {
struct {
uint64_t desc;
uint64_t avail;
uint64_t used;
};
// Software will access these using 32 bit operations. Provide a
// convenience interface for these use cases.
uint32_t words[6] = {};
};
uint16_t size = kDefaultVirtioQueueSize;
};
struct VirtioDeviceConfig {
mutable std::mutex mutex;
// Virtio device ID.
const uint16_t device_id = 0;
// Virtio device features.
const uint32_t device_features = 0;
// Pointer to device configuration.
void* config __TA_GUARDED(mutex) = nullptr;
// Number of bytes used for this device's configuration space.
//
// This should cover only bytes used for the device-specific portions of
// the configuration header, omitting any of the (transport-specific)
// shared configuration space.
const uint64_t config_size = 0;
// Virtio queues for this device.
VirtioQueueConfig* const queue_configs __TA_GUARDED(mutex) = nullptr;
// Number of Virtio queues.
const uint16_t num_queues = 0;
// Invoked when the driver has made a change to the queue configuration.
using ConfigQueueFn = fit::function<zx_status_t(uint16_t queue, uint16_t size, zx_gpaddr_t desc,
zx_gpaddr_t avail, zx_gpaddr_t used)>;
const ConfigQueueFn config_queue;
// Invoked when the driver sends notifications on a queue to the device.
//
// TODO(abdulla): Remove this once all devices are out-of-process.
using NotifyQueueFn = fit::function<zx_status_t(uint16_t queue)>;
const NotifyQueueFn notify_queue;
// Invoked when the driver has made a change to the device configuration.
using ConfigDeviceFn = fit::function<zx_status_t(uint64_t addr, const IoValue& value)>;
const ConfigDeviceFn config_device;
// Invoked when the driver has accepted features and set the device into a
// 'Ready' state.
//
// Devices can place logic here that depends on the set of negotiated
// features with the driver.
using ReadyDeviceFn = fit::function<zx_status_t(uint32_t negotiated_features)>;
const ReadyDeviceFn ready_device;
};
// Virtio PCI transport implementation.
class VirtioPci : public PciDevice {
public:
explicit VirtioPci(VirtioDeviceConfig* device_config);
// Read a value at |bar| and |offset| from this device.
zx_status_t ReadBar(uint8_t bar, uint64_t offset, IoValue* value) const override;
// Write a value at |bar| and |offset| to this device.
zx_status_t WriteBar(uint8_t bar, uint64_t offset, const IoValue& value) override;
// ISR flag values.
enum IsrFlags : uint8_t {
// Interrupt is caused by a queue.
ISR_QUEUE = 1 << 0,
// Interrupt is caused by a device config change.
ISR_CONFIG = 1 << 1,
};
// Sets the given flags in the ISR register.
void add_isr_flags(uint8_t flags) {
std::lock_guard<std::mutex> lock(mutex_);
isr_status_ |= flags;
}
// Device features.
//
// These are feature bits that are supported by the device. They may or
// may not correspond to the set of feature flags that have been negotiated
// at runtime. For negotiated features, see |has_negotiated_features|.
bool has_device_features(uint32_t features) const {
std::lock_guard<std::mutex> lock(mutex_);
return (device_config_->device_features & features) == features;
}
// Returns true if the set of features have been negotiated to be enabled.
bool has_negotiated_features(uint32_t features) const {
std::lock_guard<std::mutex> lock(mutex_);
return (device_config_->device_features & driver_features_ & features) == features;
}
private:
bool HasPendingInterrupt() const override;
// Handle accesses to the general configuration BAR.
zx_status_t ConfigBarRead(uint64_t addr, IoValue* value) const;
zx_status_t ConfigBarWrite(uint64_t addr, const IoValue& value);
// Handle accesses to the common configuration region.
zx_status_t CommonCfgRead(uint64_t addr, IoValue* value) const;
zx_status_t CommonCfgWrite(uint64_t addr, const IoValue& value);
// Handle writes to the notify BAR.
zx_status_t NotifyBarWrite(uint64_t addr, const IoValue& value);
void SetupCaps();
uint16_t queue_sel() const;
VirtioDeviceConfig* const device_config_;
// We need one of these for every virtio_pci_cap_t structure we expose.
pci_cap_t capabilities_[kVirtioPciNumCapabilities];
// Virtio PCI capabilities.
virtio_pci_cap_t common_cfg_cap_;
virtio_pci_cap_t device_cfg_cap_;
virtio_pci_notify_cap_t notify_cfg_cap_;
virtio_pci_cap_t isr_cfg_cap_;
mutable std::mutex mutex_;
// Device feature bits.
//
// Defined in Virtio 1.0 Section 2.2.
uint32_t device_features_sel_ __TA_GUARDED(mutex_) = 0;
// Driver feature bits.
uint32_t driver_features_ __TA_GUARDED(mutex_) = 0;
uint32_t driver_features_sel_ __TA_GUARDED(mutex_) = 0;
// Device status field as defined in Virtio 1.0, Section 2.1.
uint8_t status_ __TA_GUARDED(mutex_) = 0;
// Interrupt status register.
mutable uint8_t isr_status_ __TA_GUARDED(mutex_) = 0;
// Index of the queue currently selected by the driver.
uint16_t queue_sel_ __TA_GUARDED(mutex_) = 0;
};
#endif // SRC_VIRTUALIZATION_BIN_VMM_VIRTIO_PCI_H_