mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-11 01:06:01 +03:00
189 lines
5.2 KiB
C++
189 lines
5.2 KiB
C++
/*
|
|
* Copyright (c) 2021, the SerenityOS developers.
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <Kernel/Locking/Spinlock.h>
|
|
#include <Kernel/Memory/MemoryManager.h>
|
|
#include <Kernel/Memory/ScatterGatherList.h>
|
|
|
|
namespace Kernel::VirtIO {
|
|
|
|
class Device;
|
|
class QueueChain;
|
|
|
|
#define VIRTQ_DESC_F_NEXT 1
|
|
#define VIRTQ_DESC_F_INDIRECT 4
|
|
|
|
#define VIRTQ_AVAIL_F_NO_INTERRUPT 1
|
|
#define VIRTQ_USED_F_NO_NOTIFY 1
|
|
|
|
enum class BufferType {
|
|
DeviceReadable = 0,
|
|
DeviceWritable = 2
|
|
};
|
|
|
|
class Queue {
|
|
public:
|
|
static ErrorOr<NonnullOwnPtr<Queue>> try_create(u16 queue_size, u16 notify_offset);
|
|
|
|
~Queue();
|
|
|
|
u16 notify_offset() const { return m_notify_offset; }
|
|
|
|
void enable_interrupts();
|
|
void disable_interrupts();
|
|
|
|
PhysicalAddress descriptor_area() const { return to_physical(m_descriptors); }
|
|
PhysicalAddress driver_area() const { return to_physical(m_driver); }
|
|
PhysicalAddress device_area() const { return to_physical(m_device); }
|
|
|
|
bool new_data_available() const;
|
|
bool has_free_slots() const;
|
|
Optional<u16> take_free_slot();
|
|
QueueChain pop_used_buffer_chain(size_t& used);
|
|
void discard_used_buffers();
|
|
|
|
Spinlock& lock() { return m_lock; }
|
|
|
|
bool should_notify() const;
|
|
|
|
private:
|
|
Queue(NonnullOwnPtr<Memory::Region> queue_region, u16 queue_size, u16 notify_offset);
|
|
|
|
void reclaim_buffer_chain(u16 chain_start_index, u16 chain_end_index, size_t length_of_chain);
|
|
|
|
PhysicalAddress to_physical(void const* ptr) const
|
|
{
|
|
auto offset = FlatPtr(ptr) - m_queue_region->vaddr().get();
|
|
return m_queue_region->physical_page(0)->paddr().offset(offset);
|
|
}
|
|
struct [[gnu::packed]] QueueDescriptor {
|
|
u64 address;
|
|
u32 length;
|
|
u16 flags;
|
|
u16 next;
|
|
};
|
|
|
|
struct [[gnu::packed]] QueueDriver {
|
|
u16 flags;
|
|
u16 index;
|
|
u16 rings[];
|
|
};
|
|
|
|
struct [[gnu::packed]] QueueDeviceItem {
|
|
u32 index;
|
|
u32 length;
|
|
};
|
|
|
|
struct [[gnu::packed]] QueueDevice {
|
|
u16 flags;
|
|
u16 index;
|
|
QueueDeviceItem 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 };
|
|
u16 m_driver_index_shadow { 0 };
|
|
|
|
QueueDescriptor* m_descriptors { nullptr };
|
|
QueueDriver* m_driver { nullptr };
|
|
QueueDevice* m_device { nullptr };
|
|
NonnullOwnPtr<Memory::Region> m_queue_region;
|
|
Spinlock m_lock;
|
|
|
|
friend class QueueChain;
|
|
};
|
|
|
|
class QueueChain {
|
|
public:
|
|
QueueChain(Queue& queue)
|
|
: m_queue(queue)
|
|
{
|
|
}
|
|
|
|
QueueChain(Queue& 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)
|
|
{
|
|
}
|
|
|
|
QueueChain(QueueChain&& other)
|
|
: m_queue(other.m_queue)
|
|
, m_start_of_chain_index(move(other.m_start_of_chain_index))
|
|
, m_end_of_chain_index(move(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;
|
|
}
|
|
|
|
QueueChain& operator=(QueueChain&& 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;
|
|
}
|
|
|
|
~QueueChain()
|
|
{
|
|
ensure_chain_is_empty();
|
|
}
|
|
|
|
[[nodiscard]] Queue& 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);
|
|
}
|
|
|
|
Queue& 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 };
|
|
};
|
|
|
|
}
|