xref: /openbmc/qemu/hw/i386/xen/xen_platform.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
1ad3f7e31SWei Liu /*
2ad3f7e31SWei Liu  * XEN platform pci device, formerly known as the event channel device
3ad3f7e31SWei Liu  *
4ad3f7e31SWei Liu  * Copyright (c) 2003-2004 Intel Corp.
5ad3f7e31SWei Liu  * Copyright (c) 2006 XenSource
6ad3f7e31SWei Liu  *
7ad3f7e31SWei Liu  * Permission is hereby granted, free of charge, to any person obtaining a copy
8ad3f7e31SWei Liu  * of this software and associated documentation files (the "Software"), to deal
9ad3f7e31SWei Liu  * in the Software without restriction, including without limitation the rights
10ad3f7e31SWei Liu  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11ad3f7e31SWei Liu  * copies of the Software, and to permit persons to whom the Software is
12ad3f7e31SWei Liu  * furnished to do so, subject to the following conditions:
13ad3f7e31SWei Liu  *
14ad3f7e31SWei Liu  * The above copyright notice and this permission notice shall be included in
15ad3f7e31SWei Liu  * all copies or substantial portions of the Software.
16ad3f7e31SWei Liu  *
17ad3f7e31SWei Liu  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18ad3f7e31SWei Liu  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19ad3f7e31SWei Liu  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20ad3f7e31SWei Liu  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21ad3f7e31SWei Liu  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22ad3f7e31SWei Liu  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23ad3f7e31SWei Liu  * THE SOFTWARE.
24ad3f7e31SWei Liu  */
25ad3f7e31SWei Liu 
26b6a0aa05SPeter Maydell #include "qemu/osdep.h"
27da34e65cSMarkus Armbruster #include "qapi/error.h"
286a8a8b62SBernhard Beschow #include "hw/ide/pci.h"
29ad3f7e31SWei Liu #include "hw/pci/pci.h"
30d6454270SMarkus Armbruster #include "migration/vmstate.h"
31bb346faeSJoao Martins #include "net/net.h"
32ad3f7e31SWei Liu #include "trace.h"
33da278d58SPhilippe Mathieu-Daudé #include "sysemu/xen.h"
344be74634SMarkus Armbruster #include "sysemu/block-backend.h"
35b1ecd51bSEduardo Habkost #include "qemu/error-report.h"
360b8fa32fSMarkus Armbruster #include "qemu/module.h"
37db1015e9SEduardo Habkost #include "qom/object.h"
38ad3f7e31SWei Liu 
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 
46ad3f7e31SWei Liu //#define DEBUG_PLATFORM
47ad3f7e31SWei Liu 
48ad3f7e31SWei Liu #ifdef DEBUG_PLATFORM
49ad3f7e31SWei Liu #define DPRINTF(fmt, ...) do { \
50ad3f7e31SWei Liu     fprintf(stderr, "xen_platform: " fmt, ## __VA_ARGS__); \
51ad3f7e31SWei Liu } while (0)
52ad3f7e31SWei Liu #else
53ad3f7e31SWei Liu #define DPRINTF(fmt, ...) do { } while (0)
54ad3f7e31SWei Liu #endif
55ad3f7e31SWei Liu 
56ad3f7e31SWei Liu #define PFFLAG_ROM_LOCK 1 /* Sets whether ROM memory area is RW or RO */
57ad3f7e31SWei Liu 
58db1015e9SEduardo Habkost struct PCIXenPlatformState {
59ad3f7e31SWei Liu     /*< private >*/
60ad3f7e31SWei Liu     PCIDevice parent_obj;
61ad3f7e31SWei Liu     /*< public >*/
62ad3f7e31SWei Liu 
63ad3f7e31SWei Liu     MemoryRegion fixed_io;
64ad3f7e31SWei Liu     MemoryRegion bar;
65ad3f7e31SWei Liu     MemoryRegion mmio_bar;
66ad3f7e31SWei Liu     uint8_t flags; /* used only for version_id == 2 */
67ad3f7e31SWei Liu     uint16_t driver_product_version;
68ad3f7e31SWei Liu 
69ad3f7e31SWei Liu     /* Log from guest drivers */
70ad3f7e31SWei Liu     char log_buffer[4096];
71ad3f7e31SWei Liu     int log_buffer_off;
72db1015e9SEduardo Habkost };
73ad3f7e31SWei Liu 
74ad3f7e31SWei Liu #define TYPE_XEN_PLATFORM "xen-platform"
OBJECT_DECLARE_SIMPLE_TYPE(PCIXenPlatformState,XEN_PLATFORM)758063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(PCIXenPlatformState, XEN_PLATFORM)
76ad3f7e31SWei Liu 
77ad3f7e31SWei Liu #define XEN_PLATFORM_IOPORT 0x10
78ad3f7e31SWei Liu 
79ad3f7e31SWei Liu /* Send bytes to syslog */
80ad3f7e31SWei Liu static void log_writeb(PCIXenPlatformState *s, char val)
81ad3f7e31SWei Liu {
82ad3f7e31SWei Liu     if (val == '\n' || s->log_buffer_off == sizeof(s->log_buffer) - 1) {
83ad3f7e31SWei Liu         /* Flush buffer */
84ad3f7e31SWei Liu         s->log_buffer[s->log_buffer_off] = 0;
85ad3f7e31SWei Liu         trace_xen_platform_log(s->log_buffer);
86ad3f7e31SWei Liu         s->log_buffer_off = 0;
87ad3f7e31SWei Liu     } else {
88ad3f7e31SWei Liu         s->log_buffer[s->log_buffer_off++] = val;
89ad3f7e31SWei Liu     }
90ad3f7e31SWei Liu }
91ad3f7e31SWei Liu 
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)
116ad3f7e31SWei Liu 
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)130ad3f7e31SWei Liu static void unplug_nic(PCIBus *b, PCIDevice *d, void *o)
131ad3f7e31SWei Liu {
132ad3f7e31SWei Liu     /* We have to ignore passthrough devices */
133ad3f7e31SWei Liu     if (pci_get_word(d->config + PCI_CLASS_DEVICE) ==
134ad3f7e31SWei Liu             PCI_CLASS_NETWORK_ETHERNET
1353bb1ebacSJoao Martins             && !pci_device_is_passthrough(d)) {
136ad3f7e31SWei Liu         object_unparent(OBJECT(d));
137ad3f7e31SWei Liu     }
138ad3f7e31SWei Liu }
139ad3f7e31SWei Liu 
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)155ad3f7e31SWei Liu static void pci_unplug_nics(PCIBus *bus)
156ad3f7e31SWei Liu {
1576c808651SRoss Lagerwall     qemu_foreach_nic(del_nic_peer, NULL);
158ad3f7e31SWei Liu     pci_for_each_device(bus, 0, unplug_nic, NULL);
159ad3f7e31SWei Liu }
160ad3f7e31SWei Liu 
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)
229ad3f7e31SWei Liu {
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 
234ad3f7e31SWei Liu     /* 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;
257ad3f7e31SWei Liu     }
258ad3f7e31SWei Liu }
259ad3f7e31SWei Liu 
pci_unplug_disks(PCIBus * bus,uint32_t flags)260ae4d2eb2SPaul Durrant static void pci_unplug_disks(PCIBus *bus, uint32_t flags)
261ad3f7e31SWei Liu {
262ae4d2eb2SPaul Durrant     pci_for_each_device(bus, 0, unplug_disks, &flags);
263ad3f7e31SWei Liu }
264ad3f7e31SWei Liu 
platform_fixed_ioport_writew(void * opaque,uint32_t addr,uint32_t val)265ad3f7e31SWei Liu static void platform_fixed_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
266ad3f7e31SWei Liu {
267ad3f7e31SWei Liu     PCIXenPlatformState *s = opaque;
268ad3f7e31SWei Liu 
269ad3f7e31SWei Liu     switch (addr) {
270ad3f7e31SWei Liu     case 0: {
271ad3f7e31SWei Liu         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)) {
275ad3f7e31SWei Liu             DPRINTF("unplug disks\n");
276fd56e061SDavid Gibson             pci_unplug_disks(pci_get_bus(pci_dev), val);
277ad3f7e31SWei Liu         }
278ad3f7e31SWei Liu         if (val & UNPLUG_ALL_NICS) {
279ad3f7e31SWei Liu             DPRINTF("unplug nics\n");
280fd56e061SDavid Gibson             pci_unplug_nics(pci_get_bus(pci_dev));
281ad3f7e31SWei Liu         }
282ad3f7e31SWei Liu         break;
283ad3f7e31SWei Liu     }
284ad3f7e31SWei Liu     case 2:
285ad3f7e31SWei Liu         switch (val) {
286ad3f7e31SWei Liu         case 1:
287ad3f7e31SWei Liu             DPRINTF("Citrix Windows PV drivers loaded in guest\n");
288ad3f7e31SWei Liu             break;
289ad3f7e31SWei Liu         case 0:
290ad3f7e31SWei Liu             DPRINTF("Guest claimed to be running PV product 0?\n");
291ad3f7e31SWei Liu             break;
292ad3f7e31SWei Liu         default:
293ad3f7e31SWei Liu             DPRINTF("Unknown PV product %d loaded in guest\n", val);
294ad3f7e31SWei Liu             break;
295ad3f7e31SWei Liu         }
296ad3f7e31SWei Liu         s->driver_product_version = val;
297ad3f7e31SWei Liu         break;
298ad3f7e31SWei Liu     }
299ad3f7e31SWei Liu }
300ad3f7e31SWei Liu 
platform_fixed_ioport_writel(void * opaque,uint32_t addr,uint32_t val)301ad3f7e31SWei Liu static void platform_fixed_ioport_writel(void *opaque, uint32_t addr,
302ad3f7e31SWei Liu                                          uint32_t val)
303ad3f7e31SWei Liu {
304ad3f7e31SWei Liu     switch (addr) {
305ad3f7e31SWei Liu     case 0:
306ad3f7e31SWei Liu         /* PV driver version */
307ad3f7e31SWei Liu         break;
308ad3f7e31SWei Liu     }
309ad3f7e31SWei Liu }
310ad3f7e31SWei Liu 
platform_fixed_ioport_writeb(void * opaque,uint32_t addr,uint32_t val)311ad3f7e31SWei Liu static void platform_fixed_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
312ad3f7e31SWei Liu {
313ad3f7e31SWei Liu     PCIXenPlatformState *s = opaque;
314ad3f7e31SWei Liu 
315ad3f7e31SWei Liu     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 {
322ad3f7e31SWei Liu             hvmmem_type_t mem_type = (val & PFFLAG_ROM_LOCK) ?
323ad3f7e31SWei Liu                 HVMMEM_ram_ro : HVMMEM_ram_rw;
324bb346faeSJoao Martins 
3258f25e754SPaul Durrant             if (xen_set_mem_type(xen_domid, mem_type, 0xc0, 0x40)) {
326ad3f7e31SWei Liu                 DPRINTF("unable to change ro/rw state of ROM memory area!\n");
327ad3f7e31SWei Liu             } else {
328ad3f7e31SWei Liu                 s->flags = val & PFFLAG_ROM_LOCK;
329ad3f7e31SWei Liu                 DPRINTF("changed ro/rw state of ROM memory area. now is %s state.\n",
330ad3f7e31SWei Liu                         (mem_type == HVMMEM_ram_ro ? "ro" : "rw"));
331ad3f7e31SWei Liu             }
332bb346faeSJoao Martins #endif
333ad3f7e31SWei Liu         }
334bb346faeSJoao Martins         break;
335bb346faeSJoao Martins 
336ad3f7e31SWei Liu     case 2:
337ad3f7e31SWei Liu         log_writeb(s, val);
338ad3f7e31SWei Liu         break;
339ad3f7e31SWei Liu     }
340ad3f7e31SWei Liu }
341ad3f7e31SWei Liu 
platform_fixed_ioport_readw(void * opaque,uint32_t addr)342ad3f7e31SWei Liu static uint32_t platform_fixed_ioport_readw(void *opaque, uint32_t addr)
343ad3f7e31SWei Liu {
344ad3f7e31SWei Liu     switch (addr) {
345ad3f7e31SWei Liu     case 0:
346ad3f7e31SWei Liu         /* Magic value so that you can identify the interface. */
347ad3f7e31SWei Liu         return 0x49d2;
348ad3f7e31SWei Liu     default:
349ad3f7e31SWei Liu         return 0xffff;
350ad3f7e31SWei Liu     }
351ad3f7e31SWei Liu }
352ad3f7e31SWei Liu 
platform_fixed_ioport_readb(void * opaque,uint32_t addr)353ad3f7e31SWei Liu static uint32_t platform_fixed_ioport_readb(void *opaque, uint32_t addr)
354ad3f7e31SWei Liu {
355ad3f7e31SWei Liu     PCIXenPlatformState *s = opaque;
356ad3f7e31SWei Liu 
357ad3f7e31SWei Liu     switch (addr) {
358ad3f7e31SWei Liu     case 0:
359ad3f7e31SWei Liu         /* Platform flags */
360ad3f7e31SWei Liu         return s->flags;
361ad3f7e31SWei Liu     case 2:
362ad3f7e31SWei Liu         /* Version number */
363ad3f7e31SWei Liu         return 1;
364ad3f7e31SWei Liu     default:
365ad3f7e31SWei Liu         return 0xff;
366ad3f7e31SWei Liu     }
367ad3f7e31SWei Liu }
368ad3f7e31SWei Liu 
platform_fixed_ioport_reset(void * opaque)369ad3f7e31SWei Liu static void platform_fixed_ioport_reset(void *opaque)
370ad3f7e31SWei Liu {
371ad3f7e31SWei Liu     PCIXenPlatformState *s = opaque;
372ad3f7e31SWei Liu 
373ad3f7e31SWei Liu     platform_fixed_ioport_writeb(s, 0, 0);
374ad3f7e31SWei Liu }
375ad3f7e31SWei Liu 
platform_fixed_ioport_read(void * opaque,hwaddr addr,unsigned size)376ad3f7e31SWei Liu static uint64_t platform_fixed_ioport_read(void *opaque,
377ad3f7e31SWei Liu                                            hwaddr addr,
378ad3f7e31SWei Liu                                            unsigned size)
379ad3f7e31SWei Liu {
380ad3f7e31SWei Liu     switch (size) {
381ad3f7e31SWei Liu     case 1:
382ad3f7e31SWei Liu         return platform_fixed_ioport_readb(opaque, addr);
383ad3f7e31SWei Liu     case 2:
384ad3f7e31SWei Liu         return platform_fixed_ioport_readw(opaque, addr);
385ad3f7e31SWei Liu     default:
386ad3f7e31SWei Liu         return -1;
387ad3f7e31SWei Liu     }
388ad3f7e31SWei Liu }
389ad3f7e31SWei Liu 
platform_fixed_ioport_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)390ad3f7e31SWei Liu static void platform_fixed_ioport_write(void *opaque, hwaddr addr,
391ad3f7e31SWei Liu 
392ad3f7e31SWei Liu                                         uint64_t val, unsigned size)
393ad3f7e31SWei Liu {
394ad3f7e31SWei Liu     switch (size) {
395ad3f7e31SWei Liu     case 1:
396ad3f7e31SWei Liu         platform_fixed_ioport_writeb(opaque, addr, val);
397ad3f7e31SWei Liu         break;
398ad3f7e31SWei Liu     case 2:
399ad3f7e31SWei Liu         platform_fixed_ioport_writew(opaque, addr, val);
400ad3f7e31SWei Liu         break;
401ad3f7e31SWei Liu     case 4:
402ad3f7e31SWei Liu         platform_fixed_ioport_writel(opaque, addr, val);
403ad3f7e31SWei Liu         break;
404ad3f7e31SWei Liu     }
405ad3f7e31SWei Liu }
406ad3f7e31SWei Liu 
407ad3f7e31SWei Liu 
408ad3f7e31SWei Liu static const MemoryRegionOps platform_fixed_io_ops = {
409ad3f7e31SWei Liu     .read = platform_fixed_ioport_read,
410ad3f7e31SWei Liu     .write = platform_fixed_ioport_write,
411ad3f7e31SWei Liu     .valid = {
412ad3f7e31SWei Liu         .unaligned = true,
413ad3f7e31SWei Liu     },
414ad3f7e31SWei Liu     .impl = {
415ad3f7e31SWei Liu         .min_access_size = 1,
416ad3f7e31SWei Liu         .max_access_size = 4,
417ad3f7e31SWei Liu         .unaligned = true,
418ad3f7e31SWei Liu     },
419ad3f7e31SWei Liu     .endianness = DEVICE_LITTLE_ENDIAN,
420ad3f7e31SWei Liu };
421ad3f7e31SWei Liu 
platform_fixed_ioport_init(PCIXenPlatformState * s)422ad3f7e31SWei Liu static void platform_fixed_ioport_init(PCIXenPlatformState* s)
423ad3f7e31SWei Liu {
424ad3f7e31SWei Liu     memory_region_init_io(&s->fixed_io, OBJECT(s), &platform_fixed_io_ops, s,
425ad3f7e31SWei Liu                           "xen-fixed", 16);
426ad3f7e31SWei Liu     memory_region_add_subregion(get_system_io(), XEN_PLATFORM_IOPORT,
427ad3f7e31SWei Liu                                 &s->fixed_io);
428ad3f7e31SWei Liu }
429ad3f7e31SWei Liu 
430ad3f7e31SWei Liu /* Xen Platform PCI Device */
431ad3f7e31SWei Liu 
xen_platform_ioport_readb(void * opaque,hwaddr addr,unsigned int size)432ad3f7e31SWei Liu static uint64_t xen_platform_ioport_readb(void *opaque, hwaddr addr,
433ad3f7e31SWei Liu                                           unsigned int size)
434ad3f7e31SWei Liu {
435ad3f7e31SWei Liu     if (addr == 0) {
436ad3f7e31SWei Liu         return platform_fixed_ioport_readb(opaque, 0);
437ad3f7e31SWei Liu     } else {
438ad3f7e31SWei Liu         return ~0u;
439ad3f7e31SWei Liu     }
440ad3f7e31SWei Liu }
441ad3f7e31SWei Liu 
xen_platform_ioport_writeb(void * opaque,hwaddr addr,uint64_t val,unsigned int size)442ad3f7e31SWei Liu static void xen_platform_ioport_writeb(void *opaque, hwaddr addr,
443ad3f7e31SWei Liu                                        uint64_t val, unsigned int size)
444ad3f7e31SWei Liu {
445ad3f7e31SWei Liu     PCIXenPlatformState *s = opaque;
44635132016SOlaf Hering     PCIDevice *pci_dev = PCI_DEVICE(s);
447ad3f7e31SWei Liu 
448ad3f7e31SWei Liu     switch (addr) {
449ad3f7e31SWei Liu     case 0: /* Platform flags */
450ad3f7e31SWei Liu         platform_fixed_ioport_writeb(opaque, 0, (uint32_t)val);
451ad3f7e31SWei Liu         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;
466ad3f7e31SWei Liu     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:
475ad3f7e31SWei Liu             log_writeb(s, (uint32_t)val);
476ad3f7e31SWei Liu             break;
47735132016SOlaf Hering         }
47835132016SOlaf Hering         break;
479ad3f7e31SWei Liu     default:
480ad3f7e31SWei Liu         break;
481ad3f7e31SWei Liu     }
482ad3f7e31SWei Liu }
483ad3f7e31SWei Liu 
484ad3f7e31SWei Liu static const MemoryRegionOps xen_pci_io_ops = {
485ad3f7e31SWei Liu     .read  = xen_platform_ioport_readb,
486ad3f7e31SWei Liu     .write = xen_platform_ioport_writeb,
487ad3f7e31SWei Liu     .impl.min_access_size = 1,
488ad3f7e31SWei Liu     .impl.max_access_size = 1,
489ad3f7e31SWei Liu };
490ad3f7e31SWei Liu 
platform_ioport_bar_setup(PCIXenPlatformState * d)491ad3f7e31SWei Liu static void platform_ioport_bar_setup(PCIXenPlatformState *d)
492ad3f7e31SWei Liu {
493ad3f7e31SWei Liu     memory_region_init_io(&d->bar, OBJECT(d), &xen_pci_io_ops, d,
494ad3f7e31SWei Liu                           "xen-pci", 0x100);
495ad3f7e31SWei Liu }
496ad3f7e31SWei Liu 
platform_mmio_read(void * opaque,hwaddr addr,unsigned size)497ad3f7e31SWei Liu static uint64_t platform_mmio_read(void *opaque, hwaddr addr,
498ad3f7e31SWei Liu                                    unsigned size)
499ad3f7e31SWei Liu {
500ad3f7e31SWei Liu     DPRINTF("Warning: attempted read from physical address "
501883f2c59SPhilippe Mathieu-Daudé             "0x" HWADDR_FMT_plx " in xen platform mmio space\n", addr);
502ad3f7e31SWei Liu 
503ad3f7e31SWei Liu     return 0;
504ad3f7e31SWei Liu }
505ad3f7e31SWei Liu 
platform_mmio_write(void * opaque,hwaddr addr,uint64_t val,unsigned size)506ad3f7e31SWei Liu static void platform_mmio_write(void *opaque, hwaddr addr,
507ad3f7e31SWei Liu                                 uint64_t val, unsigned size)
508ad3f7e31SWei Liu {
509ad3f7e31SWei Liu     DPRINTF("Warning: attempted write of 0x%"PRIx64" to physical "
510883f2c59SPhilippe Mathieu-Daudé             "address 0x" HWADDR_FMT_plx " in xen platform mmio space\n",
511ad3f7e31SWei Liu             val, addr);
512ad3f7e31SWei Liu }
513ad3f7e31SWei Liu 
514ad3f7e31SWei Liu static const MemoryRegionOps platform_mmio_handler = {
515ad3f7e31SWei Liu     .read = &platform_mmio_read,
516ad3f7e31SWei Liu     .write = &platform_mmio_write,
517ad3f7e31SWei Liu     .endianness = DEVICE_NATIVE_ENDIAN,
518ad3f7e31SWei Liu };
519ad3f7e31SWei Liu 
platform_mmio_setup(PCIXenPlatformState * d)520ad3f7e31SWei Liu static void platform_mmio_setup(PCIXenPlatformState *d)
521ad3f7e31SWei Liu {
522ad3f7e31SWei Liu     memory_region_init_io(&d->mmio_bar, OBJECT(d), &platform_mmio_handler, d,
523ad3f7e31SWei Liu                           "xen-mmio", 0x1000000);
524ad3f7e31SWei Liu }
525ad3f7e31SWei Liu 
xen_platform_post_load(void * opaque,int version_id)526ad3f7e31SWei Liu static int xen_platform_post_load(void *opaque, int version_id)
527ad3f7e31SWei Liu {
528ad3f7e31SWei Liu     PCIXenPlatformState *s = opaque;
529ad3f7e31SWei Liu 
530ad3f7e31SWei Liu     platform_fixed_ioport_writeb(s, 0, s->flags);
531ad3f7e31SWei Liu 
532ad3f7e31SWei Liu     return 0;
533ad3f7e31SWei Liu }
534ad3f7e31SWei Liu 
535ad3f7e31SWei Liu static const VMStateDescription vmstate_xen_platform = {
536ad3f7e31SWei Liu     .name = "platform",
537ad3f7e31SWei Liu     .version_id = 4,
538ad3f7e31SWei Liu     .minimum_version_id = 4,
539ad3f7e31SWei Liu     .post_load = xen_platform_post_load,
5409231a017SRichard Henderson     .fields = (const VMStateField[]) {
541ad3f7e31SWei Liu         VMSTATE_PCI_DEVICE(parent_obj, PCIXenPlatformState),
542ad3f7e31SWei Liu         VMSTATE_UINT8(flags, PCIXenPlatformState),
543ad3f7e31SWei Liu         VMSTATE_END_OF_LIST()
544ad3f7e31SWei Liu     }
545ad3f7e31SWei Liu };
546ad3f7e31SWei Liu 
xen_platform_realize(PCIDevice * dev,Error ** errp)5474098d49dSStefano Stabellini static void xen_platform_realize(PCIDevice *dev, Error **errp)
548ad3f7e31SWei Liu {
549ad3f7e31SWei Liu     PCIXenPlatformState *d = XEN_PLATFORM(dev);
550ad3f7e31SWei Liu     uint8_t *pci_conf;
551ad3f7e31SWei Liu 
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 
558ad3f7e31SWei Liu     pci_conf = dev->config;
559ad3f7e31SWei Liu 
560ad3f7e31SWei Liu     pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
561ad3f7e31SWei Liu 
562ad3f7e31SWei Liu     pci_config_set_prog_interface(pci_conf, 0);
563ad3f7e31SWei Liu 
564ad3f7e31SWei Liu     pci_conf[PCI_INTERRUPT_PIN] = 1;
565ad3f7e31SWei Liu 
566ad3f7e31SWei Liu     platform_ioport_bar_setup(d);
567ad3f7e31SWei Liu     pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->bar);
568ad3f7e31SWei Liu 
569ad3f7e31SWei Liu     /* reserve 16MB mmio address for share memory*/
570ad3f7e31SWei Liu     platform_mmio_setup(d);
571ad3f7e31SWei Liu     pci_register_bar(dev, 1, PCI_BASE_ADDRESS_MEM_PREFETCH,
572ad3f7e31SWei Liu                      &d->mmio_bar);
573ad3f7e31SWei Liu 
574ad3f7e31SWei Liu     platform_fixed_ioport_init(d);
575ad3f7e31SWei Liu }
576ad3f7e31SWei Liu 
platform_reset(DeviceState * dev)577ad3f7e31SWei Liu static void platform_reset(DeviceState *dev)
578ad3f7e31SWei Liu {
579ad3f7e31SWei Liu     PCIXenPlatformState *s = XEN_PLATFORM(dev);
580ad3f7e31SWei Liu 
581ad3f7e31SWei Liu     platform_fixed_ioport_reset(s);
582ad3f7e31SWei Liu }
583ad3f7e31SWei Liu 
xen_platform_class_init(ObjectClass * klass,void * data)584ad3f7e31SWei Liu static void xen_platform_class_init(ObjectClass *klass, void *data)
585ad3f7e31SWei Liu {
586ad3f7e31SWei Liu     DeviceClass *dc = DEVICE_CLASS(klass);
587ad3f7e31SWei Liu     PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
588ad3f7e31SWei Liu 
5894098d49dSStefano Stabellini     k->realize = xen_platform_realize;
590ad3f7e31SWei Liu     k->vendor_id = PCI_VENDOR_ID_XEN;
591ad3f7e31SWei Liu     k->device_id = PCI_DEVICE_ID_XEN_PLATFORM;
592ad3f7e31SWei Liu     k->class_id = PCI_CLASS_OTHERS << 8 | 0x80;
593ad3f7e31SWei Liu     k->subsystem_vendor_id = PCI_VENDOR_ID_XEN;
594ad3f7e31SWei Liu     k->subsystem_id = PCI_DEVICE_ID_XEN_PLATFORM;
595ad3f7e31SWei Liu     k->revision = 1;
596ad3f7e31SWei Liu     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
597ad3f7e31SWei Liu     dc->desc = "XEN platform pci device";
598*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, platform_reset);
599ad3f7e31SWei Liu     dc->vmsd = &vmstate_xen_platform;
600ad3f7e31SWei Liu }
601ad3f7e31SWei Liu 
602ad3f7e31SWei Liu static const TypeInfo xen_platform_info = {
603ad3f7e31SWei Liu     .name          = TYPE_XEN_PLATFORM,
604ad3f7e31SWei Liu     .parent        = TYPE_PCI_DEVICE,
605ad3f7e31SWei Liu     .instance_size = sizeof(PCIXenPlatformState),
606ad3f7e31SWei Liu     .class_init    = xen_platform_class_init,
607fd3b02c8SEduardo Habkost     .interfaces = (InterfaceInfo[]) {
608fd3b02c8SEduardo Habkost         { INTERFACE_CONVENTIONAL_PCI_DEVICE },
609fd3b02c8SEduardo Habkost         { },
610fd3b02c8SEduardo Habkost     },
611ad3f7e31SWei Liu };
612ad3f7e31SWei Liu 
xen_platform_register_types(void)613ad3f7e31SWei Liu static void xen_platform_register_types(void)
614ad3f7e31SWei Liu {
615ad3f7e31SWei Liu     type_register_static(&xen_platform_info);
616ad3f7e31SWei Liu }
617ad3f7e31SWei Liu 
618ad3f7e31SWei Liu type_init(xen_platform_register_types)
619