]>
Commit | Line | Data |
---|---|---|
f7d6f3fa MA |
1 | /* |
2 | * Generic PCI Express Root Port emulation | |
3 | * | |
4 | * Copyright (C) 2017 Red Hat Inc | |
5 | * | |
6 | * Authors: | |
7 | * Marcel Apfelbaum <marcel@redhat.com> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | */ | |
12 | ||
13 | #include "qemu/osdep.h" | |
14 | #include "qapi/error.h" | |
0b8fa32f | 15 | #include "qemu/module.h" |
f7d6f3fa MA |
16 | #include "hw/pci/msix.h" |
17 | #include "hw/pci/pcie_port.h" | |
a27bd6c7 | 18 | #include "hw/qdev-properties.h" |
ce35e229 | 19 | #include "hw/qdev-properties-system.h" |
d6454270 | 20 | #include "migration/vmstate.h" |
db1015e9 | 21 | #include "qom/object.h" |
f7d6f3fa MA |
22 | |
23 | #define TYPE_GEN_PCIE_ROOT_PORT "pcie-root-port" | |
8063396b | 24 | OBJECT_DECLARE_SIMPLE_TYPE(GenPCIERootPort, GEN_PCIE_ROOT_PORT) |
f7d6f3fa MA |
25 | |
26 | #define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100 | |
e07fb4b5 KO |
27 | #define GEN_PCIE_ROOT_PORT_ACS_OFFSET \ |
28 | (GEN_PCIE_ROOT_PORT_AER_OFFSET + PCI_ERR_SIZEOF) | |
29 | ||
f7d6f3fa | 30 | #define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR 1 |
e2a6290a | 31 | #define GEN_PCIE_ROOT_DEFAULT_IO_RANGE 4096 |
f7d6f3fa | 32 | |
db1015e9 | 33 | struct GenPCIERootPort { |
bc277a52 MA |
34 | /*< private >*/ |
35 | PCIESlot parent_obj; | |
36 | /*< public >*/ | |
37 | ||
38 | bool migrate_msix; | |
226263fb | 39 | |
9e899399 JL |
40 | /* additional resources to reserve */ |
41 | PCIResReserve res_reserve; | |
db1015e9 | 42 | }; |
bc277a52 | 43 | |
f7d6f3fa MA |
44 | static uint8_t gen_rp_aer_vector(const PCIDevice *d) |
45 | { | |
46 | return 0; | |
47 | } | |
48 | ||
49 | static int gen_rp_interrupts_init(PCIDevice *d, Error **errp) | |
50 | { | |
51 | int rc; | |
52 | ||
ee640c62 | 53 | rc = msix_init_exclusive_bar(d, GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR, 0, errp); |
f7d6f3fa MA |
54 | |
55 | if (rc < 0) { | |
56 | assert(rc == -ENOTSUP); | |
f7d6f3fa MA |
57 | } else { |
58 | msix_vector_use(d, 0); | |
59 | } | |
60 | ||
61 | return rc; | |
62 | } | |
63 | ||
64 | static void gen_rp_interrupts_uninit(PCIDevice *d) | |
65 | { | |
66 | msix_uninit_exclusive_bar(d); | |
67 | } | |
68 | ||
bc277a52 MA |
69 | static bool gen_rp_test_migrate_msix(void *opaque, int version_id) |
70 | { | |
71 | GenPCIERootPort *rp = opaque; | |
72 | ||
73 | return rp->migrate_msix; | |
74 | } | |
75 | ||
226263fb AB |
76 | static void gen_rp_realize(DeviceState *dev, Error **errp) |
77 | { | |
78 | PCIDevice *d = PCI_DEVICE(dev); | |
e2a6290a | 79 | PCIESlot *s = PCIE_SLOT(d); |
226263fb AB |
80 | GenPCIERootPort *grp = GEN_PCIE_ROOT_PORT(d); |
81 | PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d); | |
fced4d00 | 82 | Error *local_err = NULL; |
226263fb | 83 | |
fced4d00 MA |
84 | rpc->parent_realize(dev, &local_err); |
85 | if (local_err) { | |
86 | error_propagate(errp, local_err); | |
87 | return; | |
88 | } | |
226263fb | 89 | |
1d77e157 IM |
90 | /* |
91 | * reserving IO space led to worse issues in 6.1, when this hunk was | |
92 | * introduced. (see commit: 211afe5c69b59). Keep this broken for 6.1 | |
93 | * machine type ABI compatibility only | |
94 | */ | |
95 | if (s->hide_native_hotplug_cap && grp->res_reserve.io == -1 && s->hotplug) { | |
e2a6290a MA |
96 | grp->res_reserve.io = GEN_PCIE_ROOT_DEFAULT_IO_RANGE; |
97 | } | |
9e899399 JL |
98 | int rc = pci_bridge_qemu_reserve_cap_init(d, 0, |
99 | grp->res_reserve, errp); | |
226263fb AB |
100 | |
101 | if (rc < 0) { | |
102 | rpc->parent_class.exit(d); | |
103 | return; | |
104 | } | |
8e36c336 | 105 | |
9e899399 | 106 | if (!grp->res_reserve.io) { |
8e36c336 MA |
107 | pci_word_test_and_clear_mask(d->wmask + PCI_COMMAND, |
108 | PCI_COMMAND_IO); | |
109 | d->wmask[PCI_IO_BASE] = 0; | |
110 | d->wmask[PCI_IO_LIMIT] = 0; | |
111 | } | |
226263fb AB |
112 | } |
113 | ||
f7d6f3fa MA |
114 | static const VMStateDescription vmstate_rp_dev = { |
115 | .name = "pcie-root-port", | |
9d6b9db1 | 116 | .priority = MIG_PRI_PCI_BUS, |
f7d6f3fa MA |
117 | .version_id = 1, |
118 | .minimum_version_id = 1, | |
119 | .post_load = pcie_cap_slot_post_load, | |
f026c578 | 120 | .fields = (const VMStateField[]) { |
f7d6f3fa MA |
121 | VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot), |
122 | VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log, | |
123 | PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog), | |
bc277a52 MA |
124 | VMSTATE_MSIX_TEST(parent_obj.parent_obj.parent_obj.parent_obj, |
125 | GenPCIERootPort, | |
126 | gen_rp_test_migrate_msix), | |
f7d6f3fa MA |
127 | VMSTATE_END_OF_LIST() |
128 | } | |
129 | }; | |
130 | ||
bc277a52 | 131 | static Property gen_rp_props[] = { |
9e899399 JL |
132 | DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort, |
133 | migrate_msix, true), | |
134 | DEFINE_PROP_UINT32("bus-reserve", GenPCIERootPort, | |
135 | res_reserve.bus, -1), | |
136 | DEFINE_PROP_SIZE("io-reserve", GenPCIERootPort, | |
137 | res_reserve.io, -1), | |
138 | DEFINE_PROP_SIZE("mem-reserve", GenPCIERootPort, | |
139 | res_reserve.mem_non_pref, -1), | |
140 | DEFINE_PROP_SIZE("pref32-reserve", GenPCIERootPort, | |
141 | res_reserve.mem_pref_32, -1), | |
142 | DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort, | |
143 | res_reserve.mem_pref_64, -1), | |
c2a490e3 | 144 | DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot, |
a09d2038 | 145 | speed, PCIE_LINK_SPEED_16), |
c2a490e3 | 146 | DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot, |
a09d2038 | 147 | width, PCIE_LINK_WIDTH_32), |
bc277a52 MA |
148 | DEFINE_PROP_END_OF_LIST() |
149 | }; | |
150 | ||
f7d6f3fa MA |
151 | static void gen_rp_dev_class_init(ObjectClass *klass, void *data) |
152 | { | |
153 | DeviceClass *dc = DEVICE_CLASS(klass); | |
154 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); | |
155 | PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass); | |
156 | ||
157 | k->vendor_id = PCI_VENDOR_ID_REDHAT; | |
158 | k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_RP; | |
159 | dc->desc = "PCI Express Root Port"; | |
160 | dc->vmsd = &vmstate_rp_dev; | |
4f67d30b | 161 | device_class_set_props(dc, gen_rp_props); |
226263fb | 162 | |
bf853881 | 163 | device_class_set_parent_realize(dc, gen_rp_realize, &rpc->parent_realize); |
226263fb | 164 | |
f7d6f3fa MA |
165 | rpc->aer_vector = gen_rp_aer_vector; |
166 | rpc->interrupts_init = gen_rp_interrupts_init; | |
167 | rpc->interrupts_uninit = gen_rp_interrupts_uninit; | |
168 | rpc->aer_offset = GEN_PCIE_ROOT_PORT_AER_OFFSET; | |
e07fb4b5 | 169 | rpc->acs_offset = GEN_PCIE_ROOT_PORT_ACS_OFFSET; |
f7d6f3fa MA |
170 | } |
171 | ||
172 | static const TypeInfo gen_rp_dev_info = { | |
173 | .name = TYPE_GEN_PCIE_ROOT_PORT, | |
174 | .parent = TYPE_PCIE_ROOT_PORT, | |
bc277a52 | 175 | .instance_size = sizeof(GenPCIERootPort), |
f7d6f3fa MA |
176 | .class_init = gen_rp_dev_class_init, |
177 | }; | |
178 | ||
179 | static void gen_rp_register_types(void) | |
180 | { | |
181 | type_register_static(&gen_rp_dev_info); | |
182 | } | |
183 | type_init(gen_rp_register_types) |