mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:37:45 +00:00
Kernel: Add base support for VirtIO devices
Based on pull #3236 by tomuta, this adds helper methods for generic device initialization, and partily-broken virtqueue helper methods Co-authored-by: Tom <tomut@yahoo.com> Co-authored-by: Sahan <sahan.h.fernando@gmail.com>
This commit is contained in:
parent
40a1f89d67
commit
62303d46d1
9 changed files with 832 additions and 2 deletions
249
Kernel/VirtIO/VirtIO.h
Normal file
249
Kernel/VirtIO/VirtIO.h
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Copyright (c) 2020, the SerenityOS developers.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <Kernel/IO.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/PCI/Access.h>
|
||||
#include <Kernel/PCI/Device.h>
|
||||
#include <Kernel/VM/MemoryManager.h>
|
||||
#include <Kernel/VirtIO/VirtIOQueue.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
#define VIRTIO_PCI_VENDOR_ID 0x1AF4
|
||||
|
||||
#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_VERSION_1 ((u64)1 << 32)
|
||||
#define VIRTIO_F_RING_PACKED ((u64)1 << 34)
|
||||
|
||||
#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
|
||||
|
||||
class VirtIO {
|
||||
public:
|
||||
static void detect();
|
||||
};
|
||||
|
||||
class VirtIODevice : public PCI::Device {
|
||||
public:
|
||||
VirtIODevice(PCI::Address, const char*);
|
||||
virtual ~VirtIODevice() override;
|
||||
|
||||
protected:
|
||||
const char* const m_class_name;
|
||||
|
||||
struct MappedMMIO {
|
||||
OwnPtr<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;
|
||||
}
|
||||
};
|
||||
|
||||
struct Configuration {
|
||||
u8 cfg_type;
|
||||
u8 bar;
|
||||
u32 offset;
|
||||
u32 length;
|
||||
};
|
||||
|
||||
const Configuration* get_config(u8 cfg_type, u32 index = 0) const
|
||||
{
|
||||
for (const auto& cfg : m_configs) {
|
||||
if (cfg.cfg_type != cfg_type)
|
||||
continue;
|
||||
if (index > 0) {
|
||||
index--;
|
||||
continue;
|
||||
}
|
||||
return &cfg;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
const Configuration* get_common_config(u32 index = 0) const;
|
||||
const Configuration* get_device_config(u32 index = 0) const;
|
||||
|
||||
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 clear_status_bit(u8);
|
||||
void set_status_bit(u8);
|
||||
u64 get_device_features();
|
||||
bool finish_init();
|
||||
|
||||
VirtIOQueue* get_queue(u16 queue_index)
|
||||
{
|
||||
return &m_queues[queue_index];
|
||||
}
|
||||
void set_requested_queue_count(u16);
|
||||
|
||||
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_buffer_and_notify(u16 queue_index, const u8* buffer, u32 len, BufferType);
|
||||
|
||||
virtual void handle_irq(const RegisterState&) override;
|
||||
virtual void handle_device_config_change() = 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_queues();
|
||||
bool setup_queue(u16 queue_index);
|
||||
void notify_queue(u16 queue_index);
|
||||
|
||||
void reset_device();
|
||||
|
||||
u8 isr_status();
|
||||
|
||||
NonnullOwnPtrVector<VirtIOQueue> m_queues;
|
||||
Vector<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 };
|
||||
u32 m_notify_multiplier { 0 };
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue