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