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