| // 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 <zircon/types.h> |
| |
| #include <string_view> |
| |
| #include <virtio/virtio.h> |
| |
| #include "src/virtualization/bin/vmm/pci.h" |
| |
| // Virtio PCI Bar Layout. |
| // |
| // Expose all read/write fields on one BAR using a strongly ordered |
| // mapping. Map the Queue notify region to a second BAR 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. |
| // |
| // CONFIG BAR NOTIFY BAR |
| // ------------ 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. |
| |
| // 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, std::string_view name); |
| |
| // 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; |
| } |
| |
| // Get the BAR for the device's configuration region / notification region. |
| const PciBar& config_bar() const { return *bar(config_bar_); } |
| const PciBar& notify_bar() const { return *bar(notify_bar_); } |
| |
| private: |
| // Routes callbacks from the configuration PCI BAR. |
| class ConfigBarCallback : public PciBar::Callback { |
| public: |
| explicit ConfigBarCallback(VirtioPci* parent) : parent_(parent) {} |
| zx_status_t Read(uint64_t addr, IoValue* value) override; |
| zx_status_t Write(uint64_t addr, const IoValue& value) override; |
| |
| private: |
| VirtioPci* parent_; |
| }; |
| |
| // Routes callbacks from the notification PCI BAR. |
| class NotifyBarCallback : public PciBar::Callback { |
| public: |
| explicit NotifyBarCallback(VirtioPci* parent) : parent_(parent) {} |
| zx_status_t Read(uint64_t addr, IoValue* value) override; |
| zx_status_t Write(uint64_t addr, const IoValue& value) override; |
| |
| private: |
| VirtioPci* parent_; |
| }; |
| |
| 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 offset, const IoValue& value); |
| |
| zx_status_t SetupCaps(); |
| |
| uint16_t queue_sel() const; |
| |
| VirtioDeviceConfig* const device_config_; |
| |
| mutable std::mutex mutex_; |
| |
| // Callback objects for BAR accesses. |
| ConfigBarCallback config_bar_callback_{this}; |
| NotifyBarCallback notify_bar_callback_{this}; |
| |
| // Notify bar and config bar indices. |
| // |
| // These values are constant after class initialisation. |
| size_t config_bar_; |
| size_t notify_bar_; |
| |
| // 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_ |