xref: /kvmtool/pci.c (revision ba8246776e98668cf494837822903917c554257b)
160742802SPekka Enberg #include "kvm/pci.h"
260742802SPekka Enberg #include "kvm/ioport.h"
376f9c841SCyrill Gorcunov #include "kvm/util.h"
460742802SPekka Enberg 
560742802SPekka Enberg #include <stdint.h>
660742802SPekka Enberg 
74402a581SPekka Enberg static struct pci_config_address pci_config_address;
860742802SPekka Enberg 
9*ba824677SPekka Enberg static void *pci_config_address_ptr(uint16_t port)
10*ba824677SPekka Enberg {
11*ba824677SPekka Enberg 	unsigned long offset;
12*ba824677SPekka Enberg 	void *base;
13*ba824677SPekka Enberg 
14*ba824677SPekka Enberg 	offset		= port - PCI_CONFIG_ADDRESS;
15*ba824677SPekka Enberg 	base		= &pci_config_address;
16*ba824677SPekka Enberg 
17*ba824677SPekka Enberg 	return base + offset;
18*ba824677SPekka Enberg }
19*ba824677SPekka Enberg 
20305b72ceSCyrill Gorcunov static bool pci_config_address_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
2160742802SPekka Enberg {
22*ba824677SPekka Enberg 	void *p = pci_config_address_ptr(port);
2360742802SPekka Enberg 
24*ba824677SPekka Enberg 	memcpy(p, data, size);
2560742802SPekka Enberg 
2660742802SPekka Enberg 	return true;
2760742802SPekka Enberg }
2860742802SPekka Enberg 
29305b72ceSCyrill Gorcunov static bool pci_config_address_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
3060742802SPekka Enberg {
31*ba824677SPekka Enberg 	void *p = pci_config_address_ptr(port);
3260742802SPekka Enberg 
33*ba824677SPekka Enberg 	memcpy(data, p, size);
3460742802SPekka Enberg 
3560742802SPekka Enberg 	return true;
3660742802SPekka Enberg }
3760742802SPekka Enberg 
38305b72ceSCyrill Gorcunov static struct ioport_operations pci_config_address_ops = {
39305b72ceSCyrill Gorcunov 	.io_in		= pci_config_address_in,
40305b72ceSCyrill Gorcunov 	.io_out		= pci_config_address_out,
4160742802SPekka Enberg };
4260742802SPekka Enberg 
43305b72ceSCyrill Gorcunov static bool pci_config_data_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
4460742802SPekka Enberg {
4560742802SPekka Enberg 	return true;
4660742802SPekka Enberg }
4760742802SPekka Enberg 
4876f9c841SCyrill Gorcunov #define PCI_VENDOR_ID_REDHAT_QUMRANET		0x1af4
4976f9c841SCyrill Gorcunov #define PCI_DEVICE_ID_VIRTIO_BLK		0x1001
5078732ce2SAsias He #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
5178732ce2SAsias He #define PCI_SUBSYSTEM_ID_VIRTIO_BLK		0x0002
5276f9c841SCyrill Gorcunov 
5376f9c841SCyrill Gorcunov static struct pci_device_header virtio_device = {
5476f9c841SCyrill Gorcunov 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
5576f9c841SCyrill Gorcunov 	.device_id		= PCI_DEVICE_ID_VIRTIO_BLK,
5676f9c841SCyrill Gorcunov 	.header_type		= PCI_HEADER_TYPE_NORMAL,
5778732ce2SAsias He 	.revision_id		= 0,
5878732ce2SAsias He 	.class			= 0x010000,
5978732ce2SAsias He 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
6078732ce2SAsias He 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_BLK,
6178732ce2SAsias He 	.bar[0]			= IOPORT_VIRTIO | PCI_BASE_ADDRESS_SPACE_IO,
6276f9c841SCyrill Gorcunov };
6376f9c841SCyrill Gorcunov 
6476f9c841SCyrill Gorcunov static bool pci_device_matches(uint8_t bus_number, uint8_t device_number, uint8_t function_number)
6576f9c841SCyrill Gorcunov {
6676f9c841SCyrill Gorcunov 	if (pci_config_address.bus_number != bus_number)
6776f9c841SCyrill Gorcunov 		return false;
6876f9c841SCyrill Gorcunov 
6976f9c841SCyrill Gorcunov 	if (pci_config_address.device_number != device_number)
7076f9c841SCyrill Gorcunov 		return false;
7176f9c841SCyrill Gorcunov 
7276f9c841SCyrill Gorcunov 	return pci_config_address.function_number == function_number;
7376f9c841SCyrill Gorcunov }
7476f9c841SCyrill Gorcunov 
75305b72ceSCyrill Gorcunov static bool pci_config_data_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
7660742802SPekka Enberg {
77e4d2cea2SPekka Enberg 	unsigned long start;
78e4d2cea2SPekka Enberg 
79e4d2cea2SPekka Enberg 	/*
80e4d2cea2SPekka Enberg 	 * If someone accesses PCI configuration space offsets that are not
8170d08766SCyrill Gorcunov 	 * aligned to 4 bytes, it uses ioports to signify that.
82e4d2cea2SPekka Enberg 	 */
8370d08766SCyrill Gorcunov 	start = port - PCI_CONFIG_DATA;
84e4d2cea2SPekka Enberg 
85e498ea08SPekka Enberg 	if (pci_device_matches(0, 1, 0)) {
86598419d5SPekka Enberg 		unsigned long offset;
87598419d5SPekka Enberg 
88e4d2cea2SPekka Enberg 		offset = start + (pci_config_address.register_number << 2);
89598419d5SPekka Enberg 		if (offset < sizeof(struct pci_device_header)) {
90e498ea08SPekka Enberg 			void *p = &virtio_device;
9118ae021aSPekka Enberg 			memcpy(data, p + offset, size);
92598419d5SPekka Enberg 		} else
93598419d5SPekka Enberg 			memset(data, 0x00, size);
94e498ea08SPekka Enberg 	} else
95e498ea08SPekka Enberg 		memset(data, 0xff, size);
964c797ebcSCyrill Gorcunov 
9760742802SPekka Enberg 	return true;
9860742802SPekka Enberg }
9960742802SPekka Enberg 
100305b72ceSCyrill Gorcunov static struct ioport_operations pci_config_data_ops = {
101305b72ceSCyrill Gorcunov 	.io_in		= pci_config_data_in,
102305b72ceSCyrill Gorcunov 	.io_out		= pci_config_data_out,
10360742802SPekka Enberg };
10460742802SPekka Enberg 
105beb095ebSCyrill Gorcunov static bool virtio_in(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
106beb095ebSCyrill Gorcunov {
107beb095ebSCyrill Gorcunov 	return true;
108beb095ebSCyrill Gorcunov }
109beb095ebSCyrill Gorcunov 
110beb095ebSCyrill Gorcunov static bool virtio_out(struct kvm *self, uint16_t port, void *data, int size, uint32_t count)
111beb095ebSCyrill Gorcunov {
112beb095ebSCyrill Gorcunov 	return true;
113beb095ebSCyrill Gorcunov }
114beb095ebSCyrill Gorcunov 
115beb095ebSCyrill Gorcunov static struct ioport_operations virtio_io_ops = {
116beb095ebSCyrill Gorcunov 	.io_in		= virtio_in,
117beb095ebSCyrill Gorcunov 	.io_out		= virtio_out,
118beb095ebSCyrill Gorcunov };
119beb095ebSCyrill Gorcunov 
12060742802SPekka Enberg void pci__init(void)
12160742802SPekka Enberg {
122beb095ebSCyrill Gorcunov 	ioport__register(IOPORT_VIRTIO, &virtio_io_ops);
123beb095ebSCyrill Gorcunov 
12470d08766SCyrill Gorcunov 	ioport__register(PCI_CONFIG_DATA + 0, &pci_config_data_ops);
12570d08766SCyrill Gorcunov 	ioport__register(PCI_CONFIG_DATA + 1, &pci_config_data_ops);
12670d08766SCyrill Gorcunov 	ioport__register(PCI_CONFIG_DATA + 2, &pci_config_data_ops);
12770d08766SCyrill Gorcunov 	ioport__register(PCI_CONFIG_DATA + 3, &pci_config_data_ops);
12870d08766SCyrill Gorcunov 
129*ba824677SPekka Enberg 	ioport__register(PCI_CONFIG_ADDRESS + 0, &pci_config_address_ops);
130*ba824677SPekka Enberg 	ioport__register(PCI_CONFIG_ADDRESS + 1, &pci_config_address_ops);
131*ba824677SPekka Enberg 	ioport__register(PCI_CONFIG_ADDRESS + 2, &pci_config_address_ops);
132*ba824677SPekka Enberg 	ioport__register(PCI_CONFIG_ADDRESS + 3, &pci_config_address_ops);
13360742802SPekka Enberg }
134