3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/Device.h"
5#include "pedigree/kernel/machine/Pci.h"
6#include "pedigree/kernel/panic.h"
7#include "pedigree/kernel/processor/IoBase.h"
8#include "pedigree/kernel/time/Time.h"
12using namespace Virtio;
15constexpr uint8_t VendorCapability = 9;
16constexpr uint8_t CommonConfig = 1;
17constexpr uint8_t NotifyConfig = 2;
18constexpr uint8_t IsrConfig = 3;
19constexpr uint8_t DeviceConfig = 4;
20constexpr uint64_t Version1 = 1ULL << 32;
21constexpr uint8_t Acknowledge = 1;
22constexpr uint8_t Driver = 2;
23constexpr uint8_t DriverOk = 4;
24constexpr uint8_t FeaturesOk = 8;
25constexpr uint8_t Failed = 128;
28PciTransport::PciTransport(
Device* device)
37 m_NotifyMultiplier(0),
45PciTransport::~PciTransport() {
46 if (m_Common.io && !reset()) {
47 panic(
"virtio: cannot stop DMA during transport teardown");
50 !PciBus::instance().updateCommand(m_Device, 0x407U, (m_Original.command & 3U) | 0x400U)) {
51 panic(
"virtio: cannot leave PCI function quiesced");
55bool PciTransport::locateCapabilities() {
56 auto& pci = PciBus::instance();
57 uint16_t pciStatus = 0;
59 if (!pci.readConfig16(m_Device, 6, pciStatus) || !(pciStatus & 0x10U) ||
60 !pci.readConfig8(m_Device, 0x34, cap)) {
64 for (
unsigned count = 0; cap && count < 48; ++count) {
65 if (cap < 0x40 || (cap & 3U) || (visited & (1ULL << (cap / 4)))) {
68 visited |= 1ULL << (cap / 4);
69 uint8_t
id = 0, next = 0;
70 if (!pci.readConfig8(m_Device, cap,
id) || !pci.readConfig8(m_Device, cap + 1, next)) {
73 if (
id == VendorCapability) {
74 uint8_t length = 0, type = 0;
75 if (!pci.readConfig8(m_Device, cap + 2, length) || length < 16 || cap + length > 256 ||
76 !pci.readConfig8(m_Device, cap + 3, type)) {
79 if (type >= CommonConfig && type <= DeviceConfig) {
81 uint32_t offset = 0, bytes = 0;
82 if (!pci.readConfig8(m_Device, cap + 4, bar) || bar >= 6 ||
83 !pci.readConfig32(m_Device, cap + 8, offset) ||
84 !pci.readConfig32(m_Device, cap + 12, bytes) || !bytes) {
90 for (
auto* candidate : m_Device->addresses()) {
91 if (candidate->m_Name == name && !candidate->m_IsIoSpace &&
92 uint64_t(offset) + bytes <= candidate->m_Size) {
101 if (!address->m_Io || address->m_Io->size() < uint64_t(offset) + bytes) {
104 Capability* target =
nullptr;
108 if (bytes < 56 || (offset & 3U)) {
114 if (length < 20 || bytes < 2 || (offset & 1U) ||
115 !pci.readConfig32(m_Device, cap + 16, m_NotifyMultiplier)) {
123 target = &m_DeviceConfig;
129 *target = {address->m_Io, offset, bytes};
134 return !cap && m_Common.io && m_Notify.io && m_Isr.io;
137bool PciTransport::status(uint8_t bits) {
141 const uint8_t before = m_Common.io->
read8(m_Common.offset + 20);
142 if (before == 0xff || (before & Failed)) {
145 const uint8_t desired = before | bits;
146 m_Common.io->
write8(desired, m_Common.offset + 20);
147 return (m_Common.io->
read8(m_Common.offset + 20) & bits) == bits;
150bool PciTransport::initialise() {
151 if (m_Initialised || !m_Device || m_Device->
getPciVendorId() != 0x1af4 ||
155 auto& pci = PciBus::instance();
156 if (!pci.inspectFunction(m_Device, m_Original)) {
157 ERROR(
"virtio-pci: invalid PCI function state");
161 if (!pci.updateCommand(m_Device, 4U, 2U | 0x400U) ||
162 !pci.disableMessageInterrupts(m_Device, m_Original) ||
163 !pci.resourcesUnchanged(m_Device, m_Original)) {
164 ERROR(
"virtio-pci: could not prepare PCI function");
167 if (!locateCapabilities()) {
168 ERROR(
"virtio-pci: invalid or missing modern PCI capabilities");
171 if (!reset() || !status(Acknowledge) || !status(Driver)) {
172 ERROR(
"virtio-pci: device did not acknowledge reset or driver status");
175 m_NumQueues = m_Common.io->
read16(m_Common.offset + 18);
177 ERROR(
"virtio-pci: device exposes no queues");
180 m_Initialised =
true;
184bool PciTransport::negotiate(uint64_t supportedFeatures, uint64_t requiredFeatures) {
185 if (!m_Initialised || m_Negotiated || (requiredFeatures & ~supportedFeatures)) {
188 auto* io = m_Common.io;
189 const uint32_t base = m_Common.offset;
190 io->write32(0, base);
191 const uint64_t offeredLow = io->read32(base + 4);
192 io->write32(1, base);
193 const uint64_t offered = offeredLow | (uint64_t(io->read32(base + 4)) << 32);
194 if (!(offered & Version1) || (requiredFeatures & ~offered)) {
195 ERROR(
"virtio-pci: required modern features unavailable");
198 m_Features = offered & (supportedFeatures | Version1);
199 io->write32(0, base + 8);
200 io->write32(m_Features, base + 12);
201 io->write32(1, base + 8);
202 io->write32(m_Features >> 32, base + 12);
203 io->write16(0xffff, base + 16);
204 if (!status(FeaturesOk) || !(io->read8(base + 20) & FeaturesOk)) {
205 ERROR(
"virtio-pci: feature negotiation rejected");
212bool PciTransport::setupQueue(uint16_t index,
Queue& queue) {
213 if (!m_Negotiated || m_DmaActive || index >= m_NumQueues || index >= MaxQueues ||
217 auto* io = m_Common.io;
218 const uint32_t base = m_Common.offset;
219 io->write16(index, base + 22);
220 uint16_t maximum = io->read16(base + 24);
221 if (maximum < 2 || io->read16(base + 28)) {
222 ERROR(
"virtio-pci: queue " <<
Dec << index <<
" unavailable or already enabled" <<
Hex);
225 if (maximum > Queue::MaxDepth) {
226 maximum = Queue::MaxDepth;
229 while (depth <= maximum / 2)
231 const uint16_t notify = io->read16(base + 30);
232 const uint64_t notifyOffset = uint64_t(notify) * m_NotifyMultiplier;
233 if (notifyOffset + 2 > m_Notify.length || (notifyOffset & 1U) || !queue.initialise(depth,
this)) {
234 ERROR(
"virtio-pci: queue " <<
Dec << index <<
" notification or DMA setup failed" <<
Hex);
237 io->write16(depth, base + 24);
238 io->write16(0xffff, base + 26);
239 io->write32(queue.descriptorAddress(), base + 32);
240 io->write32(queue.descriptorAddress() >> 32, base + 36);
241 io->write32(queue.availableAddress(), base + 40);
242 io->write32(queue.availableAddress() >> 32, base + 44);
243 io->write32(queue.usedAddress(), base + 48);
244 io->write32(queue.usedAddress() >> 32, base + 52);
246 io->write16(1, base + 28);
247 if (io->read16(base + 28) != 1) {
248 ERROR(
"virtio-pci: queue " <<
Dec << index <<
" enable rejected" <<
Hex);
251 m_NotifyOffsets[index] = notifyOffset;
252 m_Queues[index] = &queue;
253 queue.m_Attached =
true;
257bool PciTransport::ready() {
258 if (!m_Negotiated || m_DmaActive) {
261 bool haveQueue =
false;
262 for (
auto* queue : m_Queues)
263 haveQueue |= queue != nullptr;
267 auto& pci = PciBus::instance();
268 if (!pci.updateCommand(m_Device, 0, 6U | 0x400U)) {
271 for (
auto* queue : m_Queues)
273 queue->m_DmaArmed =
true;
276 if (!status(DriverOk) || !pci.updateCommand(m_Device, 0x400U, 6U)) {
278 panic(
"virtio: device could not be reset after failed activation");
285bool PciTransport::reset() {
289 auto& pci = PciBus::instance();
290 const bool busStopped = pci.updateCommand(m_Device, 4U, 0x400U);
294 m_Common.io->
write8(0, m_Common.offset + 20);
295 const auto deadline = Time::getTicks() + Time::Multiplier::Second;
296 while (m_Common.io->
read8(m_Common.offset + 20) != 0 && Time::getTicks() < deadline)
297 Time::delay(Time::Multiplier::Millisecond);
298 if (m_Common.io->
read8(m_Common.offset + 20) != 0 || !busStopped) {
302 m_Negotiated =
false;
303 m_Initialised =
false;
305 for (
auto*& queue : m_Queues) {
308 queue->m_DmaArmed =
false;
309 queue->m_Attached =
false;
316uint8_t PciTransport::readIsr() {
317 return m_Initialised && m_Isr.io ? m_Isr.io->
read8(m_Isr.offset) : 0;
320void PciTransport::notify(uint16_t index) {
321 if (!m_Negotiated || index >= MaxQueues || !m_Queues[index]) {
325 m_Notify.io->
write16(index, m_Notify.offset + m_NotifyOffsets[index]);
328bool PciTransport::writeDeviceConfig8(uint16_t offset, uint8_t value) {
329 if (!m_Initialised || !m_DeviceConfig.io || offset >= m_DeviceConfig.length) {
332 m_DeviceConfig.io->
write8(value, m_DeviceConfig.offset + offset);
336bool PciTransport::readConfig(uint16_t offset,
unsigned width, uint64_t& value) {
337 if (!m_Initialised || !m_DeviceConfig.io || uint32_t(offset) + width > m_DeviceConfig.length) {
340 const uint32_t common = m_Common.offset;
341 const uint32_t device = m_DeviceConfig.offset + offset;
342 for (
unsigned attempt = 0; attempt < 8; ++attempt) {
343 const uint8_t generation = m_Common.io->
read8(common + 21);
346 value = m_DeviceConfig.io->
read8(device);
349 value = m_DeviceConfig.io->
read16(device);
352 value = m_DeviceConfig.io->
read32(device);
355 value = m_DeviceConfig.io->
read32(device) |
356 (uint64_t(m_DeviceConfig.io->
read32(device + 4)) << 32);
361 if (generation == m_Common.io->
read8(common + 21)) {
368bool PciTransport::readDeviceConfig8(uint16_t offset, uint8_t& value) {
370 if (!readConfig(offset, 1, result)) {
376bool PciTransport::readDeviceConfig16(uint16_t offset, uint16_t& value) {
378 if (!readConfig(offset, 2, result)) {
384bool PciTransport::readDeviceConfig32(uint16_t offset, uint32_t& value) {
386 if (!readConfig(offset, 4, result)) {
392bool PciTransport::readDeviceConfig64(uint16_t offset, uint64_t& value) {
393 return readConfig(offset, 8, value);
uint16_t getPciDeviceId()
uint16_t getPciVendorId()
virtual uint8_t read8(size_t offset=0)=0
virtual uint32_t read32(size_t offset=0)=0
virtual void write8(uint8_t value, size_t offset=0)=0
virtual void write16(uint16_t value, size_t offset=0)=0
virtual uint16_t read16(size_t offset=0)=0
void EXPORTED_PUBLIC panic(const char *msg) NORETURN