3#include "pedigree/kernel/Log.h"
4#include "pedigree/kernel/machine/Device.h"
5#include "pedigree/kernel/machine/IrqHandler.h"
6#include "pedigree/kernel/machine/IrqManager.h"
7#include "pedigree/kernel/machine/Machine.h"
8#include "pedigree/kernel/machine/Pci.h"
9#include "pedigree/kernel/panic.h"
10#include "pedigree/kernel/processor/IoBase.h"
11#include "pedigree/kernel/time/Time.h"
15using namespace Virtio;
18constexpr uint8_t VendorCapability = 9;
19constexpr uint8_t CommonConfig = 1;
20constexpr uint8_t NotifyConfig = 2;
21constexpr uint8_t IsrConfig = 3;
22constexpr uint8_t DeviceConfig = 4;
23constexpr uint64_t Version1 = 1ULL << 32;
24constexpr uint8_t Acknowledge = 1;
25constexpr uint8_t Driver = 2;
26constexpr uint8_t DriverOk = 4;
27constexpr uint8_t FeaturesOk = 8;
28constexpr uint8_t Failed = 128;
31PciTransport::PciTransport(
Device* device)
40 m_NotifyMultiplier(0),
49PciTransport::~PciTransport() {
50 if (m_Common.io && !reset()) {
51 panic(
"virtio: cannot stop DMA during transport teardown");
54 !PciBus::instance().updateCommand(m_Device, 0x407U, (m_Original.command & 3U) | 0x400U)) {
55 panic(
"virtio: cannot leave PCI function quiesced");
59bool PciTransport::locateCapabilities() {
60 auto& pci = PciBus::instance();
61 uint16_t pciStatus = 0;
63 if (!pci.readConfig16(m_Device, 6, pciStatus) || !(pciStatus & 0x10U) ||
64 !pci.readConfig8(m_Device, 0x34, cap)) {
68 for (
unsigned count = 0; cap && count < 48; ++count) {
69 if (cap < 0x40 || (cap & 3U) || (visited & (1ULL << (cap / 4)))) {
72 visited |= 1ULL << (cap / 4);
73 uint8_t
id = 0, next = 0;
74 if (!pci.readConfig8(m_Device, cap,
id) || !pci.readConfig8(m_Device, cap + 1, next)) {
77 if (
id == VendorCapability) {
78 uint8_t length = 0, type = 0;
79 if (!pci.readConfig8(m_Device, cap + 2, length) || length < 16 || cap + length > 256 ||
80 !pci.readConfig8(m_Device, cap + 3, type)) {
83 if (type >= CommonConfig && type <= DeviceConfig) {
85 uint32_t offset = 0, bytes = 0;
86 if (!pci.readConfig8(m_Device, cap + 4, bar) || bar >= 6 ||
87 !pci.readConfig32(m_Device, cap + 8, offset) ||
88 !pci.readConfig32(m_Device, cap + 12, bytes) || !bytes) {
94 for (
auto* candidate : m_Device->addresses()) {
95 if (candidate->m_Name == name && !candidate->m_IsIoSpace &&
96 uint64_t(offset) + bytes <= candidate->m_Size) {
105 if (!address->m_Io || address->m_Io->size() < uint64_t(offset) + bytes) {
108 Capability* target =
nullptr;
112 if (bytes < 56 || (offset & 3U)) {
118 if (length < 20 || bytes < 2 || (offset & 1U) ||
119 !pci.readConfig32(m_Device, cap + 16, m_NotifyMultiplier)) {
127 target = &m_DeviceConfig;
133 *target = {address->m_Io, offset, bytes};
138 return !cap && m_Common.io && m_Notify.io && m_Isr.io;
141bool PciTransport::status(uint8_t bits) {
145 const uint8_t before = m_Common.io->
read8(m_Common.offset + 20);
146 if (before == 0xff || (before & Failed)) {
149 const uint8_t desired = before | bits;
150 m_Common.io->
write8(desired, m_Common.offset + 20);
151 return (m_Common.io->
read8(m_Common.offset + 20) & bits) == bits;
154bool PciTransport::initialise() {
155 if (m_Initialised || !m_Device || m_Device->
getPciVendorId() != 0x1af4 ||
159 auto& pci = PciBus::instance();
160 if (!pci.inspectFunction(m_Device, m_Original,
false)) {
161 ERROR(
"virtio-pci: invalid PCI function state");
165 if (!pci.updateCommand(m_Device, 4U, 2U | 0x400U) ||
166 !pci.disableMessageInterrupts(m_Device, m_Original) ||
167 !pci.resourcesUnchanged(m_Device, m_Original)) {
168 ERROR(
"virtio-pci: could not prepare PCI function");
171 if (!locateCapabilities()) {
172 ERROR(
"virtio-pci: invalid or missing modern PCI capabilities");
175 if (!reset() || !status(Acknowledge) || !status(Driver)) {
176 ERROR(
"virtio-pci: device did not acknowledge reset or driver status");
179 m_NumQueues = m_Common.io->
read16(m_Common.offset + 18);
181 ERROR(
"virtio-pci: device exposes no queues");
184 m_Initialised =
true;
188bool PciTransport::negotiate(uint64_t supportedFeatures, uint64_t requiredFeatures) {
189 if (!m_Initialised || m_Negotiated || (requiredFeatures & ~supportedFeatures)) {
192 auto* io = m_Common.io;
193 const uint32_t base = m_Common.offset;
194 io->write32(0, base);
195 const uint64_t offeredLow = io->read32(base + 4);
196 io->write32(1, base);
197 const uint64_t offered = offeredLow | (uint64_t(io->read32(base + 4)) << 32);
198 if (!(offered & Version1) || (requiredFeatures & ~offered)) {
199 ERROR(
"virtio-pci: required modern features unavailable");
202 m_Features = offered & (supportedFeatures | Version1);
203 io->write32(0, base + 8);
204 io->write32(m_Features, base + 12);
205 io->write32(1, base + 8);
206 io->write32(m_Features >> 32, base + 12);
207 io->write16(0xffff, base + 16);
208 if (!status(FeaturesOk) || !(io->read8(base + 20) & FeaturesOk)) {
209 ERROR(
"virtio-pci: feature negotiation rejected");
216bool PciTransport::setupQueue(uint16_t index,
Queue& queue) {
217 if (!m_Negotiated || m_DmaActive || index >= m_NumQueues || index >= MaxQueues ||
221 auto* io = m_Common.io;
222 const uint32_t base = m_Common.offset;
223 io->write16(index, base + 22);
224 uint16_t maximum = io->read16(base + 24);
225 if (maximum < 2 || io->read16(base + 28)) {
226 ERROR(
"virtio-pci: queue " <<
Dec << index <<
" unavailable or already enabled" <<
Hex);
229 if (maximum > Queue::MaxDepth) {
230 maximum = Queue::MaxDepth;
233 while (depth <= maximum / 2)
235 const uint16_t notify = io->read16(base + 30);
236 const uint64_t notifyOffset = uint64_t(notify) * m_NotifyMultiplier;
237 if (notifyOffset + 2 > m_Notify.length || (notifyOffset & 1U) || !queue.initialise(depth,
this)) {
238 ERROR(
"virtio-pci: queue " <<
Dec << index <<
" notification or DMA setup failed" <<
Hex);
241 io->write16(depth, base + 24);
242 io->write16(0xffff, base + 26);
243 io->write32(queue.descriptorAddress(), base + 32);
244 io->write32(queue.descriptorAddress() >> 32, base + 36);
245 io->write32(queue.availableAddress(), base + 40);
246 io->write32(queue.availableAddress() >> 32, base + 44);
247 io->write32(queue.usedAddress(), base + 48);
248 io->write32(queue.usedAddress() >> 32, base + 52);
250 io->write16(1, base + 28);
251 if (io->read16(base + 28) != 1) {
252 ERROR(
"virtio-pci: queue " <<
Dec << index <<
" enable rejected" <<
Hex);
255 m_NotifyOffsets[index] = notifyOffset;
256 m_Queues[index] = &queue;
257 queue.m_Attached =
true;
261bool PciTransport::configureMsix() {
263 if (!m_Original.msix) {
266 uint16_t control = 0;
267 if (!PciBus::instance().readConfig16(m_Device, m_Original.msix + 2, control)) {
270 if (!(control & 0x8000U)) {
274 auto* io = m_Common.io;
275 const uint32_t base = m_Common.offset;
276 io->write16(0, base + 16);
277 if (io->read16(base + 16) != 0) {
280 for (
size_t index = 0; index < MaxQueues; ++index) {
281 if (!m_Queues[index]) {
284 io->write16(index, base + 22);
285 io->write16(0, base + 26);
286 if (io->read16(base + 26) != 0) {
294irq_id_t PciTransport::registerInterrupt(
IrqHandler* handler) {
295 if (!m_Negotiated || m_DmaActive || !handler) {
298 IrqManager* manager = Machine::instance().getIrqManager();
299 const IrqPolicy policy = IrqPolicy::pciIntxThreaded();
304 if (configureMsix()) {
308 panic(
"virtio-pci: could not retire rejected MSI-X route");
311 if (
id && !configureMsix()) {
313 panic(
"virtio-pci: could not retire invalid INTx route");
320bool PciTransport::ready() {
321 if (!m_Negotiated || m_DmaActive) {
324 bool haveQueue =
false;
325 for (
auto* queue : m_Queues)
326 haveQueue |= queue != nullptr;
330 auto& pci = PciBus::instance();
331 if (!pci.updateCommand(m_Device, 0, 6U | 0x400U)) {
334 for (
auto* queue : m_Queues)
336 queue->m_DmaArmed =
true;
339 if (!status(DriverOk) || !pci.updateCommand(m_Device, 0x400U, 6U)) {
341 panic(
"virtio: device could not be reset after failed activation");
348bool PciTransport::reset() {
352 auto& pci = PciBus::instance();
353 const bool busStopped = pci.updateCommand(m_Device, 4U, 0x400U);
357 m_Common.io->
write8(0, m_Common.offset + 20);
358 const auto deadline = Time::getTicks() + Time::Multiplier::Second;
359 while (m_Common.io->
read8(m_Common.offset + 20) != 0 && Time::getTicks() < deadline)
360 Time::delay(Time::Multiplier::Millisecond);
361 if (m_Common.io->
read8(m_Common.offset + 20) != 0 || !busStopped) {
365 m_Negotiated =
false;
366 m_Initialised =
false;
369 for (
auto*& queue : m_Queues) {
372 queue->m_DmaArmed =
false;
373 queue->m_Attached =
false;
380uint8_t PciTransport::readIsr() {
381 if (!m_Initialised) {
384 return m_UsingMsix ? 1 : (m_Isr.io ? m_Isr.io->
read8(m_Isr.offset) : 0);
387void PciTransport::notify(uint16_t index) {
388 if (!m_Negotiated || index >= MaxQueues || !m_Queues[index]) {
392 m_Notify.io->
write16(index, m_Notify.offset + m_NotifyOffsets[index]);
395bool PciTransport::writeDeviceConfig8(uint16_t offset, uint8_t value) {
396 if (!m_Initialised || !m_DeviceConfig.io || offset >= m_DeviceConfig.length) {
399 m_DeviceConfig.io->
write8(value, m_DeviceConfig.offset + offset);
403bool PciTransport::readConfig(uint16_t offset,
unsigned width, uint64_t& value) {
404 if (!m_Initialised || !m_DeviceConfig.io || uint32_t(offset) + width > m_DeviceConfig.length) {
407 const uint32_t common = m_Common.offset;
408 const uint32_t device = m_DeviceConfig.offset + offset;
409 for (
unsigned attempt = 0; attempt < 8; ++attempt) {
410 const uint8_t generation = m_Common.io->
read8(common + 21);
413 value = m_DeviceConfig.io->
read8(device);
416 value = m_DeviceConfig.io->
read16(device);
419 value = m_DeviceConfig.io->
read32(device);
422 value = m_DeviceConfig.io->
read32(device) |
423 (uint64_t(m_DeviceConfig.io->
read32(device + 4)) << 32);
428 if (generation == m_Common.io->
read8(common + 21)) {
435bool PciTransport::readDeviceConfig8(uint16_t offset, uint8_t& value) {
437 if (!readConfig(offset, 1, result)) {
443bool PciTransport::readDeviceConfig16(uint16_t offset, uint16_t& value) {
445 if (!readConfig(offset, 2, result)) {
451bool PciTransport::readDeviceConfig32(uint16_t offset, uint32_t& value) {
453 if (!readConfig(offset, 4, result)) {
459bool PciTransport::readDeviceConfig64(uint16_t offset, uint64_t& value) {
460 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
virtual bool unregisterHandler(irq_id_t Id, IrqHandlerBase *handler)=0
virtual irq_id_t registerPciIrqHandler(IrqHandler *handler, Device *pDevice, const IrqPolicy &policy)=0
virtual irq_id_t registerPciMessageIrqHandler(IrqHandler *handler, Device *pDevice, const IrqPolicy &intxFallbackPolicy)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN