Commit 6f4cbd3950b10ceec95fcda3946839be2e009027
Committed by
Anthony Liguori
1 parent
14e12559
qemu/pci: add routines to manage PCI capabilities
Add routines to manage PCI capability list. First user will be MSI-X. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Showing
2 changed files
with
90 additions
and
2 deletions
hw/pci.c
| ... | ... | @@ -164,7 +164,6 @@ int pci_device_load(PCIDevice *s, QEMUFile *f) |
| 164 | 164 | if (version_id >= 2) |
| 165 | 165 | for (i = 0; i < 4; i ++) |
| 166 | 166 | s->irq_state[i] = qemu_get_be32(f); |
| 167 | - | |
| 168 | 167 | return 0; |
| 169 | 168 | } |
| 170 | 169 | |
| ... | ... | @@ -895,3 +894,76 @@ PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) |
| 895 | 894 | |
| 896 | 895 | return (PCIDevice *)dev; |
| 897 | 896 | } |
| 897 | + | |
| 898 | +static int pci_find_space(PCIDevice *pdev, uint8_t size) | |
| 899 | +{ | |
| 900 | + int offset = PCI_CONFIG_HEADER_SIZE; | |
| 901 | + int i; | |
| 902 | + for (i = PCI_CONFIG_HEADER_SIZE; i < PCI_CONFIG_SPACE_SIZE; ++i) | |
| 903 | + if (pdev->used[i]) | |
| 904 | + offset = i + 1; | |
| 905 | + else if (i - offset + 1 == size) | |
| 906 | + return offset; | |
| 907 | + return 0; | |
| 908 | +} | |
| 909 | + | |
| 910 | +static uint8_t pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id, | |
| 911 | + uint8_t *prev_p) | |
| 912 | +{ | |
| 913 | + uint8_t next, prev; | |
| 914 | + | |
| 915 | + if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST)) | |
| 916 | + return 0; | |
| 917 | + | |
| 918 | + for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]); | |
| 919 | + prev = next + PCI_CAP_LIST_NEXT) | |
| 920 | + if (pdev->config[next + PCI_CAP_LIST_ID] == cap_id) | |
| 921 | + break; | |
| 922 | + | |
| 923 | + if (prev_p) | |
| 924 | + *prev_p = prev; | |
| 925 | + return next; | |
| 926 | +} | |
| 927 | + | |
| 928 | +/* Reserve space and add capability to the linked list in pci config space */ | |
| 929 | +int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) | |
| 930 | +{ | |
| 931 | + uint8_t offset = pci_find_space(pdev, size); | |
| 932 | + uint8_t *config = pdev->config + offset; | |
| 933 | + if (!offset) | |
| 934 | + return -ENOSPC; | |
| 935 | + config[PCI_CAP_LIST_ID] = cap_id; | |
| 936 | + config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST]; | |
| 937 | + pdev->config[PCI_CAPABILITY_LIST] = offset; | |
| 938 | + pdev->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST; | |
| 939 | + memset(pdev->used + offset, 0xFF, size); | |
| 940 | + /* Make capability read-only by default */ | |
| 941 | + memset(pdev->wmask + offset, 0, size); | |
| 942 | + return offset; | |
| 943 | +} | |
| 944 | + | |
| 945 | +/* Unlink capability from the pci config space. */ | |
| 946 | +void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) | |
| 947 | +{ | |
| 948 | + uint8_t prev, offset = pci_find_capability_list(pdev, cap_id, &prev); | |
| 949 | + if (!offset) | |
| 950 | + return; | |
| 951 | + pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; | |
| 952 | + /* Make capability writeable again */ | |
| 953 | + memset(pdev->wmask + offset, 0xff, size); | |
| 954 | + memset(pdev->used + offset, 0, size); | |
| 955 | + | |
| 956 | + if (!pdev->config[PCI_CAPABILITY_LIST]) | |
| 957 | + pdev->config[PCI_STATUS] &= ~PCI_STATUS_CAP_LIST; | |
| 958 | +} | |
| 959 | + | |
| 960 | +/* Reserve space for capability at a known offset (to call after load). */ | |
| 961 | +void pci_reserve_capability(PCIDevice *pdev, uint8_t offset, uint8_t size) | |
| 962 | +{ | |
| 963 | + memset(pdev->used + offset, 0xff, size); | |
| 964 | +} | |
| 965 | + | |
| 966 | +uint8_t pci_find_capability(PCIDevice *pdev, uint8_t cap_id) | |
| 967 | +{ | |
| 968 | + return pci_find_capability_list(pdev, cap_id, NULL); | |
| 969 | +} | ... | ... |
hw/pci.h
| ... | ... | @@ -121,6 +121,10 @@ typedef struct PCIIORegion { |
| 121 | 121 | #define PCI_MIN_GNT 0x3e /* 8 bits */ |
| 122 | 122 | #define PCI_MAX_LAT 0x3f /* 8 bits */ |
| 123 | 123 | |
| 124 | +/* Capability lists */ | |
| 125 | +#define PCI_CAP_LIST_ID 0 /* Capability ID */ | |
| 126 | +#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ | |
| 127 | + | |
| 124 | 128 | #define PCI_REVISION 0x08 /* obsolete, use PCI_REVISION_ID */ |
| 125 | 129 | #define PCI_SUBVENDOR_ID 0x2c /* obsolete, use PCI_SUBSYSTEM_VENDOR_ID */ |
| 126 | 130 | #define PCI_SUBDEVICE_ID 0x2e /* obsolete, use PCI_SUBSYSTEM_ID */ |
| ... | ... | @@ -128,7 +132,7 @@ typedef struct PCIIORegion { |
| 128 | 132 | /* Bits in the PCI Status Register (PCI 2.3 spec) */ |
| 129 | 133 | #define PCI_STATUS_RESERVED1 0x007 |
| 130 | 134 | #define PCI_STATUS_INT_STATUS 0x008 |
| 131 | -#define PCI_STATUS_CAPABILITIES 0x010 | |
| 135 | +#define PCI_STATUS_CAP_LIST 0x010 | |
| 132 | 136 | #define PCI_STATUS_66MHZ 0x020 |
| 133 | 137 | #define PCI_STATUS_RESERVED2 0x040 |
| 134 | 138 | #define PCI_STATUS_FAST_BACK 0x080 |
| ... | ... | @@ -158,6 +162,9 @@ struct PCIDevice { |
| 158 | 162 | /* Used to implement R/W bytes */ |
| 159 | 163 | uint8_t wmask[PCI_CONFIG_SPACE_SIZE]; |
| 160 | 164 | |
| 165 | + /* Used to allocate config space for capabilities. */ | |
| 166 | + uint8_t used[PCI_CONFIG_SPACE_SIZE]; | |
| 167 | + | |
| 161 | 168 | /* the following fields are read only */ |
| 162 | 169 | PCIBus *bus; |
| 163 | 170 | int devfn; |
| ... | ... | @@ -186,6 +193,15 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, |
| 186 | 193 | uint32_t size, int type, |
| 187 | 194 | PCIMapIORegionFunc *map_func); |
| 188 | 195 | |
| 196 | +int pci_add_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size); | |
| 197 | + | |
| 198 | +void pci_del_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size); | |
| 199 | + | |
| 200 | +void pci_reserve_capability(PCIDevice *pci_dev, uint8_t offset, uint8_t size); | |
| 201 | + | |
| 202 | +uint8_t pci_find_capability(PCIDevice *pci_dev, uint8_t cap_id); | |
| 203 | + | |
| 204 | + | |
| 189 | 205 | uint32_t pci_default_read_config(PCIDevice *d, |
| 190 | 206 | uint32_t address, int len); |
| 191 | 207 | void pci_default_write_config(PCIDevice *d, | ... | ... |