/* * Copyright (c) 2007, Neocleus Corporation. * Copyright (c) 2007, Intel Corporation. * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. * * Alex Novik * Allen Kay * Guy Zana * * This file implements direct PCI assignment to a HVM guest */ #include "qemu-timer.h" #include "xen_backend.h" #include "xen_pt.h" #define XEN_PT_MERGE_VALUE(value, data, val_mask) \ (((value) & (val_mask)) | ((data) & ~(val_mask))) #define XEN_PT_INVALID_REG 0xFFFFFFFF /* invalid register value */ /* prototype */ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data); /* helper */ /* A return value of 1 means the capability should NOT be exposed to guest. */ static int xen_pt_hide_dev_cap(const XenHostPCIDevice *d, uint8_t grp_id) { switch (grp_id) { case PCI_CAP_ID_EXP: /* The PCI Express Capability Structure of the VF of Intel 82599 10GbE * Controller looks trivial, e.g., the PCI Express Capabilities * Register is 0. We should not try to expose it to guest. * * The datasheet is available at * http://download.intel.com/design/network/datashts/82599_datasheet.pdf * * See 'Table 9.7. VF PCIe Configuration Space' of the datasheet, the * PCI Express Capability Structure of the VF of Intel 82599 10GbE * Controller looks trivial, e.g., the PCI Express Capabilities * Register is 0, so the Capability Version is 0 and * xen_pt_pcie_size_init() would fail. */ if (d->vendor_id == PCI_VENDOR_ID_INTEL && d->device_id == PCI_DEVICE_ID_INTEL_82599_SFP_VF) { return 1; } break; } return 0; } /* find emulate register group entry */ XenPTRegGroup *xen_pt_find_reg_grp(XenPCIPassthroughState *s, uint32_t address) { XenPTRegGroup *entry = NULL; /* find register group entry */ QLIST_FOREACH(entry, &s->reg_grps, entries) { /* check address */ if ((entry->base_offset <= address) && ((entry->base_offset + entry->size) > address)) { return entry; } } /* group entry not found */ return NULL; } /* find emulate register entry */ XenPTReg *xen_pt_find_reg(XenPTRegGroup *reg_grp, uint32_t address) { XenPTReg *reg_entry = NULL; XenPTRegInfo *reg = NULL; uint32_t real_offset = 0; /* find register entry */ QLIST_FOREACH(reg_entry, ®_grp->reg_tbl_list, entries) { reg = reg_entry->reg; real_offset = reg_grp->base_offset + reg->offset; /* check address */ if ((real_offset <= address) && ((real_offset + reg->size) > address)) { return reg_entry; } } return NULL; } /**************** * general register functions */ /* register initialization function */ static int xen_pt_common_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { *data = reg->init_val; return 0; } /* Read register functions */ static int xen_pt_byte_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint8_t *value, uint8_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint8_t valid_emu_mask = 0; /* emulate byte register */ valid_emu_mask = reg->emu_mask & valid_mask; *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); return 0; } static int xen_pt_word_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *value, uint16_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint16_t valid_emu_mask = 0; /* emulate word register */ valid_emu_mask = reg->emu_mask & valid_mask; *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); return 0; } static int xen_pt_long_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint32_t *value, uint32_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint32_t valid_emu_mask = 0; /* emulate long register */ valid_emu_mask = reg->emu_mask & valid_mask; *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); return 0; } /* Write register functions */ static int xen_pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint8_t *val, uint8_t dev_value, uint8_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint8_t writable_mask = 0; uint8_t throughable_mask = 0; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; } static int xen_pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *val, uint16_t dev_value, uint16_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; uint16_t throughable_mask = 0; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; } static int xen_pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint32_t *val, uint32_t dev_value, uint32_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; uint32_t throughable_mask = 0; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; } /* XenPTRegInfo declaration * - only for emulated register (either a part or whole bit). * - for passthrough register that need special behavior (like interacting with * other component), set emu_mask to all 0 and specify r/w func properly. * - do NOT use ALL F for init_val, otherwise the tbl will not be registered. */ /******************** * Header Type0 */ static int xen_pt_vendor_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { *data = s->real_device.vendor_id; return 0; } static int xen_pt_device_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { *data = s->real_device.device_id; return 0; } static int xen_pt_status_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { XenPTRegGroup *reg_grp_entry = NULL; XenPTReg *reg_entry = NULL; uint32_t reg_field = 0; /* find Header register group */ reg_grp_entry = xen_pt_find_reg_grp(s, PCI_CAPABILITY_LIST); if (reg_grp_entry) { /* find Capabilities Pointer register */ reg_entry = xen_pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST); if (reg_entry) { /* check Capabilities Pointer register */ if (reg_entry->data) { reg_field |= PCI_STATUS_CAP_LIST; } else { reg_field &= ~PCI_STATUS_CAP_LIST; } } else { xen_shutdown_fatal_error("Internal error: Couldn't find XenPTReg*" " for Capabilities Pointer register." " (%s)\n", __func__); return -1; } } else { xen_shutdown_fatal_error("Internal error: Couldn't find XenPTRegGroup" " for Header. (%s)\n", __func__); return -1; } *data = reg_field; return 0; } static int xen_pt_header_type_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { /* read PCI_HEADER_TYPE */ *data = reg->init_val | 0x80; return 0; } /* initialize Interrupt Pin register */ static int xen_pt_irqpin_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { *data = xen_pt_pci_read_intx(s); return 0; } /* Command register */ static int xen_pt_cmd_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *value, uint16_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint16_t valid_emu_mask = 0; uint16_t emu_mask = reg->emu_mask; if (s->is_virtfn) { emu_mask |= PCI_COMMAND_MEMORY; } /* emulate word register */ valid_emu_mask = emu_mask & valid_mask; *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); return 0; } static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *val, uint16_t dev_value, uint16_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; uint16_t throughable_mask = 0; uint16_t emu_mask = reg->emu_mask; if (s->is_virtfn) { emu_mask |= PCI_COMMAND_MEMORY; } /* modify emulate register */ writable_mask = ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ throughable_mask = ~emu_mask & valid_mask; if (*val & PCI_COMMAND_INTX_DISABLE) { throughable_mask |= PCI_COMMAND_INTX_DISABLE; } else { if (s->machine_irq) { throughable_mask |= PCI_COMMAND_INTX_DISABLE; } } *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; } /* BAR */ #define XEN_PT_BAR_MEM_RO_MASK 0x0000000F /* BAR ReadOnly mask(Memory) */ #define XEN_PT_BAR_MEM_EMU_MASK 0xFFFFFFF0 /* BAR emul mask(Memory) */ #define XEN_PT_BAR_IO_RO_MASK 0x00000003 /* BAR ReadOnly mask(I/O) */ #define XEN_PT_BAR_IO_EMU_MASK 0xFFFFFFFC /* BAR emul mask(I/O) */ static XenPTBarFlag xen_pt_bar_reg_parse(XenPCIPassthroughState *s, XenPTRegInfo *reg) { PCIDevice *d = &s->dev; XenPTRegion *region = NULL; PCIIORegion *r; int index = 0; /* check 64bit BAR */ index = xen_pt_bar_offset_to_index(reg->offset); if ((0 < index) && (index < PCI_ROM_SLOT)) { int type = s->real_device.io_regions[index - 1].type; if ((type & XEN_HOST_PCI_REGION_TYPE_MEM) && (type & XEN_HOST_PCI_REGION_TYPE_MEM_64)) { region = &s->bases[index - 1]; if (region->bar_flag != XEN_PT_BAR_FLAG_UPPER) { return XEN_PT_BAR_FLAG_UPPER; } } } /* check unused BAR */ r = &d->io_regions[index]; if (r->size == 0) { return XEN_PT_BAR_FLAG_UNUSED; } /* for ExpROM BAR */ if (index == PCI_ROM_SLOT) { return XEN_PT_BAR_FLAG_MEM; } /* check BAR I/O indicator */ if (s->real_device.io_regions[index].type & XEN_HOST_PCI_REGION_TYPE_IO) { return XEN_PT_BAR_FLAG_IO; } else { return XEN_PT_BAR_FLAG_MEM; } } static inline uint32_t base_address_with_flags(XenHostPCIIORegion *hr) { if (hr->type & XEN_HOST_PCI_REGION_TYPE_IO) { return hr->base_addr | (hr->bus_flags & ~PCI_BASE_ADDRESS_IO_MASK); } else { return hr->base_addr | (hr->bus_flags & ~PCI_BASE_ADDRESS_MEM_MASK); } } static int xen_pt_bar_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { uint32_t reg_field = 0; int index; index = xen_pt_bar_offset_to_index(reg->offset); if (index < 0 || index >= PCI_NUM_REGIONS) { XEN_PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); return -1; } /* set BAR flag */ s->bases[index].bar_flag = xen_pt_bar_reg_parse(s, reg); if (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED) { reg_field = XEN_PT_INVALID_REG; } *data = reg_field; return 0; } static int xen_pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint32_t *value, uint32_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint32_t valid_emu_mask = 0; uint32_t bar_emu_mask = 0; int index; /* get BAR index */ index = xen_pt_bar_offset_to_index(reg->offset); if (index < 0 || index >= PCI_NUM_REGIONS) { XEN_PT_ERR(&s->dev, "Internal error: Invalid BAR index [%d].\n", index); return -1; } /* use fixed-up value from kernel sysfs */ *value = base_address_with_flags(&s->real_device.io_regions[index]); /* set emulate mask depend on BAR flag */ switch (s->bases[index].bar_flag) { case XEN_PT_BAR_FLAG_MEM: bar_emu_mask = XEN_PT_BAR_MEM_EMU_MASK; break; case XEN_PT_BAR_FLAG_IO: bar_emu_mask = XEN_PT_BAR_IO_EMU_MASK; break; case XEN_PT_BAR_FLAG_UPPER: bar_emu_mask = XEN_PT_BAR_ALLF; break; default: break; } /* emulate BAR */ valid_emu_mask = bar_emu_mask & valid_mask; *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); return 0; } static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint32_t *val, uint32_t dev_value, uint32_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; XenPTRegion *base = NULL; PCIDevice *d = &s->dev; const PCIIORegion *r; uint32_t writable_mask = 0; uint32_t throughable_mask = 0; uint32_t bar_emu_mask = 0; uint32_t bar_ro_mask = 0; uint32_t r_size = 0; int index = 0; index = xen_pt_bar_offset_to_index(reg->offset); if (index < 0 || index >= PCI_NUM_REGIONS) { XEN_PT_ERR(d, "Internal error: Invalid BAR index [%d].\n", index); return -1; } r = &d->io_regions[index]; base = &s->bases[index]; r_size = xen_pt_get_emul_size(base->bar_flag, r->size); /* set emulate mask and read-only mask values depend on the BAR flag */ switch (s->bases[index].bar_flag) { case XEN_PT_BAR_FLAG_MEM: bar_emu_mask = XEN_PT_BAR_MEM_EMU_MASK; bar_ro_mask = XEN_PT_BAR_MEM_RO_MASK | (r_size - 1); break; case XEN_PT_BAR_FLAG_IO: bar_emu_mask = XEN_PT_BAR_IO_EMU_MASK; bar_ro_mask = XEN_PT_BAR_IO_RO_MASK | (r_size - 1); break; case XEN_PT_BAR_FLAG_UPPER: bar_emu_mask = XEN_PT_BAR_ALLF; bar_ro_mask = 0; /* all upper 32bit are R/W */ break; default: break; } /* modify emulate register */ writable_mask = bar_emu_mask & ~bar_ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* check whether we need to update the virtual region address or not */ switch (s->bases[index].bar_flag) { case XEN_PT_BAR_FLAG_MEM: /* nothing to do */ break; case XEN_PT_BAR_FLAG_IO: /* nothing to do */ break; case XEN_PT_BAR_FLAG_UPPER: if (cfg_entry->data) { if (cfg_entry->data != (XEN_PT_BAR_ALLF & ~bar_ro_mask)) { XEN_PT_WARN(d, "Guest attempt to set high MMIO Base Address. " "Ignore mapping. " "(offset: 0x%02x, high address: 0x%08x)\n", reg->offset, cfg_entry->data); } } break; default: break; } /* create value for writing to I/O device register */ throughable_mask = ~bar_emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; } /* write Exp ROM BAR */ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint32_t *val, uint32_t dev_value, uint32_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; XenPTRegion *base = NULL; PCIDevice *d = (PCIDevice *)&s->dev; uint32_t writable_mask = 0; uint32_t throughable_mask = 0; pcibus_t r_size = 0; uint32_t bar_emu_mask = 0; uint32_t bar_ro_mask = 0; r_size = d->io_regions[PCI_ROM_SLOT].size; base = &s->bases[PCI_ROM_SLOT]; /* align memory type resource size */ r_size = xen_pt_get_emul_size(base->bar_flag, r_size); /* set emulate mask and read-only mask */ bar_emu_mask = reg->emu_mask; bar_ro_mask = (reg->ro_mask | (r_size - 1)) & ~PCI_ROM_ADDRESS_ENABLE; /* modify emulate register */ writable_mask = ~bar_ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ throughable_mask = ~bar_emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; } /* Header Type0 reg static infomation table */ static XenPTRegInfo xen_pt_emu_reg_header0[] = { /* Vendor ID reg */ { .offset = PCI_VENDOR_ID, .size = 2, .init_val = 0x0000, .ro_mask = 0xFFFF, .emu_mask = 0xFFFF, .init = xen_pt_vendor_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, }, /* Device ID reg */ { .offset = PCI_DEVICE_ID, .size = 2, .init_val = 0x0000, .ro_mask = 0xFFFF, .emu_mask = 0xFFFF, .init = xen_pt_device_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, }, /* Command reg */ { .offset = PCI_COMMAND, .size = 2, .init_val = 0x0000, .ro_mask = 0xF880, .emu_mask = 0x0740, .init = xen_pt_common_reg_init, .u.w.read = xen_pt_cmd_reg_read, .u.w.write = xen_pt_cmd_reg_write, }, /* Capabilities Pointer reg */ { .offset = PCI_CAPABILITY_LIST, .size = 1, .init_val = 0x00, .ro_mask = 0xFF, .emu_mask = 0xFF, .init = xen_pt_ptr_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, /* Status reg */ /* use emulated Cap Ptr value to initialize, * so need to be declared after Cap Ptr reg */ { .offset = PCI_STATUS, .size = 2, .init_val = 0x0000, .ro_mask = 0x06FF, .emu_mask = 0x0010, .init = xen_pt_status_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, }, /* Cache Line Size reg */ { .offset = PCI_CACHE_LINE_SIZE, .size = 1, .init_val = 0x00, .ro_mask = 0x00, .emu_mask = 0xFF, .init = xen_pt_common_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, /* Latency Timer reg */ { .offset = PCI_LATENCY_TIMER, .size = 1, .init_val = 0x00, .ro_mask = 0x00, .emu_mask = 0xFF, .init = xen_pt_common_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, /* Header Type reg */ { .offset = PCI_HEADER_TYPE, .size = 1, .init_val = 0x00, .ro_mask = 0xFF, .emu_mask = 0x00, .init = xen_pt_header_type_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, /* Interrupt Line reg */ { .offset = PCI_INTERRUPT_LINE, .size = 1, .init_val = 0x00, .ro_mask = 0x00, .emu_mask = 0xFF, .init = xen_pt_common_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, /* Interrupt Pin reg */ { .offset = PCI_INTERRUPT_PIN, .size = 1, .init_val = 0x00, .ro_mask = 0xFF, .emu_mask = 0xFF, .init = xen_pt_irqpin_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, /* BAR 0 reg */ /* mask of BAR need to be decided later, depends on IO/MEM type */ { .offset = PCI_BASE_ADDRESS_0, .size = 4, .init_val = 0x00000000, .init = xen_pt_bar_reg_init, .u.dw.read = xen_pt_bar_reg_read, .u.dw.write = xen_pt_bar_reg_write, }, /* BAR 1 reg */ { .offset = PCI_BASE_ADDRESS_1, .size = 4, .init_val = 0x00000000, .init = xen_pt_bar_reg_init, .u.dw.read = xen_pt_bar_reg_read, .u.dw.write = xen_pt_bar_reg_write, }, /* BAR 2 reg */ { .offset = PCI_BASE_ADDRESS_2, .size = 4, .init_val = 0x00000000, .init = xen_pt_bar_reg_init, .u.dw.read = xen_pt_bar_reg_read, .u.dw.write = xen_pt_bar_reg_write, }, /* BAR 3 reg */ { .offset = PCI_BASE_ADDRESS_3, .size = 4, .init_val = 0x00000000, .init = xen_pt_bar_reg_init, .u.dw.read = xen_pt_bar_reg_read, .u.dw.write = xen_pt_bar_reg_write, }, /* BAR 4 reg */ { .offset = PCI_BASE_ADDRESS_4, .size = 4, .init_val = 0x00000000, .init = xen_pt_bar_reg_init, .u.dw.read = xen_pt_bar_reg_read, .u.dw.write = xen_pt_bar_reg_write, }, /* BAR 5 reg */ { .offset = PCI_BASE_ADDRESS_5, .size = 4, .init_val = 0x00000000, .init = xen_pt_bar_reg_init, .u.dw.read = xen_pt_bar_reg_read, .u.dw.write = xen_pt_bar_reg_write, }, /* Expansion ROM BAR reg */ { .offset = PCI_ROM_ADDRESS, .size = 4, .init_val = 0x00000000, .ro_mask = 0x000007FE, .emu_mask = 0xFFFFF800, .init = xen_pt_bar_reg_init, .u.dw.read = xen_pt_long_reg_read, .u.dw.write = xen_pt_exp_rom_bar_reg_write, }, { .size = 0, }, }; /********************************* * Vital Product Data Capability */ /* Vital Product Data Capability Structure reg static infomation table */ static XenPTRegInfo xen_pt_emu_reg_vpd[] = { { .offset = PCI_CAP_LIST_NEXT, .size = 1, .init_val = 0x00, .ro_mask = 0xFF, .emu_mask = 0xFF, .init = xen_pt_ptr_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, { .size = 0, }, }; /************************************** * Vendor Specific Capability */ /* Vendor Specific Capability Structure reg static infomation table */ static XenPTRegInfo xen_pt_emu_reg_vendor[] = { { .offset = PCI_CAP_LIST_NEXT, .size = 1, .init_val = 0x00, .ro_mask = 0xFF, .emu_mask = 0xFF, .init = xen_pt_ptr_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, { .size = 0, }, }; /***************************** * PCI Express Capability */ static inline uint8_t get_capability_version(XenPCIPassthroughState *s, uint32_t offset) { uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS); return flags & PCI_EXP_FLAGS_VERS; } static inline uint8_t get_device_type(XenPCIPassthroughState *s, uint32_t offset) { uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS); return (flags & PCI_EXP_FLAGS_TYPE) >> 4; } /* initialize Link Control register */ static int xen_pt_linkctrl_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { uint8_t cap_ver = get_capability_version(s, real_offset - reg->offset); uint8_t dev_type = get_device_type(s, real_offset - reg->offset); /* no need to initialize in case of Root Complex Integrated Endpoint * with cap_ver 1.x */ if ((dev_type == PCI_EXP_TYPE_RC_END) && (cap_ver == 1)) { *data = XEN_PT_INVALID_REG; } *data = reg->init_val; return 0; } /* initialize Device Control 2 register */ static int xen_pt_devctrl2_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { uint8_t cap_ver = get_capability_version(s, real_offset - reg->offset); /* no need to initialize in case of cap_ver 1.x */ if (cap_ver == 1) { *data = XEN_PT_INVALID_REG; } *data = reg->init_val; return 0; } /* initialize Link Control 2 register */ static int xen_pt_linkctrl2_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { uint8_t cap_ver = get_capability_version(s, real_offset - reg->offset); uint32_t reg_field = 0; /* no need to initialize in case of cap_ver 1.x */ if (cap_ver == 1) { reg_field = XEN_PT_INVALID_REG; } else { /* set Supported Link Speed */ uint8_t lnkcap = pci_get_byte(s->dev.config + real_offset - reg->offset + PCI_EXP_LNKCAP); reg_field |= PCI_EXP_LNKCAP_SLS & lnkcap; } *data = reg_field; return 0; } /* PCI Express Capability Structure reg static infomation table */ static XenPTRegInfo xen_pt_emu_reg_pcie[] = { /* Next Pointer reg */ { .offset = PCI_CAP_LIST_NEXT, .size = 1, .init_val = 0x00, .ro_mask = 0xFF, .emu_mask = 0xFF, .init = xen_pt_ptr_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, /* Device Capabilities reg */ { .offset = PCI_EXP_DEVCAP, .size = 4, .init_val = 0x00000000, .ro_mask = 0x1FFCFFFF, .emu_mask = 0x10000000, .init = xen_pt_common_reg_init, .u.dw.read = xen_pt_long_reg_read, .u.dw.write = xen_pt_long_reg_write, }, /* Device Control reg */ { .offset = PCI_EXP_DEVCTL, .size = 2, .init_val = 0x2810, .ro_mask = 0x8400, .emu_mask = 0xFFFF, .init = xen_pt_common_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, }, /* Link Control reg */ { .offset = PCI_EXP_LNKCTL, .size = 2, .init_val = 0x0000, .ro_mask = 0xFC34, .emu_mask = 0xFFFF, .init = xen_pt_linkctrl_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, }, /* Device Control 2 reg */ { .offset = 0x28, .size = 2, .init_val = 0x0000, .ro_mask = 0xFFE0, .emu_mask = 0xFFFF, .init = xen_pt_devctrl2_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, }, /* Link Control 2 reg */ { .offset = 0x30, .size = 2, .init_val = 0x0000, .ro_mask = 0xE040, .emu_mask = 0xFFFF, .init = xen_pt_linkctrl2_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, }, { .size = 0, }, }; /********************************* * Power Management Capability */ /* read Power Management Control/Status register */ static int xen_pt_pmcsr_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *value, uint16_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint16_t valid_emu_mask = reg->emu_mask; valid_emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; valid_emu_mask = valid_emu_mask & valid_mask; *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); return 0; } /* write Power Management Control/Status register */ static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *val, uint16_t dev_value, uint16_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint16_t emu_mask = reg->emu_mask; uint16_t writable_mask = 0; uint16_t throughable_mask = 0; emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; /* modify emulate register */ writable_mask = emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ throughable_mask = ~emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); return 0; } /* Power Management Capability reg static infomation table */ static XenPTRegInfo xen_pt_emu_reg_pm[] = { /* Next Pointer reg */ { .offset = PCI_CAP_LIST_NEXT, .size = 1, .init_val = 0x00, .ro_mask = 0xFF, .emu_mask = 0xFF, .init = xen_pt_ptr_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, /* Power Management Capabilities reg */ { .offset = PCI_CAP_FLAGS, .size = 2, .init_val = 0x0000, .ro_mask = 0xFFFF, .emu_mask = 0xF9C8, .init = xen_pt_common_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_word_reg_write, }, /* PCI Power Management Control/Status reg */ { .offset = PCI_PM_CTRL, .size = 2, .init_val = 0x0008, .ro_mask = 0xE1FC, .emu_mask = 0x8100, .init = xen_pt_common_reg_init, .u.w.read = xen_pt_pmcsr_reg_read, .u.w.write = xen_pt_pmcsr_reg_write, }, { .size = 0, }, }; /******************************** * MSI Capability */ /* Helper */ static bool xen_pt_msgdata_check_type(uint32_t offset, uint16_t flags) { /* check the offset whether matches the type or not */ bool is_32 = (offset == PCI_MSI_DATA_32) && !(flags & PCI_MSI_FLAGS_64BIT); bool is_64 = (offset == PCI_MSI_DATA_64) && (flags & PCI_MSI_FLAGS_64BIT); return is_32 || is_64; } /* Message Control register */ static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { PCIDevice *d = &s->dev; XenPTMSI *msi = s->msi; uint16_t reg_field = 0; /* use I/O device register's value as initial value */ reg_field = pci_get_word(d->config + real_offset); if (reg_field & PCI_MSI_FLAGS_ENABLE) { XEN_PT_LOG(&s->dev, "MSI already enabled, disabling it first\n"); xen_host_pci_set_word(&s->real_device, real_offset, reg_field & ~PCI_MSI_FLAGS_ENABLE); } msi->flags |= reg_field; msi->ctrl_offset = real_offset; msi->initialized = false; msi->mapped = false; *data = reg->init_val; return 0; } static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *val, uint16_t dev_value, uint16_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; XenPTMSI *msi = s->msi; uint16_t writable_mask = 0; uint16_t throughable_mask = 0; uint16_t raw_val; /* Currently no support for multi-vector */ if (*val & PCI_MSI_FLAGS_QSIZE) { XEN_PT_WARN(&s->dev, "Tries to set more than 1 vector ctrl %x\n", *val); } /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE; /* create value for writing to I/O device register */ raw_val = *val; throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); /* update MSI */ if (raw_val & PCI_MSI_FLAGS_ENABLE) { /* setup MSI pirq for the first time */ if (!msi->initialized) { /* Init physical one */ XEN_PT_LOG(&s->dev, "setup MSI\n"); if (xen_pt_msi_setup(s)) { /* We do not broadcast the error to the framework code, so * that MSI errors are contained in MSI emulation code and * QEMU can go on running. * Guest MSI would be actually not working. */ *val &= ~PCI_MSI_FLAGS_ENABLE; XEN_PT_WARN(&s->dev, "Can not map MSI.\n"); return 0; } if (xen_pt_msi_update(s)) { *val &= ~PCI_MSI_FLAGS_ENABLE; XEN_PT_WARN(&s->dev, "Can not bind MSI\n"); return 0; } msi->initialized = true; msi->mapped = true; } msi->flags |= PCI_MSI_FLAGS_ENABLE; } else { msi->flags &= ~PCI_MSI_FLAGS_ENABLE; } /* pass through MSI_ENABLE bit */ *val &= ~PCI_MSI_FLAGS_ENABLE; *val |= raw_val & PCI_MSI_FLAGS_ENABLE; return 0; } /* initialize Message Upper Address register */ static int xen_pt_msgaddr64_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { /* no need to initialize in case of 32 bit type */ if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) { *data = XEN_PT_INVALID_REG; } else { *data = reg->init_val; } return 0; } /* this function will be called twice (for 32 bit and 64 bit type) */ /* initialize Message Data register */ static int xen_pt_msgdata_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { uint32_t flags = s->msi->flags; uint32_t offset = reg->offset; /* check the offset whether matches the type or not */ if (xen_pt_msgdata_check_type(offset, flags)) { *data = reg->init_val; } else { *data = XEN_PT_INVALID_REG; } return 0; } /* write Message Address register */ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint32_t *val, uint32_t dev_value, uint32_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; uint32_t throughable_mask = 0; uint32_t old_addr = cfg_entry->data; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); s->msi->addr_lo = cfg_entry->data; /* create value for writing to I/O device register */ throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); /* update MSI */ if (cfg_entry->data != old_addr) { if (s->msi->mapped) { xen_pt_msi_update(s); } } return 0; } /* write Message Upper Address register */ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint32_t *val, uint32_t dev_value, uint32_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint32_t writable_mask = 0; uint32_t throughable_mask = 0; uint32_t old_addr = cfg_entry->data; /* check whether the type is 64 bit or not */ if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) { XEN_PT_ERR(&s->dev, "Can't write to the upper address without 64 bit support\n"); return -1; } /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* update the msi_info too */ s->msi->addr_hi = cfg_entry->data; /* create value for writing to I/O device register */ throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); /* update MSI */ if (cfg_entry->data != old_addr) { if (s->msi->mapped) { xen_pt_msi_update(s); } } return 0; } /* this function will be called twice (for 32 bit and 64 bit type) */ /* write Message Data register */ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *val, uint16_t dev_value, uint16_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; XenPTMSI *msi = s->msi; uint16_t writable_mask = 0; uint16_t throughable_mask = 0; uint16_t old_data = cfg_entry->data; uint32_t offset = reg->offset; /* check the offset whether matches the type or not */ if (!xen_pt_msgdata_check_type(offset, msi->flags)) { /* exit I/O emulator */ XEN_PT_ERR(&s->dev, "the offset does not match the 32/64 bit type!\n"); return -1; } /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* update the msi_info too */ msi->data = cfg_entry->data; /* create value for writing to I/O device register */ throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); /* update MSI */ if (cfg_entry->data != old_data) { if (msi->mapped) { xen_pt_msi_update(s); } } return 0; } /* MSI Capability Structure reg static infomation table */ static XenPTRegInfo xen_pt_emu_reg_msi[] = { /* Next Pointer reg */ { .offset = PCI_CAP_LIST_NEXT, .size = 1, .init_val = 0x00, .ro_mask = 0xFF, .emu_mask = 0xFF, .init = xen_pt_ptr_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, /* Message Control reg */ { .offset = PCI_MSI_FLAGS, .size = 2, .init_val = 0x0000, .ro_mask = 0xFF8E, .emu_mask = 0x007F, .init = xen_pt_msgctrl_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_msgctrl_reg_write, }, /* Message Address reg */ { .offset = PCI_MSI_ADDRESS_LO, .size = 4, .init_val = 0x00000000, .ro_mask = 0x00000003, .emu_mask = 0xFFFFFFFF, .no_wb = 1, .init = xen_pt_common_reg_init, .u.dw.read = xen_pt_long_reg_read, .u.dw.write = xen_pt_msgaddr32_reg_write, }, /* Message Upper Address reg (if PCI_MSI_FLAGS_64BIT set) */ { .offset = PCI_MSI_ADDRESS_HI, .size = 4, .init_val = 0x00000000, .ro_mask = 0x00000000, .emu_mask = 0xFFFFFFFF, .no_wb = 1, .init = xen_pt_msgaddr64_reg_init, .u.dw.read = xen_pt_long_reg_read, .u.dw.write = xen_pt_msgaddr64_reg_write, }, /* Message Data reg (16 bits of data for 32-bit devices) */ { .offset = PCI_MSI_DATA_32, .size = 2, .init_val = 0x0000, .ro_mask = 0x0000, .emu_mask = 0xFFFF, .no_wb = 1, .init = xen_pt_msgdata_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_msgdata_reg_write, }, /* Message Data reg (16 bits of data for 64-bit devices) */ { .offset = PCI_MSI_DATA_64, .size = 2, .init_val = 0x0000, .ro_mask = 0x0000, .emu_mask = 0xFFFF, .no_wb = 1, .init = xen_pt_msgdata_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_msgdata_reg_write, }, { .size = 0, }, }; /************************************** * MSI-X Capability */ /* Message Control register for MSI-X */ static int xen_pt_msixctrl_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { PCIDevice *d = &s->dev; uint16_t reg_field = 0; /* use I/O device register's value as initial value */ reg_field = pci_get_word(d->config + real_offset); if (reg_field & PCI_MSIX_FLAGS_ENABLE) { XEN_PT_LOG(d, "MSIX already enabled, disabling it first\n"); xen_host_pci_set_word(&s->real_device, real_offset, reg_field & ~PCI_MSIX_FLAGS_ENABLE); } s->msix->ctrl_offset = real_offset; *data = reg->init_val; return 0; } static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, uint16_t *val, uint16_t dev_value, uint16_t valid_mask) { XenPTRegInfo *reg = cfg_entry->reg; uint16_t writable_mask = 0; uint16_t throughable_mask = 0; int debug_msix_enabled_old; /* modify emulate register */ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); /* create value for writing to I/O device register */ throughable_mask = ~reg->emu_mask & valid_mask; *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); /* update MSI-X */ if ((*val & PCI_MSIX_FLAGS_ENABLE) && !(*val & PCI_MSIX_FLAGS_MASKALL)) { xen_pt_msix_update(s); } debug_msix_enabled_old = s->msix->enabled; s->msix->enabled = !!(*val & PCI_MSIX_FLAGS_ENABLE); if (s->msix->enabled != debug_msix_enabled_old) { XEN_PT_LOG(&s->dev, "%s MSI-X\n", s->msix->enabled ? "enable" : "disable"); } return 0; } /* MSI-X Capability Structure reg static infomation table */ static XenPTRegInfo xen_pt_emu_reg_msix[] = { /* Next Pointer reg */ { .offset = PCI_CAP_LIST_NEXT, .size = 1, .init_val = 0x00, .ro_mask = 0xFF, .emu_mask = 0xFF, .init = xen_pt_ptr_reg_init, .u.b.read = xen_pt_byte_reg_read, .u.b.write = xen_pt_byte_reg_write, }, /* Message Control reg */ { .offset = PCI_MSI_FLAGS, .size = 2, .init_val = 0x0000, .ro_mask = 0x3FFF, .emu_mask = 0x0000, .init = xen_pt_msixctrl_reg_init, .u.w.read = xen_pt_word_reg_read, .u.w.write = xen_pt_msixctrl_reg_write, }, { .size = 0, }, }; /**************************** * Capabilities */ /* capability structure register group size functions */ static int xen_pt_reg_grp_size_init(XenPCIPassthroughState *s, const XenPTRegGroupInfo *grp_reg, uint32_t base_offset, uint8_t *size) { *size = grp_reg->grp_size; return 0; } /* get Vendor Specific Capability Structure register group size */ static int xen_pt_vendor_size_init(XenPCIPassthroughState *s, const XenPTRegGroupInfo *grp_reg, uint32_t base_offset, uint8_t *size) { *size = pci_get_byte(s->dev.config + base_offset + 0x02); return 0; } /* get PCI Express Capability Structure register group size */ static int xen_pt_pcie_size_init(XenPCIPassthroughState *s, const XenPTRegGroupInfo *grp_reg, uint32_t base_offset, uint8_t *size) { PCIDevice *d = &s->dev; uint8_t version = get_capability_version(s, base_offset); uint8_t type = get_device_type(s, base_offset); uint8_t pcie_size = 0; /* calculate size depend on capability version and device/port type */ /* in case of PCI Express Base Specification Rev 1.x */ if (version == 1) { /* The PCI Express Capabilities, Device Capabilities, and Device * Status/Control registers are required for all PCI Express devices. * The Link Capabilities and Link Status/Control are required for all * Endpoints that are not Root Complex Integrated Endpoints. Endpoints * are not required to implement registers other than those listed * above and terminate the capability structure. */ switch (type) { case PCI_EXP_TYPE_ENDPOINT: case PCI_EXP_TYPE_LEG_END: pcie_size = 0x14; break; case PCI_EXP_TYPE_RC_END: /* has no link */ pcie_size = 0x0C; break; /* only EndPoint passthrough is supported */ case PCI_EXP_TYPE_ROOT_PORT: case PCI_EXP_TYPE_UPSTREAM: case PCI_EXP_TYPE_DOWNSTREAM: case PCI_EXP_TYPE_PCI_BRIDGE: case PCI_EXP_TYPE_PCIE_BRIDGE: case PCI_EXP_TYPE_RC_EC: default: XEN_PT_ERR(d, "Unsupported device/port type %#x.\n", type); return -1; } } /* in case of PCI Express Base Specification Rev 2.0 */ else if (version == 2) { switch (type) { case PCI_EXP_TYPE_ENDPOINT: case PCI_EXP_TYPE_LEG_END: case PCI_EXP_TYPE_RC_END: /* For Functions that do not implement the registers, * these spaces must be hardwired to 0b. */ pcie_size = 0x3C; break; /* only EndPoint passthrough is supported */ case PCI_EXP_TYPE_ROOT_PORT: case PCI_EXP_TYPE_UPSTREAM: case PCI_EXP_TYPE_DOWNSTREAM: case PCI_EXP_TYPE_PCI_BRIDGE: case PCI_EXP_TYPE_PCIE_BRIDGE: case PCI_EXP_TYPE_RC_EC: default: XEN_PT_ERR(d, "Unsupported device/port type %#x.\n", type); return -1; } } else { XEN_PT_ERR(d, "Unsupported capability version %#x.\n", version); return -1; } *size = pcie_size; return 0; } /* get MSI Capability Structure register group size */ static int xen_pt_msi_size_init(XenPCIPassthroughState *s, const XenPTRegGroupInfo *grp_reg, uint32_t base_offset, uint8_t *size) { PCIDevice *d = &s->dev; uint16_t msg_ctrl = 0; uint8_t msi_size = 0xa; msg_ctrl = pci_get_word(d->config + (base_offset + PCI_MSI_FLAGS)); /* check if 64-bit address is capable of per-vector masking */ if (msg_ctrl & PCI_MSI_FLAGS_64BIT) { msi_size += 4; } if (msg_ctrl & PCI_MSI_FLAGS_MASKBIT) { msi_size += 10; } s->msi = g_new0(XenPTMSI, 1); s->msi->pirq = XEN_PT_UNASSIGNED_PIRQ; *size = msi_size; return 0; } /* get MSI-X Capability Structure register group size */ static int xen_pt_msix_size_init(XenPCIPassthroughState *s, const XenPTRegGroupInfo *grp_reg, uint32_t base_offset, uint8_t *size) { int rc = 0; rc = xen_pt_msix_init(s, base_offset); if (rc < 0) { XEN_PT_ERR(&s->dev, "Internal error: Invalid xen_pt_msix_init.\n"); return rc; } *size = grp_reg->grp_size; return 0; } static const XenPTRegGroupInfo xen_pt_emu_reg_grps[] = { /* Header Type0 reg group */ { .grp_id = 0xFF, .grp_type = XEN_PT_GRP_TYPE_EMU, .grp_size = 0x40, .size_init = xen_pt_reg_grp_size_init, .emu_regs = xen_pt_emu_reg_header0, }, /* PCI PowerManagement Capability reg group */ { .grp_id = PCI_CAP_ID_PM, .grp_type = XEN_PT_GRP_TYPE_EMU, .grp_size = PCI_PM_SIZEOF, .size_init = xen_pt_reg_grp_size_init, .emu_regs = xen_pt_emu_reg_pm, }, /* AGP Capability Structure reg group */ { .grp_id = PCI_CAP_ID_AGP, .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, .grp_size = 0x30, .size_init = xen_pt_reg_grp_size_init, }, /* Vital Product Data Capability Structure reg group */ { .grp_id = PCI_CAP_ID_VPD, .grp_type = XEN_PT_GRP_TYPE_EMU, .grp_size = 0x08, .size_init = xen_pt_reg_grp_size_init, .emu_regs = xen_pt_emu_reg_vpd, }, /* Slot Identification reg group */ { .grp_id = PCI_CAP_ID_SLOTID, .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, .grp_size = 0x04, .size_init = xen_pt_reg_grp_size_init, }, /* MSI Capability Structure reg group */ { .grp_id = PCI_CAP_ID_MSI, .grp_type = XEN_PT_GRP_TYPE_EMU, .grp_size = 0xFF, .size_init = xen_pt_msi_size_init, .emu_regs = xen_pt_emu_reg_msi, }, /* PCI-X Capabilities List Item reg group */ { .grp_id = PCI_CAP_ID_PCIX, .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, .grp_size = 0x18, .size_init = xen_pt_reg_grp_size_init, }, /* Vendor Specific Capability Structure reg group */ { .grp_id = PCI_CAP_ID_VNDR, .grp_type = XEN_PT_GRP_TYPE_EMU, .grp_size = 0xFF, .size_init = xen_pt_vendor_size_init, .emu_regs = xen_pt_emu_reg_vendor, }, /* SHPC Capability List Item reg group */ { .grp_id = PCI_CAP_ID_SHPC, .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, .grp_size = 0x08, .size_init = xen_pt_reg_grp_size_init, }, /* Subsystem ID and Subsystem Vendor ID Capability List Item reg group */ { .grp_id = PCI_CAP_ID_SSVID, .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, .grp_size = 0x08, .size_init = xen_pt_reg_grp_size_init, }, /* AGP 8x Capability Structure reg group */ { .grp_id = PCI_CAP_ID_AGP3, .grp_type = XEN_PT_GRP_TYPE_HARDWIRED, .grp_size = 0x30, .size_init = xen_pt_reg_grp_size_init, }, /* PCI Express Capability Structure reg group */ { .grp_id = PCI_CAP_ID_EXP, .grp_type = XEN_PT_GRP_TYPE_EMU, .grp_size = 0xFF, .size_init = xen_pt_pcie_size_init, .emu_regs = xen_pt_emu_reg_pcie, }, /* MSI-X Capability Structure reg group */ { .grp_id = PCI_CAP_ID_MSIX, .grp_type = XEN_PT_GRP_TYPE_EMU, .grp_size = 0x0C, .size_init = xen_pt_msix_size_init, .emu_regs = xen_pt_emu_reg_msix, }, { .grp_size = 0, }, }; /* initialize Capabilities Pointer or Next Pointer register */ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { int i; uint8_t *config = s->dev.config; uint32_t reg_field = pci_get_byte(config + real_offset); uint8_t cap_id = 0; /* find capability offset */ while (reg_field) { for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) { if (xen_pt_hide_dev_cap(&s->real_device, xen_pt_emu_reg_grps[i].grp_id)) { continue; } cap_id = pci_get_byte(config + reg_field + PCI_CAP_LIST_ID); if (xen_pt_emu_reg_grps[i].grp_id == cap_id) { if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU) { goto out; } /* ignore the 0 hardwired capability, find next one */ break; } } /* next capability */ reg_field = pci_get_byte(config + reg_field + PCI_CAP_LIST_NEXT); } out: *data = reg_field; return 0; } /************* * Main */ static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap) { uint8_t id; unsigned max_cap = PCI_CAP_MAX; uint8_t pos = PCI_CAPABILITY_LIST; uint8_t status = 0; if (xen_host_pci_get_byte(&s->real_device, PCI_STATUS, &status)) { return 0; } if ((status & PCI_STATUS_CAP_LIST) == 0) { return 0; } while (max_cap--) { if (xen_host_pci_get_byte(&s->real_device, pos, &pos)) { break; } if (pos < PCI_CONFIG_HEADER_SIZE) { break; } pos &= ~3; if (xen_host_pci_get_byte(&s->real_device, pos + PCI_CAP_LIST_ID, &id)) { break; } if (id == 0xff) { break; } if (id == cap) { return pos; } pos += PCI_CAP_LIST_NEXT; } return 0; } static int xen_pt_config_reg_init(XenPCIPassthroughState *s, XenPTRegGroup *reg_grp, XenPTRegInfo *reg) { XenPTReg *reg_entry; uint32_t data = 0; int rc = 0; reg_entry = g_new0(XenPTReg, 1); reg_entry->reg = reg; if (reg->init) { /* initialize emulate register */ rc = reg->init(s, reg_entry->reg, reg_grp->base_offset + reg->offset, &data); if (rc < 0) { free(reg_entry); return rc; } if (data == XEN_PT_INVALID_REG) { /* free unused BAR register entry */ free(reg_entry); return 0; } /* set register value */ reg_entry->data = data; } /* list add register entry */ QLIST_INSERT_HEAD(®_grp->reg_tbl_list, reg_entry, entries); return 0; } int xen_pt_config_init(XenPCIPassthroughState *s) { int i, rc; QLIST_INIT(&s->reg_grps); for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) { uint32_t reg_grp_offset = 0; XenPTRegGroup *reg_grp_entry = NULL; if (xen_pt_emu_reg_grps[i].grp_id != 0xFF) { if (xen_pt_hide_dev_cap(&s->real_device, xen_pt_emu_reg_grps[i].grp_id)) { continue; } reg_grp_offset = find_cap_offset(s, xen_pt_emu_reg_grps[i].grp_id); if (!reg_grp_offset) { continue; } } reg_grp_entry = g_new0(XenPTRegGroup, 1); QLIST_INIT(®_grp_entry->reg_tbl_list); QLIST_INSERT_HEAD(&s->reg_grps, reg_grp_entry, entries); reg_grp_entry->base_offset = reg_grp_offset; reg_grp_entry->reg_grp = xen_pt_emu_reg_grps + i; if (xen_pt_emu_reg_grps[i].size_init) { /* get register group size */ rc = xen_pt_emu_reg_grps[i].size_init(s, reg_grp_entry->reg_grp, reg_grp_offset, ®_grp_entry->size); if (rc < 0) { xen_pt_config_delete(s); return rc; } } if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU) { if (xen_pt_emu_reg_grps[i].emu_regs) { int j = 0; XenPTRegInfo *regs = xen_pt_emu_reg_grps[i].emu_regs; /* initialize capability register */ for (j = 0; regs->size != 0; j++, regs++) { /* initialize capability register */ rc = xen_pt_config_reg_init(s, reg_grp_entry, regs); if (rc < 0) { xen_pt_config_delete(s); return rc; } } } } } return 0; } /* delete all emulate register */ void xen_pt_config_delete(XenPCIPassthroughState *s) { struct XenPTRegGroup *reg_group, *next_grp; struct XenPTReg *reg, *next_reg; /* free MSI/MSI-X info table */ if (s->msix) { xen_pt_msix_delete(s); } if (s->msi) { g_free(s->msi); } /* free all register group entry */ QLIST_FOREACH_SAFE(reg_group, &s->reg_grps, entries, next_grp) { /* free all register entry */ QLIST_FOREACH_SAFE(reg, ®_group->reg_tbl_list, entries, next_reg) { QLIST_REMOVE(reg, entries); g_free(reg); } QLIST_REMOVE(reg_group, entries); g_free(reg_group); } }