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 "qemu/module.h" |
26 | #include "hw/pci/pci_bus.h" |
27 | #include "trace.h" |
28 | |
29 | /* debug PCI */ |
30 | //#define DEBUG_PCI |
31 | |
32 | #ifdef DEBUG_PCI |
33 | #define PCI_DPRINTF(fmt, ...) \ |
34 | do { printf("pci_host_data: " fmt , ## __VA_ARGS__); } while (0) |
35 | #else |
36 | #define PCI_DPRINTF(fmt, ...) |
37 | #endif |
38 | |
39 | /* |
40 | * PCI address |
41 | * bit 16 - 24: bus number |
42 | * bit 8 - 15: devfun number |
43 | * bit 0 - 7: offset in configuration space of a given pci device |
44 | */ |
45 | |
46 | /* the helper function to get a PCIDevice* for a given pci address */ |
47 | static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr) |
48 | { |
49 | uint8_t bus_num = addr >> 16; |
50 | uint8_t devfn = addr >> 8; |
51 | |
52 | return pci_find_device(bus, bus_num, devfn); |
53 | } |
54 | |
55 | static void pci_adjust_config_limit(PCIBus *bus, uint32_t *limit) |
56 | { |
57 | if ((*limit > PCI_CONFIG_SPACE_SIZE) && |
58 | !pci_bus_allows_extended_config_space(bus)) { |
59 | *limit = PCI_CONFIG_SPACE_SIZE; |
60 | } |
61 | } |
62 | |
63 | void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, |
64 | uint32_t limit, uint32_t val, uint32_t len) |
65 | { |
66 | pci_adjust_config_limit(pci_get_bus(pci_dev), &limit); |
67 | if (limit <= addr) { |
68 | return; |
69 | } |
70 | |
71 | assert(len <= 4); |
72 | /* non-zero functions are only exposed when function 0 is present, |
73 | * allowing direct removal of unexposed functions. |
74 | */ |
75 | if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) { |
76 | return; |
77 | } |
78 | |
79 | trace_pci_cfg_write(pci_dev->name, PCI_SLOT(pci_dev->devfn), |
80 | PCI_FUNC(pci_dev->devfn), addr, val); |
81 | pci_dev->config_write(pci_dev, addr, val, MIN(len, limit - addr)); |
82 | } |
83 | |
84 | uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, |
85 | uint32_t limit, uint32_t len) |
86 | { |
87 | uint32_t ret; |
88 | |
89 | pci_adjust_config_limit(pci_get_bus(pci_dev), &limit); |
90 | if (limit <= addr) { |
91 | return ~0x0; |
92 | } |
93 | |
94 | assert(len <= 4); |
95 | /* non-zero functions are only exposed when function 0 is present, |
96 | * allowing direct removal of unexposed functions. |
97 | */ |
98 | if (pci_dev->qdev.hotplugged && !pci_get_function_0(pci_dev)) { |
99 | return ~0x0; |
100 | } |
101 | |
102 | ret = pci_dev->config_read(pci_dev, addr, MIN(len, limit - addr)); |
103 | trace_pci_cfg_read(pci_dev->name, PCI_SLOT(pci_dev->devfn), |
104 | PCI_FUNC(pci_dev->devfn), addr, ret); |
105 | |
106 | return ret; |
107 | } |
108 | |
109 | void pci_data_write(PCIBus *s, uint32_t addr, uint32_t val, int len) |
110 | { |
111 | PCIDevice *pci_dev = pci_dev_find_by_addr(s, addr); |
112 | uint32_t config_addr = addr & (PCI_CONFIG_SPACE_SIZE - 1); |
113 | |
114 | if (!pci_dev) { |
115 | return; |
116 | } |
117 | |
118 | PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n" , |
119 | __func__, pci_dev->name, config_addr, val, len); |
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, int 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 | uint32_t val; |
129 | |
130 | if (!pci_dev) { |
131 | return ~0x0; |
132 | } |
133 | |
134 | val = pci_host_config_read_common(pci_dev, config_addr, |
135 | PCI_CONFIG_SPACE_SIZE, len); |
136 | PCI_DPRINTF("%s: %s: addr=%02" PRIx32" val=%08" PRIx32" len=%d\n" , |
137 | __func__, pci_dev->name, config_addr, val, len); |
138 | |
139 | return val; |
140 | } |
141 | |
142 | static void pci_host_config_write(void *opaque, hwaddr addr, |
143 | uint64_t val, unsigned len) |
144 | { |
145 | PCIHostState *s = opaque; |
146 | |
147 | PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %" PRIx64"\n" , |
148 | __func__, addr, len, val); |
149 | if (addr != 0 || len != 4) { |
150 | return; |
151 | } |
152 | s->config_reg = val; |
153 | } |
154 | |
155 | static uint64_t pci_host_config_read(void *opaque, hwaddr addr, |
156 | unsigned len) |
157 | { |
158 | PCIHostState *s = opaque; |
159 | uint32_t val = s->config_reg; |
160 | |
161 | PCI_DPRINTF("%s addr " TARGET_FMT_plx " len %d val %" PRIx32"\n" , |
162 | __func__, addr, len, val); |
163 | return val; |
164 | } |
165 | |
166 | static void pci_host_data_write(void *opaque, hwaddr addr, |
167 | uint64_t val, unsigned len) |
168 | { |
169 | PCIHostState *s = opaque; |
170 | PCI_DPRINTF("write addr " TARGET_FMT_plx " len %d val %x\n" , |
171 | addr, len, (unsigned)val); |
172 | if (s->config_reg & (1u << 31)) |
173 | pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); |
174 | } |
175 | |
176 | static uint64_t pci_host_data_read(void *opaque, |
177 | hwaddr addr, unsigned len) |
178 | { |
179 | PCIHostState *s = opaque; |
180 | uint32_t val; |
181 | if (!(s->config_reg & (1U << 31))) { |
182 | return 0xffffffff; |
183 | } |
184 | val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); |
185 | PCI_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n" , |
186 | addr, len, val); |
187 | return val; |
188 | } |
189 | |
190 | const MemoryRegionOps pci_host_conf_le_ops = { |
191 | .read = pci_host_config_read, |
192 | .write = pci_host_config_write, |
193 | .endianness = DEVICE_LITTLE_ENDIAN, |
194 | }; |
195 | |
196 | const MemoryRegionOps pci_host_conf_be_ops = { |
197 | .read = pci_host_config_read, |
198 | .write = pci_host_config_write, |
199 | .endianness = DEVICE_BIG_ENDIAN, |
200 | }; |
201 | |
202 | const MemoryRegionOps pci_host_data_le_ops = { |
203 | .read = pci_host_data_read, |
204 | .write = pci_host_data_write, |
205 | .endianness = DEVICE_LITTLE_ENDIAN, |
206 | }; |
207 | |
208 | const MemoryRegionOps pci_host_data_be_ops = { |
209 | .read = pci_host_data_read, |
210 | .write = pci_host_data_write, |
211 | .endianness = DEVICE_BIG_ENDIAN, |
212 | }; |
213 | |
214 | static const TypeInfo pci_host_type_info = { |
215 | .name = TYPE_PCI_HOST_BRIDGE, |
216 | .parent = TYPE_SYS_BUS_DEVICE, |
217 | .abstract = true, |
218 | .class_size = sizeof(PCIHostBridgeClass), |
219 | .instance_size = sizeof(PCIHostState), |
220 | }; |
221 | |
222 | static void pci_host_register_types(void) |
223 | { |
224 | type_register_static(&pci_host_type_info); |
225 | } |
226 | |
227 | type_init(pci_host_register_types) |
228 | |