diff options
Diffstat (limited to 'Kernel/Bus/VirtIO/VirtIO.h')
-rw-r--r-- | Kernel/Bus/VirtIO/VirtIO.h | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/Kernel/Bus/VirtIO/VirtIO.h b/Kernel/Bus/VirtIO/VirtIO.h new file mode 100644 index 0000000000..b83b7358f5 --- /dev/null +++ b/Kernel/Bus/VirtIO/VirtIO.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2021, the SerenityOS developers. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <AK/NonnullOwnPtrVector.h> +#include <Kernel/Bus/PCI/Access.h> +#include <Kernel/Bus/PCI/Device.h> +#include <Kernel/Bus/VirtIO/VirtIOQueue.h> +#include <Kernel/IO.h> +#include <Kernel/Interrupts/IRQHandler.h> +#include <Kernel/Memory/MemoryManager.h> + +namespace Kernel { + +#define REG_DEVICE_FEATURES 0x0 +#define REG_GUEST_FEATURES 0x4 +#define REG_QUEUE_ADDRESS 0x8 +#define REG_QUEUE_SIZE 0xc +#define REG_QUEUE_SELECT 0xe +#define REG_QUEUE_NOTIFY 0x10 +#define REG_DEVICE_STATUS 0x12 +#define REG_ISR_STATUS 0x13 + +#define DEVICE_STATUS_ACKNOWLEDGE (1 << 0) +#define DEVICE_STATUS_DRIVER (1 << 1) +#define DEVICE_STATUS_DRIVER_OK (1 << 2) +#define DEVICE_STATUS_FEATURES_OK (1 << 3) +#define DEVICE_STATUS_DEVICE_NEEDS_RESET (1 << 6) +#define DEVICE_STATUS_FAILED (1 << 7) + +#define VIRTIO_F_INDIRECT_DESC ((u64)1 << 28) +#define VIRTIO_F_VERSION_1 ((u64)1 << 32) +#define VIRTIO_F_RING_PACKED ((u64)1 << 34) +#define VIRTIO_F_IN_ORDER ((u64)1 << 35) + +#define VIRTIO_PCI_CAP_COMMON_CFG 1 +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 +#define VIRTIO_PCI_CAP_ISR_CFG 3 +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 +#define VIRTIO_PCI_CAP_PCI_CFG 5 + +// virtio_pci_common_cfg +#define COMMON_CFG_DEVICE_FEATURE_SELECT 0x0 +#define COMMON_CFG_DEVICE_FEATURE 0x4 +#define COMMON_CFG_DRIVER_FEATURE_SELECT 0x8 +#define COMMON_CFG_DRIVER_FEATURE 0xc +#define COMMON_CFG_MSIX_CONFIG 0x10 +#define COMMON_CFG_NUM_QUEUES 0x12 +#define COMMON_CFG_DEVICE_STATUS 0x14 +#define COMMON_CFG_CONFIG_GENERATION 0x15 +#define COMMON_CFG_QUEUE_SELECT 0x16 +#define COMMON_CFG_QUEUE_SIZE 0x18 +#define COMMON_CFG_QUEUE_MSIX_VECTOR 0x1a +#define COMMON_CFG_QUEUE_ENABLE 0x1c +#define COMMON_CFG_QUEUE_NOTIFY_OFF 0x1e +#define COMMON_CFG_QUEUE_DESC 0x20 +#define COMMON_CFG_QUEUE_DRIVER 0x28 +#define COMMON_CFG_QUEUE_DEVICE 0x30 + +#define QUEUE_INTERRUPT 0x1 +#define DEVICE_CONFIG_INTERRUPT 0x2 + +enum class ConfigurationType : u8 { + Common = 1, + Notify = 2, + ISR = 3, + Device = 4, + PCI = 5 +}; + +struct Configuration { + ConfigurationType cfg_type; + u8 bar; + u32 offset; + u32 length; +}; + +class VirtIO { +public: + static void detect(); +}; + +class VirtIODevice : public PCI::Device { +public: + VirtIODevice(PCI::Address, String); + virtual ~VirtIODevice() override; + +protected: + const String m_class_name; + + struct MappedMMIO { + OwnPtr<Memory::Region> base; + size_t size { 0 }; + + template<typename T> + T read(u32 offset) const + { + if (!base) + return 0; + VERIFY(size >= sizeof(T)); + VERIFY(offset + sizeof(T) <= size); + return *(volatile T*)(base->vaddr().offset(offset).get()); + } + + template<typename T> + void write(u32 offset, T value) + { + if (!base) + return; + VERIFY(size >= sizeof(T)); + VERIFY(offset + sizeof(T) <= size); + *(volatile T*)(base->vaddr().offset(offset).get()) = value; + } + }; + + const Configuration* get_config(ConfigurationType cfg_type, u32 index = 0) const + { + for (auto& cfg : m_configs) { + if (cfg.cfg_type != cfg_type) + continue; + if (index > 0) { + index--; + continue; + } + return &cfg; + } + return nullptr; + } + + template<typename F> + void read_config_atomic(F f) + { + if (m_common_cfg) { + u8 generation_before, generation_after; + do { + generation_before = config_read8(*m_common_cfg, 0x15); + f(); + generation_after = config_read8(*m_common_cfg, 0x15); + } while (generation_before != generation_after); + } else { + f(); + } + } + + u8 config_read8(const Configuration&, u32); + u16 config_read16(const Configuration&, u32); + u32 config_read32(const Configuration&, u32); + void config_write8(const Configuration&, u32, u8); + void config_write16(const Configuration&, u32, u16); + void config_write32(const Configuration&, u32, u32); + void config_write64(const Configuration&, u32, u64); + + auto mapping_for_bar(u8) -> MappedMMIO&; + + u8 read_status_bits(); + void mask_status_bits(u8 status_mask); + void set_status_bit(u8); + u64 get_device_features(); + bool setup_queues(u16 requested_queue_count = 0); + void finish_init(); + + VirtIOQueue& get_queue(u16 queue_index) + { + VERIFY(queue_index < m_queue_count); + return m_queues[queue_index]; + } + + const VirtIOQueue& get_queue(u16 queue_index) const + { + VERIFY(queue_index < m_queue_count); + return m_queues[queue_index]; + } + + template<typename F> + bool negotiate_features(F f) + { + u64 device_features = get_device_features(); + u64 accept_features = f(device_features); + VERIFY(!(~device_features & accept_features)); + return accept_device_features(device_features, accept_features); + } + + static bool is_feature_set(u64 feature_set, u64 test_feature) + { + // features can have more than one bit + return (feature_set & test_feature) == test_feature; + } + bool is_feature_accepted(u64 feature) const + { + VERIFY(m_did_accept_features); + return is_feature_set(m_accepted_features, feature); + } + + void supply_chain_and_notify(u16 queue_index, VirtIOQueueChain& chain); + + virtual bool handle_device_config_change() = 0; + virtual void handle_queue_update(u16 queue_index) = 0; + +private: + template<typename T> + void out(u16 address, T value) + { + m_io_base.offset(address).out(value); + } + + template<typename T> + T in(u16 address) + { + return m_io_base.offset(address).in<T>(); + } + + bool accept_device_features(u64 device_features, u64 accepted_features); + + bool setup_queue(u16 queue_index); + bool activate_queue(u16 queue_index); + void notify_queue(u16 queue_index); + + void reset_device(); + + u8 isr_status(); + virtual bool handle_irq(const RegisterState&) override; + + NonnullOwnPtrVector<VirtIOQueue> m_queues; + NonnullOwnPtrVector<Configuration> m_configs; + const Configuration* m_common_cfg { nullptr }; // Cached due to high usage + const Configuration* m_notify_cfg { nullptr }; // Cached due to high usage + const Configuration* m_isr_cfg { nullptr }; // Cached due to high usage + + IOAddress m_io_base; + MappedMMIO m_mmio[6]; + u16 m_queue_count { 0 }; + bool m_use_mmio { false }; + u8 m_status { 0 }; + u64 m_accepted_features { 0 }; + bool m_did_accept_features { false }; + bool m_did_setup_queues { false }; + u32 m_notify_multiplier { 0 }; +}; + +} |