2021-01-02 19:53:05 +02:00
|
|
|
/*
|
2021-04-15 19:39:48 +10:00
|
|
|
* Copyright (c) 2021, the SerenityOS developers.
|
2021-01-02 19:53:05 +02:00
|
|
|
*
|
2021-04-22 01:24:48 -07:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2021-01-02 19:53:05 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <Kernel/SpinLock.h>
|
|
|
|
#include <Kernel/VM/MemoryManager.h>
|
2021-04-15 19:39:48 +10:00
|
|
|
#include <Kernel/VM/ScatterGatherList.h>
|
2021-01-02 19:53:05 +02:00
|
|
|
|
|
|
|
namespace Kernel {
|
|
|
|
|
2021-04-15 19:39:48 +10:00
|
|
|
#define VIRTQ_DESC_F_NEXT 1
|
|
|
|
#define VIRTQ_DESC_F_INDIRECT 4
|
|
|
|
|
2021-04-24 11:05:51 +10:00
|
|
|
#define VIRTQ_AVAIL_F_NO_INTERRUPT 1
|
|
|
|
#define VIRTQ_USED_F_NO_NOTIFY 1
|
|
|
|
|
2021-01-02 19:53:05 +02:00
|
|
|
enum class BufferType {
|
|
|
|
DeviceReadable = 0,
|
2021-04-15 19:17:49 +10:00
|
|
|
DeviceWritable = 2
|
2021-01-02 19:53:05 +02:00
|
|
|
};
|
|
|
|
|
2021-04-15 19:22:02 +10:00
|
|
|
class VirtIODevice;
|
2021-04-24 11:05:51 +10:00
|
|
|
class VirtIOQueueChain;
|
2021-04-15 19:22:02 +10:00
|
|
|
|
2021-01-02 19:53:05 +02:00
|
|
|
class VirtIOQueue {
|
|
|
|
public:
|
|
|
|
VirtIOQueue(u16 queue_size, u16 notify_offset);
|
|
|
|
~VirtIOQueue();
|
|
|
|
|
2021-04-15 19:39:48 +10:00
|
|
|
bool is_null() const { return !m_queue_region; }
|
2021-01-02 19:53:05 +02:00
|
|
|
u16 notify_offset() const { return m_notify_offset; }
|
|
|
|
|
|
|
|
void enable_interrupts();
|
|
|
|
void disable_interrupts();
|
|
|
|
|
2021-04-15 19:12:06 +10:00
|
|
|
PhysicalAddress descriptor_area() const { return to_physical(m_descriptors.ptr()); }
|
|
|
|
PhysicalAddress driver_area() const { return to_physical(m_driver.ptr()); }
|
|
|
|
PhysicalAddress device_area() const { return to_physical(m_device.ptr()); }
|
2021-01-02 19:53:05 +02:00
|
|
|
|
|
|
|
bool new_data_available() const;
|
2021-04-24 11:05:51 +10:00
|
|
|
bool has_free_slots() const;
|
|
|
|
Optional<u16> take_free_slot();
|
|
|
|
VirtIOQueueChain pop_used_buffer_chain(size_t& used);
|
2021-04-15 19:39:48 +10:00
|
|
|
void discard_used_buffers();
|
2021-01-02 19:53:05 +02:00
|
|
|
|
2021-04-24 11:05:51 +10:00
|
|
|
SpinLock<u8>& lock() { return m_lock; }
|
|
|
|
|
|
|
|
bool should_notify() const;
|
|
|
|
|
2021-01-02 19:53:05 +02:00
|
|
|
private:
|
2021-04-24 11:05:51 +10:00
|
|
|
void reclaim_buffer_chain(u16 chain_start_index, u16 chain_end_index, size_t length_of_chain);
|
2021-04-15 19:39:48 +10:00
|
|
|
|
2021-04-15 19:12:06 +10:00
|
|
|
PhysicalAddress to_physical(const void* ptr) const
|
2021-01-02 19:53:05 +02:00
|
|
|
{
|
2021-04-15 19:39:48 +10:00
|
|
|
auto offset = FlatPtr(ptr) - m_queue_region->vaddr().get();
|
|
|
|
return m_queue_region->physical_page(0)->paddr().offset(offset);
|
2021-01-02 19:53:05 +02:00
|
|
|
}
|
2021-04-15 19:12:06 +10:00
|
|
|
struct [[gnu::packed]] VirtIOQueueDescriptor {
|
2021-01-02 19:53:05 +02:00
|
|
|
u64 address;
|
|
|
|
u32 length;
|
|
|
|
u16 flags;
|
|
|
|
u16 next;
|
|
|
|
};
|
|
|
|
|
2021-04-15 19:12:06 +10:00
|
|
|
struct [[gnu::packed]] VirtIOQueueDriver {
|
2021-01-02 19:53:05 +02:00
|
|
|
u16 flags;
|
|
|
|
u16 index;
|
|
|
|
u16 rings[];
|
|
|
|
};
|
|
|
|
|
2021-04-15 19:12:06 +10:00
|
|
|
struct [[gnu::packed]] VirtIOQueueDeviceItem {
|
2021-01-02 19:53:05 +02:00
|
|
|
u32 index;
|
|
|
|
u32 length;
|
|
|
|
};
|
|
|
|
|
2021-04-15 19:12:06 +10:00
|
|
|
struct [[gnu::packed]] VirtIOQueueDevice {
|
2021-01-02 19:53:05 +02:00
|
|
|
u16 flags;
|
|
|
|
u16 index;
|
|
|
|
VirtIOQueueDeviceItem rings[];
|
|
|
|
};
|
|
|
|
|
|
|
|
const u16 m_queue_size;
|
|
|
|
const u16 m_notify_offset;
|
|
|
|
u16 m_free_buffers;
|
|
|
|
u16 m_free_head { 0 };
|
|
|
|
u16 m_used_tail { 0 };
|
2021-04-15 19:17:49 +10:00
|
|
|
u16 m_driver_index_shadow { 0 };
|
2021-01-02 19:53:05 +02:00
|
|
|
|
2021-04-15 19:12:06 +10:00
|
|
|
OwnPtr<VirtIOQueueDescriptor> m_descriptors { nullptr };
|
|
|
|
OwnPtr<VirtIOQueueDriver> m_driver { nullptr };
|
|
|
|
OwnPtr<VirtIOQueueDevice> m_device { nullptr };
|
2021-04-15 19:39:48 +10:00
|
|
|
OwnPtr<Region> m_queue_region;
|
2021-01-02 19:53:05 +02:00
|
|
|
SpinLock<u8> m_lock;
|
2021-04-24 11:05:51 +10:00
|
|
|
|
|
|
|
friend class VirtIOQueueChain;
|
|
|
|
};
|
|
|
|
|
|
|
|
class VirtIOQueueChain {
|
|
|
|
public:
|
|
|
|
VirtIOQueueChain(VirtIOQueue& queue)
|
|
|
|
: m_queue(queue)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
VirtIOQueueChain(VirtIOQueue& queue, u16 start_index, u16 end_index, size_t chain_length)
|
|
|
|
: m_queue(queue)
|
|
|
|
, m_start_of_chain_index(start_index)
|
|
|
|
, m_end_of_chain_index(end_index)
|
|
|
|
, m_chain_length(chain_length)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
VirtIOQueueChain(VirtIOQueueChain&& other)
|
|
|
|
: m_queue(other.m_queue)
|
|
|
|
, m_start_of_chain_index(other.m_start_of_chain_index)
|
|
|
|
, m_end_of_chain_index(other.m_end_of_chain_index)
|
|
|
|
, m_chain_length(other.m_chain_length)
|
|
|
|
, m_chain_has_writable_pages(other.m_chain_has_writable_pages)
|
|
|
|
{
|
|
|
|
other.m_start_of_chain_index = {};
|
|
|
|
other.m_end_of_chain_index = {};
|
|
|
|
other.m_chain_length = 0;
|
|
|
|
other.m_chain_has_writable_pages = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
VirtIOQueueChain& operator=(VirtIOQueueChain&& other)
|
|
|
|
{
|
|
|
|
VERIFY(&m_queue == &other.m_queue);
|
|
|
|
ensure_chain_is_empty();
|
|
|
|
m_start_of_chain_index = other.m_start_of_chain_index;
|
|
|
|
m_end_of_chain_index = other.m_end_of_chain_index;
|
|
|
|
m_chain_length = other.m_chain_length;
|
|
|
|
m_chain_has_writable_pages = other.m_chain_has_writable_pages;
|
|
|
|
other.m_start_of_chain_index = {};
|
|
|
|
other.m_end_of_chain_index = {};
|
|
|
|
other.m_chain_length = 0;
|
|
|
|
other.m_chain_has_writable_pages = false;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
~VirtIOQueueChain()
|
|
|
|
{
|
|
|
|
ensure_chain_is_empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
[[nodiscard]] VirtIOQueue& queue() const { return m_queue; }
|
|
|
|
[[nodiscard]] bool is_empty() const { return m_chain_length == 0; }
|
|
|
|
[[nodiscard]] size_t length() const { return m_chain_length; }
|
|
|
|
bool add_buffer_to_chain(PhysicalAddress buffer_start, size_t buffer_length, BufferType buffer_type);
|
|
|
|
void submit_to_queue();
|
|
|
|
void release_buffer_slots_to_queue();
|
|
|
|
|
|
|
|
void for_each(Function<void(PhysicalAddress, size_t)> callback)
|
|
|
|
{
|
|
|
|
VERIFY(m_queue.lock().is_locked());
|
|
|
|
if (!m_start_of_chain_index.has_value())
|
|
|
|
return;
|
|
|
|
auto index = m_start_of_chain_index.value();
|
|
|
|
for (size_t i = 0; i < m_chain_length; ++i) {
|
|
|
|
auto addr = m_queue.m_descriptors[index].address;
|
|
|
|
auto length = m_queue.m_descriptors[index].length;
|
|
|
|
callback(PhysicalAddress(addr), length);
|
|
|
|
index = m_queue.m_descriptors[index].next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void ensure_chain_is_empty() const
|
|
|
|
{
|
|
|
|
VERIFY(!m_start_of_chain_index.has_value());
|
|
|
|
VERIFY(!m_end_of_chain_index.has_value());
|
|
|
|
VERIFY(m_chain_length == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
VirtIOQueue& m_queue;
|
|
|
|
Optional<u16> m_start_of_chain_index {};
|
|
|
|
Optional<u16> m_end_of_chain_index {};
|
|
|
|
size_t m_chain_length {};
|
|
|
|
bool m_chain_has_writable_pages { false };
|
2021-01-02 19:53:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|