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