1 #include "kvm/virtio-pci.h"
2
3 #include "kvm/ioport.h"
4 #include "kvm/virtio.h"
5
virtio_pci__specific_data_in(struct kvm * kvm,struct virtio_device * vdev,void * data,u32 size,unsigned long offset)6 static bool virtio_pci__specific_data_in(struct kvm *kvm, struct virtio_device *vdev,
7 void *data, u32 size, unsigned long offset)
8 {
9 u32 config_offset;
10 struct virtio_pci *vpci = vdev->virtio;
11 int type = virtio__get_dev_specific_field(offset - 20,
12 virtio_pci__msix_enabled(vpci),
13 &config_offset);
14 if (type == VIRTIO_PCI_O_MSIX) {
15 switch (offset) {
16 case VIRTIO_MSI_CONFIG_VECTOR:
17 ioport__write16(data, vpci->config_vector);
18 break;
19 case VIRTIO_MSI_QUEUE_VECTOR:
20 ioport__write16(data, vpci->vq_vector[vpci->queue_selector]);
21 break;
22 };
23
24 return true;
25 } else if (type == VIRTIO_PCI_O_CONFIG) {
26 return virtio_access_config(kvm, vdev, vpci->dev, config_offset,
27 data, size, false);
28 }
29
30 return false;
31 }
32
virtio_pci__data_in(struct kvm_cpu * vcpu,struct virtio_device * vdev,unsigned long offset,void * data,u32 size)33 static bool virtio_pci__data_in(struct kvm_cpu *vcpu, struct virtio_device *vdev,
34 unsigned long offset, void *data, u32 size)
35 {
36 bool ret = true;
37 struct virtio_pci *vpci;
38 struct virt_queue *vq;
39 struct kvm *kvm;
40 u32 val;
41
42 kvm = vcpu->kvm;
43 vpci = vdev->virtio;
44
45 switch (offset) {
46 case VIRTIO_PCI_HOST_FEATURES:
47 val = vdev->ops->get_host_features(kvm, vpci->dev);
48 ioport__write32(data, val);
49 break;
50 case VIRTIO_PCI_QUEUE_PFN:
51 vq = vdev->ops->get_vq(kvm, vpci->dev, vpci->queue_selector);
52 ioport__write32(data, vq->vring_addr.pfn);
53 break;
54 case VIRTIO_PCI_QUEUE_NUM:
55 val = vdev->ops->get_size_vq(kvm, vpci->dev, vpci->queue_selector);
56 ioport__write16(data, val);
57 break;
58 case VIRTIO_PCI_STATUS:
59 ioport__write8(data, vpci->status);
60 break;
61 case VIRTIO_PCI_ISR:
62 ioport__write8(data, vpci->isr);
63 kvm__irq_line(kvm, vpci->legacy_irq_line, VIRTIO_IRQ_LOW);
64 vpci->isr = 0;
65 break;
66 default:
67 ret = virtio_pci__specific_data_in(kvm, vdev, data, size, offset);
68 break;
69 };
70
71 return ret;
72 }
73
virtio_pci__specific_data_out(struct kvm * kvm,struct virtio_device * vdev,void * data,u32 size,unsigned long offset)74 static bool virtio_pci__specific_data_out(struct kvm *kvm, struct virtio_device *vdev,
75 void *data, u32 size, unsigned long offset)
76 {
77 struct virtio_pci *vpci = vdev->virtio;
78 u32 config_offset, vec;
79 int gsi;
80 int type = virtio__get_dev_specific_field(offset - 20, virtio_pci__msix_enabled(vpci),
81 &config_offset);
82 if (type == VIRTIO_PCI_O_MSIX) {
83 switch (offset) {
84 case VIRTIO_MSI_CONFIG_VECTOR:
85 vec = vpci->config_vector = ioport__read16(data);
86
87 gsi = virtio_pci__add_msix_route(vpci, vec);
88 if (gsi < 0)
89 break;
90
91 vpci->config_gsi = gsi;
92 break;
93 case VIRTIO_MSI_QUEUE_VECTOR:
94 vec = ioport__read16(data);
95 vpci->vq_vector[vpci->queue_selector] = vec;
96
97 gsi = virtio_pci__add_msix_route(vpci, vec);
98 if (gsi < 0)
99 break;
100
101 vpci->gsis[vpci->queue_selector] = gsi;
102 if (vdev->ops->notify_vq_gsi)
103 vdev->ops->notify_vq_gsi(kvm, vpci->dev,
104 vpci->queue_selector,
105 gsi);
106 break;
107 };
108
109 return true;
110 } else if (type == VIRTIO_PCI_O_CONFIG) {
111 return virtio_access_config(kvm, vdev, vpci->dev, config_offset,
112 data, size, true);
113 }
114
115 return false;
116 }
117
virtio_pci__data_out(struct kvm_cpu * vcpu,struct virtio_device * vdev,unsigned long offset,void * data,u32 size)118 static bool virtio_pci__data_out(struct kvm_cpu *vcpu, struct virtio_device *vdev,
119 unsigned long offset, void *data, u32 size)
120 {
121 bool ret = true;
122 struct virtio_pci *vpci;
123 struct virt_queue *vq;
124 struct kvm *kvm;
125 u32 val;
126 unsigned int vq_count;
127
128 kvm = vcpu->kvm;
129 vpci = vdev->virtio;
130 vq_count = vdev->ops->get_vq_count(kvm, vpci->dev);
131
132 switch (offset) {
133 case VIRTIO_PCI_GUEST_FEATURES:
134 val = ioport__read32(data);
135 virtio_set_guest_features(kvm, vdev, vpci->dev, val);
136 break;
137 case VIRTIO_PCI_QUEUE_PFN:
138 val = ioport__read32(data);
139 if (val) {
140 vq = vdev->ops->get_vq(kvm, vpci->dev,
141 vpci->queue_selector);
142 vq->vring_addr = (struct vring_addr) {
143 .legacy = true,
144 .pfn = val,
145 .align = VIRTIO_PCI_VRING_ALIGN,
146 .pgsize = 1 << VIRTIO_PCI_QUEUE_ADDR_SHIFT,
147 };
148 virtio_pci_init_vq(kvm, vdev, vpci->queue_selector);
149 } else {
150 virtio_pci_exit_vq(kvm, vdev, vpci->queue_selector);
151 }
152 break;
153 case VIRTIO_PCI_QUEUE_SEL:
154 val = ioport__read16(data);
155 if (val >= vq_count) {
156 WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n",
157 val, vq_count);
158 return false;
159 }
160 vpci->queue_selector = val;
161 break;
162 case VIRTIO_PCI_QUEUE_NOTIFY:
163 val = ioport__read16(data);
164 if (val >= vq_count) {
165 WARN_ONCE(1, "QUEUE_SEL value (%u) is larger than VQ count (%u)\n",
166 val, vq_count);
167 return false;
168 }
169 vdev->ops->notify_vq(kvm, vpci->dev, val);
170 break;
171 case VIRTIO_PCI_STATUS:
172 vpci->status = ioport__read8(data);
173 if (!vpci->status) /* Sample endianness on reset */
174 vdev->endian = kvm_cpu__get_endianness(vcpu);
175 virtio_notify_status(kvm, vdev, vpci->dev, vpci->status);
176 break;
177 default:
178 ret = virtio_pci__specific_data_out(kvm, vdev, data, size, offset);
179 break;
180 };
181
182 return ret;
183 }
184
virtio_pci_legacy__io_mmio_callback(struct kvm_cpu * vcpu,u64 addr,u8 * data,u32 len,u8 is_write,void * ptr)185 void virtio_pci_legacy__io_mmio_callback(struct kvm_cpu *vcpu, u64 addr,
186 u8 *data, u32 len, u8 is_write,
187 void *ptr)
188 {
189 struct virtio_device *vdev = ptr;
190 struct virtio_pci *vpci = vdev->virtio;
191 u32 ioport_addr = virtio_pci__port_addr(vpci);
192 u32 base_addr;
193
194 if (addr >= ioport_addr &&
195 addr < ioport_addr + pci__bar_size(&vpci->pci_hdr, 0))
196 base_addr = ioport_addr;
197 else
198 base_addr = virtio_pci__mmio_addr(vpci);
199
200 if (!is_write)
201 virtio_pci__data_in(vcpu, vdev, addr - base_addr, data, len);
202 else
203 virtio_pci__data_out(vcpu, vdev, addr - base_addr, data, len);
204 }
205
206