1930876d5SJean-Philippe Brucker #include "kvm/virtio-pci.h"
2930876d5SJean-Philippe Brucker
3930876d5SJean-Philippe Brucker #include "kvm/ioport.h"
4930876d5SJean-Philippe Brucker #include "kvm/virtio.h"
5930876d5SJean-Philippe Brucker
virtio_pci__specific_data_in(struct kvm * kvm,struct virtio_device * vdev,void * data,u32 size,unsigned long offset)6930876d5SJean-Philippe Brucker static bool virtio_pci__specific_data_in(struct kvm *kvm, struct virtio_device *vdev,
7930876d5SJean-Philippe Brucker void *data, u32 size, unsigned long offset)
8930876d5SJean-Philippe Brucker {
9930876d5SJean-Philippe Brucker u32 config_offset;
10930876d5SJean-Philippe Brucker struct virtio_pci *vpci = vdev->virtio;
11930876d5SJean-Philippe Brucker int type = virtio__get_dev_specific_field(offset - 20,
12930876d5SJean-Philippe Brucker virtio_pci__msix_enabled(vpci),
13930876d5SJean-Philippe Brucker &config_offset);
14930876d5SJean-Philippe Brucker if (type == VIRTIO_PCI_O_MSIX) {
15930876d5SJean-Philippe Brucker switch (offset) {
16930876d5SJean-Philippe Brucker case VIRTIO_MSI_CONFIG_VECTOR:
17930876d5SJean-Philippe Brucker ioport__write16(data, vpci->config_vector);
18930876d5SJean-Philippe Brucker break;
19930876d5SJean-Philippe Brucker case VIRTIO_MSI_QUEUE_VECTOR:
20930876d5SJean-Philippe Brucker ioport__write16(data, vpci->vq_vector[vpci->queue_selector]);
21930876d5SJean-Philippe Brucker break;
22930876d5SJean-Philippe Brucker };
23930876d5SJean-Philippe Brucker
24930876d5SJean-Philippe Brucker return true;
25930876d5SJean-Philippe Brucker } else if (type == VIRTIO_PCI_O_CONFIG) {
26930876d5SJean-Philippe Brucker return virtio_access_config(kvm, vdev, vpci->dev, config_offset,
27930876d5SJean-Philippe Brucker data, size, false);
28930876d5SJean-Philippe Brucker }
29930876d5SJean-Philippe Brucker
30930876d5SJean-Philippe Brucker return false;
31930876d5SJean-Philippe Brucker }
32930876d5SJean-Philippe Brucker
virtio_pci__data_in(struct kvm_cpu * vcpu,struct virtio_device * vdev,unsigned long offset,void * data,u32 size)33930876d5SJean-Philippe Brucker static bool virtio_pci__data_in(struct kvm_cpu *vcpu, struct virtio_device *vdev,
34930876d5SJean-Philippe Brucker unsigned long offset, void *data, u32 size)
35930876d5SJean-Philippe Brucker {
36930876d5SJean-Philippe Brucker bool ret = true;
37930876d5SJean-Philippe Brucker struct virtio_pci *vpci;
38930876d5SJean-Philippe Brucker struct virt_queue *vq;
39930876d5SJean-Philippe Brucker struct kvm *kvm;
40930876d5SJean-Philippe Brucker u32 val;
41930876d5SJean-Philippe Brucker
42930876d5SJean-Philippe Brucker kvm = vcpu->kvm;
43930876d5SJean-Philippe Brucker vpci = vdev->virtio;
44930876d5SJean-Philippe Brucker
45930876d5SJean-Philippe Brucker switch (offset) {
46930876d5SJean-Philippe Brucker case VIRTIO_PCI_HOST_FEATURES:
47930876d5SJean-Philippe Brucker val = vdev->ops->get_host_features(kvm, vpci->dev);
48930876d5SJean-Philippe Brucker ioport__write32(data, val);
49930876d5SJean-Philippe Brucker break;
50930876d5SJean-Philippe Brucker case VIRTIO_PCI_QUEUE_PFN:
51930876d5SJean-Philippe Brucker vq = vdev->ops->get_vq(kvm, vpci->dev, vpci->queue_selector);
52930876d5SJean-Philippe Brucker ioport__write32(data, vq->vring_addr.pfn);
53930876d5SJean-Philippe Brucker break;
54930876d5SJean-Philippe Brucker case VIRTIO_PCI_QUEUE_NUM:
55930876d5SJean-Philippe Brucker val = vdev->ops->get_size_vq(kvm, vpci->dev, vpci->queue_selector);
56930876d5SJean-Philippe Brucker ioport__write16(data, val);
57930876d5SJean-Philippe Brucker break;
58930876d5SJean-Philippe Brucker case VIRTIO_PCI_STATUS:
59930876d5SJean-Philippe Brucker ioport__write8(data, vpci->status);
60930876d5SJean-Philippe Brucker break;
61930876d5SJean-Philippe Brucker case VIRTIO_PCI_ISR:
62930876d5SJean-Philippe Brucker ioport__write8(data, vpci->isr);
63930876d5SJean-Philippe Brucker kvm__irq_line(kvm, vpci->legacy_irq_line, VIRTIO_IRQ_LOW);
64*d560235fSKeir Fraser vpci->isr = 0;
65930876d5SJean-Philippe Brucker break;
66930876d5SJean-Philippe Brucker default:
67930876d5SJean-Philippe Brucker ret = virtio_pci__specific_data_in(kvm, vdev, data, size, offset);
68930876d5SJean-Philippe Brucker break;
69930876d5SJean-Philippe Brucker };
70930876d5SJean-Philippe Brucker
71930876d5SJean-Philippe Brucker return ret;
72930876d5SJean-Philippe Brucker }
73930876d5SJean-Philippe Brucker
virtio_pci__specific_data_out(struct kvm * kvm,struct virtio_device * vdev,void * data,u32 size,unsigned long offset)74930876d5SJean-Philippe Brucker static bool virtio_pci__specific_data_out(struct kvm *kvm, struct virtio_device *vdev,
75930876d5SJean-Philippe Brucker void *data, u32 size, unsigned long offset)
76930876d5SJean-Philippe Brucker {
77930876d5SJean-Philippe Brucker struct virtio_pci *vpci = vdev->virtio;
78930876d5SJean-Philippe Brucker u32 config_offset, vec;
79930876d5SJean-Philippe Brucker int gsi;
80930876d5SJean-Philippe Brucker int type = virtio__get_dev_specific_field(offset - 20, virtio_pci__msix_enabled(vpci),
81930876d5SJean-Philippe Brucker &config_offset);
82930876d5SJean-Philippe Brucker if (type == VIRTIO_PCI_O_MSIX) {
83930876d5SJean-Philippe Brucker switch (offset) {
84930876d5SJean-Philippe Brucker case VIRTIO_MSI_CONFIG_VECTOR:
85930876d5SJean-Philippe Brucker vec = vpci->config_vector = ioport__read16(data);
86930876d5SJean-Philippe Brucker
87930876d5SJean-Philippe Brucker gsi = virtio_pci__add_msix_route(vpci, vec);
88930876d5SJean-Philippe Brucker if (gsi < 0)
89930876d5SJean-Philippe Brucker break;
90930876d5SJean-Philippe Brucker
91930876d5SJean-Philippe Brucker vpci->config_gsi = gsi;
92930876d5SJean-Philippe Brucker break;
93930876d5SJean-Philippe Brucker case VIRTIO_MSI_QUEUE_VECTOR:
94930876d5SJean-Philippe Brucker vec = ioport__read16(data);
95930876d5SJean-Philippe Brucker vpci->vq_vector[vpci->queue_selector] = vec;
96930876d5SJean-Philippe Brucker
97930876d5SJean-Philippe Brucker gsi = virtio_pci__add_msix_route(vpci, vec);
98930876d5SJean-Philippe Brucker if (gsi < 0)
99930876d5SJean-Philippe Brucker break;
100930876d5SJean-Philippe Brucker
101930876d5SJean-Philippe Brucker vpci->gsis[vpci->queue_selector] = gsi;
102930876d5SJean-Philippe Brucker if (vdev->ops->notify_vq_gsi)
103930876d5SJean-Philippe Brucker vdev->ops->notify_vq_gsi(kvm, vpci->dev,
104930876d5SJean-Philippe Brucker vpci->queue_selector,
105930876d5SJean-Philippe Brucker gsi);
106930876d5SJean-Philippe Brucker break;
107930876d5SJean-Philippe Brucker };
108930876d5SJean-Philippe Brucker
109930876d5SJean-Philippe Brucker return true;
110930876d5SJean-Philippe Brucker } else if (type == VIRTIO_PCI_O_CONFIG) {
111930876d5SJean-Philippe Brucker return virtio_access_config(kvm, vdev, vpci->dev, config_offset,
112930876d5SJean-Philippe Brucker data, size, true);
113930876d5SJean-Philippe Brucker }
114930876d5SJean-Philippe Brucker
115930876d5SJean-Philippe Brucker return false;
116930876d5SJean-Philippe Brucker }
117930876d5SJean-Philippe Brucker
virtio_pci__data_out(struct kvm_cpu * vcpu,struct virtio_device * vdev,unsigned long offset,void * data,u32 size)118930876d5SJean-Philippe Brucker static bool virtio_pci__data_out(struct kvm_cpu *vcpu, struct virtio_device *vdev,
119930876d5SJean-Philippe Brucker unsigned long offset, void *data, u32 size)
120930876d5SJean-Philippe Brucker {
121930876d5SJean-Philippe Brucker bool ret = true;
122930876d5SJean-Philippe Brucker struct virtio_pci *vpci;
123930876d5SJean-Philippe Brucker struct virt_queue *vq;
124930876d5SJean-Philippe Brucker struct kvm *kvm;
125930876d5SJean-Philippe Brucker u32 val;
126930876d5SJean-Philippe Brucker unsigned int vq_count;
127930876d5SJean-Philippe Brucker
128930876d5SJean-Philippe Brucker kvm = vcpu->kvm;
129930876d5SJean-Philippe Brucker vpci = vdev->virtio;
130930876d5SJean-Philippe Brucker vq_count = vdev->ops->get_vq_count(kvm, vpci->dev);
131930876d5SJean-Philippe Brucker
132930876d5SJean-Philippe Brucker switch (offset) {
133930876d5SJean-Philippe Brucker case VIRTIO_PCI_GUEST_FEATURES:
134930876d5SJean-Philippe Brucker val = ioport__read32(data);
135930876d5SJean-Philippe Brucker virtio_set_guest_features(kvm, vdev, vpci->dev, val);
136930876d5SJean-Philippe Brucker break;
137930876d5SJean-Philippe Brucker case VIRTIO_PCI_QUEUE_PFN:
138930876d5SJean-Philippe Brucker val = ioport__read32(data);
139930876d5SJean-Philippe Brucker if (val) {
140930876d5SJean-Philippe Brucker vq = vdev->ops->get_vq(kvm, vpci->dev,
141930876d5SJean-Philippe Brucker vpci->queue_selector);
142930876d5SJean-Philippe Brucker vq->vring_addr = (struct vring_addr) {
143930876d5SJean-Philippe Brucker .legacy = true,
144930876d5SJean-Philippe Brucker .pfn = val,
145930876d5SJean-Philippe Brucker .align = VIRTIO_PCI_VRING_ALIGN,
146930876d5SJean-Philippe Brucker .pgsize = 1 << VIRTIO_PCI_QUEUE_ADDR_SHIFT,
147930876d5SJean-Philippe Brucker };
148930876d5SJean-Philippe Brucker virtio_pci_init_vq(kvm, vdev, vpci->queue_selector);
149930876d5SJean-Philippe Brucker } else {
150930876d5SJean-Philippe Brucker virtio_pci_exit_vq(kvm, vdev, vpci->queue_selector);
151930876d5SJean-Philippe Brucker }
152930876d5SJean-Philippe Brucker break;
153930876d5SJean-Philippe Brucker case VIRTIO_PCI_QUEUE_SEL:
154930876d5SJean-Philippe Brucker val = ioport__read16(data);
155930876d5SJean-Philippe Brucker if (val >= vq_count) {
156930876d5SJean-Philippe Brucker WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n",
157930876d5SJean-Philippe Brucker val, vq_count);
158930876d5SJean-Philippe Brucker return false;
159930876d5SJean-Philippe Brucker }
160930876d5SJean-Philippe Brucker vpci->queue_selector = val;
161930876d5SJean-Philippe Brucker break;
162930876d5SJean-Philippe Brucker case VIRTIO_PCI_QUEUE_NOTIFY:
163930876d5SJean-Philippe Brucker val = ioport__read16(data);
164930876d5SJean-Philippe Brucker if (val >= vq_count) {
165930876d5SJean-Philippe Brucker WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n",
166930876d5SJean-Philippe Brucker val, vq_count);
167930876d5SJean-Philippe Brucker return false;
168930876d5SJean-Philippe Brucker }
169930876d5SJean-Philippe Brucker vdev->ops->notify_vq(kvm, vpci->dev, val);
170930876d5SJean-Philippe Brucker break;
171930876d5SJean-Philippe Brucker case VIRTIO_PCI_STATUS:
172930876d5SJean-Philippe Brucker vpci->status = ioport__read8(data);
173930876d5SJean-Philippe Brucker if (!vpci->status) /* Sample endianness on reset */
174930876d5SJean-Philippe Brucker vdev->endian = kvm_cpu__get_endianness(vcpu);
175930876d5SJean-Philippe Brucker virtio_notify_status(kvm, vdev, vpci->dev, vpci->status);
176930876d5SJean-Philippe Brucker break;
177930876d5SJean-Philippe Brucker default:
178930876d5SJean-Philippe Brucker ret = virtio_pci__specific_data_out(kvm, vdev, data, size, offset);
179930876d5SJean-Philippe Brucker break;
180930876d5SJean-Philippe Brucker };
181930876d5SJean-Philippe Brucker
182930876d5SJean-Philippe Brucker return ret;
183930876d5SJean-Philippe Brucker }
184930876d5SJean-Philippe Brucker
virtio_pci_legacy__io_mmio_callback(struct kvm_cpu * vcpu,u64 addr,u8 * data,u32 len,u8 is_write,void * ptr)185930876d5SJean-Philippe Brucker void virtio_pci_legacy__io_mmio_callback(struct kvm_cpu *vcpu, u64 addr,
186930876d5SJean-Philippe Brucker u8 *data, u32 len, u8 is_write,
187930876d5SJean-Philippe Brucker void *ptr)
188930876d5SJean-Philippe Brucker {
189930876d5SJean-Philippe Brucker struct virtio_device *vdev = ptr;
190930876d5SJean-Philippe Brucker struct virtio_pci *vpci = vdev->virtio;
191930876d5SJean-Philippe Brucker u32 ioport_addr = virtio_pci__port_addr(vpci);
192930876d5SJean-Philippe Brucker u32 base_addr;
193930876d5SJean-Philippe Brucker
194930876d5SJean-Philippe Brucker if (addr >= ioport_addr &&
195930876d5SJean-Philippe Brucker addr < ioport_addr + pci__bar_size(&vpci->pci_hdr, 0))
196930876d5SJean-Philippe Brucker base_addr = ioport_addr;
197930876d5SJean-Philippe Brucker else
198930876d5SJean-Philippe Brucker base_addr = virtio_pci__mmio_addr(vpci);
199930876d5SJean-Philippe Brucker
200930876d5SJean-Philippe Brucker if (!is_write)
201930876d5SJean-Philippe Brucker virtio_pci__data_in(vcpu, vdev, addr - base_addr, data, len);
202930876d5SJean-Philippe Brucker else
203930876d5SJean-Philippe Brucker virtio_pci__data_out(vcpu, vdev, addr - base_addr, data, len);
204930876d5SJean-Philippe Brucker }
205930876d5SJean-Philippe Brucker
206