blob: f1e30a2470776b3242a29df4bddf72c785c28796 [file] [log] [blame]
Gleb Natapov89acfa32010-05-10 11:36:37 +03001/* virtio-pci.c - pci interface for virtio interface
2 *
3 * (c) Copyright 2008 Bull S.A.S.
4 *
5 * Author: Laurent Vivier <Laurent.Vivier@bull.net>
6 *
7 * some parts from Linux Virtio PCI driver
8 *
9 * Copyright IBM Corp. 2007
10 * Authors: Anthony Liguori <aliguori@us.ibm.com>
11 *
12 * Adopted for Seabios: Gleb Natapov <gleb@redhat.com>
13 *
14 * This work is licensed under the terms of the GNU LGPLv3
15 * See the COPYING file in the top-level directory.
16 */
17
Kevin O'Connor7d09d0e2010-05-10 21:51:38 -040018#include "config.h" // CONFIG_DEBUG_LEVEL
Kevin O'Connor9dea5902013-09-14 20:23:54 -040019#include "malloc.h" // free
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040020#include "output.h" // dprintf
Sebastian Herbszt70451b62011-11-21 12:23:20 +010021#include "pci.h" // pci_config_readl
22#include "pci_regs.h" // PCI_BASE_ADDRESS_0
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040023#include "string.h" // memset
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040024#include "virtio-pci.h"
25#include "virtio-ring.h"
Gleb Natapov89acfa32010-05-10 11:36:37 +030026
Gerd Hoffmann46d17922015-06-25 12:28:46 +020027u64 vp_get_features(struct vp_device *vp)
28{
29 u32 f0, f1;
30
31 if (vp->use_modern) {
32 vp_write(&vp->common, virtio_pci_common_cfg, device_feature_select, 0);
33 f0 = vp_read(&vp->common, virtio_pci_common_cfg, device_feature);
34 vp_write(&vp->common, virtio_pci_common_cfg, device_feature_select, 1);
35 f1 = vp_read(&vp->common, virtio_pci_common_cfg, device_feature);
36 } else {
37 f0 = vp_read(&vp->legacy, virtio_pci_legacy, host_features);
38 f1 = 0;
39 }
40 return ((u64)f1 << 32) | f0;
41}
42
43void vp_set_features(struct vp_device *vp, u64 features)
44{
45 u32 f0, f1;
46
47 f0 = features;
48 f1 = features >> 32;
49
50 if (vp->use_modern) {
51 vp_write(&vp->common, virtio_pci_common_cfg, guest_feature_select, 0);
52 vp_write(&vp->common, virtio_pci_common_cfg, guest_feature, f0);
53 vp_write(&vp->common, virtio_pci_common_cfg, guest_feature_select, 1);
54 vp_write(&vp->common, virtio_pci_common_cfg, guest_feature, f1);
55 } else {
56 vp_write(&vp->legacy, virtio_pci_legacy, guest_features, f0);
57 }
58}
59
Gerd Hoffmann6ee97762015-06-25 16:37:41 +020060u8 vp_get_status(struct vp_device *vp)
61{
62 if (vp->use_modern) {
63 return vp_read(&vp->common, virtio_pci_common_cfg, device_status);
64 } else {
65 return vp_read(&vp->legacy, virtio_pci_legacy, status);
66 }
67}
68
69void vp_set_status(struct vp_device *vp, u8 status)
70{
71 if (status == 0) /* reset */
72 return;
73 if (vp->use_modern) {
74 vp_write(&vp->common, virtio_pci_common_cfg, device_status, status);
75 } else {
76 vp_write(&vp->legacy, virtio_pci_legacy, status, status);
77 }
78}
79
Gerd Hoffmannadeb2e32015-06-25 16:43:17 +020080u8 vp_get_isr(struct vp_device *vp)
81{
82 if (vp->use_modern) {
83 return vp_read(&vp->isr, virtio_pci_isr, isr);
84 } else {
85 return vp_read(&vp->legacy, virtio_pci_legacy, isr);
86 }
87}
88
Gerd Hoffmann984db762015-06-25 16:44:06 +020089void vp_reset(struct vp_device *vp)
90{
91 if (vp->use_modern) {
92 vp_write(&vp->common, virtio_pci_common_cfg, device_status, 0);
93 vp_read(&vp->isr, virtio_pci_isr, isr);
94 } else {
95 vp_write(&vp->legacy, virtio_pci_legacy, status, 0);
96 vp_read(&vp->legacy, virtio_pci_legacy, isr);
97 }
98}
99
Gerd Hoffmann2e68fb12015-06-26 08:19:28 +0200100void vp_notify(struct vp_device *vp, struct vring_virtqueue *vq)
101{
102 if (vp->use_modern) {
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500103 u32 offset = vq->queue_notify_off * vp->notify_off_multiplier;
Gerd Hoffmann2e68fb12015-06-26 08:19:28 +0200104 if (vp->notify.is_io) {
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500105 outw(vq->queue_index, vp->notify.ioaddr + offset);
Gerd Hoffmann2e68fb12015-06-26 08:19:28 +0200106 } else {
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500107 writew(vp->notify.memaddr + offset, vq->queue_index);
Gerd Hoffmann2e68fb12015-06-26 08:19:28 +0200108 }
109 dprintf(9, "vp notify %x (%d) -- 0x%x\n",
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500110 vp->notify.ioaddr, 2, vq->queue_index);
Gerd Hoffmann2e68fb12015-06-26 08:19:28 +0200111 } else {
112 vp_write(&vp->legacy, virtio_pci_legacy, queue_notify, vq->queue_index);
113 }
114}
115
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200116int vp_find_vq(struct vp_device *vp, int queue_index,
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100117 struct vring_virtqueue **p_vq)
Gleb Natapov89acfa32010-05-10 11:36:37 +0300118{
Gleb Natapov89acfa32010-05-10 11:36:37 +0300119 u16 num;
120
121 ASSERT32FLAT();
Gerd Hoffmann6cfebb42015-07-01 14:39:30 +0200122 struct vring_virtqueue *vq = *p_vq = memalign_high(PAGE_SIZE, sizeof(*vq));
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100123 if (!vq) {
124 warn_noalloc();
125 goto fail;
126 }
127 memset(vq, 0, sizeof(*vq));
128
Gleb Natapov89acfa32010-05-10 11:36:37 +0300129
Gerd Hoffmann6c14fef2015-06-26 09:07:59 +0200130 /* select the queue */
131 if (vp->use_modern) {
132 vp_write(&vp->common, virtio_pci_common_cfg, queue_select, queue_index);
133 } else {
134 vp_write(&vp->legacy, virtio_pci_legacy, queue_sel, queue_index);
135 }
Gleb Natapov89acfa32010-05-10 11:36:37 +0300136
137 /* check if the queue is available */
Gerd Hoffmann6c14fef2015-06-26 09:07:59 +0200138 if (vp->use_modern) {
139 num = vp_read(&vp->common, virtio_pci_common_cfg, queue_size);
140 if (num > MAX_QUEUE_NUM) {
141 vp_write(&vp->common, virtio_pci_common_cfg, queue_size,
142 MAX_QUEUE_NUM);
143 num = vp_read(&vp->common, virtio_pci_common_cfg, queue_size);
144 }
145 } else {
146 num = vp_read(&vp->legacy, virtio_pci_legacy, queue_num);
147 }
Gleb Natapov89acfa32010-05-10 11:36:37 +0300148 if (!num) {
149 dprintf(1, "ERROR: queue size is 0\n");
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100150 goto fail;
Gleb Natapov89acfa32010-05-10 11:36:37 +0300151 }
Gleb Natapov89acfa32010-05-10 11:36:37 +0300152 if (num > MAX_QUEUE_NUM) {
153 dprintf(1, "ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM);
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100154 goto fail;
Gleb Natapov89acfa32010-05-10 11:36:37 +0300155 }
156
157 /* check if the queue is already active */
Gerd Hoffmann6c14fef2015-06-26 09:07:59 +0200158 if (vp->use_modern) {
159 if (vp_read(&vp->common, virtio_pci_common_cfg, queue_enable)) {
160 dprintf(1, "ERROR: queue already active\n");
161 goto fail;
162 }
163 } else {
164 if (vp_read(&vp->legacy, virtio_pci_legacy, queue_pfn)) {
165 dprintf(1, "ERROR: queue already active\n");
166 goto fail;
167 }
Gleb Natapov89acfa32010-05-10 11:36:37 +0300168 }
Gleb Natapov89acfa32010-05-10 11:36:37 +0300169 vq->queue_index = queue_index;
170
171 /* initialize the queue */
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100172 struct vring * vr = &vq->vring;
Gleb Natapov89acfa32010-05-10 11:36:37 +0300173 vring_init(vr, num, (unsigned char*)&vq->queue);
174
175 /* activate the queue
176 *
177 * NOTE: vr->desc is initialized by vring_init()
178 */
179
Gerd Hoffmann6c14fef2015-06-26 09:07:59 +0200180 if (vp->use_modern) {
181 vp_write(&vp->common, virtio_pci_common_cfg, queue_desc_lo,
182 (unsigned long)virt_to_phys(vr->desc));
183 vp_write(&vp->common, virtio_pci_common_cfg, queue_desc_hi, 0);
184 vp_write(&vp->common, virtio_pci_common_cfg, queue_avail_lo,
185 (unsigned long)virt_to_phys(vr->avail));
186 vp_write(&vp->common, virtio_pci_common_cfg, queue_avail_hi, 0);
187 vp_write(&vp->common, virtio_pci_common_cfg, queue_used_lo,
188 (unsigned long)virt_to_phys(vr->used));
189 vp_write(&vp->common, virtio_pci_common_cfg, queue_used_hi, 0);
190 vp_write(&vp->common, virtio_pci_common_cfg, queue_enable, 1);
191 vq->queue_notify_off = vp_read(&vp->common, virtio_pci_common_cfg,
192 queue_notify_off);
193 } else {
194 vp_write(&vp->legacy, virtio_pci_legacy, queue_pfn,
195 (unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT);
196 }
Gleb Natapov89acfa32010-05-10 11:36:37 +0300197 return num;
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100198
199fail:
200 free(vq);
201 *p_vq = NULL;
202 return -1;
Gleb Natapov89acfa32010-05-10 11:36:37 +0300203}
Paolo Bonzini4e343322011-11-16 13:02:56 +0100204
Gerd Hoffmann74d0cdc2015-06-25 10:24:27 +0200205void vp_init_simple(struct vp_device *vp, struct pci_device *pci)
Paolo Bonzini4e343322011-11-16 13:02:56 +0100206{
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200207 u8 cap = pci_find_capability(pci, PCI_CAP_ID_VNDR, 0);
208 struct vp_cap *vp_cap;
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500209 u32 offset, mul;
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200210 u8 type;
211
212 memset(vp, 0, sizeof(*vp));
213 while (cap != 0) {
214 type = pci_config_readb(pci->bdf, cap +
215 offsetof(struct virtio_pci_cap, cfg_type));
216 switch (type) {
217 case VIRTIO_PCI_CAP_COMMON_CFG:
218 vp_cap = &vp->common;
219 break;
220 case VIRTIO_PCI_CAP_NOTIFY_CFG:
221 vp_cap = &vp->notify;
Gerd Hoffmann2e68fb12015-06-26 08:19:28 +0200222 mul = offsetof(struct virtio_pci_notify_cap, notify_off_multiplier);
223 vp->notify_off_multiplier = pci_config_readl(pci->bdf, cap + mul);
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200224 break;
225 case VIRTIO_PCI_CAP_ISR_CFG:
226 vp_cap = &vp->isr;
227 break;
228 case VIRTIO_PCI_CAP_DEVICE_CFG:
229 vp_cap = &vp->device;
230 break;
231 default:
232 vp_cap = NULL;
233 break;
234 }
235 if (vp_cap && !vp_cap->cap) {
236 vp_cap->cap = cap;
237 vp_cap->bar = pci_config_readb(pci->bdf, cap +
238 offsetof(struct virtio_pci_cap, bar));
239 offset = pci_config_readl(pci->bdf, cap +
240 offsetof(struct virtio_pci_cap, offset));
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500241 u32 bar = PCI_BASE_ADDRESS_0 + 4 * vp_cap->bar;
242 if (pci_config_readl(pci->bdf, bar) & PCI_BASE_ADDRESS_SPACE_IO) {
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200243 vp_cap->is_io = 1;
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500244 u32 addr = pci_enable_iobar(pci, bar);
245 if (!addr)
246 return;
247 vp_cap->ioaddr = addr + offset;
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200248 } else {
249 vp_cap->is_io = 0;
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500250 void *addr = pci_enable_membar(pci, bar);
251 if (!addr)
252 return;
253 vp_cap->memaddr = addr + offset;
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200254 }
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200255 dprintf(3, "pci dev %x:%x virtio cap at 0x%x type %d "
256 "bar %d at 0x%08x off +0x%04x [%s]\n",
257 pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf),
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500258 vp_cap->cap, type, vp_cap->bar, vp_cap->ioaddr, offset,
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200259 vp_cap->is_io ? "io" : "mmio");
260 }
261
262 cap = pci_find_capability(pci, PCI_CAP_ID_VNDR, cap);
263 }
264
265 if (vp->common.cap && vp->notify.cap && vp->isr.cap && vp->device.cap) {
Gerd Hoffmann084eec82015-06-26 08:45:11 +0200266 dprintf(1, "pci dev %x:%x using modern (1.0) virtio mode\n",
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200267 pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf));
Gerd Hoffmann084eec82015-06-26 08:45:11 +0200268 vp->use_modern = 1;
269 } else {
270 dprintf(1, "pci dev %x:%x using legacy (0.9.5) virtio mode\n",
271 pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf));
272 vp->legacy.bar = 0;
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500273 vp->legacy.ioaddr = pci_enable_iobar(pci, PCI_BASE_ADDRESS_0);
274 if (!vp->legacy.ioaddr)
275 return;
Gerd Hoffmann084eec82015-06-26 08:45:11 +0200276 vp->legacy.is_io = 1;
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200277 }
278
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200279 vp_reset(vp);
Kevin O'Connorf46739b2016-02-02 22:34:27 -0500280 pci_enable_busmaster(pci);
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200281 vp_set_status(vp, VIRTIO_CONFIG_S_ACKNOWLEDGE |
Paolo Bonzini4e343322011-11-16 13:02:56 +0100282 VIRTIO_CONFIG_S_DRIVER );
Paolo Bonzini4e343322011-11-16 13:02:56 +0100283}