xref: /openbmc/qemu/hw/pci/pci_host.c (revision 35c7f525)
1 /*
2  * pci_host.c
3  *
4  * Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
5  *                    VA Linux Systems Japan K.K.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16 
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "qemu/osdep.h"
22 #include "hw/pci/pci.h"
23 #include "hw/pci/pci_bridge.h"
24 #include "hw/pci/pci_host.h"
25 #include "hw/qdev-properties.h"
26 #include "qemu/module.h"
27 #include "hw/pci/pci_bus.h"
28 #include "migration/vmstate.h"
29 #include "trace.h"
30 
31 /* debug PCI */
32 //#define DEBUG_PCI
33 
34 #ifdef DEBUG_PCI
35 #define PCI_DPRINTF(fmt, ...) \
36 do { printf("pci_host_data: " fmt , ## __VA_ARGS__); } while (0)
37 #else
38 #define PCI_DPRINTF(fmt, ...)
39 #endif
40 
41 /*
42  * PCI address
43  * bit 16 - 24: bus number
44  * bit  8 - 15: devfun number
45  * bit  0 -  7: offset in configuration space of a given pci device
46  */
47 
48 /* the helper function to get a PCIDevice* for a given pci address */
49 static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr)
50 {
51     uint8_t bus_num = addr >> 16;
52     uint8_t devfn = addr >> 8;
53 
54     return pci_find_device(bus, bus_num, devfn);
55 }
56 
57 static void pci_adjust_config_limit(PCIBus *bus, uint32_t *limit)
58 {
59     if ((*limit > PCI_CONFIG_SPACE_SIZE) &&
60         !pci_bus_allows_extended_config_space(bus)) {
61         *limit = PCI_CONFIG_SPACE_SIZE;
62     }
63 }
64 
65 void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr,
66                                   uint32_t limit, uint32_t val, uint32_t len)
67 {
68     pci_adjust_config_limit(pci_get_bus(pci_dev), &limit);
69     if (limit <= addr) {
70         return;
71     }
72 
73     assert(len <= 4);
74     /* non-zero functions are only exposed when function 0 is present,
75      * allowing direct removal of unexposed functions.
76      */
77     if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) {
78         return;
79     }
80 
81     trace_pci_cfg_write(pci_dev->name, PCI_SLOT(pci_dev->devfn),
82                         PCI_FUNC(pci_dev->devfn), addr, val);
83     pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr));
84 }
85 
86 uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr,
87                                      uint32_t limit, uint32_t len)
88 {
89     uint32_t ret;
90 
91     pci_adjust_config_limit(pci_get_bus(pci_dev), &limit);
92     if (limit <= addr) {
93         return ~0x0;
94     }
95 
96     assert(len <= 4);
97     /* non-zero functions are only exposed when function 0 is present,
98      * allowing direct removal of unexposed functions.
99      */
100     if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) {
101         return ~0x0;
102     }
103 
104     ret = pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr));
105     trace_pci_cfg_read(pci_dev->name, PCI_SLOT(pci_dev->devfn),
106                        PCI_FUNC(pci_dev->devfn), addr, ret);
107 
108     return ret;
109 }
110 
111 void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, unsigned len)
112 {
113     PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr);
114     uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1);
115 
116     if (!pci_dev) {
117         return;
118     }
119 
120     pci_host_config_write_common(pci_dev, config_addr, PCI_CONFIG_SPACE_SIZE,
121                                  val, len);
122 }
123 
124 uint32_t pci_data_read(PCIBus *s, uint32_t addr, unsigned len)
125 {
126     PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr);
127     uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1);
128 
129     if (!pci_dev) {
130         return ~0x0;
131     }
132 
133     return pci_host_config_read_common(pci_dev, config_addr,
134                                        PCI_CONFIG_SPACE_SIZE, len);
135 }
136 
137 static void pci_host_config_write(void *opaque, hwaddr addr,
138                                   uint64_t val, unsigned len)
139 {
140     PCIHostState *s = opaque;
141 
142     PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx64"\n",
143                 __func__, addr, len, val);
144     if (addr != 0 || len != 4) {
145         return;
146     }
147     s->config_reg = val;
148 }
149 
150 static uint64_t pci_host_config_read(void *opaque, hwaddr addr,
151                                      unsigned len)
152 {
153     PCIHostState *s = opaque;
154     uint32_t val = s->config_reg;
155 
156     PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %"PRIx32"\n",
157                 __func__, addr, len, val);
158     return val;
159 }
160 
161 static void pci_host_data_write(void *opaque, hwaddr addr,
162                                 uint64_t val, unsigned len)
163 {
164     PCIHostState *s = opaque;
165 
166     if (s->config_reg & (1u << 31))
167         pci_data_write(s->bus, s->config_reg | (addr & 3), val, len);
168 }
169 
170 static uint64_t pci_host_data_read(void *opaque,
171                                    hwaddr addr, unsigned len)
172 {
173     PCIHostState *s = opaque;
174 
175     if (!(s->config_reg & (1U << 31))) {
176         return 0xffffffff;
177     }
178     return pci_data_read(s->bus, s->config_reg | (addr & 3), len);
179 }
180 
181 const MemoryRegionOps pci_host_conf_le_ops = {
182     .read = pci_host_config_read,
183     .write = pci_host_config_write,
184     .endianness = DEVICE_LITTLE_ENDIAN,
185 };
186 
187 const MemoryRegionOps pci_host_conf_be_ops = {
188     .read = pci_host_config_read,
189     .write = pci_host_config_write,
190     .endianness = DEVICE_BIG_ENDIAN,
191 };
192 
193 const MemoryRegionOps pci_host_data_le_ops = {
194     .read = pci_host_data_read,
195     .write = pci_host_data_write,
196     .endianness = DEVICE_LITTLE_ENDIAN,
197 };
198 
199 const MemoryRegionOps pci_host_data_be_ops = {
200     .read = pci_host_data_read,
201     .write = pci_host_data_write,
202     .endianness = DEVICE_BIG_ENDIAN,
203 };
204 
205 static bool pci_host_needed(void *opaque)
206 {
207     PCIHostState *s = opaque;
208     return s->mig_enabled;
209 }
210 
211 const VMStateDescription vmstate_pcihost = {
212     .name = "PCIHost",
213     .needed = pci_host_needed,
214     .version_id = 1,
215     .minimum_version_id = 1,
216     .fields = (VMStateField[]) {
217         VMSTATE_UINT32(config_reg, PCIHostState),
218         VMSTATE_END_OF_LIST()
219     }
220 };
221 
222 static Property pci_host_properties_common[] = {
223     DEFINE_PROP_BOOL("x-config-reg-migration-enabled", PCIHostState,
224                      mig_enabled, true),
225     DEFINE_PROP_END_OF_LIST(),
226 };
227 
228 static void pci_host_class_init(ObjectClass *klass, void *data)
229 {
230     DeviceClass *dc = DEVICE_CLASS(klass);
231     device_class_set_props(dc, pci_host_properties_common);
232     dc->vmsd = &vmstate_pcihost;
233 }
234 
235 static const TypeInfo pci_host_type_info = {
236     .name = TYPE_PCI_HOST_BRIDGE,
237     .parent = TYPE_SYS_BUS_DEVICE,
238     .abstract = true,
239     .class_size = sizeof(PCIHostBridgeClass),
240     .instance_size = sizeof(PCIHostState),
241     .class_init = pci_host_class_init,
242 };
243 
244 static void pci_host_register_types(void)
245 {
246     type_register_static(&pci_host_type_info);
247 }
248 
249 type_init(pci_host_register_types)
250