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 
12 namespace 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.
IoReadLocked(uint16_t offset,uint8_t * val)16 void 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 }
IoReadLocked(uint16_t offset,uint16_t * val)20 void 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 }
IoReadLocked(uint16_t offset,uint32_t * val)24 void 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 }
IoWriteLocked(uint16_t offset,uint8_t val)28 void 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 }
IoWriteLocked(uint16_t offset,uint16_t val)32 void 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 }
IoWriteLocked(uint16_t offset,uint32_t val)36 void 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 
Init()41 zx_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 
~PciLegacyBackend()65 PciLegacyBackend::~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
DeviceConfigRead(uint16_t offset,uint8_t * value)72 void 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 
DeviceConfigRead(uint16_t offset,uint16_t * value)77 void 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 
DeviceConfigRead(uint16_t offset,uint32_t * value)82 void 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 
DeviceConfigRead(uint16_t offset,uint64_t * value)87 void 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 
DeviceConfigWrite(uint16_t offset,uint8_t value)95 void 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 
DeviceConfigWrite(uint16_t offset,uint16_t value)100 void 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 
DeviceConfigWrite(uint16_t offset,uint32_t value)105 void 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 }
DeviceConfigWrite(uint16_t offset,uint64_t value)109 void 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
GetRingSize(uint16_t index)117 uint16_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.
SetRing(uint16_t index,uint16_t count,zx_paddr_t pa_desc,zx_paddr_t pa_avail,zx_paddr_t pa_used)127 void 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 
RingKick(uint16_t ring_index)137 void 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 
ReadFeature(uint32_t feature)143 bool 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 
SetFeature(uint32_t feature)159 void 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.
ConfirmFeatures()175 zx_status_t PciLegacyBackend::ConfirmFeatures() {
176     return ZX_OK;
177 }
178 
DeviceReset()179 void PciLegacyBackend::DeviceReset() {
180     fbl::AutoLock lock(&lock_);
181     IoWriteLocked(VIRTIO_PCI_DEVICE_STATUS, 0u);
182     zxlogf(SPEW, "%s: device reset\n", tag());
183 }
184 
SetStatusBits(uint8_t bits)185 void 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 
DriverStatusAck()192 void PciLegacyBackend::DriverStatusAck() {
193     SetStatusBits(VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER);
194     zxlogf(SPEW, "%s: driver acknowledge\n", tag());
195 }
196 
DriverStatusOk()197 void PciLegacyBackend::DriverStatusOk() {
198     SetStatusBits(VIRTIO_STATUS_DRIVER_OK);
199     zxlogf(SPEW, "%s: driver ok\n", tag());
200 }
201 
IsrStatus()202 uint32_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