xref: /kvmtool/pci.c (revision 3fdf659d959233a543d1f29a358f8da994cec0fb)
160742802SPekka Enberg #include "kvm/pci.h"
260742802SPekka Enberg #include "kvm/ioport.h"
376f9c841SCyrill Gorcunov #include "kvm/util.h"
460742802SPekka Enberg 
5b30d05adSPekka Enberg #include <assert.h>
660742802SPekka Enberg 
7b30d05adSPekka Enberg #define PCI_MAX_DEVICES			256
8b30d05adSPekka Enberg 
9b30d05adSPekka Enberg static struct pci_device_header		*pci_devices[PCI_MAX_DEVICES];
10b30d05adSPekka Enberg 
114402a581SPekka Enberg static struct pci_config_address	pci_config_address;
1260742802SPekka Enberg 
13*3fdf659dSSasha Levin static void *pci_config_address_ptr(u16 port)
14ba824677SPekka Enberg {
15ba824677SPekka Enberg 	unsigned long offset;
16ba824677SPekka Enberg 	void *base;
17ba824677SPekka Enberg 
18ba824677SPekka Enberg 	offset		= port - PCI_CONFIG_ADDRESS;
19ba824677SPekka Enberg 	base		= &pci_config_address;
20ba824677SPekka Enberg 
21ba824677SPekka Enberg 	return base + offset;
22ba824677SPekka Enberg }
23ba824677SPekka Enberg 
24*3fdf659dSSasha Levin static bool pci_config_address_out(struct kvm *self, u16 port, void *data, int size, u32 count)
2560742802SPekka Enberg {
26ba824677SPekka Enberg 	void *p = pci_config_address_ptr(port);
2760742802SPekka Enberg 
28ba824677SPekka Enberg 	memcpy(p, data, size);
2960742802SPekka Enberg 
3060742802SPekka Enberg 	return true;
3160742802SPekka Enberg }
3260742802SPekka Enberg 
33*3fdf659dSSasha Levin static bool pci_config_address_in(struct kvm *self, u16 port, void *data, int size, u32 count)
3460742802SPekka Enberg {
35ba824677SPekka Enberg 	void *p = pci_config_address_ptr(port);
3660742802SPekka Enberg 
37ba824677SPekka Enberg 	memcpy(data, p, size);
3860742802SPekka Enberg 
3960742802SPekka Enberg 	return true;
4060742802SPekka Enberg }
4160742802SPekka Enberg 
42305b72ceSCyrill Gorcunov static struct ioport_operations pci_config_address_ops = {
43305b72ceSCyrill Gorcunov 	.io_in		= pci_config_address_in,
44305b72ceSCyrill Gorcunov 	.io_out		= pci_config_address_out,
4560742802SPekka Enberg };
4660742802SPekka Enberg 
47*3fdf659dSSasha Levin static bool pci_config_data_out(struct kvm *self, u16 port, void *data, int size, u32 count)
4860742802SPekka Enberg {
4960742802SPekka Enberg 	return true;
5060742802SPekka Enberg }
5160742802SPekka Enberg 
52*3fdf659dSSasha Levin static bool pci_device_exists(u8 bus_number, u8 device_number, u8 function_number)
5376f9c841SCyrill Gorcunov {
54b30d05adSPekka Enberg 	struct pci_device_header *dev;
55b30d05adSPekka Enberg 
5676f9c841SCyrill Gorcunov 	if (pci_config_address.bus_number != bus_number)
5776f9c841SCyrill Gorcunov 		return false;
5876f9c841SCyrill Gorcunov 
59b30d05adSPekka Enberg 	if (pci_config_address.function_number != function_number)
6076f9c841SCyrill Gorcunov 		return false;
6176f9c841SCyrill Gorcunov 
62b30d05adSPekka Enberg 	if (device_number >= PCI_MAX_DEVICES)
63b30d05adSPekka Enberg 		return false;
64b30d05adSPekka Enberg 
65b30d05adSPekka Enberg 	dev		= pci_devices[device_number];
66b30d05adSPekka Enberg 
67b30d05adSPekka Enberg 	return dev != NULL;
6876f9c841SCyrill Gorcunov }
6976f9c841SCyrill Gorcunov 
70*3fdf659dSSasha Levin static bool pci_config_data_in(struct kvm *self, u16 port, void *data, int size, u32 count)
7160742802SPekka Enberg {
72e4d2cea2SPekka Enberg 	unsigned long start;
73*3fdf659dSSasha Levin 	u8 dev_num;
74e4d2cea2SPekka Enberg 
75e4d2cea2SPekka Enberg 	/*
76e4d2cea2SPekka Enberg 	 * If someone accesses PCI configuration space offsets that are not
7770d08766SCyrill Gorcunov 	 * aligned to 4 bytes, it uses ioports to signify that.
78e4d2cea2SPekka Enberg 	 */
7970d08766SCyrill Gorcunov 	start = port - PCI_CONFIG_DATA;
80e4d2cea2SPekka Enberg 
81b30d05adSPekka Enberg 	dev_num		= pci_config_address.device_number;
82b30d05adSPekka Enberg 
83b30d05adSPekka Enberg 	if (pci_device_exists(0, dev_num, 0)) {
84598419d5SPekka Enberg 		unsigned long offset;
85598419d5SPekka Enberg 
86e4d2cea2SPekka Enberg 		offset = start + (pci_config_address.register_number << 2);
87598419d5SPekka Enberg 		if (offset < sizeof(struct pci_device_header)) {
88b30d05adSPekka Enberg 			void *p = pci_devices[dev_num];
89b30d05adSPekka Enberg 
9018ae021aSPekka Enberg 			memcpy(data, p + offset, size);
91598419d5SPekka Enberg 		} else
92598419d5SPekka Enberg 			memset(data, 0x00, size);
93e498ea08SPekka Enberg 	} else
94e498ea08SPekka Enberg 		memset(data, 0xff, size);
954c797ebcSCyrill Gorcunov 
9660742802SPekka Enberg 	return true;
9760742802SPekka Enberg }
9860742802SPekka Enberg 
99305b72ceSCyrill Gorcunov static struct ioport_operations pci_config_data_ops = {
100305b72ceSCyrill Gorcunov 	.io_in		= pci_config_data_in,
101305b72ceSCyrill Gorcunov 	.io_out		= pci_config_data_out,
10260742802SPekka Enberg };
10360742802SPekka Enberg 
104*3fdf659dSSasha Levin void pci__register(struct pci_device_header *dev, u8 dev_num)
105beb095ebSCyrill Gorcunov {
106b30d05adSPekka Enberg 	assert(dev_num < PCI_MAX_DEVICES);
107beb095ebSCyrill Gorcunov 
108b30d05adSPekka Enberg 	pci_devices[dev_num]	= dev;
109beb095ebSCyrill Gorcunov }
110beb095ebSCyrill Gorcunov 
11160742802SPekka Enberg void pci__init(void)
11260742802SPekka Enberg {
1133e553514SPekka Enberg 	ioport__register(PCI_CONFIG_DATA + 0, &pci_config_data_ops, 4);
1143e553514SPekka Enberg 	ioport__register(PCI_CONFIG_ADDRESS + 0, &pci_config_address_ops, 4);
11560742802SPekka Enberg }
116