| // 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 GARNET_LIB_MACHINA_VIRTIO_PCI_H_ |
| #define GARNET_LIB_MACHINA_VIRTIO_PCI_H_ |
| |
| #include <lib/fit/function.h> |
| #include <virtio/virtio.h> |
| #include <zircon/types.h> |
| |
| #include "garnet/lib/machina/pci.h" |
| |
| namespace machina { |
| |
| // 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; |
| |
| zx_status_t Interrupt() 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: |
| // 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; |
| }; |
| |
| } // namespace machina |
| |
| #endif // GARNET_LIB_MACHINA_VIRTIO_PCI_H_ |