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" |
15 | #include "qemu/module.h" |
16 | #include "hw/pci/msix.h" |
17 | #include "hw/pci/pcie_port.h" |
18 | #include "hw/qdev-properties.h" |
19 | #include "migration/vmstate.h" |
20 | |
21 | #define TYPE_GEN_PCIE_ROOT_PORT "pcie-root-port" |
22 | #define GEN_PCIE_ROOT_PORT(obj) \ |
23 | OBJECT_CHECK(GenPCIERootPort, (obj), TYPE_GEN_PCIE_ROOT_PORT) |
24 | |
25 | #define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100 |
26 | #define GEN_PCIE_ROOT_PORT_ACS_OFFSET \ |
27 | (GEN_PCIE_ROOT_PORT_AER_OFFSET + PCI_ERR_SIZEOF) |
28 | |
29 | #define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR 1 |
30 | |
31 | typedef struct GenPCIERootPort { |
32 | /*< private >*/ |
33 | PCIESlot parent_obj; |
34 | /*< public >*/ |
35 | |
36 | bool migrate_msix; |
37 | |
38 | /* additional resources to reserve */ |
39 | PCIResReserve res_reserve; |
40 | } GenPCIERootPort; |
41 | |
42 | static uint8_t gen_rp_aer_vector(const PCIDevice *d) |
43 | { |
44 | return 0; |
45 | } |
46 | |
47 | static int gen_rp_interrupts_init(PCIDevice *d, Error **errp) |
48 | { |
49 | int rc; |
50 | |
51 | rc = msix_init_exclusive_bar(d, GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR, 0, errp); |
52 | |
53 | if (rc < 0) { |
54 | assert(rc == -ENOTSUP); |
55 | } else { |
56 | msix_vector_use(d, 0); |
57 | } |
58 | |
59 | return rc; |
60 | } |
61 | |
62 | static void gen_rp_interrupts_uninit(PCIDevice *d) |
63 | { |
64 | msix_uninit_exclusive_bar(d); |
65 | } |
66 | |
67 | static bool gen_rp_test_migrate_msix(void *opaque, int version_id) |
68 | { |
69 | GenPCIERootPort *rp = opaque; |
70 | |
71 | return rp->migrate_msix; |
72 | } |
73 | |
74 | static void gen_rp_realize(DeviceState *dev, Error **errp) |
75 | { |
76 | PCIDevice *d = PCI_DEVICE(dev); |
77 | GenPCIERootPort *grp = GEN_PCIE_ROOT_PORT(d); |
78 | PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d); |
79 | Error *local_err = NULL; |
80 | |
81 | rpc->parent_realize(dev, &local_err); |
82 | if (local_err) { |
83 | error_propagate(errp, local_err); |
84 | return; |
85 | } |
86 | |
87 | int rc = pci_bridge_qemu_reserve_cap_init(d, 0, |
88 | grp->res_reserve, errp); |
89 | |
90 | if (rc < 0) { |
91 | rpc->parent_class.exit(d); |
92 | return; |
93 | } |
94 | |
95 | if (!grp->res_reserve.io) { |
96 | pci_word_test_and_clear_mask(d->wmask + PCI_COMMAND, |
97 | PCI_COMMAND_IO); |
98 | d->wmask[PCI_IO_BASE] = 0; |
99 | d->wmask[PCI_IO_LIMIT] = 0; |
100 | } |
101 | } |
102 | |
103 | static const VMStateDescription vmstate_rp_dev = { |
104 | .name = "pcie-root-port" , |
105 | .priority = MIG_PRI_PCI_BUS, |
106 | .version_id = 1, |
107 | .minimum_version_id = 1, |
108 | .post_load = pcie_cap_slot_post_load, |
109 | .fields = (VMStateField[]) { |
110 | VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot), |
111 | VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log, |
112 | PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog), |
113 | VMSTATE_MSIX_TEST(parent_obj.parent_obj.parent_obj.parent_obj, |
114 | GenPCIERootPort, |
115 | gen_rp_test_migrate_msix), |
116 | VMSTATE_END_OF_LIST() |
117 | } |
118 | }; |
119 | |
120 | static Property gen_rp_props[] = { |
121 | DEFINE_PROP_BOOL("x-migrate-msix" , GenPCIERootPort, |
122 | migrate_msix, true), |
123 | DEFINE_PROP_UINT32("bus-reserve" , GenPCIERootPort, |
124 | res_reserve.bus, -1), |
125 | DEFINE_PROP_SIZE("io-reserve" , GenPCIERootPort, |
126 | res_reserve.io, -1), |
127 | DEFINE_PROP_SIZE("mem-reserve" , GenPCIERootPort, |
128 | res_reserve.mem_non_pref, -1), |
129 | DEFINE_PROP_SIZE("pref32-reserve" , GenPCIERootPort, |
130 | res_reserve.mem_pref_32, -1), |
131 | DEFINE_PROP_SIZE("pref64-reserve" , GenPCIERootPort, |
132 | res_reserve.mem_pref_64, -1), |
133 | DEFINE_PROP_PCIE_LINK_SPEED("x-speed" , PCIESlot, |
134 | speed, PCIE_LINK_SPEED_16), |
135 | DEFINE_PROP_PCIE_LINK_WIDTH("x-width" , PCIESlot, |
136 | width, PCIE_LINK_WIDTH_32), |
137 | DEFINE_PROP_END_OF_LIST() |
138 | }; |
139 | |
140 | static void gen_rp_dev_class_init(ObjectClass *klass, void *data) |
141 | { |
142 | DeviceClass *dc = DEVICE_CLASS(klass); |
143 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
144 | PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass); |
145 | |
146 | k->vendor_id = PCI_VENDOR_ID_REDHAT; |
147 | k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_RP; |
148 | dc->desc = "PCI Express Root Port" ; |
149 | dc->vmsd = &vmstate_rp_dev; |
150 | dc->props = gen_rp_props; |
151 | |
152 | device_class_set_parent_realize(dc, gen_rp_realize, &rpc->parent_realize); |
153 | |
154 | rpc->aer_vector = gen_rp_aer_vector; |
155 | rpc->interrupts_init = gen_rp_interrupts_init; |
156 | rpc->interrupts_uninit = gen_rp_interrupts_uninit; |
157 | rpc->aer_offset = GEN_PCIE_ROOT_PORT_AER_OFFSET; |
158 | rpc->acs_offset = GEN_PCIE_ROOT_PORT_ACS_OFFSET; |
159 | } |
160 | |
161 | static const TypeInfo gen_rp_dev_info = { |
162 | .name = TYPE_GEN_PCIE_ROOT_PORT, |
163 | .parent = TYPE_PCIE_ROOT_PORT, |
164 | .instance_size = sizeof(GenPCIERootPort), |
165 | .class_init = gen_rp_dev_class_init, |
166 | }; |
167 | |
168 | static void gen_rp_register_types(void) |
169 | { |
170 | type_register_static(&gen_rp_dev_info); |
171 | } |
172 | type_init(gen_rp_register_types) |
173 | |