1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <ddk/debug.h> 6#include <fbl/auto_lock.h> 7#include <hw/inout.h> 8#include <inttypes.h> 9 10#include "pci.h" 11 12namespace virtio { 13 14// These require the backend lock to be held due to the value held in 15// bar0_base_ rather than anything having to do with the IO writes. 16void PciLegacyBackend::IoReadLocked(uint16_t offset, uint8_t* val) TA_REQ(lock_) { 17 *val = inp(static_cast<uint16_t>(bar0_base_ + offset)); 18 zxlogf(SPEW, "%s: IoReadLocked8(%#x) = %#x\n", tag(), offset, *val); 19} 20void PciLegacyBackend::IoReadLocked(uint16_t offset, uint16_t* val) TA_REQ(lock_) { 21 *val = inpw(static_cast<uint16_t>(bar0_base_ + offset)); 22 zxlogf(SPEW, "%s: IoReadLocked16(%#x) = %#x\n", tag(), offset, *val); 23} 24void PciLegacyBackend::IoReadLocked(uint16_t offset, uint32_t* val) TA_REQ(lock_) { 25 *val = inpd(static_cast<uint16_t>(bar0_base_ + offset)); 26 zxlogf(SPEW, "%s: IoReadLocked32(%#x) = %#x\n", tag(), offset, *val); 27} 28void PciLegacyBackend::IoWriteLocked(uint16_t offset, uint8_t val) TA_REQ(lock_) { 29 outp(static_cast<uint16_t>(bar0_base_ + offset), val); 30 zxlogf(SPEW, "%s: IoWriteLocked8(%#x) = %#x\n", tag(), offset, val); 31} 32void PciLegacyBackend::IoWriteLocked(uint16_t offset, uint16_t val) TA_REQ(lock_) { 33 outpw(static_cast<uint16_t>(bar0_base_ + offset), val); 34 zxlogf(SPEW, "%s: IoWriteLocked16(%#x) = %#x\n", tag(), offset, val); 35} 36void PciLegacyBackend::IoWriteLocked(uint16_t offset, uint32_t val) TA_REQ(lock_) { 37 outpd(static_cast<uint16_t>(bar0_base_ + offset), val); 38 zxlogf(SPEW, "%s: IoWriteLocked32(%#x) = %#x\n", tag(), offset, val); 39} 40 41zx_status_t PciLegacyBackend::Init() { 42 fbl::AutoLock lock(&lock_); 43 zx_pci_bar_t bar0; 44 zx_status_t status = pci_get_bar(&pci_, 0u, &bar0); 45 if (status != ZX_OK) { 46 zxlogf(ERROR, "%s: Couldn't get IO bar for device: %d\n", tag(), status); 47 return status; 48 } 49 50 if (bar0.type != ZX_PCI_BAR_TYPE_PIO) { 51 return ZX_ERR_WRONG_TYPE; 52 } 53 54 bar0_base_ = static_cast<uint16_t>(bar0.addr & 0xffff); 55 // TODO(cja): When MSI support is added we need to dynamically add 56 // the extra two fields here that offset the device config. 57 // Virtio 1.0 section 4.1.4.8 58 device_cfg_offset_ = VIRTIO_PCI_CONFIG_OFFSET_NOMSIX; 59 zxlogf(INFO, "%s: %02x:%02x.%01x using legacy backend (io base %#04x, " 60 "io size: %#04zx, device base %#04x\n", tag(), info_.bus_id, 61 info_.dev_id, info_.func_id, bar0_base_, bar0.size, device_cfg_offset_); 62 return ZX_OK; 63} 64 65PciLegacyBackend::~PciLegacyBackend() { 66 fbl::AutoLock lock(&lock_); 67 bar0_base_ = 0; 68 device_cfg_offset_ = 0; 69} 70 71// value pointers are used to maintain type safety with field width 72void PciLegacyBackend::DeviceConfigRead(uint16_t offset, uint8_t* value) { 73 fbl::AutoLock lock(&lock_); 74 IoReadLocked(static_cast<uint16_t>(device_cfg_offset_ + offset), value); 75} 76 77void PciLegacyBackend::DeviceConfigRead(uint16_t offset, uint16_t* value) { 78 fbl::AutoLock lock(&lock_); 79 IoReadLocked(static_cast<uint16_t>(device_cfg_offset_ + offset), value); 80} 81 82void PciLegacyBackend::DeviceConfigRead(uint16_t offset, uint32_t* value) { 83 fbl::AutoLock lock(&lock_); 84 IoReadLocked(static_cast<uint16_t>(device_cfg_offset_ + offset), value); 85} 86 87void PciLegacyBackend::DeviceConfigRead(uint16_t offset, uint64_t* value) { 88 fbl::AutoLock lock(&lock_); 89 auto val = reinterpret_cast<uint32_t*>(value); 90 91 IoReadLocked(static_cast<uint16_t>(device_cfg_offset_ + offset), &val[0]); 92 IoReadLocked(static_cast<uint16_t>(device_cfg_offset_ + offset + sizeof(uint32_t)), &val[1]); 93} 94 95void PciLegacyBackend::DeviceConfigWrite(uint16_t offset, uint8_t value) { 96 fbl::AutoLock lock(&lock_); 97 IoWriteLocked(static_cast<uint16_t>(device_cfg_offset_ + offset), value); 98} 99 100void PciLegacyBackend::DeviceConfigWrite(uint16_t offset, uint16_t value) { 101 fbl::AutoLock lock(&lock_); 102 IoWriteLocked(static_cast<uint16_t>(device_cfg_offset_ + offset), value); 103} 104 105void PciLegacyBackend::DeviceConfigWrite(uint16_t offset, uint32_t value) { 106 fbl::AutoLock lock(&lock_); 107 IoWriteLocked(static_cast<uint16_t>(device_cfg_offset_ + offset), value); 108} 109void PciLegacyBackend::DeviceConfigWrite(uint16_t offset, uint64_t value) { 110 fbl::AutoLock lock(&lock_); 111 auto words = reinterpret_cast<uint32_t*>(&value); 112 IoWriteLocked(static_cast<uint16_t>(device_cfg_offset_ + offset), words[0]); 113 IoWriteLocked(static_cast<uint16_t>(device_cfg_offset_ + offset + sizeof(uint32_t)), words[1]); 114} 115 116// Get the ring size of a specific index 117uint16_t PciLegacyBackend::GetRingSize(uint16_t index) { 118 fbl::AutoLock lock(&lock_); 119 uint16_t val; 120 IoWriteLocked(VIRTIO_PCI_QUEUE_SELECT, index); 121 IoReadLocked(VIRTIO_PCI_QUEUE_SIZE, &val); 122 zxlogf(SPEW, "%s: ring %u size = %u\n", tag(), index, val); 123 return val; 124} 125 126// Set up ring descriptors with the backend. 127void PciLegacyBackend::SetRing(uint16_t index, uint16_t count, zx_paddr_t pa_desc, 128 zx_paddr_t pa_avail, zx_paddr_t pa_used) { 129 fbl::AutoLock lock(&lock_); 130 // Virtio 1.0 section 2.4.2 131 IoWriteLocked(VIRTIO_PCI_QUEUE_SELECT, index); 132 IoWriteLocked(VIRTIO_PCI_QUEUE_SIZE, count); 133 IoWriteLocked(VIRTIO_PCI_QUEUE_PFN, static_cast<uint32_t>(pa_desc / 4096)); 134 zxlogf(SPEW, "%s: set ring %u (# = %u, addr = %#lx)\n", tag(), index, count, pa_desc); 135} 136 137void PciLegacyBackend::RingKick(uint16_t ring_index) { 138 fbl::AutoLock lock(&lock_); 139 IoWriteLocked(VIRTIO_PCI_QUEUE_NOTIFY, ring_index); 140 zxlogf(SPEW, "%s: kicked ring %u\n", tag(), ring_index); 141} 142 143bool PciLegacyBackend::ReadFeature(uint32_t feature) { 144 // Legacy PCI back-end can only support one feature word. 145 if (feature >= 32) { 146 return false; 147 } 148 149 fbl::AutoLock lock(&lock_); 150 uint32_t val; 151 152 ZX_DEBUG_ASSERT((feature & (feature - 1)) == 0); 153 IoReadLocked(VIRTIO_PCI_DEVICE_FEATURES, &val); 154 bool is_set = (val & (1u << feature)) > 0; 155 zxlogf(SPEW, "%s: read feature bit %u = %u\n", tag(), feature, is_set); 156 return is_set; 157} 158 159void PciLegacyBackend::SetFeature(uint32_t feature) { 160 // Legacy PCI back-end can only support one feature word. 161 if (feature >= 32) { 162 return; 163 } 164 165 fbl::AutoLock lock(&lock_); 166 uint32_t val; 167 ZX_DEBUG_ASSERT((feature & (feature - 1)) == 0); 168 IoReadLocked(VIRTIO_PCI_DRIVER_FEATURES, &val); 169 IoWriteLocked(VIRTIO_PCI_DRIVER_FEATURES, val | (1u << feature)); 170 zxlogf(SPEW, "%s: feature bit %u now set\n", tag(), feature); 171} 172 173// Virtio v0.9.5 does not support the FEATURES_OK negotiation so this should 174// always succeed. 175zx_status_t PciLegacyBackend::ConfirmFeatures() { 176 return ZX_OK; 177} 178 179void PciLegacyBackend::DeviceReset() { 180 fbl::AutoLock lock(&lock_); 181 IoWriteLocked(VIRTIO_PCI_DEVICE_STATUS, 0u); 182 zxlogf(SPEW, "%s: device reset\n", tag()); 183} 184 185void PciLegacyBackend::SetStatusBits(uint8_t bits) { 186 fbl::AutoLock lock(&lock_); 187 uint8_t status; 188 IoReadLocked(VIRTIO_PCI_DEVICE_STATUS, &status); 189 IoWriteLocked(VIRTIO_PCI_DEVICE_STATUS, static_cast<uint8_t>(status | bits)); 190} 191 192void PciLegacyBackend::DriverStatusAck() { 193 SetStatusBits(VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER); 194 zxlogf(SPEW, "%s: driver acknowledge\n", tag()); 195} 196 197void PciLegacyBackend::DriverStatusOk() { 198 SetStatusBits(VIRTIO_STATUS_DRIVER_OK); 199 zxlogf(SPEW, "%s: driver ok\n", tag()); 200} 201 202uint32_t PciLegacyBackend::IsrStatus() { 203 fbl::AutoLock lock(&lock_); 204 uint8_t isr_status; 205 IoReadLocked(VIRTIO_PCI_ISR_STATUS, &isr_status); 206 return isr_status & (VIRTIO_ISR_QUEUE_INT | VIRTIO_ISR_DEV_CFG_INT); 207} 208 209} // namespace virtio 210