xref: /qemu/hw/i386/xen/xen_platform.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
101195b73SSteven Smith /*
201195b73SSteven Smith  * XEN platform pci device, formerly known as the event channel device
301195b73SSteven Smith  *
401195b73SSteven Smith  * Copyright (c) 2003-2004 Intel Corp.
501195b73SSteven Smith  * Copyright (c) 2006 XenSource
601195b73SSteven Smith  *
701195b73SSteven Smith  * Permission is hereby granted, free of charge, to any person obtaining a copy
801195b73SSteven Smith  * of this software and associated documentation files (the "Software"), to deal
901195b73SSteven Smith  * in the Software without restriction, including without limitation the rights
1001195b73SSteven Smith  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1101195b73SSteven Smith  * copies of the Software, and to permit persons to whom the Software is
1201195b73SSteven Smith  * furnished to do so, subject to the following conditions:
1301195b73SSteven Smith  *
1401195b73SSteven Smith  * The above copyright notice and this permission notice shall be included in
1501195b73SSteven Smith  * all copies or substantial portions of the Software.
1601195b73SSteven Smith  *
1701195b73SSteven Smith  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1801195b73SSteven Smith  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1901195b73SSteven Smith  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2001195b73SSteven Smith  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2101195b73SSteven Smith  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2201195b73SSteven Smith  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2301195b73SSteven Smith  * THE SOFTWARE.
2401195b73SSteven Smith  */
2501195b73SSteven Smith 
26b6a0aa05SPeter Maydell #include "qemu/osdep.h"
27da34e65cSMarkus Armbruster #include "qapi/error.h"
286a8a8b62SBernhard Beschow #include "hw/ide/pci.h"
2983c9f4caSPaolo Bonzini #include "hw/pci/pci.h"
30d6454270SMarkus Armbruster #include "migration/vmstate.h"
31bb346faeSJoao Martins #include "net/net.h"
3201195b73SSteven Smith #include "trace.h"
3332cad1ffSPhilippe Mathieu-Daudé #include "system/xen.h"
3432cad1ffSPhilippe Mathieu-Daudé #include "system/block-backend.h"
35b1ecd51bSEduardo Habkost #include "qemu/error-report.h"
360b8fa32fSMarkus Armbruster #include "qemu/module.h"
37db1015e9SEduardo Habkost #include "qom/object.h"
3801195b73SSteven Smith 
39bb346faeSJoao Martins #ifdef CONFIG_XEN
40e2abfe5eSDavid Woodhouse #include "hw/xen/xen_native.h"
41bb346faeSJoao Martins #endif
42bb346faeSJoao Martins 
43e2abfe5eSDavid Woodhouse /* The rule is that xen_native.h must come first */
44e2abfe5eSDavid Woodhouse #include "hw/xen/xen.h"
45e2abfe5eSDavid Woodhouse 
4601195b73SSteven Smith //#define DEBUG_PLATFORM
4701195b73SSteven Smith 
4801195b73SSteven Smith #ifdef DEBUG_PLATFORM
4901195b73SSteven Smith #define DPRINTF(fmt, ...) do { \
5001195b73SSteven Smith     fprintf(stderr, "xen_platform: " fmt, ## __VA_ARGS__); \
5101195b73SSteven Smith } while (0)
5201195b73SSteven Smith #else
5301195b73SSteven Smith #define DPRINTF(fmt, ...) do { } while (0)
5401195b73SSteven Smith #endif
5501195b73SSteven Smith 
5601195b73SSteven Smith #define PFFLAG_ROM_LOCK 1 /* Sets whether ROM memory area is RW or RO */
5701195b73SSteven Smith 
58db1015e9SEduardo Habkost struct PCIXenPlatformState {
59dc4aa51bSAndreas Färber     /*< private >*/
60dc4aa51bSAndreas Färber     PCIDevice parent_obj;
61dc4aa51bSAndreas Färber     /*< public >*/
62dc4aa51bSAndreas Färber 
63de00982eSAvi Kivity     MemoryRegion fixed_io;
64de00982eSAvi Kivity     MemoryRegion bar;
65de00982eSAvi Kivity     MemoryRegion mmio_bar;
6601195b73SSteven Smith     uint8_t flags; /* used only for version_id == 2 */
6701195b73SSteven Smith     uint16_t driver_product_version;
6801195b73SSteven Smith 
6901195b73SSteven Smith     /* Log from guest drivers */
7001195b73SSteven Smith     char log_buffer[4096];
7101195b73SSteven Smith     int log_buffer_off;
72db1015e9SEduardo Habkost };
7301195b73SSteven Smith 
7451a3fe99SPeter Crosthwaite #define TYPE_XEN_PLATFORM "xen-platform"
OBJECT_DECLARE_SIMPLE_TYPE(PCIXenPlatformState,XEN_PLATFORM)758063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(PCIXenPlatformState, XEN_PLATFORM)
7651a3fe99SPeter Crosthwaite 
7701195b73SSteven Smith #define XEN_PLATFORM_IOPORT 0x10
7801195b73SSteven Smith 
7901195b73SSteven Smith /* Send bytes to syslog */
8001195b73SSteven Smith static void log_writeb(PCIXenPlatformState *s, char val)
8101195b73SSteven Smith {
8201195b73SSteven Smith     if (val == '\n' || s->log_buffer_off == sizeof(s->log_buffer) - 1) {
8301195b73SSteven Smith         /* Flush buffer */
8401195b73SSteven Smith         s->log_buffer[s->log_buffer_off] = 0;
8501195b73SSteven Smith         trace_xen_platform_log(s->log_buffer);
8601195b73SSteven Smith         s->log_buffer_off = 0;
8701195b73SSteven Smith     } else {
8801195b73SSteven Smith         s->log_buffer[s->log_buffer_off++] = val;
8901195b73SSteven Smith     }
9001195b73SSteven Smith }
9101195b73SSteven Smith 
9204d6da4fSStefano Stabellini /*
9304d6da4fSStefano Stabellini  * Unplug device flags.
9404d6da4fSStefano Stabellini  *
9504d6da4fSStefano Stabellini  * The logic got a little confused at some point in the past but this is
9604d6da4fSStefano Stabellini  * what they do now.
9704d6da4fSStefano Stabellini  *
9804d6da4fSStefano Stabellini  * bit 0: Unplug all IDE and SCSI disks.
9904d6da4fSStefano Stabellini  * bit 1: Unplug all NICs.
10004d6da4fSStefano Stabellini  * bit 2: Unplug IDE disks except primary master. This is overridden if
10104d6da4fSStefano Stabellini  *        bit 0 is also present in the mask.
10204d6da4fSStefano Stabellini  * bit 3: Unplug all NVMe disks.
10304d6da4fSStefano Stabellini  *
10404d6da4fSStefano Stabellini  */
10504d6da4fSStefano Stabellini #define _UNPLUG_IDE_SCSI_DISKS 0
10604d6da4fSStefano Stabellini #define UNPLUG_IDE_SCSI_DISKS (1u << _UNPLUG_IDE_SCSI_DISKS)
10704d6da4fSStefano Stabellini 
10804d6da4fSStefano Stabellini #define _UNPLUG_ALL_NICS 1
10904d6da4fSStefano Stabellini #define UNPLUG_ALL_NICS (1u << _UNPLUG_ALL_NICS)
11004d6da4fSStefano Stabellini 
11104d6da4fSStefano Stabellini #define _UNPLUG_AUX_IDE_DISKS 2
11204d6da4fSStefano Stabellini #define UNPLUG_AUX_IDE_DISKS (1u << _UNPLUG_AUX_IDE_DISKS)
11304d6da4fSStefano Stabellini 
11404d6da4fSStefano Stabellini #define _UNPLUG_NVME_DISKS 3
11504d6da4fSStefano Stabellini #define UNPLUG_NVME_DISKS (1u << _UNPLUG_NVME_DISKS)
116679f4f8bSStefano Stabellini 
pci_device_is_passthrough(PCIDevice * d)1173bb1ebacSJoao Martins static bool pci_device_is_passthrough(PCIDevice *d)
1183bb1ebacSJoao Martins {
1193bb1ebacSJoao Martins     if (!strcmp(d->name, "xen-pci-passthrough")) {
1203bb1ebacSJoao Martins         return true;
1213bb1ebacSJoao Martins     }
1223bb1ebacSJoao Martins 
1233bb1ebacSJoao Martins     if (xen_mode == XEN_EMULATE && !strcmp(d->name, "vfio-pci")) {
1243bb1ebacSJoao Martins         return true;
1253bb1ebacSJoao Martins     }
1263bb1ebacSJoao Martins 
1273bb1ebacSJoao Martins     return false;
1283bb1ebacSJoao Martins }
1293bb1ebacSJoao Martins 
unplug_nic(PCIBus * b,PCIDevice * d,void * o)1307aa8cbb9SAnthony PERARD static void unplug_nic(PCIBus *b, PCIDevice *d, void *o)
131679f4f8bSStefano Stabellini {
132bd4982a6SAnthony PERARD     /* We have to ignore passthrough devices */
133679f4f8bSStefano Stabellini     if (pci_get_word(d->config + PCI_CLASS_DEVICE) ==
134bd4982a6SAnthony PERARD             PCI_CLASS_NETWORK_ETHERNET
1353bb1ebacSJoao Martins             && !pci_device_is_passthrough(d)) {
13602a5c4c9SStefan Hajnoczi         object_unparent(OBJECT(d));
137679f4f8bSStefano Stabellini     }
138679f4f8bSStefano Stabellini }
139679f4f8bSStefano Stabellini 
1406c808651SRoss Lagerwall /* Remove the peer of the NIC device. Normally, this would be a tap device. */
del_nic_peer(NICState * nic,void * opaque)1416c808651SRoss Lagerwall static void del_nic_peer(NICState *nic, void *opaque)
1426c808651SRoss Lagerwall {
14325511f3eSDavid Woodhouse     NetClientState *nc = qemu_get_queue(nic);
14425511f3eSDavid Woodhouse     ObjectClass *klass = module_object_class_by_name(nc->model);
1456c808651SRoss Lagerwall 
14625511f3eSDavid Woodhouse     /* Only delete peers of PCI NICs that we're about to delete */
14725511f3eSDavid Woodhouse     if (!klass || !object_class_dynamic_cast(klass, TYPE_PCI_DEVICE)) {
14825511f3eSDavid Woodhouse         return;
14925511f3eSDavid Woodhouse     }
15025511f3eSDavid Woodhouse 
1516c808651SRoss Lagerwall     if (nc->peer)
1526c808651SRoss Lagerwall         qemu_del_net_client(nc->peer);
1536c808651SRoss Lagerwall }
1546c808651SRoss Lagerwall 
pci_unplug_nics(PCIBus * bus)155679f4f8bSStefano Stabellini static void pci_unplug_nics(PCIBus *bus)
156679f4f8bSStefano Stabellini {
1576c808651SRoss Lagerwall     qemu_foreach_nic(del_nic_peer, NULL);
1587aa8cbb9SAnthony PERARD     pci_for_each_device(bus, 0, unplug_nic, NULL);
159679f4f8bSStefano Stabellini }
160679f4f8bSStefano Stabellini 
1616a8a8b62SBernhard Beschow /*
1626a8a8b62SBernhard Beschow  * The Xen HVM unplug protocol [1] specifies a mechanism to allow guests to
1636a8a8b62SBernhard Beschow  * request unplug of 'aux' disks (which is stated to mean all IDE disks,
1646a8a8b62SBernhard Beschow  * except the primary master).
1656a8a8b62SBernhard Beschow  *
1666a8a8b62SBernhard Beschow  * NOTE: The semantics of what happens if unplug of all disks and 'aux' disks
1676a8a8b62SBernhard Beschow  *       is simultaneously requested is not clear. The implementation assumes
1686a8a8b62SBernhard Beschow  *       that an 'all' request overrides an 'aux' request.
1696a8a8b62SBernhard Beschow  *
1706a8a8b62SBernhard Beschow  * [1] https://xenbits.xen.org/gitweb/?p=xen.git;a=blob;f=docs/misc/hvm-emulated-unplug.pandoc
1716a8a8b62SBernhard Beschow  */
172a7304995SDavid Woodhouse struct ide_unplug_state {
173a7304995SDavid Woodhouse     bool aux;
174a7304995SDavid Woodhouse     int nr_unplugged;
175a7304995SDavid Woodhouse };
176a7304995SDavid Woodhouse 
ide_dev_unplug(DeviceState * dev,void * _st)177a7304995SDavid Woodhouse static int ide_dev_unplug(DeviceState *dev, void *_st)
1786a8a8b62SBernhard Beschow {
179a7304995SDavid Woodhouse     struct ide_unplug_state *st = _st;
1806a8a8b62SBernhard Beschow     IDEDevice *idedev;
1816a8a8b62SBernhard Beschow     IDEBus *idebus;
1826a8a8b62SBernhard Beschow     BlockBackend *blk;
183a7304995SDavid Woodhouse     int unit;
1846a8a8b62SBernhard Beschow 
185a7304995SDavid Woodhouse     idedev = IDE_DEVICE(object_dynamic_cast(OBJECT(dev), "ide-hd"));
186a7304995SDavid Woodhouse     if (!idedev) {
187a7304995SDavid Woodhouse         return 0;
1886a8a8b62SBernhard Beschow     }
1896a8a8b62SBernhard Beschow 
190a7304995SDavid Woodhouse     idebus = IDE_BUS(qdev_get_parent_bus(dev));
191a7304995SDavid Woodhouse 
192a7304995SDavid Woodhouse     unit = (idedev == idebus->slave);
193a7304995SDavid Woodhouse     assert(unit || idedev == idebus->master);
194a7304995SDavid Woodhouse 
195a7304995SDavid Woodhouse     if (st->aux && !unit && !strcmp(BUS(idebus)->name, "ide.0")) {
196a7304995SDavid Woodhouse         return 0;
197a7304995SDavid Woodhouse     }
198a7304995SDavid Woodhouse 
199a7304995SDavid Woodhouse     blk = idebus->ifs[unit].blk;
200a7304995SDavid Woodhouse     if (blk) {
2016a8a8b62SBernhard Beschow         blk_drain(blk);
2026a8a8b62SBernhard Beschow         blk_flush(blk);
2036a8a8b62SBernhard Beschow 
2046a8a8b62SBernhard Beschow         blk_detach_dev(blk, DEVICE(idedev));
205a7304995SDavid Woodhouse         idebus->ifs[unit].blk = NULL;
2066a8a8b62SBernhard Beschow         idedev->conf.blk = NULL;
2076a8a8b62SBernhard Beschow         monitor_remove_blk(blk);
2086a8a8b62SBernhard Beschow         blk_unref(blk);
2096a8a8b62SBernhard Beschow     }
210a7304995SDavid Woodhouse 
211a7304995SDavid Woodhouse     object_unparent(OBJECT(dev));
212a7304995SDavid Woodhouse     st->nr_unplugged++;
213a7304995SDavid Woodhouse 
214a7304995SDavid Woodhouse     return 0;
2156a8a8b62SBernhard Beschow }
216a7304995SDavid Woodhouse 
pci_xen_ide_unplug(PCIDevice * d,bool aux)217a7304995SDavid Woodhouse static void pci_xen_ide_unplug(PCIDevice *d, bool aux)
218a7304995SDavid Woodhouse {
219a7304995SDavid Woodhouse     struct ide_unplug_state st = { aux, 0 };
220a7304995SDavid Woodhouse     DeviceState *dev = DEVICE(d);
221a7304995SDavid Woodhouse 
222a7304995SDavid Woodhouse     qdev_walk_children(dev, NULL, NULL, ide_dev_unplug, NULL, &st);
223a7304995SDavid Woodhouse     if (st.nr_unplugged) {
224856ca10fSOlaf Hering         pci_device_reset(d);
2256a8a8b62SBernhard Beschow     }
226a7304995SDavid Woodhouse }
2276a8a8b62SBernhard Beschow 
unplug_disks(PCIBus * b,PCIDevice * d,void * opaque)228ae4d2eb2SPaul Durrant static void unplug_disks(PCIBus *b, PCIDevice *d, void *opaque)
229679f4f8bSStefano Stabellini {
230ae4d2eb2SPaul Durrant     uint32_t flags = *(uint32_t *)opaque;
231ae4d2eb2SPaul Durrant     bool aux = (flags & UNPLUG_AUX_IDE_DISKS) &&
23204d6da4fSStefano Stabellini         !(flags & UNPLUG_IDE_SCSI_DISKS);
233ae4d2eb2SPaul Durrant 
234bd4982a6SAnthony PERARD     /* We have to ignore passthrough devices */
2353bb1ebacSJoao Martins     if (pci_device_is_passthrough(d))
2363d89e3f7SPaul Durrant         return;
2373d89e3f7SPaul Durrant 
2383d89e3f7SPaul Durrant     switch (pci_get_word(d->config + PCI_CLASS_DEVICE)) {
2393d89e3f7SPaul Durrant     case PCI_CLASS_STORAGE_IDE:
240a7304995SDavid Woodhouse     case PCI_CLASS_STORAGE_SATA:
241856ca10fSOlaf Hering         pci_xen_ide_unplug(d, aux);
2423d89e3f7SPaul Durrant         break;
2433d89e3f7SPaul Durrant 
2443d89e3f7SPaul Durrant     case PCI_CLASS_STORAGE_SCSI:
245ae4d2eb2SPaul Durrant         if (!aux) {
24678f66897SOlaf Hering             object_unparent(OBJECT(d));
247ae4d2eb2SPaul Durrant         }
2483d89e3f7SPaul Durrant         break;
2493d89e3f7SPaul Durrant 
25004d6da4fSStefano Stabellini     case PCI_CLASS_STORAGE_EXPRESS:
25104d6da4fSStefano Stabellini         if (flags & UNPLUG_NVME_DISKS) {
25204d6da4fSStefano Stabellini             object_unparent(OBJECT(d));
25304d6da4fSStefano Stabellini         }
25404d6da4fSStefano Stabellini 
2553d89e3f7SPaul Durrant     default:
2563d89e3f7SPaul Durrant         break;
257679f4f8bSStefano Stabellini     }
258679f4f8bSStefano Stabellini }
259679f4f8bSStefano Stabellini 
pci_unplug_disks(PCIBus * bus,uint32_t flags)260ae4d2eb2SPaul Durrant static void pci_unplug_disks(PCIBus *bus, uint32_t flags)
261679f4f8bSStefano Stabellini {
262ae4d2eb2SPaul Durrant     pci_for_each_device(bus, 0, unplug_disks, &flags);
263679f4f8bSStefano Stabellini }
26401195b73SSteven Smith 
platform_fixed_ioport_writew(void * opaque,uint32_t addr,uint32_t val)26501195b73SSteven Smith static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
26601195b73SSteven Smith {
26701195b73SSteven Smith     PCIXenPlatformState *s = opaque;
26801195b73SSteven Smith 
269e7b48c97SAnthony PERARD     switch (addr) {
270dc4aa51bSAndreas Färber     case 0: {
271dc4aa51bSAndreas Färber         PCIDevice *pci_dev = PCI_DEVICE(s);
27204d6da4fSStefano Stabellini         /* Unplug devices. See comment above flag definitions */
27304d6da4fSStefano Stabellini         if (val & (UNPLUG_IDE_SCSI_DISKS | UNPLUG_AUX_IDE_DISKS |
27404d6da4fSStefano Stabellini                    UNPLUG_NVME_DISKS)) {
275679f4f8bSStefano Stabellini             DPRINTF("unplug disks\n");
276fd56e061SDavid Gibson             pci_unplug_disks(pci_get_bus(pci_dev), val);
277679f4f8bSStefano Stabellini         }
278679f4f8bSStefano Stabellini         if (val & UNPLUG_ALL_NICS) {
279679f4f8bSStefano Stabellini             DPRINTF("unplug nics\n");
280fd56e061SDavid Gibson             pci_unplug_nics(pci_get_bus(pci_dev));
281679f4f8bSStefano Stabellini         }
28201195b73SSteven Smith         break;
283dc4aa51bSAndreas Färber     }
28401195b73SSteven Smith     case 2:
28501195b73SSteven Smith         switch (val) {
28601195b73SSteven Smith         case 1:
28701195b73SSteven Smith             DPRINTF("Citrix Windows PV drivers loaded in guest\n");
28801195b73SSteven Smith             break;
28901195b73SSteven Smith         case 0:
29001195b73SSteven Smith             DPRINTF("Guest claimed to be running PV product 0?\n");
29101195b73SSteven Smith             break;
29201195b73SSteven Smith         default:
29301195b73SSteven Smith             DPRINTF("Unknown PV product %d loaded in guest\n", val);
29401195b73SSteven Smith             break;
29501195b73SSteven Smith         }
29601195b73SSteven Smith         s->driver_product_version = val;
29701195b73SSteven Smith         break;
29801195b73SSteven Smith     }
29901195b73SSteven Smith }
30001195b73SSteven Smith 
platform_fixed_ioport_writel(void * opaque,uint32_t addr,uint32_t val)30101195b73SSteven Smith static void platform_fixed_ioport_writel(void *opaque, uint32_t addr,
30201195b73SSteven Smith                                          uint32_t val)
30301195b73SSteven Smith {
304e7b48c97SAnthony PERARD     switch (addr) {
30501195b73SSteven Smith     case 0:
30601195b73SSteven Smith         /* PV driver version */
30701195b73SSteven Smith         break;
30801195b73SSteven Smith     }
30901195b73SSteven Smith }
31001195b73SSteven Smith 
platform_fixed_ioport_writeb(void * opaque,uint32_t addr,uint32_t val)31101195b73SSteven Smith static void platform_fixed_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
31201195b73SSteven Smith {
31301195b73SSteven Smith     PCIXenPlatformState *s = opaque;
31401195b73SSteven Smith 
315e7b48c97SAnthony PERARD     switch (addr) {
316bb346faeSJoao Martins     case 0: /* Platform flags */
317bb346faeSJoao Martins         if (xen_mode == XEN_EMULATE) {
318bb346faeSJoao Martins             /* XX: Use i440gx/q35 PAM setup to do this? */
319bb346faeSJoao Martins             s->flags = val & PFFLAG_ROM_LOCK;
320bb346faeSJoao Martins #ifdef CONFIG_XEN
321bb346faeSJoao Martins         } else {
32201195b73SSteven Smith             hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ?
32301195b73SSteven Smith                 HVMMEM_ram_ro : HVMMEM_ram_rw;
324bb346faeSJoao Martins 
3258f25e754SPaul Durrant             if (xen_set_mem_type(xen_domid, mem_type, 0xc0, 0x40)) {
32601195b73SSteven Smith                 DPRINTF("unable to change ro/rw state of ROM memory area!\n");
32701195b73SSteven Smith             } else {
32801195b73SSteven Smith                 s->flags = val & PFFLAG_ROM_LOCK;
32901195b73SSteven Smith                 DPRINTF("changed ro/rw state of ROM memory area. now is %s state.\n",
33001195b73SSteven Smith                         (mem_type == HVMMEM_ram_ro ? "ro" : "rw"));
33101195b73SSteven Smith             }
332bb346faeSJoao Martins #endif
33301195b73SSteven Smith         }
334bb346faeSJoao Martins         break;
335bb346faeSJoao Martins 
33601195b73SSteven Smith     case 2:
33701195b73SSteven Smith         log_writeb(s, val);
33801195b73SSteven Smith         break;
33901195b73SSteven Smith     }
34001195b73SSteven Smith }
34101195b73SSteven Smith 
platform_fixed_ioport_readw(void * opaque,uint32_t addr)34201195b73SSteven Smith static uint32_t platform_fixed_ioport_readw(void *opaque, uint32_t addr)
34301195b73SSteven Smith {
344e7b48c97SAnthony PERARD     switch (addr) {
34501195b73SSteven Smith     case 0:
34601195b73SSteven Smith         /* Magic value so that you can identify the interface. */
34701195b73SSteven Smith         return 0x49d2;
34801195b73SSteven Smith     default:
34901195b73SSteven Smith         return 0xffff;
35001195b73SSteven Smith     }
35101195b73SSteven Smith }
35201195b73SSteven Smith 
platform_fixed_ioport_readb(void * opaque,uint32_t addr)35301195b73SSteven Smith static uint32_t platform_fixed_ioport_readb(void *opaque, uint32_t addr)
35401195b73SSteven Smith {
35501195b73SSteven Smith     PCIXenPlatformState *s = opaque;
35601195b73SSteven Smith 
357e7b48c97SAnthony PERARD     switch (addr) {
35801195b73SSteven Smith     case 0:
35901195b73SSteven Smith         /* Platform flags */
36001195b73SSteven Smith         return s->flags;
36101195b73SSteven Smith     case 2:
36201195b73SSteven Smith         /* Version number */
36301195b73SSteven Smith         return 1;
36401195b73SSteven Smith     default:
36501195b73SSteven Smith         return 0xff;
36601195b73SSteven Smith     }
36701195b73SSteven Smith }
36801195b73SSteven Smith 
platform_fixed_ioport_reset(void * opaque)36901195b73SSteven Smith static void platform_fixed_ioport_reset(void *opaque)
37001195b73SSteven Smith {
37101195b73SSteven Smith     PCIXenPlatformState *s = opaque;
37201195b73SSteven Smith 
373e7b48c97SAnthony PERARD     platform_fixed_ioport_writeb(s, 0, 0);
37401195b73SSteven Smith }
37501195b73SSteven Smith 
platform_fixed_ioport_read(void * opaque,hwaddr addr,unsigned size)376626c7a17SAlexander Graf static uint64_t platform_fixed_ioport_read(void *opaque,
377626c7a17SAlexander Graf                                            hwaddr addr,
378626c7a17SAlexander Graf                                            unsigned size)
379626c7a17SAlexander Graf {
380626c7a17SAlexander Graf     switch (size) {
381626c7a17SAlexander Graf     case 1:
382626c7a17SAlexander Graf         return platform_fixed_ioport_readb(opaque, addr);
383626c7a17SAlexander Graf     case 2:
384626c7a17SAlexander Graf         return platform_fixed_ioport_readw(opaque, addr);
385626c7a17SAlexander Graf     default:
386626c7a17SAlexander Graf         return -1;
387626c7a17SAlexander Graf     }
388626c7a17SAlexander Graf }
389626c7a17SAlexander Graf 
platform_fixed_ioport_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)390626c7a17SAlexander Graf static void platform_fixed_ioport_write(void *opaque, hwaddr addr,
391626c7a17SAlexander Graf 
392626c7a17SAlexander Graf                                         uint64_t val, unsigned size)
393626c7a17SAlexander Graf {
394626c7a17SAlexander Graf     switch (size) {
395626c7a17SAlexander Graf     case 1:
396626c7a17SAlexander Graf         platform_fixed_ioport_writeb(opaque, addr, val);
397626c7a17SAlexander Graf         break;
398626c7a17SAlexander Graf     case 2:
399626c7a17SAlexander Graf         platform_fixed_ioport_writew(opaque, addr, val);
400626c7a17SAlexander Graf         break;
401626c7a17SAlexander Graf     case 4:
402626c7a17SAlexander Graf         platform_fixed_ioport_writel(opaque, addr, val);
403626c7a17SAlexander Graf         break;
404626c7a17SAlexander Graf     }
405626c7a17SAlexander Graf }
406626c7a17SAlexander Graf 
407de00982eSAvi Kivity 
408de00982eSAvi Kivity static const MemoryRegionOps platform_fixed_io_ops = {
409626c7a17SAlexander Graf     .read = platform_fixed_ioport_read,
410626c7a17SAlexander Graf     .write = platform_fixed_ioport_write,
411962b03fcSJan Kiszka     .valid = {
412962b03fcSJan Kiszka         .unaligned = true,
413962b03fcSJan Kiszka     },
414626c7a17SAlexander Graf     .impl = {
415626c7a17SAlexander Graf         .min_access_size = 1,
416626c7a17SAlexander Graf         .max_access_size = 4,
417962b03fcSJan Kiszka         .unaligned = true,
418626c7a17SAlexander Graf     },
419626c7a17SAlexander Graf     .endianness = DEVICE_LITTLE_ENDIAN,
420de00982eSAvi Kivity };
421de00982eSAvi Kivity 
platform_fixed_ioport_init(PCIXenPlatformState * s)42201195b73SSteven Smith static void platform_fixed_ioport_init(PCIXenPlatformState* s)
42301195b73SSteven Smith {
42422fc860bSPaolo Bonzini     memory_region_init_io(&s->fixed_io, OBJECT(s), &platform_fixed_io_ops, s,
425de00982eSAvi Kivity                           "xen-fixed", 16);
426de00982eSAvi Kivity     memory_region_add_subregion(get_system_io(), XEN_PLATFORM_IOPORT,
427de00982eSAvi Kivity                                 &s->fixed_io);
42801195b73SSteven Smith }
42901195b73SSteven Smith 
43001195b73SSteven Smith /* Xen Platform PCI Device */
43101195b73SSteven Smith 
xen_platform_ioport_readb(void * opaque,hwaddr addr,unsigned int size)4327a652efaSHervé Poussineau static uint64_t xen_platform_ioport_readb(void *opaque, hwaddr addr,
4337a652efaSHervé Poussineau                                           unsigned int size)
43401195b73SSteven Smith {
43501195b73SSteven Smith     if (addr == 0) {
436e7b48c97SAnthony PERARD         return platform_fixed_ioport_readb(opaque, 0);
43701195b73SSteven Smith     } else {
43801195b73SSteven Smith         return ~0u;
43901195b73SSteven Smith     }
44001195b73SSteven Smith }
44101195b73SSteven Smith 
xen_platform_ioport_writeb(void * opaque,hwaddr addr,uint64_t val,unsigned int size)4427a652efaSHervé Poussineau static void xen_platform_ioport_writeb(void *opaque, hwaddr addr,
4437a652efaSHervé Poussineau                                        uint64_t val, unsigned int size)
44401195b73SSteven Smith {
44501195b73SSteven Smith     PCIXenPlatformState *s = opaque;
44635132016SOlaf Hering     PCIDevice *pci_dev = PCI_DEVICE(s);
44701195b73SSteven Smith 
44801195b73SSteven Smith     switch (addr) {
44901195b73SSteven Smith     case 0: /* Platform flags */
4507a652efaSHervé Poussineau         platform_fixed_ioport_writeb(opaque, 0, (uint32_t)val);
45101195b73SSteven Smith         break;
45235132016SOlaf Hering     case 4:
45335132016SOlaf Hering         if (val == 1) {
45435132016SOlaf Hering             /*
45535132016SOlaf Hering              * SUSE unplug for Xenlinux
45635132016SOlaf Hering              * xen-kmp used this since xen-3.0.4, instead the official protocol
45735132016SOlaf Hering              * from xen-3.3+ It did an unconditional "outl(1, (ioaddr + 4));"
45835132016SOlaf Hering              * Pre VMDP 1.7 used 4 and 8 depending on how VMDP was configured.
45935132016SOlaf Hering              * If VMDP was to control both disk and LAN it would use 4.
46035132016SOlaf Hering              * If it controlled just disk or just LAN, it would use 8 below.
46135132016SOlaf Hering              */
462fd56e061SDavid Gibson             pci_unplug_disks(pci_get_bus(pci_dev), UNPLUG_IDE_SCSI_DISKS);
463fd56e061SDavid Gibson             pci_unplug_nics(pci_get_bus(pci_dev));
46435132016SOlaf Hering         }
46535132016SOlaf Hering         break;
46601195b73SSteven Smith     case 8:
46735132016SOlaf Hering         switch (val) {
46835132016SOlaf Hering         case 1:
469fd56e061SDavid Gibson             pci_unplug_disks(pci_get_bus(pci_dev), UNPLUG_IDE_SCSI_DISKS);
47035132016SOlaf Hering             break;
47135132016SOlaf Hering         case 2:
472fd56e061SDavid Gibson             pci_unplug_nics(pci_get_bus(pci_dev));
47335132016SOlaf Hering             break;
47435132016SOlaf Hering         default:
4757a652efaSHervé Poussineau             log_writeb(s, (uint32_t)val);
47601195b73SSteven Smith             break;
47735132016SOlaf Hering         }
47835132016SOlaf Hering         break;
47901195b73SSteven Smith     default:
48001195b73SSteven Smith         break;
48101195b73SSteven Smith     }
48201195b73SSteven Smith }
48301195b73SSteven Smith 
484de00982eSAvi Kivity static const MemoryRegionOps xen_pci_io_ops = {
4857a652efaSHervé Poussineau     .read  = xen_platform_ioport_readb,
4867a652efaSHervé Poussineau     .write = xen_platform_ioport_writeb,
4877a652efaSHervé Poussineau     .impl.min_access_size = 1,
4887a652efaSHervé Poussineau     .impl.max_access_size = 1,
489de00982eSAvi Kivity };
490de00982eSAvi Kivity 
platform_ioport_bar_setup(PCIXenPlatformState * d)491de00982eSAvi Kivity static void platform_ioport_bar_setup(PCIXenPlatformState *d)
492de00982eSAvi Kivity {
49322fc860bSPaolo Bonzini     memory_region_init_io(&d->bar, OBJECT(d), &xen_pci_io_ops, d,
49422fc860bSPaolo Bonzini                           "xen-pci", 0x100);
49501195b73SSteven Smith }
49601195b73SSteven Smith 
platform_mmio_read(void * opaque,hwaddr addr,unsigned size)497a8170e5eSAvi Kivity static uint64_t platform_mmio_read(void *opaque, hwaddr addr,
498de00982eSAvi Kivity                                    unsigned size)
49901195b73SSteven Smith {
50001195b73SSteven Smith     DPRINTF("Warning: attempted read from physical address "
501883f2c59SPhilippe Mathieu-Daudé             "0x" HWADDR_FMT_plx " in xen platform mmio space\n", addr);
50201195b73SSteven Smith 
50301195b73SSteven Smith     return 0;
50401195b73SSteven Smith }
50501195b73SSteven Smith 
platform_mmio_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)506a8170e5eSAvi Kivity static void platform_mmio_write(void *opaque, hwaddr addr,
507de00982eSAvi Kivity                                 uint64_t val, unsigned size)
50801195b73SSteven Smith {
509de00982eSAvi Kivity     DPRINTF("Warning: attempted write of 0x%"PRIx64" to physical "
510883f2c59SPhilippe Mathieu-Daudé             "address 0x" HWADDR_FMT_plx " in xen platform mmio space\n",
51101195b73SSteven Smith             val, addr);
51201195b73SSteven Smith }
51301195b73SSteven Smith 
514de00982eSAvi Kivity static const MemoryRegionOps platform_mmio_handler = {
51501195b73SSteven Smith     .read = &platform_mmio_read,
51601195b73SSteven Smith     .write = &platform_mmio_write,
517a115ab5bSPhilippe Mathieu-Daudé     .endianness = DEVICE_LITTLE_ENDIAN,
51801195b73SSteven Smith };
51901195b73SSteven Smith 
platform_mmio_setup(PCIXenPlatformState * d)520de00982eSAvi Kivity static void platform_mmio_setup(PCIXenPlatformState *d)
52101195b73SSteven Smith {
52222fc860bSPaolo Bonzini     memory_region_init_io(&d->mmio_bar, OBJECT(d), &platform_mmio_handler, d,
523de00982eSAvi Kivity                           "xen-mmio", 0x1000000);
52401195b73SSteven Smith }
52501195b73SSteven Smith 
xen_platform_post_load(void * opaque,int version_id)52601195b73SSteven Smith static int xen_platform_post_load(void *opaque, int version_id)
52701195b73SSteven Smith {
52801195b73SSteven Smith     PCIXenPlatformState *s = opaque;
52901195b73SSteven Smith 
530e7b48c97SAnthony PERARD     platform_fixed_ioport_writeb(s, 0, s->flags);
53101195b73SSteven Smith 
53201195b73SSteven Smith     return 0;
53301195b73SSteven Smith }
53401195b73SSteven Smith 
53501195b73SSteven Smith static const VMStateDescription vmstate_xen_platform = {
53601195b73SSteven Smith     .name = "platform",
53701195b73SSteven Smith     .version_id = 4,
53801195b73SSteven Smith     .minimum_version_id = 4,
53901195b73SSteven Smith     .post_load = xen_platform_post_load,
5409231a017SRichard Henderson     .fields = (const VMStateField[]) {
541dc4aa51bSAndreas Färber         VMSTATE_PCI_DEVICE(parent_obj, PCIXenPlatformState),
54201195b73SSteven Smith         VMSTATE_UINT8(flags, PCIXenPlatformState),
54301195b73SSteven Smith         VMSTATE_END_OF_LIST()
54401195b73SSteven Smith     }
54501195b73SSteven Smith };
54601195b73SSteven Smith 
xen_platform_realize(PCIDevice * dev,Error ** errp)5474098d49dSStefano Stabellini static void xen_platform_realize(PCIDevice *dev, Error **errp)
54801195b73SSteven Smith {
54951a3fe99SPeter Crosthwaite     PCIXenPlatformState *d = XEN_PLATFORM(dev);
55001195b73SSteven Smith     uint8_t *pci_conf;
55101195b73SSteven Smith 
552dbb7405dSEduardo Habkost     /* Device will crash on reset if xen is not initialized */
553bb346faeSJoao Martins     if (xen_mode == XEN_DISABLED) {
554bb346faeSJoao Martins         error_setg(errp, "xen-platform device requires a Xen guest");
555b1ecd51bSEduardo Habkost         return;
556b1ecd51bSEduardo Habkost     }
557dbb7405dSEduardo Habkost 
558dc4aa51bSAndreas Färber     pci_conf = dev->config;
55901195b73SSteven Smith 
56001195b73SSteven Smith     pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
56101195b73SSteven Smith 
56201195b73SSteven Smith     pci_config_set_prog_interface(pci_conf, 0);
56301195b73SSteven Smith 
56401195b73SSteven Smith     pci_conf[PCI_INTERRUPT_PIN] = 1;
56501195b73SSteven Smith 
566de00982eSAvi Kivity     platform_ioport_bar_setup(d);
567dc4aa51bSAndreas Färber     pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->bar);
56801195b73SSteven Smith 
56901195b73SSteven Smith     /* reserve 16MB mmio address for share memory*/
570de00982eSAvi Kivity     platform_mmio_setup(d);
571dc4aa51bSAndreas Färber     pci_register_bar(dev, 1, PCI_BASE_ADDRESS_MEM_PREFETCH,
572e824b2ccSAvi Kivity                      &d->mmio_bar);
57301195b73SSteven Smith 
57401195b73SSteven Smith     platform_fixed_ioport_init(d);
57501195b73SSteven Smith }
57601195b73SSteven Smith 
platform_reset(DeviceState * dev)57701195b73SSteven Smith static void platform_reset(DeviceState *dev)
57801195b73SSteven Smith {
57951a3fe99SPeter Crosthwaite     PCIXenPlatformState *s = XEN_PLATFORM(dev);
58001195b73SSteven Smith 
58101195b73SSteven Smith     platform_fixed_ioport_reset(s);
58201195b73SSteven Smith }
58301195b73SSteven Smith 
xen_platform_class_init(ObjectClass * klass,const void * data)58412d1a768SPhilippe Mathieu-Daudé static void xen_platform_class_init(ObjectClass *klass, const void *data)
58540021f08SAnthony Liguori {
58639bffca2SAnthony Liguori     DeviceClass *dc = DEVICE_CLASS(klass);
58740021f08SAnthony Liguori     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
5880d2b962dSMichael S. Tsirkin 
5894098d49dSStefano Stabellini     k->realize = xen_platform_realize;
59040021f08SAnthony Liguori     k->vendor_id = PCI_VENDOR_ID_XEN;
59140021f08SAnthony Liguori     k->device_id = PCI_DEVICE_ID_XEN_PLATFORM;
59240021f08SAnthony Liguori     k->class_id = PCI_CLASS_OTHERS << 8 | 0x80;
59340021f08SAnthony Liguori     k->subsystem_vendor_id = PCI_VENDOR_ID_XEN;
59440021f08SAnthony Liguori     k->subsystem_id = PCI_DEVICE_ID_XEN_PLATFORM;
59540021f08SAnthony Liguori     k->revision = 1;
596125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
59739bffca2SAnthony Liguori     dc->desc = "XEN platform pci device";
598e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, platform_reset);
59939bffca2SAnthony Liguori     dc->vmsd = &vmstate_xen_platform;
60040021f08SAnthony Liguori }
60140021f08SAnthony Liguori 
6028c43a6f0SAndreas Färber static const TypeInfo xen_platform_info = {
60351a3fe99SPeter Crosthwaite     .name          = TYPE_XEN_PLATFORM,
60439bffca2SAnthony Liguori     .parent        = TYPE_PCI_DEVICE,
60539bffca2SAnthony Liguori     .instance_size = sizeof(PCIXenPlatformState),
60640021f08SAnthony Liguori     .class_init    = xen_platform_class_init,
607*2cd09e47SPhilippe Mathieu-Daudé     .interfaces = (const InterfaceInfo[]) {
608fd3b02c8SEduardo Habkost         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
609fd3b02c8SEduardo Habkost         { },
610fd3b02c8SEduardo Habkost     },
61101195b73SSteven Smith };
61201195b73SSteven Smith 
xen_platform_register_types(void)61383f7d43aSAndreas Färber static void xen_platform_register_types(void)
61401195b73SSteven Smith {
61539bffca2SAnthony Liguori     type_register_static(&xen_platform_info);
61601195b73SSteven Smith }
61701195b73SSteven Smith 
61883f7d43aSAndreas Färber type_init(xen_platform_register_types)
619