blob: c9c79b3fe47a0383f16c49c6c3af09407b555072 [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 Hoffmanndaf5cc92015-06-25 09:36:16 +020080int vp_find_vq(struct vp_device *vp, int queue_index,
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +010081 struct vring_virtqueue **p_vq)
Gleb Natapov89acfa32010-05-10 11:36:37 +030082{
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +020083 int ioaddr = GET_LOWFLAT(vp->ioaddr);
Gleb Natapov89acfa32010-05-10 11:36:37 +030084 u16 num;
85
86 ASSERT32FLAT();
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +010087 struct vring_virtqueue *vq = *p_vq = memalign_low(PAGE_SIZE, sizeof(*vq));
88 if (!vq) {
89 warn_noalloc();
90 goto fail;
91 }
92 memset(vq, 0, sizeof(*vq));
93
Gleb Natapov89acfa32010-05-10 11:36:37 +030094 /* select the queue */
95
96 outw(queue_index, ioaddr + VIRTIO_PCI_QUEUE_SEL);
97
98 /* check if the queue is available */
99
100 num = inw(ioaddr + VIRTIO_PCI_QUEUE_NUM);
101 if (!num) {
102 dprintf(1, "ERROR: queue size is 0\n");
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100103 goto fail;
Gleb Natapov89acfa32010-05-10 11:36:37 +0300104 }
105
106 if (num > MAX_QUEUE_NUM) {
107 dprintf(1, "ERROR: queue size %d > %d\n", num, MAX_QUEUE_NUM);
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100108 goto fail;
Gleb Natapov89acfa32010-05-10 11:36:37 +0300109 }
110
111 /* check if the queue is already active */
112
113 if (inl(ioaddr + VIRTIO_PCI_QUEUE_PFN)) {
114 dprintf(1, "ERROR: queue already active\n");
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100115 goto fail;
Gleb Natapov89acfa32010-05-10 11:36:37 +0300116 }
117
118 vq->queue_index = queue_index;
119
120 /* initialize the queue */
121
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100122 struct vring * vr = &vq->vring;
Gleb Natapov89acfa32010-05-10 11:36:37 +0300123 vring_init(vr, num, (unsigned char*)&vq->queue);
124
125 /* activate the queue
126 *
127 * NOTE: vr->desc is initialized by vring_init()
128 */
129
130 outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT,
131 ioaddr + VIRTIO_PCI_QUEUE_PFN);
132
133 return num;
Paolo Bonzinie1a17bb2011-11-16 13:02:57 +0100134
135fail:
136 free(vq);
137 *p_vq = NULL;
138 return -1;
Gleb Natapov89acfa32010-05-10 11:36:37 +0300139}
Paolo Bonzini4e343322011-11-16 13:02:56 +0100140
Gerd Hoffmann74d0cdc2015-06-25 10:24:27 +0200141void vp_init_simple(struct vp_device *vp, struct pci_device *pci)
Paolo Bonzini4e343322011-11-16 13:02:56 +0100142{
Gerd Hoffmannd8966462015-06-25 16:14:21 +0200143 u8 cap = pci_find_capability(pci, PCI_CAP_ID_VNDR, 0);
144 struct vp_cap *vp_cap;
145 u32 addr, offset;
146 u8 type;
147
148 memset(vp, 0, sizeof(*vp));
149 while (cap != 0) {
150 type = pci_config_readb(pci->bdf, cap +
151 offsetof(struct virtio_pci_cap, cfg_type));
152 switch (type) {
153 case VIRTIO_PCI_CAP_COMMON_CFG:
154 vp_cap = &vp->common;
155 break;
156 case VIRTIO_PCI_CAP_NOTIFY_CFG:
157 vp_cap = &vp->notify;
158 break;
159 case VIRTIO_PCI_CAP_ISR_CFG:
160 vp_cap = &vp->isr;
161 break;
162 case VIRTIO_PCI_CAP_DEVICE_CFG:
163 vp_cap = &vp->device;
164 break;
165 default:
166 vp_cap = NULL;
167 break;
168 }
169 if (vp_cap && !vp_cap->cap) {
170 vp_cap->cap = cap;
171 vp_cap->bar = pci_config_readb(pci->bdf, cap +
172 offsetof(struct virtio_pci_cap, bar));
173 offset = pci_config_readl(pci->bdf, cap +
174 offsetof(struct virtio_pci_cap, offset));
175 addr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0 + 4 * vp_cap->bar);
176 if (addr & PCI_BASE_ADDRESS_SPACE_IO) {
177 vp_cap->is_io = 1;
178 addr &= PCI_BASE_ADDRESS_IO_MASK;
179 } else {
180 vp_cap->is_io = 0;
181 addr &= PCI_BASE_ADDRESS_MEM_MASK;
182 }
183 vp_cap->addr = addr + offset;
184 dprintf(3, "pci dev %x:%x virtio cap at 0x%x type %d "
185 "bar %d at 0x%08x off +0x%04x [%s]\n",
186 pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf),
187 vp_cap->cap, type, vp_cap->bar, addr, offset,
188 vp_cap->is_io ? "io" : "mmio");
189 }
190
191 cap = pci_find_capability(pci, PCI_CAP_ID_VNDR, cap);
192 }
193
194 if (vp->common.cap && vp->notify.cap && vp->isr.cap && vp->device.cap) {
195 dprintf(1, "pci dev %x:%x supports virtio 1.0\n",
196 pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf));
197 }
198
Gerd Hoffmann0de78912015-06-30 08:50:09 +0200199 vp->legacy.bar = 0;
200 vp->legacy.addr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) &
Paolo Bonzini4e343322011-11-16 13:02:56 +0100201 PCI_BASE_ADDRESS_IO_MASK;
Gerd Hoffmann0de78912015-06-30 08:50:09 +0200202 vp->legacy.is_io = 1;
203 vp->ioaddr = vp->legacy.addr; /* temporary */
Paolo Bonzini4e343322011-11-16 13:02:56 +0100204
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200205 vp_reset(vp);
Gerd Hoffmann74d0cdc2015-06-25 10:24:27 +0200206 pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
Gerd Hoffmanndaf5cc92015-06-25 09:36:16 +0200207 vp_set_status(vp, VIRTIO_CONFIG_S_ACKNOWLEDGE |
Paolo Bonzini4e343322011-11-16 13:02:56 +0100208 VIRTIO_CONFIG_S_DRIVER );
Paolo Bonzini4e343322011-11-16 13:02:56 +0100209}