xref: /kvmtool/virtio/pci-legacy.c (revision d560235f45568a08c6006c2e1bd25399f8e9fea8)
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