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