From: Ian Jackson Date: Fri, 23 May 2008 17:23:48 +0000 (+0100) Subject: Copy hw/pass-through.[ch] from xen-unstable 17647:f12724194ec6 X-Git-Tag: xen-3.3.0-rc1~183 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=cf858dfba66eeaf3d3881fef827e53f7789ececa;p=qemu-xen-4.1-testing.git Copy hw/pass-through.[ch] from xen-unstable 17647:f12724194ec6 --- diff --git a/hw/pass-through.c b/hw/pass-through.c new file mode 100644 index 000000000..996ffc138 --- /dev/null +++ b/hw/pass-through.c @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2007, Neocleus Corporation. + * Copyright (c) 2007, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + * + * Alex Novik + * Allen Kay + * Guy Zana + * + * This file implements direct PCI assignment to a HVM guest + */ + +#include "vl.h" +#include "pass-through.h" +#include "pci/header.h" +#include "pci/pci.h" +#include "pt-msi.h" + +extern FILE *logfile; + +struct php_dev { + struct pt_dev *pt_dev; + uint8_t valid; + uint8_t r_bus; + uint8_t r_dev; + uint8_t r_func; +}; +struct dpci_infos { + + struct php_dev php_devs[PHP_SLOT_LEN]; + + PCIBus *e_bus; + struct pci_access *pci_access; + +} dpci_infos; + +static int token_value(char *token) +{ + return strtol(token, NULL, 16); +} + +static int next_bdf(char **str, int *seg, int *bus, int *dev, int *func) +{ + char *token, *delim = ":.-"; + + if ( !(*str) || + ( !strchr(*str, ':') && !strchr(*str, '.')) ) + return 0; + + token = strsep(str, delim); + *seg = token_value(token); + + token = strsep(str, delim); + *bus = token_value(token); + + token = strsep(str, delim); + *dev = token_value(token); + + token = strsep(str, delim); + *func = token_value(token); + + return 1; +} + +/* Insert a new pass-through device into a specific pci slot. + * input dom:bus:dev.func@slot, chose free one if slot == 0 + * return -1: required slot not available + * 0: no free hotplug slots, but normal slot should okay + * >0: the new hotplug slot + */ +static int __insert_to_pci_slot(int bus, int dev, int func, int slot) +{ + int i, php_slot; + + /* preferred virt pci slot */ + if ( slot >= PHP_SLOT_START && slot < PHP_SLOT_END ) + { + php_slot = PCI_TO_PHP_SLOT(slot); + if ( !dpci_infos.php_devs[php_slot].valid ) + { + goto found; + } + else + return -1; + } + + if ( slot != 0 ) + return -1; + + /* slot == 0, pick up a free one */ + for ( i = 0; i < PHP_SLOT_LEN; i++ ) + { + if ( !dpci_infos.php_devs[i].valid ) + { + php_slot = i; + goto found; + } + } + + /* not found */ + return 0; + +found: + dpci_infos.php_devs[php_slot].valid = 1; + dpci_infos.php_devs[php_slot].r_bus = bus; + dpci_infos.php_devs[php_slot].r_dev = dev; + dpci_infos.php_devs[php_slot].r_func = func; + return PHP_TO_PCI_SLOT(php_slot); +} + +/* Insert a new pass-through device into a specific pci slot. + * input dom:bus:dev.func@slot + */ +int insert_to_pci_slot(char *bdf_slt) +{ + int seg, bus, dev, func, slot; + char *bdf_str, *slt_str, *delim="@"; + + bdf_str = strsep(&bdf_slt, delim); + slt_str = bdf_slt; + slot = token_value(slt_str); + + if ( !next_bdf(&bdf_str, &seg, &bus, &dev, &func)) + { + return -1; + } + + return __insert_to_pci_slot(bus, dev, func, slot); + +} + +/* Test if a pci slot has a device + * 1: present + * 0: not present + * -1: invalide pci slot input + */ +int test_pci_slot(int slot) +{ + int php_slot; + + if ( slot < PHP_SLOT_START || slot >= PHP_SLOT_END ) + return -1; + + php_slot = PCI_TO_PHP_SLOT(slot); + if ( dpci_infos.php_devs[php_slot].valid ) + return 1; + else + return 0; +} + +/* find the pci slot for pass-through dev with specified BDF */ +int bdf_to_slot(char *bdf_str) +{ + int seg, bus, dev, func, i; + + if ( !next_bdf(&bdf_str, &seg, &bus, &dev, &func)) + { + return -1; + } + + /* locate the virtual pci slot for this VTd device */ + for ( i = 0; i < PHP_SLOT_LEN; i++ ) + { + if ( dpci_infos.php_devs[i].valid && + dpci_infos.php_devs[i].r_bus == bus && + dpci_infos.php_devs[i].r_dev == dev && + dpci_infos.php_devs[i].r_func == func ) + { + return PHP_TO_PCI_SLOT(i); + } + } + + return -1; +} + +/* Being called each time a mmio region has been updated */ +void pt_iomem_map(PCIDevice *d, int i, uint32_t e_phys, uint32_t e_size, + int type) +{ + struct pt_dev *assigned_device = (struct pt_dev *)d; + uint32_t old_ebase = assigned_device->bases[i].e_physbase; + int first_map = ( assigned_device->bases[i].e_size == 0 ); + int ret = 0; + + assigned_device->bases[i].e_physbase = e_phys; + assigned_device->bases[i].e_size= e_size; + + PT_LOG("e_phys=%08x maddr=%08x type=%d len=%08x index=%d\n", + e_phys, assigned_device->bases[i].access.maddr, type, e_size, i); + + if ( e_size == 0 ) + return; + + if ( !first_map ) + { + add_msix_mapping(assigned_device, i); + /* Remove old mapping */ + ret = xc_domain_memory_mapping(xc_handle, domid, + old_ebase >> XC_PAGE_SHIFT, + assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT, + (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT, + DPCI_REMOVE_MAPPING); + if ( ret != 0 ) + { + PT_LOG("Error: remove old mapping failed!\n"); + return; + } + } + + /* Create new mapping */ + ret = xc_domain_memory_mapping(xc_handle, domid, + assigned_device->bases[i].e_physbase >> XC_PAGE_SHIFT, + assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT, + (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT, + DPCI_ADD_MAPPING); + if ( ret != 0 ) + PT_LOG("Error: create new mapping failed!\n"); + + ret = remove_msix_mapping(assigned_device, i); + if ( ret != 0 ) + PT_LOG("Error: remove MSX-X mmio mapping failed!\n"); +} + +/* Being called each time a pio region has been updated */ +void pt_ioport_map(PCIDevice *d, int i, + uint32_t e_phys, uint32_t e_size, int type) +{ + struct pt_dev *assigned_device = (struct pt_dev *)d; + uint32_t old_ebase = assigned_device->bases[i].e_physbase; + int first_map = ( assigned_device->bases[i].e_size == 0 ); + int ret = 0; + + assigned_device->bases[i].e_physbase = e_phys; + assigned_device->bases[i].e_size= e_size; + + PT_LOG("e_phys=%04x pio_base=%04x len=%04x index=%d\n", + (uint16_t)e_phys, (uint16_t)assigned_device->bases[i].access.pio_base, + (uint16_t)e_size, i); + + if ( e_size == 0 ) + return; + + if ( !first_map ) + { + /* Remove old mapping */ + ret = xc_domain_ioport_mapping(xc_handle, domid, old_ebase, + assigned_device->bases[i].access.pio_base, e_size, + DPCI_REMOVE_MAPPING); + if ( ret != 0 ) + { + PT_LOG("Error: remove old mapping failed!\n"); + return; + } + } + + /* Create new mapping */ + ret = xc_domain_ioport_mapping(xc_handle, domid, e_phys, + assigned_device->bases[i].access.pio_base, e_size, + DPCI_ADD_MAPPING); + if ( ret != 0 ) + PT_LOG("Error: create new mapping failed!\n"); + +} + +static void pt_pci_write_config(PCIDevice *d, uint32_t address, uint32_t val, + int len) +{ + struct pt_dev *assigned_device = (struct pt_dev *)d; + struct pci_dev *pci_dev = assigned_device->pci_dev; + +#ifdef PT_DEBUG_PCI_CONFIG_ACCESS + PT_LOG("(%x.%x): address=%04x val=0x%08x len=%d\n", + (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len); +#endif + + /* Pre-write hooking */ + switch ( address ) { + case 0x0C ... 0x3F: + pci_default_write_config(d, address, val, len); + return; + } + + if ( pt_msi_write(assigned_device, address, val, len) ) + return; + + if ( pt_msix_write(assigned_device, address, val, len) ) + return; + + /* PCI config pass-through */ + if (address == 0x4) { + switch (len){ + case 1: + pci_write_byte(pci_dev, address, val); + break; + case 2: + pci_write_word(pci_dev, address, val); + break; + case 4: + pci_write_long(pci_dev, address, val); + break; + } + } + + if (address == 0x4) { + /* Post-write hooking */ + pci_default_write_config(d, address, val, len); + } +} + +static uint32_t pt_pci_read_config(PCIDevice *d, uint32_t address, int len) +{ + struct pt_dev *assigned_device = (struct pt_dev *)d; + struct pci_dev *pci_dev = assigned_device->pci_dev; + uint32_t val = 0xFF; + + /* Pre-hooking */ + switch ( address ) { + case 0x0C ... 0x3F: + val = pci_default_read_config(d, address, len); + goto exit; + } + + switch ( len ) { + case 1: + val = pci_read_byte(pci_dev, address); + break; + case 2: + val = pci_read_word(pci_dev, address); + break; + case 4: + val = pci_read_long(pci_dev, address); + break; + } + + pt_msi_read(assigned_device, address, len, &val); + pt_msix_read(assigned_device, address, len, &val); +exit: + +#ifdef PT_DEBUG_PCI_CONFIG_ACCESS + PT_LOG("(%x.%x): address=%04x val=0x%08x len=%d\n", + (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len); +#endif + + return val; +} + +static int pt_register_regions(struct pt_dev *assigned_device) +{ + int i = 0; + uint32_t bar_data = 0; + struct pci_dev *pci_dev = assigned_device->pci_dev; + PCIDevice *d = &assigned_device->dev; + + /* Register PIO/MMIO BARs */ + for ( i = 0; i < PCI_BAR_ENTRIES; i++ ) + { + if ( pci_dev->base_addr[i] ) + { + assigned_device->bases[i].e_physbase = pci_dev->base_addr[i]; + assigned_device->bases[i].access.u = pci_dev->base_addr[i]; + + /* Register current region */ + bar_data = *((uint32_t*)(d->config + PCI_BASE_ADDRESS_0) + i); + if ( bar_data & PCI_ADDRESS_SPACE_IO ) + pci_register_io_region((PCIDevice *)assigned_device, i, + (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_IO, + pt_ioport_map); + else if ( bar_data & PCI_ADDRESS_SPACE_MEM_PREFETCH ) + pci_register_io_region((PCIDevice *)assigned_device, i, + (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_MEM_PREFETCH, + pt_iomem_map); + else + pci_register_io_region((PCIDevice *)assigned_device, i, + (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_MEM, + pt_iomem_map); + + PT_LOG("IO region registered (size=0x%08x base_addr=0x%08x)\n", + (uint32_t)(pci_dev->size[i]), + (uint32_t)(pci_dev->base_addr[i])); + } + } + + /* Register expansion ROM address */ + if ( pci_dev->rom_base_addr && pci_dev->rom_size ) + { + assigned_device->bases[PCI_ROM_SLOT].e_physbase = + pci_dev->rom_base_addr; + assigned_device->bases[PCI_ROM_SLOT].access.maddr = + pci_dev->rom_base_addr; + pci_register_io_region((PCIDevice *)assigned_device, PCI_ROM_SLOT, + pci_dev->rom_size, PCI_ADDRESS_SPACE_MEM_PREFETCH, + pt_iomem_map); + + PT_LOG("Expansion ROM registered (size=0x%08x base_addr=0x%08x)\n", + (uint32_t)(pci_dev->rom_size), (uint32_t)(pci_dev->rom_base_addr)); + } + + return 0; +} + +static int pt_unregister_regions(struct pt_dev *assigned_device) +{ + int i, type, ret; + uint32_t e_size; + PCIDevice *d = (PCIDevice*)assigned_device; + + for ( i = 0; i < PCI_NUM_REGIONS; i++ ) + { + e_size = assigned_device->bases[i].e_size; + if ( e_size == 0 ) + continue; + + type = d->io_regions[i].type; + + if ( type == PCI_ADDRESS_SPACE_MEM || + type == PCI_ADDRESS_SPACE_MEM_PREFETCH ) + { + ret = xc_domain_memory_mapping(xc_handle, domid, + assigned_device->bases[i].e_physbase >> XC_PAGE_SHIFT, + assigned_device->bases[i].access.maddr >> XC_PAGE_SHIFT, + (e_size+XC_PAGE_SIZE-1) >> XC_PAGE_SHIFT, + DPCI_REMOVE_MAPPING); + if ( ret != 0 ) + { + PT_LOG("Error: remove old mem mapping failed!\n"); + continue; + } + + } + else if ( type == PCI_ADDRESS_SPACE_IO ) + { + ret = xc_domain_ioport_mapping(xc_handle, domid, + assigned_device->bases[i].e_physbase, + assigned_device->bases[i].access.pio_base, + e_size, + DPCI_REMOVE_MAPPING); + if ( ret != 0 ) + { + PT_LOG("Error: remove old io mapping failed!\n"); + continue; + } + + } + + } + +} + +uint8_t find_cap_offset(struct pci_dev *pci_dev, uint8_t cap) +{ + int id; + int max_cap = 48; + int pos = PCI_CAPABILITY_LIST; + int status; + + status = pci_read_byte(pci_dev, PCI_STATUS); + if ( (status & PCI_STATUS_CAP_LIST) == 0 ) + return 0; + + while ( max_cap-- ) + { + pos = pci_read_byte(pci_dev, pos); + if ( pos < 0x40 ) + break; + + pos &= ~3; + id = pci_read_byte(pci_dev, pos + PCI_CAP_LIST_ID); + + if ( id == 0xff ) + break; + if ( id == cap ) + return pos; + + pos += PCI_CAP_LIST_NEXT; + } + return 0; +} + +struct pt_dev * register_real_device(PCIBus *e_bus, + const char *e_dev_name, int e_devfn, uint8_t r_bus, uint8_t r_dev, + uint8_t r_func, uint32_t machine_irq, struct pci_access *pci_access) +{ + int rc = -1, i, pos; + struct pt_dev *assigned_device = NULL; + struct pci_dev *pci_dev; + uint8_t e_device, e_intx; + struct pci_config_cf8 machine_bdf; + int free_pci_slot = -1; + + PT_LOG("Assigning real physical device %02x:%02x.%x ...\n", + r_bus, r_dev, r_func); + + /* Find real device structure */ + for (pci_dev = pci_access->devices; pci_dev != NULL; + pci_dev = pci_dev->next) + { + if ((r_bus == pci_dev->bus) && (r_dev == pci_dev->dev) + && (r_func == pci_dev->func)) + break; + } + if ( pci_dev == NULL ) + { + PT_LOG("Error: couldn't locate device in libpci structures\n"); + return NULL; + } + + if ( e_devfn == PT_VIRT_DEVFN_AUTO ) { + /*indicate a static assignment(not hotplug), so find a free PCI hot plug slot */ + free_pci_slot = __insert_to_pci_slot(r_bus, r_dev, r_func, 0); + if ( free_pci_slot > 0 ) + e_devfn = free_pci_slot << 3; + else + PT_LOG("Error: no free virtual PCI hot plug slot, thus no live migration.\n"); + } + + /* Register device */ + assigned_device = (struct pt_dev *) pci_register_device(e_bus, e_dev_name, + sizeof(struct pt_dev), e_devfn, + pt_pci_read_config, pt_pci_write_config); + if ( assigned_device == NULL ) + { + PT_LOG("Error: couldn't register real device\n"); + return NULL; + } + + if ( free_pci_slot > 0 ) + dpci_infos.php_devs[PCI_TO_PHP_SLOT(free_pci_slot)].pt_dev = assigned_device; + + assigned_device->pci_dev = pci_dev; + + + /* Assign device */ + machine_bdf.reg = 0; + machine_bdf.bus = r_bus; + machine_bdf.dev = r_dev; + machine_bdf.func = r_func; + rc = xc_assign_device(xc_handle, domid, machine_bdf.value); + if ( rc < 0 ) + PT_LOG("Error: xc_assign_device error %d\n", rc); + + /* Initialize virtualized PCI configuration (Extended 256 Bytes) */ + for ( i = 0; i < PCI_CONFIG_SIZE; i++ ) + assigned_device->dev.config[i] = pci_read_byte(pci_dev, i); + + if ( (pos = find_cap_offset(pci_dev, PCI_CAP_ID_MSI)) ) + pt_msi_init(assigned_device, pos); + + if ( (pos = find_cap_offset(pci_dev, PCI_CAP_ID_MSIX)) ) + pt_msix_init(assigned_device, pos); + + /* Handle real device's MMIO/PIO BARs */ + pt_register_regions(assigned_device); + + /* Bind interrupt */ + e_device = (assigned_device->dev.devfn >> 3) & 0x1f; + e_intx = assigned_device->dev.config[0x3d]-1; + + if ( PT_MACHINE_IRQ_AUTO == machine_irq ) + { + int pirq = pci_dev->irq; + + machine_irq = pci_dev->irq; + rc = xc_physdev_map_pirq(xc_handle, domid, MAP_PIRQ_TYPE_GSI, + machine_irq, &pirq); + + if ( rc ) + { + /* TBD: unregister device in case of an error */ + PT_LOG("Error: Mapping irq failed, rc = %d\n", rc); + } + else + machine_irq = pirq; + } + + /* bind machine_irq to device */ + if ( 0 != machine_irq ) + { + rc = xc_domain_bind_pt_pci_irq(xc_handle, domid, machine_irq, 0, + e_device, e_intx); + if ( rc < 0 ) + { + /* TBD: unregister device in case of an error */ + PT_LOG("Error: Binding of interrupt failed! rc=%d\n", rc); + } + } + else { + /* Disable PCI intx assertion (turn on bit10 of devctl) */ + assigned_device->dev.config[0x05] |= 0x04; + pci_write_word(pci_dev, 0x04, + *(uint16_t *)(&assigned_device->dev.config[0x04])); + } + + PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n", + r_bus, r_dev, r_func); + + return assigned_device; +} + +int unregister_real_device(int php_slot) +{ + struct php_dev *php_dev; + struct pci_dev *pci_dev; + uint8_t e_device, e_intx; + struct pt_dev *assigned_device = NULL; + uint32_t machine_irq; + uint32_t bdf = 0; + int rc = -1; + + if ( php_slot < 0 || php_slot >= PHP_SLOT_LEN ) + return -1; + + php_dev = &dpci_infos.php_devs[php_slot]; + assigned_device = php_dev->pt_dev; + + if ( !assigned_device || !php_dev->valid ) + return -1; + + pci_dev = assigned_device->pci_dev; + + /* hide pci dev from qemu */ + pci_hide_device((PCIDevice*)assigned_device); + + /* Unbind interrupt */ + e_device = (assigned_device->dev.devfn >> 3) & 0x1f; + e_intx = assigned_device->dev.config[0x3d]-1; + machine_irq = pci_dev->irq; + + if ( machine_irq != 0 ) { + rc = xc_domain_unbind_pt_irq(xc_handle, domid, machine_irq, PT_IRQ_TYPE_PCI, 0, + e_device, e_intx, 0); + if ( rc < 0 ) + { + /* TBD: unregister device in case of an error */ + PT_LOG("Error: Unbinding of interrupt failed! rc=%d\n", rc); + } + } + + /* unregister real device's MMIO/PIO BARs */ + pt_unregister_regions(assigned_device); + + /* deassign the dev to dom0 */ + bdf |= (pci_dev->bus & 0xff) << 16; + bdf |= (pci_dev->dev & 0x1f) << 11; + bdf |= (pci_dev->func & 0x1f) << 8; + if ( (rc = xc_deassign_device(xc_handle, domid, bdf)) != 0) + PT_LOG("Error: Revoking the device failed! rc=%d\n", rc); + + /* mark this slot as free */ + php_dev->valid = 0; + php_dev->pt_dev = NULL; + qemu_free(assigned_device); + + return 0; +} + +int power_on_php_slot(int php_slot) +{ + struct php_dev *php_dev = &dpci_infos.php_devs[php_slot]; + int pci_slot = php_slot + PHP_SLOT_START; + struct pt_dev *pt_dev; + pt_dev = + register_real_device(dpci_infos.e_bus, + "DIRECT PCI", + pci_slot << 3, + php_dev->r_bus, + php_dev->r_dev, + php_dev->r_func, + PT_MACHINE_IRQ_AUTO, + dpci_infos.pci_access); + + php_dev->pt_dev = pt_dev; + + return 0; + +} + +int power_off_php_slot(int php_slot) +{ + return unregister_real_device(php_slot); +} + +int pt_init(PCIBus *e_bus, char *direct_pci) +{ + int seg, b, d, f, php_slot = 0; + struct pt_dev *pt_dev; + struct pci_access *pci_access; + char *vslots; + char slot_str[8]; + + /* Initialize libpci */ + pci_access = pci_alloc(); + if ( pci_access == NULL ) + { + PT_LOG("pci_access is NULL\n"); + return -1; + } + pci_init(pci_access); + pci_scan_bus(pci_access); + + memset(&dpci_infos, 0, sizeof(struct dpci_infos)); + dpci_infos.pci_access = pci_access; + dpci_infos.e_bus = e_bus; + + if ( strlen(direct_pci) == 0 ) { + return 0; + } + + /* the virtual pci slots of all pass-through devs + * with hex format: xx;xx...; + */ + vslots = qemu_mallocz ( strlen(direct_pci) / 3 ); + + /* Assign given devices to guest */ + while ( next_bdf(&direct_pci, &seg, &b, &d, &f) ) + { + /* Register real device with the emulated bus */ + pt_dev = register_real_device(e_bus, "DIRECT PCI", PT_VIRT_DEVFN_AUTO, + b, d, f, PT_MACHINE_IRQ_AUTO, pci_access); + if ( pt_dev == NULL ) + { + PT_LOG("Error: Registration failed (%02x:%02x.%x)\n", b, d, f); + return -1; + } + + /* Record the virtual slot info */ + if ( php_slot < PHP_SLOT_LEN && + dpci_infos.php_devs[php_slot].pt_dev == pt_dev ) + { + sprintf(slot_str, "0x%x;", PHP_TO_PCI_SLOT(php_slot)); + } + else + sprintf(slot_str, "0x%x;", 0); + + strcat(vslots, slot_str); + php_slot++; + } + + /* Write virtual slots info to xenstore for Control panel use */ + xenstore_write_vslots(vslots); + + qemu_free(vslots); + + /* Success */ + return 0; +} diff --git a/hw/pass-through.h b/hw/pass-through.h new file mode 100644 index 000000000..50f7af556 --- /dev/null +++ b/hw/pass-through.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2007, Neocleus Corporation. + * Copyright (c) 2007, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PASSTHROUGH_H__ +#define __PASSTHROUGH_H__ + +#include "vl.h" +#include "pci/header.h" +#include "pci/pci.h" + +/* Log acesss */ +#define PT_LOGGING_ENABLED + +#ifdef PT_LOGGING_ENABLED +#define PT_LOG(_f, _a...) fprintf(logfile, "%s: " _f, __func__, ##_a) +#else +#define PT_LOG(_f, _a...) +#endif + +/* Some compilation flags */ +// #define PT_DEBUG_PCI_CONFIG_ACCESS + +#define PT_MACHINE_IRQ_AUTO (0xFFFFFFFF) +#define PT_VIRT_DEVFN_AUTO (-1) + +/* Misc PCI constants that should be moved to a separate library :) */ +#define PCI_CONFIG_SIZE (256) +#define PCI_EXP_DEVCAP_FLR (1 << 28) +#define PCI_EXP_DEVCTL_FLR (1 << 15) +#define PCI_BAR_ENTRIES (6) + +struct pt_region { + /* Virtual phys base & size */ + uint32_t e_physbase; + uint32_t e_size; + /* Index of region in qemu */ + uint32_t memory_index; + /* Translation of the emulated address */ + union { + uint32_t maddr; + uint32_t pio_base; + uint32_t u; + } access; +}; + +struct pt_msi_info { + uint32_t flags; + int offset; + int size; + int pirq; /* guest pirq corresponding */ +}; + +struct msix_entry_info { + int pirq; /* -1 means unmapped */ + int flags; /* flags indicting whether MSI ADDR or DATA is updated */ + uint32_t io_mem[4]; +}; + +struct pt_msix_info { + int enabled; + int offset; + int total_entries; + int bar_index; + uint32_t table_off; + uint64_t mmio_base_addr; + int mmio_index; + int fd; + void *phys_iomem_base; + struct msix_entry_info msix_entry[0]; +}; + +/* + This structure holds the context of the mapping functions + and data that is relevant for qemu device management. +*/ +struct pt_dev { + PCIDevice dev; + struct pci_dev *pci_dev; /* libpci struct */ + struct pt_region bases[PCI_NUM_REGIONS]; /* Access regions */ + struct pt_msi_info *msi; /* MSI virtualization */ + struct pt_msix_info *msix; /* MSI-X virtualization */ +}; + +/* Used for formatting PCI BDF into cf8 format */ +struct pci_config_cf8 { + union { + unsigned int value; + struct { + unsigned int reserved1:2; + unsigned int reg:6; + unsigned int func:3; + unsigned int dev:5; + unsigned int bus:8; + unsigned int reserved2:7; + unsigned int enable:1; + }; + }; +}; + +int pt_init(PCIBus * e_bus, char * direct_pci); + +#endif /* __PASSTHROUGH_H__ */ +