Marcel Apfelbaum | f7d6f3f | 2017-01-23 21:20:20 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Generic PCI Express Root Port emulation |
| 3 | * |
| 4 | * Copyright (C) 2017 Red Hat Inc |
| 5 | * |
| 6 | * Authors: |
| 7 | * Marcel Apfelbaum <marcel@redhat.com> |
| 8 | * |
| 9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| 10 | * See the COPYING file in the top-level directory. |
| 11 | */ |
| 12 | |
| 13 | #include "qemu/osdep.h" |
| 14 | #include "qapi/error.h" |
| 15 | #include "hw/pci/msix.h" |
| 16 | #include "hw/pci/pcie_port.h" |
| 17 | |
| 18 | #define TYPE_GEN_PCIE_ROOT_PORT "pcie-root-port" |
Aleksandr Bezzubikov | 226263f | 2017-08-18 02:36:49 +0300 | [diff] [blame] | 19 | #define GEN_PCIE_ROOT_PORT(obj) \ |
| 20 | OBJECT_CHECK(GenPCIERootPort, (obj), TYPE_GEN_PCIE_ROOT_PORT) |
Marcel Apfelbaum | f7d6f3f | 2017-01-23 21:20:20 +0200 | [diff] [blame] | 21 | |
| 22 | #define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100 |
| 23 | #define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR 1 |
| 24 | |
Marcel Apfelbaum | bc277a5 | 2017-06-07 15:43:59 +0300 | [diff] [blame] | 25 | typedef struct GenPCIERootPort { |
| 26 | /*< private >*/ |
| 27 | PCIESlot parent_obj; |
| 28 | /*< public >*/ |
| 29 | |
| 30 | bool migrate_msix; |
Aleksandr Bezzubikov | 226263f | 2017-08-18 02:36:49 +0300 | [diff] [blame] | 31 | |
| 32 | /* additional resources to reserve on firmware init */ |
| 33 | uint32_t bus_reserve; |
| 34 | uint64_t io_reserve; |
| 35 | uint64_t mem_reserve; |
| 36 | uint64_t pref32_reserve; |
| 37 | uint64_t pref64_reserve; |
Marcel Apfelbaum | bc277a5 | 2017-06-07 15:43:59 +0300 | [diff] [blame] | 38 | } GenPCIERootPort; |
| 39 | |
Marcel Apfelbaum | f7d6f3f | 2017-01-23 21:20:20 +0200 | [diff] [blame] | 40 | static uint8_t gen_rp_aer_vector(const PCIDevice *d) |
| 41 | { |
| 42 | return 0; |
| 43 | } |
| 44 | |
| 45 | static int gen_rp_interrupts_init(PCIDevice *d, Error **errp) |
| 46 | { |
| 47 | int rc; |
| 48 | |
Cao jin | ee640c6 | 2017-01-17 14:18:48 +0800 | [diff] [blame] | 49 | rc = msix_init_exclusive_bar(d, GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR, 0, errp); |
Marcel Apfelbaum | f7d6f3f | 2017-01-23 21:20:20 +0200 | [diff] [blame] | 50 | |
| 51 | if (rc < 0) { |
| 52 | assert(rc == -ENOTSUP); |
Marcel Apfelbaum | f7d6f3f | 2017-01-23 21:20:20 +0200 | [diff] [blame] | 53 | } else { |
| 54 | msix_vector_use(d, 0); |
| 55 | } |
| 56 | |
| 57 | return rc; |
| 58 | } |
| 59 | |
| 60 | static void gen_rp_interrupts_uninit(PCIDevice *d) |
| 61 | { |
| 62 | msix_uninit_exclusive_bar(d); |
| 63 | } |
| 64 | |
Marcel Apfelbaum | bc277a5 | 2017-06-07 15:43:59 +0300 | [diff] [blame] | 65 | static bool gen_rp_test_migrate_msix(void *opaque, int version_id) |
| 66 | { |
| 67 | GenPCIERootPort *rp = opaque; |
| 68 | |
| 69 | return rp->migrate_msix; |
| 70 | } |
| 71 | |
Aleksandr Bezzubikov | 226263f | 2017-08-18 02:36:49 +0300 | [diff] [blame] | 72 | static void gen_rp_realize(DeviceState *dev, Error **errp) |
| 73 | { |
| 74 | PCIDevice *d = PCI_DEVICE(dev); |
| 75 | GenPCIERootPort *grp = GEN_PCIE_ROOT_PORT(d); |
| 76 | PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(d); |
| 77 | |
| 78 | rpc->parent_realize(dev, errp); |
| 79 | |
| 80 | int rc = pci_bridge_qemu_reserve_cap_init(d, 0, grp->bus_reserve, |
| 81 | grp->io_reserve, grp->mem_reserve, grp->pref32_reserve, |
| 82 | grp->pref64_reserve, errp); |
| 83 | |
| 84 | if (rc < 0) { |
| 85 | rpc->parent_class.exit(d); |
| 86 | return; |
| 87 | } |
Marcel Apfelbaum | 8e36c33 | 2017-10-02 13:31:35 +0300 | [diff] [blame] | 88 | |
| 89 | if (!grp->io_reserve) { |
| 90 | pci_word_test_and_clear_mask(d->wmask + PCI_COMMAND, |
| 91 | PCI_COMMAND_IO); |
| 92 | d->wmask[PCI_IO_BASE] = 0; |
| 93 | d->wmask[PCI_IO_LIMIT] = 0; |
| 94 | } |
Aleksandr Bezzubikov | 226263f | 2017-08-18 02:36:49 +0300 | [diff] [blame] | 95 | } |
| 96 | |
Marcel Apfelbaum | f7d6f3f | 2017-01-23 21:20:20 +0200 | [diff] [blame] | 97 | static const VMStateDescription vmstate_rp_dev = { |
| 98 | .name = "pcie-root-port", |
| 99 | .version_id = 1, |
| 100 | .minimum_version_id = 1, |
| 101 | .post_load = pcie_cap_slot_post_load, |
| 102 | .fields = (VMStateField[]) { |
| 103 | VMSTATE_PCI_DEVICE(parent_obj.parent_obj.parent_obj, PCIESlot), |
| 104 | VMSTATE_STRUCT(parent_obj.parent_obj.parent_obj.exp.aer_log, |
| 105 | PCIESlot, 0, vmstate_pcie_aer_log, PCIEAERLog), |
Marcel Apfelbaum | bc277a5 | 2017-06-07 15:43:59 +0300 | [diff] [blame] | 106 | VMSTATE_MSIX_TEST(parent_obj.parent_obj.parent_obj.parent_obj, |
| 107 | GenPCIERootPort, |
| 108 | gen_rp_test_migrate_msix), |
Marcel Apfelbaum | f7d6f3f | 2017-01-23 21:20:20 +0200 | [diff] [blame] | 109 | VMSTATE_END_OF_LIST() |
| 110 | } |
| 111 | }; |
| 112 | |
Marcel Apfelbaum | bc277a5 | 2017-06-07 15:43:59 +0300 | [diff] [blame] | 113 | static Property gen_rp_props[] = { |
| 114 | DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort, migrate_msix, true), |
Aleksandr Bezzubikov | 226263f | 2017-08-18 02:36:49 +0300 | [diff] [blame] | 115 | DEFINE_PROP_UINT32("bus-reserve", GenPCIERootPort, bus_reserve, -1), |
| 116 | DEFINE_PROP_SIZE("io-reserve", GenPCIERootPort, io_reserve, -1), |
| 117 | DEFINE_PROP_SIZE("mem-reserve", GenPCIERootPort, mem_reserve, -1), |
| 118 | DEFINE_PROP_SIZE("pref32-reserve", GenPCIERootPort, pref32_reserve, -1), |
| 119 | DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort, pref64_reserve, -1), |
Marcel Apfelbaum | bc277a5 | 2017-06-07 15:43:59 +0300 | [diff] [blame] | 120 | DEFINE_PROP_END_OF_LIST() |
| 121 | }; |
| 122 | |
Marcel Apfelbaum | f7d6f3f | 2017-01-23 21:20:20 +0200 | [diff] [blame] | 123 | static void gen_rp_dev_class_init(ObjectClass *klass, void *data) |
| 124 | { |
| 125 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 126 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
| 127 | PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass); |
| 128 | |
| 129 | k->vendor_id = PCI_VENDOR_ID_REDHAT; |
| 130 | k->device_id = PCI_DEVICE_ID_REDHAT_PCIE_RP; |
| 131 | dc->desc = "PCI Express Root Port"; |
| 132 | dc->vmsd = &vmstate_rp_dev; |
Marcel Apfelbaum | bc277a5 | 2017-06-07 15:43:59 +0300 | [diff] [blame] | 133 | dc->props = gen_rp_props; |
Aleksandr Bezzubikov | 226263f | 2017-08-18 02:36:49 +0300 | [diff] [blame] | 134 | |
| 135 | rpc->parent_realize = dc->realize; |
| 136 | dc->realize = gen_rp_realize; |
| 137 | |
Marcel Apfelbaum | f7d6f3f | 2017-01-23 21:20:20 +0200 | [diff] [blame] | 138 | rpc->aer_vector = gen_rp_aer_vector; |
| 139 | rpc->interrupts_init = gen_rp_interrupts_init; |
| 140 | rpc->interrupts_uninit = gen_rp_interrupts_uninit; |
| 141 | rpc->aer_offset = GEN_PCIE_ROOT_PORT_AER_OFFSET; |
| 142 | } |
| 143 | |
| 144 | static const TypeInfo gen_rp_dev_info = { |
| 145 | .name = TYPE_GEN_PCIE_ROOT_PORT, |
| 146 | .parent = TYPE_PCIE_ROOT_PORT, |
Marcel Apfelbaum | bc277a5 | 2017-06-07 15:43:59 +0300 | [diff] [blame] | 147 | .instance_size = sizeof(GenPCIERootPort), |
Marcel Apfelbaum | f7d6f3f | 2017-01-23 21:20:20 +0200 | [diff] [blame] | 148 | .class_init = gen_rp_dev_class_init, |
| 149 | }; |
| 150 | |
| 151 | static void gen_rp_register_types(void) |
| 152 | { |
| 153 | type_register_static(&gen_rp_dev_info); |
| 154 | } |
| 155 | type_init(gen_rp_register_types) |