summaryrefslogtreecommitdiff
path: root/Kernel/Bus/VirtIO/VirtIO.h
diff options
context:
space:
mode:
Diffstat (limited to 'Kernel/Bus/VirtIO/VirtIO.h')
-rw-r--r--Kernel/Bus/VirtIO/VirtIO.h244
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 };
+};
+
+}