blob: cdd6f775a1483e0e3ffd3364aabd30006ba09004 [file] [log] [blame]
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +03001/*
2 * QEMU<->ACPI BIOS PCI hotplug interface
3 *
4 * QEMU supports PCI hotplug via ACPI. This module
5 * implements the interface between QEMU and the ACPI BIOS.
6 * Interface specification - see docs/specs/acpi_pci_hotplug.txt
7 *
8 * Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com)
9 * Copyright (c) 2006 Fabrice Bellard
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
Chetan Pant61f3c912020-10-23 12:44:24 +000013 * License version 2.1 as published by the Free Software Foundation.
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030014 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, see <http://www.gnu.org/licenses/>
22 *
23 * Contributions after 2012-01-13 are licensed under the terms of the
24 * GNU GPL, version 2 or (at your option) any later version.
25 */
26
Peter Maydellb6a0aa02016-01-26 18:17:03 +000027#include "qemu/osdep.h"
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030028#include "hw/acpi/pcihp.h"
29
Philippe Mathieu-Daudé0fd61a22019-02-02 20:57:47 +010030#include "hw/pci-host/i440fx.h"
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030031#include "hw/pci/pci.h"
David Hildenbrand3e520922018-12-12 10:16:18 +010032#include "hw/pci/pci_bridge.h"
Julia Suvorovac0e427d2021-07-13 02:42:01 +020033#include "hw/pci/pci_host.h"
Julia Suvorova3f3cbbb2021-07-13 02:42:02 +020034#include "hw/pci/pcie_port.h"
Igor Mammedov6b0969f2022-03-01 10:11:59 -050035#include "hw/pci-bridge/xio3130_downstream.h"
Julia Suvorovac0e427d2021-07-13 02:42:01 +020036#include "hw/i386/acpi-build.h"
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030037#include "hw/acpi/acpi.h"
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030038#include "hw/pci/pci_bus.h"
Markus Armbrusterd6454272019-08-12 07:23:45 +020039#include "migration/vmstate.h"
Markus Armbrusterda34e652016-03-14 09:01:28 +010040#include "qapi/error.h"
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030041#include "qom/qom-qobject.h"
Markus Armbrusterdf93b192019-04-02 18:18:59 +020042#include "trace.h"
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030043
Igor Mammedovb32bd762021-03-15 14:00:58 -040044#define ACPI_PCIHP_SIZE 0x0018
Igor Mammedova7b613c2014-02-03 11:44:58 +010045#define PCI_UP_BASE 0x0000
46#define PCI_DOWN_BASE 0x0004
47#define PCI_EJ_BASE 0x0008
48#define PCI_RMV_BASE 0x000c
49#define PCI_SEL_BASE 0x0010
Igor Mammedovb32bd762021-03-15 14:00:58 -040050#define PCI_AIDX_BASE 0x0014
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030051
52typedef struct AcpiPciHpFind {
53 int bsel;
54 PCIBus *bus;
55} AcpiPciHpFind;
56
57static int acpi_pcihp_get_bsel(PCIBus *bus)
58{
Kirill Batuzov7c38ecd2014-04-24 18:15:56 +040059 Error *local_err = NULL;
Marc-André Lureauc03d83d2017-06-07 20:36:15 +040060 uint64_t bsel = object_property_get_uint(OBJECT(bus), ACPI_PCIHP_PROP_BSEL,
61 &local_err);
Kirill Batuzov7c38ecd2014-04-24 18:15:56 +040062
Marc-André Lureauc03d83d2017-06-07 20:36:15 +040063 if (local_err || bsel >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
Kirill Batuzov7c38ecd2014-04-24 18:15:56 +040064 if (local_err) {
65 error_free(local_err);
66 }
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030067 return -1;
Kirill Batuzov7c38ecd2014-04-24 18:15:56 +040068 } else {
69 return bsel;
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030070 }
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +030071}
72
Igor Mammedov2940a4b2023-01-12 15:02:43 +010073typedef struct {
74 unsigned bsel_alloc;
75 bool has_bridge_hotplug;
76} BSELInfo;
77
78/* Assign BSEL property only to buses that support hotplug. */
Anthony PERARDab938ae2017-09-06 14:40:32 +010079static void *acpi_set_bsel(PCIBus *bus, void *opaque)
80{
Igor Mammedov2940a4b2023-01-12 15:02:43 +010081 BSELInfo *info = opaque;
Anthony PERARDab938ae2017-09-06 14:40:32 +010082 unsigned *bus_bsel;
Igor Mammedov2940a4b2023-01-12 15:02:43 +010083 DeviceState *br = bus->qbus.parent;
84 bool is_bridge = IS_PCI_BRIDGE(br);
Anthony PERARDab938ae2017-09-06 14:40:32 +010085
Igor Mammedov2940a4b2023-01-12 15:02:43 +010086 /* hotplugged bridges can't be described in ACPI ignore them */
Anthony PERARDab938ae2017-09-06 14:40:32 +010087 if (qbus_is_hotpluggable(BUS(bus))) {
Igor Mammedov2940a4b2023-01-12 15:02:43 +010088 if (!is_bridge || (!br->hotplugged && info->has_bridge_hotplug)) {
89 bus_bsel = g_malloc(sizeof *bus_bsel);
Anthony PERARDab938ae2017-09-06 14:40:32 +010090
Igor Mammedov2940a4b2023-01-12 15:02:43 +010091 *bus_bsel = info->bsel_alloc++;
92 object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL,
93 bus_bsel, OBJ_PROP_FLAG_READ);
94 }
Anthony PERARDab938ae2017-09-06 14:40:32 +010095 }
96
Igor Mammedov2940a4b2023-01-12 15:02:43 +010097 return info;
Anthony PERARDab938ae2017-09-06 14:40:32 +010098}
99
Igor Mammedov2940a4b2023-01-12 15:02:43 +0100100static void acpi_set_pci_info(bool has_bridge_hotplug)
Anthony PERARDab938ae2017-09-06 14:40:32 +0100101{
102 static bool bsel_is_set;
Julia Suvorovac0e427d2021-07-13 02:42:01 +0200103 Object *host = acpi_get_i386_pci_host();
Anthony PERARDab938ae2017-09-06 14:40:32 +0100104 PCIBus *bus;
Igor Mammedov2940a4b2023-01-12 15:02:43 +0100105 BSELInfo info = { .bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT,
106 .has_bridge_hotplug = has_bridge_hotplug };
Anthony PERARDab938ae2017-09-06 14:40:32 +0100107
108 if (bsel_is_set) {
109 return;
110 }
111 bsel_is_set = true;
112
Julia Suvorovac0e427d2021-07-13 02:42:01 +0200113 if (!host) {
114 return;
115 }
116
117 bus = PCI_HOST_BRIDGE(host)->bus;
Anthony PERARDab938ae2017-09-06 14:40:32 +0100118 if (bus) {
119 /* Scan all PCI buses. Set property to enable acpi based hotplug. */
Igor Mammedov2940a4b2023-01-12 15:02:43 +0100120 pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &info);
Anthony PERARDab938ae2017-09-06 14:40:32 +0100121 }
122}
123
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300124static void acpi_pcihp_test_hotplug_bus(PCIBus *bus, void *opaque)
125{
126 AcpiPciHpFind *find = opaque;
127 if (find->bsel == acpi_pcihp_get_bsel(bus)) {
128 find->bus = bus;
129 }
130}
131
132static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel)
133{
134 AcpiPciHpFind find = { .bsel = bsel, .bus = NULL };
135
136 if (bsel < 0) {
137 return NULL;
138 }
139
140 pci_for_each_bus(s->root, acpi_pcihp_test_hotplug_bus, &find);
141
142 /* Make bsel 0 eject root bus if bsel property is not set,
143 * for compatibility with non acpi setups.
144 * TODO: really needed?
145 */
146 if (!bsel && !find.bus) {
147 find.bus = s->root;
148 }
Ani Sinha8ad038a2020-09-18 14:11:02 +0530149
150 /*
151 * Check if find.bus is actually hotpluggable. If bsel is set to
152 * NULL for example on the root bus in order to make it
153 * non-hotpluggable, find.bus will match the root bus when bsel
154 * is 0. See acpi_pcihp_test_hotplug_bus() above. Since the
155 * bus is not hotpluggable however, we should not select the bus.
156 * Instead, we should set find.bus to NULL in that case. In the check
157 * below, we generalize this case for all buses, not just the root bus.
158 * The callers of this function check for a null return value and
159 * handle them appropriately.
160 */
161 if (find.bus && !qbus_is_hotpluggable(BUS(find.bus))) {
162 find.bus = NULL;
163 }
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300164 return find.bus;
165}
166
167static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev)
168{
Igor Mammedov2897ae02014-02-05 16:36:48 +0100169 DeviceClass *dc = DEVICE_GET_CLASS(dev);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300170 /*
171 * ACPI doesn't allow hotplug of bridge devices. Don't allow
172 * hot-unplug of bridge devices unless they were added by hotplug
173 * (and so, not described by acpi).
Łukasz Gieryk58660bf2022-05-09 16:16:20 +0200174 *
175 * Don't allow hot-unplug of SR-IOV Virtual Functions, as they
176 * will be removed implicitly, when Physical Function is unplugged.
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300177 */
Igor Mammedovad494272022-11-29 11:13:41 +0100178 return (IS_PCI_BRIDGE(dev) && !dev->qdev.hotplugged) || !dc->hotpluggable ||
Łukasz Gieryk58660bf2022-05-09 16:16:20 +0200179 pci_is_vf(dev);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300180}
181
182static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots)
183{
David Hildenbrandc97adf32018-12-12 10:16:19 +0100184 HotplugHandler *hotplug_ctrl;
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300185 BusChild *kid, *next;
Stefan Hajnoczi786a4ea2015-03-23 15:29:26 +0000186 int slot = ctz32(slots);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300187 PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
188
Markus Armbruster03459ea2019-04-02 18:19:00 +0200189 trace_acpi_pci_eject_slot(bsel, slot);
190
Igor Mammedova3ec4bb2020-03-26 09:56:24 -0400191 if (!bus || slot > 31) {
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300192 return;
193 }
194
195 /* Mark request as complete */
196 s->acpi_pcihp_pci_status[bsel].down &= ~(1U << slot);
Michael S. Tsirkin5a2223c2014-01-26 12:31:27 +0200197 s->acpi_pcihp_pci_status[bsel].up &= ~(1U << slot);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300198
199 QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
200 DeviceState *qdev = kid->child;
201 PCIDevice *dev = PCI_DEVICE(qdev);
202 if (PCI_SLOT(dev->devfn) == slot) {
Michael S. Tsirkin5a2223c2014-01-26 12:31:27 +0200203 if (!acpi_pcihp_pc_no_hotplug(s, dev)) {
Laurent Vivier9323f892021-11-18 14:32:23 +0100204 /*
205 * partially_hotplugged is used by virtio-net failover:
206 * failover has asked the guest OS to unplug the device
207 * but we need to keep some references to the device
208 * to be able to plug it back in case of failure so
209 * we don't execute hotplug_handler_unplug().
210 */
211 if (dev->partially_hotplugged) {
212 /*
213 * pending_deleted_event is set to true when
214 * virtio-net failover asks to unplug the device,
215 * and set to false here when the operation is done
216 * This is used by the migration loop to detect the
217 * end of the operation and really start the migration.
218 */
219 qdev->pending_deleted_event = false;
220 } else {
221 hotplug_ctrl = qdev_get_hotplug_handler(qdev);
222 hotplug_handler_unplug(hotplug_ctrl, qdev, &error_abort);
223 object_unparent(OBJECT(qdev));
224 }
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300225 }
226 }
227 }
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300228}
229
230static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel)
231{
232 BusChild *kid, *next;
233 PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel);
234
235 /* Execute any pending removes during reset */
236 while (s->acpi_pcihp_pci_status[bsel].down) {
237 acpi_pcihp_eject_slot(s, bsel, s->acpi_pcihp_pci_status[bsel].down);
238 }
239
240 s->acpi_pcihp_pci_status[bsel].hotplug_enable = ~0;
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300241
242 if (!bus) {
243 return;
244 }
245 QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
246 DeviceState *qdev = kid->child;
247 PCIDevice *pdev = PCI_DEVICE(qdev);
248 int slot = PCI_SLOT(pdev->devfn);
249
250 if (acpi_pcihp_pc_no_hotplug(s, pdev)) {
251 s->acpi_pcihp_pci_status[bsel].hotplug_enable &= ~(1U << slot);
252 }
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300253 }
254}
255
256static void acpi_pcihp_update(AcpiPciHpState *s)
257{
258 int i;
259
260 for (i = 0; i < ACPI_PCIHP_MAX_HOTPLUG_BUS; ++i) {
261 acpi_pcihp_update_hotplug_bus(s, i);
262 }
263}
264
Igor Mammedov6536e422023-03-02 17:15:42 +0100265void acpi_pcihp_reset(AcpiPciHpState *s)
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300266{
Igor Mammedov6536e422023-03-02 17:15:42 +0100267 acpi_set_pci_info(s->use_acpi_hotplug_bridge);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300268 acpi_pcihp_update(s);
269}
270
David Hildenbrandec266f42018-12-12 10:16:17 +0100271void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev,
272 DeviceState *dev, Error **errp)
273{
Igor Mammedovb32bd762021-03-15 14:00:58 -0400274 PCIDevice *pdev = PCI_DEVICE(dev);
275
David Hildenbrandec266f42018-12-12 10:16:17 +0100276 /* Only hotplugged devices need the hotplug capability. */
277 if (dev->hotplugged &&
Ani Sinha028f1a82021-08-25 08:49:46 +0530278 acpi_pcihp_get_bsel(pci_get_bus(pdev)) < 0) {
David Hildenbrandec266f42018-12-12 10:16:17 +0100279 error_setg(errp, "Unsupported bus. Bus doesn't have property '"
280 ACPI_PCIHP_PROP_BSEL "' set");
281 return;
282 }
283}
284
Igor Mammedov0058c082016-05-31 12:01:17 +0200285void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
Igor Mammedovc24d5e02014-02-05 16:36:49 +0100286 DeviceState *dev, Error **errp)
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300287{
Igor Mammedovc24d5e02014-02-05 16:36:49 +0100288 PCIDevice *pdev = PCI_DEVICE(dev);
289 int slot = PCI_SLOT(pdev->devfn);
Igor Mammedov6b0969f2022-03-01 10:11:59 -0500290 PCIDevice *bridge;
291 PCIBus *bus;
David Hildenbrandec266f42018-12-12 10:16:17 +0100292 int bsel;
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300293
294 /* Don't send event when device is enabled during qemu machine creation:
295 * it is present on boot, no hotplug event is necessary. We do send an
296 * event when the device is disabled later. */
Igor Mammedovc24d5e02014-02-05 16:36:49 +0100297 if (!dev->hotplugged) {
David Hildenbrand3e520922018-12-12 10:16:18 +0100298 /*
299 * Overwrite the default hotplug handler with the ACPI PCI one
300 * for cold plugged bridges only.
301 */
Igor Mammedov6536e422023-03-02 17:15:42 +0100302 if (s->use_acpi_hotplug_bridge &&
David Hildenbrand3e520922018-12-12 10:16:18 +0100303 object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
304 PCIBus *sec = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev));
305
Markus Armbruster9bc6bfd2020-06-30 11:03:39 +0200306 qbus_set_hotplug_handler(BUS(sec), OBJECT(hotplug_dev));
David Hildenbrand3e520922018-12-12 10:16:18 +0100307 /* We don't have to overwrite any other hotplug handler yet */
308 assert(QLIST_EMPTY(&sec->child));
309 }
310
Igor Mammedovc24d5e02014-02-05 16:36:49 +0100311 return;
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300312 }
313
Igor Mammedov6b0969f2022-03-01 10:11:59 -0500314 bus = pci_get_bus(pdev);
315 bridge = pci_bridge_get_device(bus);
316 if (object_dynamic_cast(OBJECT(bridge), TYPE_PCIE_ROOT_PORT) ||
317 object_dynamic_cast(OBJECT(bridge), TYPE_XIO3130_DOWNSTREAM)) {
318 pcie_cap_slot_enable_power(bridge);
319 }
320
321 bsel = acpi_pcihp_get_bsel(bus);
David Hildenbrandec266f42018-12-12 10:16:17 +0100322 g_assert(bsel >= 0);
Igor Mammedovc24d5e02014-02-05 16:36:49 +0100323 s->acpi_pcihp_pci_status[bsel].up |= (1U << slot);
Igor Mammedov0058c082016-05-31 12:01:17 +0200324 acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS);
Igor Mammedovc24d5e02014-02-05 16:36:49 +0100325}
326
Igor Mammedov0058c082016-05-31 12:01:17 +0200327void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s,
Igor Mammedovc24d5e02014-02-05 16:36:49 +0100328 DeviceState *dev, Error **errp)
329{
Igor Mammedov4fd7da42021-03-15 14:00:59 -0400330 PCIDevice *pdev = PCI_DEVICE(dev);
331
Ani Sinha028f1a82021-08-25 08:49:46 +0530332 trace_acpi_pci_unplug(PCI_SLOT(pdev->devfn),
333 acpi_pcihp_get_bsel(pci_get_bus(pdev)));
Igor Mammedov4fd7da42021-03-15 14:00:59 -0400334
Markus Armbruster981c3dc2020-06-10 07:31:56 +0200335 qdev_unrealize(dev);
David Hildenbrandc97adf32018-12-12 10:16:19 +0100336}
337
338void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev,
339 AcpiPciHpState *s, DeviceState *dev,
340 Error **errp)
341{
Igor Mammedovc24d5e02014-02-05 16:36:49 +0100342 PCIDevice *pdev = PCI_DEVICE(dev);
343 int slot = PCI_SLOT(pdev->devfn);
David Gibsonfd56e062017-11-29 19:46:27 +1100344 int bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev));
Markus Armbruster03459ea2019-04-02 18:19:00 +0200345
346 trace_acpi_pci_unplug_request(bsel, slot);
347
Igor Mammedovc24d5e02014-02-05 16:36:49 +0100348 if (bsel < 0) {
349 error_setg(errp, "Unsupported bus. Bus doesn't have property '"
350 ACPI_PCIHP_PROP_BSEL "' set");
351 return;
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300352 }
353
Laurent Vivier9323f892021-11-18 14:32:23 +0100354 /*
355 * pending_deleted_event is used by virtio-net failover to detect the
356 * end of the unplug operation, the flag is set to false in
357 * acpi_pcihp_eject_slot() when the operation is completed.
358 */
359 pdev->qdev.pending_deleted_event = true;
Igor Mammedov0f689cf2023-04-18 11:04:49 +0200360 /* if unplug was requested before OSPM is initialized,
361 * linux kernel will clear GPE0.sts[] bits during boot, which effectively
362 * hides unplug event. And than followup qmp_device_del() calls remain
363 * blocked by above flag permanently.
364 * Unblock qmp_device_del() by setting expire limit, so user can
365 * repeat unplug request later when OSPM has been booted.
366 */
367 pdev->qdev.pending_deleted_expires_ms =
368 qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); /* 1 msec */
369
Igor Mammedovc24d5e02014-02-05 16:36:49 +0100370 s->acpi_pcihp_pci_status[bsel].down |= (1U << slot);
Igor Mammedov0058c082016-05-31 12:01:17 +0200371 acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300372}
373
Igor Mammedovf18e29f2023-03-02 17:15:43 +0100374bool acpi_pcihp_is_hotpluggbale_bus(AcpiPciHpState *s, BusState *bus)
375{
376 Object *o = OBJECT(bus->parent);
377
378 if (s->use_acpi_hotplug_bridge &&
379 object_dynamic_cast(o, TYPE_PCI_BRIDGE)) {
380 if (object_dynamic_cast(o, TYPE_PCIE_SLOT) && !PCIE_SLOT(o)->hotplug) {
381 return false;
382 }
383 return true;
384 }
385
386 if (s->use_acpi_root_pci_hotplug) {
387 return true;
388 }
389 return false;
390}
391
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300392static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size)
393{
394 AcpiPciHpState *s = opaque;
395 uint32_t val = 0;
396 int bsel = s->hotplug_select;
397
Gongleifa365d72014-08-20 13:52:30 +0800398 if (bsel < 0 || bsel >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300399 return 0;
400 }
401
402 switch (addr) {
Igor Mammedova7b613c2014-02-03 11:44:58 +0100403 case PCI_UP_BASE:
Michael S. Tsirkin5a2223c2014-01-26 12:31:27 +0200404 val = s->acpi_pcihp_pci_status[bsel].up;
Igor Mammedov6536e422023-03-02 17:15:42 +0100405 if (s->use_acpi_hotplug_bridge) {
Igor Mammedov99d09dd2014-02-03 11:44:59 +0100406 s->acpi_pcihp_pci_status[bsel].up = 0;
407 }
Markus Armbrusterdf93b192019-04-02 18:18:59 +0200408 trace_acpi_pci_up_read(val);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300409 break;
Igor Mammedova7b613c2014-02-03 11:44:58 +0100410 case PCI_DOWN_BASE:
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300411 val = s->acpi_pcihp_pci_status[bsel].down;
Markus Armbrusterdf93b192019-04-02 18:18:59 +0200412 trace_acpi_pci_down_read(val);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300413 break;
Igor Mammedova7b613c2014-02-03 11:44:58 +0100414 case PCI_EJ_BASE:
Markus Armbrusterdf93b192019-04-02 18:18:59 +0200415 trace_acpi_pci_features_read(val);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300416 break;
Igor Mammedova7b613c2014-02-03 11:44:58 +0100417 case PCI_RMV_BASE:
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300418 val = s->acpi_pcihp_pci_status[bsel].hotplug_enable;
Markus Armbrusterdf93b192019-04-02 18:18:59 +0200419 trace_acpi_pci_rmv_read(val);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300420 break;
Igor Mammedova7b613c2014-02-03 11:44:58 +0100421 case PCI_SEL_BASE:
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300422 val = s->hotplug_select;
Markus Armbrusterdf93b192019-04-02 18:18:59 +0200423 trace_acpi_pci_sel_read(val);
Igor Mammedovb32bd762021-03-15 14:00:58 -0400424 break;
425 case PCI_AIDX_BASE:
426 val = s->acpi_index;
427 s->acpi_index = 0;
428 trace_acpi_pci_acpi_index_read(val);
429 break;
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300430 default:
431 break;
432 }
433
434 return val;
435}
436
437static void pci_write(void *opaque, hwaddr addr, uint64_t data,
438 unsigned int size)
439{
Igor Mammedovb32bd762021-03-15 14:00:58 -0400440 int slot;
441 PCIBus *bus;
442 BusChild *kid, *next;
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300443 AcpiPciHpState *s = opaque;
Igor Mammedovb32bd762021-03-15 14:00:58 -0400444
445 s->acpi_index = 0;
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300446 switch (addr) {
Igor Mammedovb32bd762021-03-15 14:00:58 -0400447 case PCI_AIDX_BASE:
448 /*
449 * fetch acpi-index for specified slot so that follow up read from
450 * PCI_AIDX_BASE can return it to guest
451 */
452 slot = ctz32(data);
453
454 if (s->hotplug_select >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
455 break;
456 }
457
458 bus = acpi_pcihp_find_hotplug_bus(s, s->hotplug_select);
Michael S. Tsirkin9bd65652021-12-21 09:45:44 -0500459 if (!bus) {
460 break;
461 }
Igor Mammedovb32bd762021-03-15 14:00:58 -0400462 QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) {
463 Object *o = OBJECT(kid->child);
464 PCIDevice *dev = PCI_DEVICE(o);
465 if (PCI_SLOT(dev->devfn) == slot) {
466 s->acpi_index = object_property_get_uint(o, "acpi-index", NULL);
467 break;
468 }
469 }
470 trace_acpi_pci_acpi_index_write(s->hotplug_select, slot, s->acpi_index);
471 break;
Igor Mammedova7b613c2014-02-03 11:44:58 +0100472 case PCI_EJ_BASE:
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300473 if (s->hotplug_select >= ACPI_PCIHP_MAX_HOTPLUG_BUS) {
474 break;
475 }
476 acpi_pcihp_eject_slot(s, s->hotplug_select, data);
Markus Armbrusterdf93b192019-04-02 18:18:59 +0200477 trace_acpi_pci_ej_write(addr, data);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300478 break;
Igor Mammedova7b613c2014-02-03 11:44:58 +0100479 case PCI_SEL_BASE:
Igor Mammedov6536e422023-03-02 17:15:42 +0100480 s->hotplug_select = s->use_acpi_hotplug_bridge ? data :
481 ACPI_PCIHP_BSEL_DEFAULT;
Markus Armbrusterdf93b192019-04-02 18:18:59 +0200482 trace_acpi_pci_sel_write(addr, data);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300483 default:
484 break;
485 }
486}
487
488static const MemoryRegionOps acpi_pcihp_io_ops = {
489 .read = pci_read,
490 .write = pci_write,
491 .endianness = DEVICE_LITTLE_ENDIAN,
492 .valid = {
493 .min_access_size = 4,
494 .max_access_size = 4,
495 },
496};
497
Igor Mammedov78c2d872015-02-18 19:14:49 +0000498void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus,
Igor Mammedov6536e422023-03-02 17:15:42 +0100499 MemoryRegion *address_space_io,
Julia Suvorovacaf108b2021-07-13 02:42:00 +0200500 uint16_t io_base)
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300501{
Igor Mammedov78c2d872015-02-18 19:14:49 +0000502 s->io_len = ACPI_PCIHP_SIZE;
Julia Suvorovacaf108b2021-07-13 02:42:00 +0200503 s->io_base = io_base;
Igor Mammedove358edc2014-02-03 11:45:01 +0100504
Xinhao Zhang510feed2020-11-03 18:26:34 +0800505 s->root = root_bus;
Igor Mammedove358edc2014-02-03 11:45:01 +0100506
Igor Mammedov78c2d872015-02-18 19:14:49 +0000507 memory_region_init_io(&s->io, owner, &acpi_pcihp_io_ops, s,
508 "acpi-pci-hotplug", s->io_len);
509 memory_region_add_subregion(address_space_io, s->io_base, &s->io);
510
511 object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_BASE_PROP, &s->io_base,
Markus Armbrusterd2623122020-05-05 17:29:22 +0200512 OBJ_PROP_FLAG_READ);
Igor Mammedov78c2d872015-02-18 19:14:49 +0000513 object_property_add_uint16_ptr(owner, ACPI_PCIHP_IO_LEN_PROP, &s->io_len,
Markus Armbrusterd2623122020-05-05 17:29:22 +0200514 OBJ_PROP_FLAG_READ);
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300515}
516
517const VMStateDescription vmstate_acpi_pcihp_pci_status = {
518 .name = "acpi_pcihp_pci_status",
519 .version_id = 1,
520 .minimum_version_id = 1,
Juan Quintelad49805a2014-04-16 15:32:32 +0200521 .fields = (VMStateField[]) {
Michael S. Tsirkindb4728e2013-10-14 18:01:11 +0300522 VMSTATE_UINT32(up, AcpiPciHpPciStatus),
523 VMSTATE_UINT32(down, AcpiPciHpPciStatus),
524 VMSTATE_END_OF_LIST()
525 }
526};