The Pedigree Project 0.1
Virtqueue.cc
1/* Copyright (c) 2026, Pedigree Developers. SPDX-License-Identifier: ISC */
2#include "Virtqueue.h"
3#include "pedigree/kernel/LockGuard.h"
4#include "pedigree/kernel/Log.h"
5#include "pedigree/kernel/panic.h"
6#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
7#include "pedigree/kernel/processor/VirtualAddressSpace.h"
8#include "pedigree/kernel/utilities/utility.h"
9
10#include "VirtioPci.h"
11
12using namespace Virtio;
13
14Queue::Queue()
15 : m_Descriptors("virtio descriptors"),
16 m_Available("virtio available ring"),
17 m_Used("virtio used ring"),
18 m_Transport(nullptr),
19 m_Cookies{},
20 m_Next{},
21 m_ChainLength{},
22 m_FreeHead(Invalid),
23 m_FreeCount(0),
24 m_Depth(0),
25 m_AvailableIndex(0),
26 m_UsedIndex(0),
27 m_Active{},
28 m_Online(false),
29 m_DmaArmed(false),
30 m_Attached(false) {}
31
32Queue::~Queue() {
33 if (m_Attached || m_DmaArmed) {
34 panic("virtio: queue freed before transport reset");
35 }
36}
37
38bool Queue::initialise(uint16_t depth, PciTransport* transport) {
39 if (m_Online || m_DmaArmed || !transport || depth < 2 || depth > MaxDepth ||
40 (depth & (depth - 1)) || PhysicalMemoryManager::getPageSize() < sizeof(Descriptor) * depth) {
41 return false;
42 }
43 auto& memory = PhysicalMemoryManager::instance();
45 if (!memory.allocateRegion(m_Descriptors, 1, PhysicalMemoryManager::continuous, flags) ||
46 !memory.allocateRegion(m_Available, 1, PhysicalMemoryManager::continuous, flags) ||
47 !memory.allocateRegion(m_Used, 1, PhysicalMemoryManager::continuous, flags)) {
48 return false;
49 }
50 const size_t page = PhysicalMemoryManager::getPageSize();
51 ByteSet(m_Descriptors.virtualAddress(), 0, page);
52 ByteSet(m_Available.virtualAddress(), 0, page);
53 ByteSet(m_Used.virtualAddress(), 0, page);
54 m_Depth = depth;
55 m_FreeCount = depth;
56 m_FreeHead = 0;
57 for (uint16_t i = 0; i < depth; ++i)
58 m_Next[i] = i + 1 == depth ? Invalid : i + 1;
59 m_Transport = transport;
60 m_Online = true;
61 return true;
62}
63
64bool Queue::submit(const Buffer* buffers, size_t count, void* cookie) {
65 if (!buffers || !count || count > MaxDepth) {
66 return false;
67 }
68 LockGuard<Mutex> guard(m_Lock);
69 if (!m_Online || count > m_FreeCount) {
70 return false;
71 }
72 for (size_t i = 0; i < count; ++i) {
73 if (!buffers[i].address || !buffers[i].length ||
74 buffers[i].address + buffers[i].length < buffers[i].address) {
75 return false;
76 }
77 }
78 auto* descriptors = static_cast<Descriptor*>(m_Descriptors.virtualAddress());
79 const uint16_t head = m_FreeHead;
80 uint16_t current = head;
81 for (size_t i = 0; i < count; ++i) {
82 const uint16_t next = m_Next[current];
83 descriptors[current].address = buffers[i].address;
84 descriptors[current].length = buffers[i].length;
85 descriptors[current].flags = (i + 1 < count ? 1U : 0U) | (buffers[i].deviceWrites ? 2U : 0U);
86 descriptors[current].next = i + 1 < count ? next : 0;
87 current = next;
88 }
89 m_FreeHead = current;
90 m_FreeCount -= count;
91 m_ChainLength[head] = count;
92 m_Cookies[head] = cookie;
93 m_Active[head] = true;
94 auto* available = static_cast<volatile uint16_t*>(m_Available.virtualAddress());
95 available[2 + (m_AvailableIndex % m_Depth)] = head;
96 FENCE();
97 available[1] = ++m_AvailableIndex;
98 FENCE();
99 return true;
100}
101
102bool Queue::pop(Completion& completion) {
103 LockGuard<Mutex> guard(m_Lock);
104 if (!m_Online) {
105 return false;
106 }
107 auto* usedIndex = static_cast<volatile uint16_t*>(m_Used.virtualAddress());
108 const uint16_t available = usedIndex[1];
109 if (available == m_UsedIndex) {
110 return false;
111 }
112 if (static_cast<uint16_t>(available - m_UsedIndex) > m_Depth) {
113 ERROR("virtio: invalid used-ring index");
114 m_Online = false;
115 return false;
116 }
117 FENCE();
118 auto* entries = static_cast<volatile uint32_t*>(m_Used.virtualAddress());
119 const size_t slot = m_UsedIndex % m_Depth;
120 const uint32_t id = entries[1 + slot * 2];
121 const uint32_t length = entries[2 + slot * 2];
122 if (id >= m_Depth || !m_Active[id] || !m_ChainLength[id]) {
123 ERROR("virtio: invalid used-ring descriptor");
124 m_Online = false;
125 return false;
126 }
127 completion.cookie = m_Cookies[id];
128 completion.length = length;
129 m_Cookies[id] = nullptr;
130 m_Active[id] = false;
131 uint16_t current = id;
132 for (uint16_t i = 0; i < m_ChainLength[id]; ++i) {
133 const uint16_t next = m_Next[current];
134 m_Next[current] = m_FreeHead;
135 m_FreeHead = current;
136 current = next;
137 }
138 m_FreeCount += m_ChainLength[id];
139 m_ChainLength[id] = 0;
140 ++m_UsedIndex;
141 return true;
142}
143
144void Queue::stop() {
145 LockGuard<Mutex> guard(m_Lock);
146 m_Online = false;
147}
void * virtualAddress() const
static PhysicalMemoryManager & instance()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117