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, | ... | ... |