2021-01-02 20:53:05 +03:00
|
|
|
/*
|
2021-04-15 12:39:48 +03:00
|
|
|
* Copyright (c) 2021, the SerenityOS developers.
|
2021-01-02 20:53:05 +03:00
|
|
|
*
|
2021-04-22 11:24:48 +03:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2021-01-02 20:53:05 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <AK/NonnullOwnPtrVector.h>
|
2021-10-01 09:58:50 +03:00
|
|
|
#include <Kernel/Arch/x86/IO.h>
|
2021-06-25 09:46:17 +03:00
|
|
|
#include <Kernel/Bus/PCI/Access.h>
|
2021-08-21 06:58:43 +03:00
|
|
|
#include <Kernel/Bus/PCI/Device.h>
|
2021-08-27 12:24:50 +03:00
|
|
|
#include <Kernel/Bus/VirtIO/Queue.h>
|
2021-01-02 20:53:05 +03:00
|
|
|
#include <Kernel/Interrupts/IRQHandler.h>
|
2021-08-06 11:45:34 +03:00
|
|
|
#include <Kernel/Memory/MemoryManager.h>
|
2021-01-02 20:53:05 +03:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2021-04-15 12:39:48 +03:00
|
|
|
#define VIRTIO_F_INDIRECT_DESC ((u64)1 << 28)
|
2021-01-02 20:53:05 +03:00
|
|
|
#define VIRTIO_F_VERSION_1 ((u64)1 << 32)
|
|
|
|
#define VIRTIO_F_RING_PACKED ((u64)1 << 34)
|
2021-04-15 12:22:02 +03:00
|
|
|
#define VIRTIO_F_IN_ORDER ((u64)1 << 35)
|
2021-01-02 20:53:05 +03:00
|
|
|
|
|
|
|
#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
|
|
|
|
|
2021-08-27 12:18:13 +03:00
|
|
|
namespace VirtIO {
|
|
|
|
|
2021-04-15 12:12:06 +03:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
2021-08-27 12:18:13 +03:00
|
|
|
void detect();
|
2021-01-02 20:53:05 +03:00
|
|
|
|
2021-08-27 12:18:13 +03:00
|
|
|
class Device
|
2021-08-21 06:58:43 +03:00
|
|
|
: public PCI::Device
|
Kernel/PCI: Delete PCI::Device in its current form
I created this class a long time ago just to be able to quickly make a
PCI device to also represent an interrupt handler (because PCI devices
have this capability for most devices).
Then after a while I introduced the PCI::DeviceController, which is
really almost the same thing (a PCI device class that has Address member
in it), but is not tied to interrupts so it can have no interrupts, or
spawn interrupt handlers however it wants to seems fit.
However I decided it's time to say goodbye for this class for
a couple of reasons:
1. It made a whole bunch of weird patterns where you had a PCI::Device
and a PCI::DeviceController being used in the topic of implementation,
where originally, they meant to be used mutually exclusively (you
can't and really don't want to use both).
2. We can really make all the classes that inherit from PCI::Device
to inherit from IRQHandler at this point. Later on, when we have MSI
interrupts support, we can go further and untie things even more.
3. It makes it possible to simplify the VirtIO implementation to a great
extent. While this commit almost doesn't change it, future changes
can untangle some complexity in the VirtIO code.
For UHCIController, E1000NetworkAdapter, NE2000NetworkAdapter,
RTL8139NetworkAdapter, RTL8168NetworkAdapter, E1000ENetworkAdapter we
are simply making them to inherit the IRQHandler. This makes some sense,
because the first 3 devices will never support anything besides IRQs.
For the last 2, they might have MSI support, so when we start to utilize
those, we might need to untie these classes from IRQHandler and spawn
IRQHandler(s) or MSIHandler(s) as needed.
The VirtIODevice class is also a case where we currently need to use
both PCI::DeviceController and IRQHandler classes as parents, but it
could also be untied from the latter.
2021-08-21 06:55:25 +03:00
|
|
|
, public IRQHandler {
|
2021-01-02 20:53:05 +03:00
|
|
|
public:
|
2021-09-01 12:05:11 +03:00
|
|
|
virtual ~Device() override = default;
|
2021-01-02 20:53:05 +03:00
|
|
|
|
2021-09-04 08:42:31 +03:00
|
|
|
virtual void initialize();
|
|
|
|
|
2021-01-02 20:53:05 +03:00
|
|
|
protected:
|
2021-10-03 01:24:00 +03:00
|
|
|
virtual StringView class_name() const { return "VirtIO::Device"sv; }
|
2021-09-23 10:20:54 +03:00
|
|
|
explicit Device(PCI::DeviceIdentifier const&);
|
2021-01-02 20:53:05 +03:00
|
|
|
struct MappedMMIO {
|
2021-08-06 14:49:36 +03:00
|
|
|
OwnPtr<Memory::Region> base;
|
2021-01-02 20:53:05 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-04-15 12:12:06 +03:00
|
|
|
const Configuration* get_config(ConfigurationType cfg_type, u32 index = 0) const
|
2021-01-02 20:53:05 +03:00
|
|
|
{
|
2021-12-08 16:00:18 +03:00
|
|
|
for (auto const& cfg : m_configs) {
|
2021-01-02 20:53:05 +03:00
|
|
|
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 {
|
2021-04-15 12:12:06 +03:00
|
|
|
generation_before = config_read8(*m_common_cfg, 0x15);
|
2021-01-02 20:53:05 +03:00
|
|
|
f();
|
2021-04-15 12:12:06 +03:00
|
|
|
generation_after = config_read8(*m_common_cfg, 0x15);
|
2021-01-02 20:53:05 +03:00
|
|
|
} while (generation_before != generation_after);
|
|
|
|
} else {
|
|
|
|
f();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-15 12:12:06 +03:00
|
|
|
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);
|
2021-01-02 20:53:05 +03:00
|
|
|
|
|
|
|
auto mapping_for_bar(u8) -> MappedMMIO&;
|
|
|
|
|
|
|
|
u8 read_status_bits();
|
2021-05-14 21:11:26 +03:00
|
|
|
void mask_status_bits(u8 status_mask);
|
2021-01-02 20:53:05 +03:00
|
|
|
void set_status_bit(u8);
|
|
|
|
u64 get_device_features();
|
2021-04-15 12:39:48 +03:00
|
|
|
bool setup_queues(u16 requested_queue_count = 0);
|
|
|
|
void finish_init();
|
2021-01-02 20:53:05 +03:00
|
|
|
|
2021-08-27 12:18:13 +03:00
|
|
|
Queue& get_queue(u16 queue_index)
|
2021-01-02 20:53:05 +03:00
|
|
|
{
|
2021-04-15 12:12:06 +03:00
|
|
|
VERIFY(queue_index < m_queue_count);
|
|
|
|
return m_queues[queue_index];
|
2021-04-18 11:10:34 +03:00
|
|
|
}
|
|
|
|
|
2021-08-27 12:18:13 +03:00
|
|
|
const Queue& get_queue(u16 queue_index) const
|
2021-04-18 11:10:34 +03:00
|
|
|
{
|
|
|
|
VERIFY(queue_index < m_queue_count);
|
|
|
|
return m_queues[queue_index];
|
2021-01-02 20:53:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-08-27 12:18:13 +03:00
|
|
|
void supply_chain_and_notify(u16 queue_index, QueueChain& chain);
|
2021-01-02 20:53:05 +03:00
|
|
|
|
2021-04-15 12:12:06 +03:00
|
|
|
virtual bool handle_device_config_change() = 0;
|
2021-04-15 12:39:48 +03:00
|
|
|
virtual void handle_queue_update(u16 queue_index) = 0;
|
2021-01-02 20:53:05 +03:00
|
|
|
|
|
|
|
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);
|
2021-04-15 12:17:49 +03:00
|
|
|
bool activate_queue(u16 queue_index);
|
2021-01-02 20:53:05 +03:00
|
|
|
void notify_queue(u16 queue_index);
|
|
|
|
|
|
|
|
void reset_device();
|
|
|
|
|
|
|
|
u8 isr_status();
|
2021-06-05 09:00:18 +03:00
|
|
|
virtual bool handle_irq(const RegisterState&) override;
|
2021-01-02 20:53:05 +03:00
|
|
|
|
2021-08-27 12:18:13 +03:00
|
|
|
NonnullOwnPtrVector<Queue> m_queues;
|
2021-04-15 12:12:06 +03:00
|
|
|
NonnullOwnPtrVector<Configuration> m_configs;
|
2021-01-02 20:53:05 +03:00
|
|
|
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];
|
2021-09-19 18:28:27 +03:00
|
|
|
|
|
|
|
StringView const m_class_name;
|
|
|
|
|
2021-01-02 20:53:05 +03:00
|
|
|
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 };
|
2021-04-15 12:39:48 +03:00
|
|
|
bool m_did_setup_queues { false };
|
2021-01-02 20:53:05 +03:00
|
|
|
u32 m_notify_multiplier { 0 };
|
|
|
|
};
|
2021-08-27 12:18:13 +03:00
|
|
|
};
|
2021-01-02 20:53:05 +03:00
|
|
|
}
|