mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-10-15 23:43:55 +00:00
xen-2015-09-10
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJV8bU4AAoJEIlPj0hw4a6QIuUP/2zKkoU+KAO1/V5f2WBTwzZc 8X/t+yGMRaQS9ibWldg/kLJ+uqHt1O0XUDyoLFK03jfBd3bJDpGuVAKe39XQmNov y0f+ytGDtLCRglBw2jJT1tu29y3GbCXYxLKLj9vHEoCt4OEdh5xQlwK5ZkzT+SOF Qxnx+5rWMb3xnzxlfg354IJ0AGq1qZemkdhqwUJ66/mFKGRxjavn1cCqcb93tbMU UYKdEkoATRPRrTIhLepUnb3x3fMtlKgZJdqpVDQ3+mwXLGa2C31qJe1h/ac8HVCj 1Rqj8h4va23LntOLS3AIYQcfDjDj1AQbfVKhpZzkYce3kPkXmJ+JwJ6CMQch0Bgw bD6q8/5sJ30Weyi0Yp+ZjVWH2LVXYguf1csPw510c+ZJIsYTDv+AxF63hVmmdp8G 8B5YHhVMKkUtgrammdardjFBhl2XF+zn072RMh6KBAruI7YBAxo0hbRjoy2EWx0h Z93VgcBZ6n6iYNlxpQ8kNxbdnJXo4mgHMBTTe9aOkfXArvllrfJZIWsi5aScrqbb aP5RbFCoRWJVA2qOWywJL8W+rLtTK9244yuqwbhaxcBVw8/fH8VhJD2XxS7yozxS LZwoYO7pjLpqwfnnqtnXOVjWD7aVlEGKWQSe7EV9wIDPrSU/RpBhP09kIu1yCqgM Qki6v4d94v3S5Ounwl4n =7+ii -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/sstabellini/tags/xen-2015-09-10-tag' into staging xen-2015-09-10 # gpg: Signature made Thu 10 Sep 2015 17:52:08 BST using RSA key ID 70E1AE90 # gpg: Good signature from "Stefano Stabellini <stefano.stabellini@eu.citrix.com>" * remotes/sstabellini/tags/xen-2015-09-10-tag: (29 commits) xen/pt: Don't slurp wholesale the PCI configuration registers xen/pt: Check for return values for xen_host_pci_[get|set] in init xen/pt: Move bulk of xen_pt_unregister_device in its own routine. xen/pt: Make xen_pt_unregister_device idempotent xen/pt: Log xen_host_pci_get/set errors in MSI code. xen/pt: Log xen_host_pci_get in two init functions xen/pt: Remove XenPTReg->data field. xen/pt: Check if reg->init function sets the 'data' past the reg->size xen/pt: Sync up the dev.config and data values. xen/pt: Use xen_host_pci_get_[byte|word] instead of dev.config xen/pt: Use XEN_PT_LOG properly to guard against compiler warnings. xen/pt/msi: Add the register value when printing logging and error messages xen: use errno instead of rc for xc_domain_add_to_physmap xen/pt: xen_host_pci_config_read returns -errno, not -1 on failure xen/pt: Make xen_pt_msi_set_enable static xen/pt: Update comments with proper function name. xen/HVM: atomically access pointers in bufioreq handling xen-hvm: When using xc_domain_add_to_physmap also include errno when reporting xen, gfx passthrough: add opregion mapping xen, gfx passthrough: register host bridge specific to passthrough ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
7b9c09f7d4
28
configure
vendored
28
configure
vendored
|
@ -1881,6 +1881,34 @@ EOF
|
||||||
#if !defined(HVM_MAX_VCPUS)
|
#if !defined(HVM_MAX_VCPUS)
|
||||||
# error HVM_MAX_VCPUS not defined
|
# error HVM_MAX_VCPUS not defined
|
||||||
#endif
|
#endif
|
||||||
|
int main(void) {
|
||||||
|
xc_interface *xc;
|
||||||
|
xs_daemon_open();
|
||||||
|
xc = xc_interface_open(0, 0, 0);
|
||||||
|
xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0);
|
||||||
|
xc_gnttab_open(NULL, 0);
|
||||||
|
xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0);
|
||||||
|
xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000);
|
||||||
|
xc_hvm_create_ioreq_server(xc, 0, HVM_IOREQSRV_BUFIOREQ_ATOMIC, NULL);
|
||||||
|
xc_reserved_device_memory_map(xc, 0, 0, 0, 0, NULL, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
compile_prog "" "$xen_libs"
|
||||||
|
then
|
||||||
|
xen_ctrl_version=460
|
||||||
|
xen=yes
|
||||||
|
|
||||||
|
# Xen 4.5
|
||||||
|
elif
|
||||||
|
cat > $TMPC <<EOF &&
|
||||||
|
#include <xenctrl.h>
|
||||||
|
#include <xenstore.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <xen/hvm/hvm_info_table.h>
|
||||||
|
#if !defined(HVM_MAX_VCPUS)
|
||||||
|
# error HVM_MAX_VCPUS not defined
|
||||||
|
#endif
|
||||||
int main(void) {
|
int main(void) {
|
||||||
xc_interface *xc;
|
xc_interface *xc;
|
||||||
xs_daemon_open();
|
xs_daemon_open();
|
||||||
|
|
|
@ -226,6 +226,20 @@ static void machine_set_usb(Object *obj, bool value, Error **errp)
|
||||||
ms->usb_disabled = !value;
|
ms->usb_disabled = !value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool machine_get_igd_gfx_passthru(Object *obj, Error **errp)
|
||||||
|
{
|
||||||
|
MachineState *ms = MACHINE(obj);
|
||||||
|
|
||||||
|
return ms->igd_gfx_passthru;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void machine_set_igd_gfx_passthru(Object *obj, bool value, Error **errp)
|
||||||
|
{
|
||||||
|
MachineState *ms = MACHINE(obj);
|
||||||
|
|
||||||
|
ms->igd_gfx_passthru = value;
|
||||||
|
}
|
||||||
|
|
||||||
static char *machine_get_firmware(Object *obj, Error **errp)
|
static char *machine_get_firmware(Object *obj, Error **errp)
|
||||||
{
|
{
|
||||||
MachineState *ms = MACHINE(obj);
|
MachineState *ms = MACHINE(obj);
|
||||||
|
@ -388,6 +402,12 @@ static void machine_initfn(Object *obj)
|
||||||
object_property_set_description(obj, "usb",
|
object_property_set_description(obj, "usb",
|
||||||
"Set on/off to enable/disable usb",
|
"Set on/off to enable/disable usb",
|
||||||
NULL);
|
NULL);
|
||||||
|
object_property_add_bool(obj, "igd-passthru",
|
||||||
|
machine_get_igd_gfx_passthru,
|
||||||
|
machine_set_igd_gfx_passthru, NULL);
|
||||||
|
object_property_set_description(obj, "igd-passthru",
|
||||||
|
"Set on/off to enable/disable igd passthrou",
|
||||||
|
NULL);
|
||||||
object_property_add_str(obj, "firmware",
|
object_property_add_str(obj, "firmware",
|
||||||
machine_get_firmware,
|
machine_get_firmware,
|
||||||
machine_set_firmware, NULL);
|
machine_set_firmware, NULL);
|
||||||
|
|
|
@ -7,6 +7,7 @@ obj-$(CONFIG_XEN) += ../xenpv/ xen/
|
||||||
|
|
||||||
obj-y += kvmvapic.o
|
obj-y += kvmvapic.o
|
||||||
obj-y += acpi-build.o
|
obj-y += acpi-build.o
|
||||||
|
obj-y += pci-assign-load-rom.o
|
||||||
|
|
||||||
gen-hex-y += hw/i386/acpi-dsdt.hex
|
gen-hex-y += hw/i386/acpi-dsdt.hex
|
||||||
gen-hex-y += hw/i386/q35-acpi-dsdt.hex
|
gen-hex-y += hw/i386/q35-acpi-dsdt.hex
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
#include "hw/pci/msi.h"
|
#include "hw/pci/msi.h"
|
||||||
#include "kvm_i386.h"
|
#include "kvm_i386.h"
|
||||||
|
#include "hw/pci/pci-assign.h"
|
||||||
|
|
||||||
#define MSIX_PAGE_SIZE 0x1000
|
#define MSIX_PAGE_SIZE 0x1000
|
||||||
|
|
||||||
|
@ -48,17 +49,6 @@
|
||||||
#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */
|
#define IORESOURCE_PREFETCH 0x00002000 /* No side effects */
|
||||||
#define IORESOURCE_MEM_64 0x00100000
|
#define IORESOURCE_MEM_64 0x00100000
|
||||||
|
|
||||||
//#define DEVICE_ASSIGNMENT_DEBUG
|
|
||||||
|
|
||||||
#ifdef DEVICE_ASSIGNMENT_DEBUG
|
|
||||||
#define DEBUG(fmt, ...) \
|
|
||||||
do { \
|
|
||||||
fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
#else
|
|
||||||
#define DEBUG(fmt, ...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct PCIRegion {
|
typedef struct PCIRegion {
|
||||||
int type; /* Memory or port I/O */
|
int type; /* Memory or port I/O */
|
||||||
int valid;
|
int valid;
|
||||||
|
@ -1896,73 +1886,15 @@ static void assign_register_types(void)
|
||||||
|
|
||||||
type_init(assign_register_types)
|
type_init(assign_register_types)
|
||||||
|
|
||||||
/*
|
|
||||||
* Scan the assigned devices for the devices that have an option ROM, and then
|
|
||||||
* load the corresponding ROM data to RAM. If an error occurs while loading an
|
|
||||||
* option ROM, we just ignore that option ROM and continue with the next one.
|
|
||||||
*/
|
|
||||||
static void assigned_dev_load_option_rom(AssignedDevice *dev)
|
static void assigned_dev_load_option_rom(AssignedDevice *dev)
|
||||||
{
|
{
|
||||||
char name[32], rom_file[64];
|
int size = 0;
|
||||||
FILE *fp;
|
|
||||||
uint8_t val;
|
|
||||||
struct stat st;
|
|
||||||
void *ptr;
|
|
||||||
|
|
||||||
/* If loading ROM from file, pci handles it */
|
pci_assign_dev_load_option_rom(&dev->dev, OBJECT(dev), &size,
|
||||||
if (dev->dev.romfile || !dev->dev.rom_bar) {
|
dev->host.domain, dev->host.bus,
|
||||||
return;
|
dev->host.slot, dev->host.function);
|
||||||
|
|
||||||
|
if (!size) {
|
||||||
|
error_report("pci-assign: Invalid ROM.");
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(rom_file, sizeof(rom_file),
|
|
||||||
"/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom",
|
|
||||||
dev->host.domain, dev->host.bus, dev->host.slot,
|
|
||||||
dev->host.function);
|
|
||||||
|
|
||||||
if (stat(rom_file, &st)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (access(rom_file, F_OK)) {
|
|
||||||
error_report("pci-assign: Insufficient privileges for %s", rom_file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write "1" to the ROM file to enable it */
|
|
||||||
fp = fopen(rom_file, "r+");
|
|
||||||
if (fp == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
val = 1;
|
|
||||||
if (fwrite(&val, 1, 1, fp) != 1) {
|
|
||||||
goto close_rom;
|
|
||||||
}
|
|
||||||
fseek(fp, 0, SEEK_SET);
|
|
||||||
|
|
||||||
snprintf(name, sizeof(name), "%s.rom",
|
|
||||||
object_get_typename(OBJECT(dev)));
|
|
||||||
memory_region_init_ram(&dev->dev.rom, OBJECT(dev), name, st.st_size,
|
|
||||||
&error_abort);
|
|
||||||
vmstate_register_ram(&dev->dev.rom, &dev->dev.qdev);
|
|
||||||
ptr = memory_region_get_ram_ptr(&dev->dev.rom);
|
|
||||||
memset(ptr, 0xff, st.st_size);
|
|
||||||
|
|
||||||
if (!fread(ptr, 1, st.st_size, fp)) {
|
|
||||||
error_report("pci-assign: Cannot read from host %s", rom_file);
|
|
||||||
error_printf("Device option ROM contents are probably invalid "
|
|
||||||
"(check dmesg).\nSkip option ROM probe with rombar=0, "
|
|
||||||
"or load from file with romfile=\n");
|
|
||||||
goto close_rom;
|
|
||||||
}
|
|
||||||
|
|
||||||
pci_register_bar(&dev->dev, PCI_ROM_SLOT, 0, &dev->dev.rom);
|
|
||||||
dev->dev.has_rom = true;
|
|
||||||
close_rom:
|
|
||||||
/* Write "0" to disable ROM */
|
|
||||||
fseek(fp, 0, SEEK_SET);
|
|
||||||
val = 0;
|
|
||||||
if (!fwrite(&val, 1, 1, fp)) {
|
|
||||||
DEBUG("%s\n", "Failed to disable pci-sysfs rom file");
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,8 @@
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "qemu/error-report.h"
|
#include "qemu/error-report.h"
|
||||||
#ifdef CONFIG_XEN
|
#ifdef CONFIG_XEN
|
||||||
# include <xen/hvm/hvm_info_table.h>
|
#include <xen/hvm/hvm_info_table.h>
|
||||||
|
#include "hw/xen/xen_pt.h"
|
||||||
#endif
|
#endif
|
||||||
#include "migration/migration.h"
|
#include "migration/migration.h"
|
||||||
|
|
||||||
|
@ -76,7 +77,8 @@ static bool has_reserved_memory = true;
|
||||||
static bool kvmclock_enabled = true;
|
static bool kvmclock_enabled = true;
|
||||||
|
|
||||||
/* PC hardware initialisation */
|
/* PC hardware initialisation */
|
||||||
static void pc_init1(MachineState *machine)
|
static void pc_init1(MachineState *machine,
|
||||||
|
const char *host_type, const char *pci_type)
|
||||||
{
|
{
|
||||||
PCMachineState *pcms = PC_MACHINE(machine);
|
PCMachineState *pcms = PC_MACHINE(machine);
|
||||||
MemoryRegion *system_memory = get_system_memory();
|
MemoryRegion *system_memory = get_system_memory();
|
||||||
|
@ -194,7 +196,9 @@ static void pc_init1(MachineState *machine)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pci_enabled) {
|
if (pci_enabled) {
|
||||||
pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, &isa_bus, gsi,
|
pci_bus = i440fx_init(host_type,
|
||||||
|
pci_type,
|
||||||
|
&i440fx_state, &piix3_devfn, &isa_bus, gsi,
|
||||||
system_memory, system_io, machine->ram_size,
|
system_memory, system_io, machine->ram_size,
|
||||||
pcms->below_4g_mem_size,
|
pcms->below_4g_mem_size,
|
||||||
pcms->above_4g_mem_size,
|
pcms->above_4g_mem_size,
|
||||||
|
@ -412,15 +416,25 @@ static void pc_init_isa(MachineState *machine)
|
||||||
}
|
}
|
||||||
x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI);
|
x86_cpu_compat_kvm_no_autoenable(FEAT_KVM, 1 << KVM_FEATURE_PV_EOI);
|
||||||
enable_compat_apic_id_mode();
|
enable_compat_apic_id_mode();
|
||||||
pc_init1(machine);
|
pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, TYPE_I440FX_PCI_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_XEN
|
#ifdef CONFIG_XEN
|
||||||
|
static void pc_xen_hvm_init_pci(MachineState *machine)
|
||||||
|
{
|
||||||
|
const char *pci_type = has_igd_gfx_passthru ?
|
||||||
|
TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE : TYPE_I440FX_PCI_DEVICE;
|
||||||
|
|
||||||
|
pc_init1(machine,
|
||||||
|
TYPE_I440FX_PCI_HOST_BRIDGE,
|
||||||
|
pci_type);
|
||||||
|
}
|
||||||
|
|
||||||
static void pc_xen_hvm_init(MachineState *machine)
|
static void pc_xen_hvm_init(MachineState *machine)
|
||||||
{
|
{
|
||||||
PCIBus *bus;
|
PCIBus *bus;
|
||||||
|
|
||||||
pc_init1(machine);
|
pc_xen_hvm_init_pci(machine);
|
||||||
|
|
||||||
bus = pci_find_primary_bus();
|
bus = pci_find_primary_bus();
|
||||||
if (bus != NULL) {
|
if (bus != NULL) {
|
||||||
|
@ -436,7 +450,8 @@ static void pc_xen_hvm_init(MachineState *machine)
|
||||||
if (compat) { \
|
if (compat) { \
|
||||||
compat(machine); \
|
compat(machine); \
|
||||||
} \
|
} \
|
||||||
pc_init1(machine); \
|
pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, \
|
||||||
|
TYPE_I440FX_PCI_DEVICE); \
|
||||||
} \
|
} \
|
||||||
DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)
|
DEFINE_PC_MACHINE(suffix, name, pc_init_##suffix, optionfn)
|
||||||
|
|
||||||
|
@ -878,6 +893,118 @@ static void pc_i440fx_0_10_machine_options(MachineClass *m)
|
||||||
DEFINE_I440FX_MACHINE(v0_10, "pc-0.10", pc_compat_0_13,
|
DEFINE_I440FX_MACHINE(v0_10, "pc-0.10", pc_compat_0_13,
|
||||||
pc_i440fx_0_10_machine_options);
|
pc_i440fx_0_10_machine_options);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t gpu_device_id;
|
||||||
|
uint16_t pch_device_id;
|
||||||
|
uint8_t pch_revision_id;
|
||||||
|
} IGDDeviceIDInfo;
|
||||||
|
|
||||||
|
/* In real world different GPU should have different PCH. But actually
|
||||||
|
* the different PCH DIDs likely map to different PCH SKUs. We do the
|
||||||
|
* same thing for the GPU. For PCH, the different SKUs are going to be
|
||||||
|
* all the same silicon design and implementation, just different
|
||||||
|
* features turn on and off with fuses. The SW interfaces should be
|
||||||
|
* consistent across all SKUs in a given family (eg LPT). But just same
|
||||||
|
* features may not be supported.
|
||||||
|
*
|
||||||
|
* Most of these different PCH features probably don't matter to the
|
||||||
|
* Gfx driver, but obviously any difference in display port connections
|
||||||
|
* will so it should be fine with any PCH in case of passthrough.
|
||||||
|
*
|
||||||
|
* So currently use one PCH version, 0x8c4e, to cover all HSW(Haswell)
|
||||||
|
* scenarios, 0x9cc3 for BDW(Broadwell).
|
||||||
|
*/
|
||||||
|
static const IGDDeviceIDInfo igd_combo_id_infos[] = {
|
||||||
|
/* HSW Classic */
|
||||||
|
{0x0402, 0x8c4e, 0x04}, /* HSWGT1D, HSWD_w7 */
|
||||||
|
{0x0406, 0x8c4e, 0x04}, /* HSWGT1M, HSWM_w7 */
|
||||||
|
{0x0412, 0x8c4e, 0x04}, /* HSWGT2D, HSWD_w7 */
|
||||||
|
{0x0416, 0x8c4e, 0x04}, /* HSWGT2M, HSWM_w7 */
|
||||||
|
{0x041E, 0x8c4e, 0x04}, /* HSWGT15D, HSWD_w7 */
|
||||||
|
/* HSW ULT */
|
||||||
|
{0x0A06, 0x8c4e, 0x04}, /* HSWGT1UT, HSWM_w7 */
|
||||||
|
{0x0A16, 0x8c4e, 0x04}, /* HSWGT2UT, HSWM_w7 */
|
||||||
|
{0x0A26, 0x8c4e, 0x06}, /* HSWGT3UT, HSWM_w7 */
|
||||||
|
{0x0A2E, 0x8c4e, 0x04}, /* HSWGT3UT28W, HSWM_w7 */
|
||||||
|
{0x0A1E, 0x8c4e, 0x04}, /* HSWGT2UX, HSWM_w7 */
|
||||||
|
{0x0A0E, 0x8c4e, 0x04}, /* HSWGT1ULX, HSWM_w7 */
|
||||||
|
/* HSW CRW */
|
||||||
|
{0x0D26, 0x8c4e, 0x04}, /* HSWGT3CW, HSWM_w7 */
|
||||||
|
{0x0D22, 0x8c4e, 0x04}, /* HSWGT3CWDT, HSWD_w7 */
|
||||||
|
/* HSW Server */
|
||||||
|
{0x041A, 0x8c4e, 0x04}, /* HSWSVGT2, HSWD_w7 */
|
||||||
|
/* HSW SRVR */
|
||||||
|
{0x040A, 0x8c4e, 0x04}, /* HSWSVGT1, HSWD_w7 */
|
||||||
|
/* BSW */
|
||||||
|
{0x1606, 0x9cc3, 0x03}, /* BDWULTGT1, BDWM_w7 */
|
||||||
|
{0x1616, 0x9cc3, 0x03}, /* BDWULTGT2, BDWM_w7 */
|
||||||
|
{0x1626, 0x9cc3, 0x03}, /* BDWULTGT3, BDWM_w7 */
|
||||||
|
{0x160E, 0x9cc3, 0x03}, /* BDWULXGT1, BDWM_w7 */
|
||||||
|
{0x161E, 0x9cc3, 0x03}, /* BDWULXGT2, BDWM_w7 */
|
||||||
|
{0x1602, 0x9cc3, 0x03}, /* BDWHALOGT1, BDWM_w7 */
|
||||||
|
{0x1612, 0x9cc3, 0x03}, /* BDWHALOGT2, BDWM_w7 */
|
||||||
|
{0x1622, 0x9cc3, 0x03}, /* BDWHALOGT3, BDWM_w7 */
|
||||||
|
{0x162B, 0x9cc3, 0x03}, /* BDWHALO28W, BDWM_w7 */
|
||||||
|
{0x162A, 0x9cc3, 0x03}, /* BDWGT3WRKS, BDWM_w7 */
|
||||||
|
{0x162D, 0x9cc3, 0x03}, /* BDWGT3SRVR, BDWM_w7 */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void isa_bridge_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
dc->desc = "ISA bridge faked to support IGD PT";
|
||||||
|
k->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||||
|
k->class_id = PCI_CLASS_BRIDGE_ISA;
|
||||||
|
};
|
||||||
|
|
||||||
|
static TypeInfo isa_bridge_info = {
|
||||||
|
.name = "igd-passthrough-isa-bridge",
|
||||||
|
.parent = TYPE_PCI_DEVICE,
|
||||||
|
.instance_size = sizeof(PCIDevice),
|
||||||
|
.class_init = isa_bridge_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pt_graphics_register_types(void)
|
||||||
|
{
|
||||||
|
type_register_static(&isa_bridge_info);
|
||||||
|
}
|
||||||
|
type_init(pt_graphics_register_types)
|
||||||
|
|
||||||
|
void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id)
|
||||||
|
{
|
||||||
|
struct PCIDevice *bridge_dev;
|
||||||
|
int i, num;
|
||||||
|
uint16_t pch_dev_id = 0xffff;
|
||||||
|
uint8_t pch_rev_id;
|
||||||
|
|
||||||
|
num = ARRAY_SIZE(igd_combo_id_infos);
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
if (gpu_dev_id == igd_combo_id_infos[i].gpu_device_id) {
|
||||||
|
pch_dev_id = igd_combo_id_infos[i].pch_device_id;
|
||||||
|
pch_rev_id = igd_combo_id_infos[i].pch_revision_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pch_dev_id == 0xffff) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Currently IGD drivers always need to access PCH by 1f.0. */
|
||||||
|
bridge_dev = pci_create_simple(bus, PCI_DEVFN(0x1f, 0),
|
||||||
|
"igd-passthrough-isa-bridge");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that vendor id is always PCI_VENDOR_ID_INTEL.
|
||||||
|
*/
|
||||||
|
if (!bridge_dev) {
|
||||||
|
fprintf(stderr, "set igd-passthrough-isa-bridge failed!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pci_config_set_device_id(bridge_dev->config, pch_dev_id);
|
||||||
|
pci_config_set_revision(bridge_dev->config, pch_rev_id);
|
||||||
|
}
|
||||||
|
|
||||||
static void isapc_machine_options(MachineClass *m)
|
static void isapc_machine_options(MachineClass *m)
|
||||||
{
|
{
|
||||||
|
|
91
hw/i386/pci-assign-load-rom.c
Normal file
91
hw/i386/pci-assign-load-rom.c
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* This is splited from hw/i386/kvm/pci-assign.c
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "hw/hw.h"
|
||||||
|
#include "hw/i386/pc.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "ui/console.h"
|
||||||
|
#include "hw/loader.h"
|
||||||
|
#include "monitor/monitor.h"
|
||||||
|
#include "qemu/range.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
#include "hw/pci/pci.h"
|
||||||
|
#include "hw/pci/pci-assign.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scan the assigned devices for the devices that have an option ROM, and then
|
||||||
|
* load the corresponding ROM data to RAM. If an error occurs while loading an
|
||||||
|
* option ROM, we just ignore that option ROM and continue with the next one.
|
||||||
|
*/
|
||||||
|
void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner,
|
||||||
|
int *size, unsigned int domain,
|
||||||
|
unsigned int bus, unsigned int slot,
|
||||||
|
unsigned int function)
|
||||||
|
{
|
||||||
|
char name[32], rom_file[64];
|
||||||
|
FILE *fp;
|
||||||
|
uint8_t val;
|
||||||
|
struct stat st;
|
||||||
|
void *ptr = NULL;
|
||||||
|
|
||||||
|
/* If loading ROM from file, pci handles it */
|
||||||
|
if (dev->romfile || !dev->rom_bar) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(rom_file, sizeof(rom_file),
|
||||||
|
"/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom",
|
||||||
|
domain, bus, slot, function);
|
||||||
|
|
||||||
|
if (stat(rom_file, &st)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (access(rom_file, F_OK)) {
|
||||||
|
error_report("pci-assign: Insufficient privileges for %s", rom_file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write "1" to the ROM file to enable it */
|
||||||
|
fp = fopen(rom_file, "r+");
|
||||||
|
if (fp == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
val = 1;
|
||||||
|
if (fwrite(&val, 1, 1, fp) != 1) {
|
||||||
|
goto close_rom;
|
||||||
|
}
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
snprintf(name, sizeof(name), "%s.rom", object_get_typename(owner));
|
||||||
|
memory_region_init_ram(&dev->rom, owner, name, st.st_size, &error_abort);
|
||||||
|
vmstate_register_ram(&dev->rom, &dev->qdev);
|
||||||
|
ptr = memory_region_get_ram_ptr(&dev->rom);
|
||||||
|
memset(ptr, 0xff, st.st_size);
|
||||||
|
|
||||||
|
if (!fread(ptr, 1, st.st_size, fp)) {
|
||||||
|
error_report("pci-assign: Cannot read from host %s", rom_file);
|
||||||
|
error_printf("Device option ROM contents are probably invalid "
|
||||||
|
"(check dmesg).\nSkip option ROM probe with rombar=0, "
|
||||||
|
"or load from file with romfile=\n");
|
||||||
|
goto close_rom;
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_register_bar(dev, PCI_ROM_SLOT, 0, &dev->rom);
|
||||||
|
dev->has_rom = true;
|
||||||
|
*size = st.st_size;
|
||||||
|
close_rom:
|
||||||
|
/* Write "0" to disable ROM */
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
val = 0;
|
||||||
|
if (!fwrite(&val, 1, 1, fp)) {
|
||||||
|
DEBUG("%s\n", "Failed to disable pci-sysfs rom file");
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
|
@ -40,7 +40,6 @@
|
||||||
* http://download.intel.com/design/chipsets/datashts/29054901.pdf
|
* http://download.intel.com/design/chipsets/datashts/29054901.pdf
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost"
|
|
||||||
#define I440FX_PCI_HOST_BRIDGE(obj) \
|
#define I440FX_PCI_HOST_BRIDGE(obj) \
|
||||||
OBJECT_CHECK(I440FXState, (obj), TYPE_I440FX_PCI_HOST_BRIDGE)
|
OBJECT_CHECK(I440FXState, (obj), TYPE_I440FX_PCI_HOST_BRIDGE)
|
||||||
|
|
||||||
|
@ -95,7 +94,6 @@ typedef struct PIIX3State {
|
||||||
#define PIIX3_PCI_DEVICE(obj) \
|
#define PIIX3_PCI_DEVICE(obj) \
|
||||||
OBJECT_CHECK(PIIX3State, (obj), TYPE_PIIX3_PCI_DEVICE)
|
OBJECT_CHECK(PIIX3State, (obj), TYPE_PIIX3_PCI_DEVICE)
|
||||||
|
|
||||||
#define TYPE_I440FX_PCI_DEVICE "i440FX"
|
|
||||||
#define I440FX_PCI_DEVICE(obj) \
|
#define I440FX_PCI_DEVICE(obj) \
|
||||||
OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE)
|
OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE)
|
||||||
|
|
||||||
|
@ -305,7 +303,8 @@ static void i440fx_realize(PCIDevice *dev, Error **errp)
|
||||||
dev->config[I440FX_SMRAM] = 0x02;
|
dev->config[I440FX_SMRAM] = 0x02;
|
||||||
}
|
}
|
||||||
|
|
||||||
PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
|
PCIBus *i440fx_init(const char *host_type, const char *pci_type,
|
||||||
|
PCII440FXState **pi440fx_state,
|
||||||
int *piix3_devfn,
|
int *piix3_devfn,
|
||||||
ISABus **isa_bus, qemu_irq *pic,
|
ISABus **isa_bus, qemu_irq *pic,
|
||||||
MemoryRegion *address_space_mem,
|
MemoryRegion *address_space_mem,
|
||||||
|
@ -325,7 +324,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
|
||||||
unsigned i;
|
unsigned i;
|
||||||
I440FXState *i440fx;
|
I440FXState *i440fx;
|
||||||
|
|
||||||
dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST_BRIDGE);
|
dev = qdev_create(NULL, host_type);
|
||||||
s = PCI_HOST_BRIDGE(dev);
|
s = PCI_HOST_BRIDGE(dev);
|
||||||
b = pci_bus_new(dev, NULL, pci_address_space,
|
b = pci_bus_new(dev, NULL, pci_address_space,
|
||||||
address_space_io, 0, TYPE_PCI_BUS);
|
address_space_io, 0, TYPE_PCI_BUS);
|
||||||
|
@ -333,7 +332,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state,
|
||||||
object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL);
|
object_property_add_child(qdev_get_machine(), "i440fx", OBJECT(dev), NULL);
|
||||||
qdev_init_nofail(dev);
|
qdev_init_nofail(dev);
|
||||||
|
|
||||||
d = pci_create_simple(b, 0, TYPE_I440FX_PCI_DEVICE);
|
d = pci_create_simple(b, 0, pci_type);
|
||||||
*pi440fx_state = I440FX_PCI_DEVICE(d);
|
*pi440fx_state = I440FX_PCI_DEVICE(d);
|
||||||
f = *pi440fx_state;
|
f = *pi440fx_state;
|
||||||
f->system_memory = address_space_mem;
|
f->system_memory = address_space_mem;
|
||||||
|
@ -740,6 +739,90 @@ static const TypeInfo i440fx_info = {
|
||||||
.class_init = i440fx_class_init,
|
.class_init = i440fx_class_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* IGD Passthrough Host Bridge. */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t offset;
|
||||||
|
uint8_t len;
|
||||||
|
} IGDHostInfo;
|
||||||
|
|
||||||
|
/* Here we just expose minimal host bridge offset subset. */
|
||||||
|
static const IGDHostInfo igd_host_bridge_infos[] = {
|
||||||
|
{0x08, 2}, /* revision id */
|
||||||
|
{0x2c, 2}, /* sybsystem vendor id */
|
||||||
|
{0x2e, 2}, /* sybsystem id */
|
||||||
|
{0x50, 2}, /* SNB: processor graphics control register */
|
||||||
|
{0x52, 2}, /* processor graphics control register */
|
||||||
|
{0xa4, 4}, /* SNB: graphics base of stolen memory */
|
||||||
|
{0xa8, 4}, /* SNB: base of GTT stolen memory */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int host_pci_config_read(int pos, int len, uint32_t val)
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
int config_fd;
|
||||||
|
ssize_t size = sizeof(path);
|
||||||
|
/* Access real host bridge. */
|
||||||
|
int rc = snprintf(path, size, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s",
|
||||||
|
0, 0, 0, 0, "config");
|
||||||
|
|
||||||
|
if (rc >= size || rc < 0) {
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
config_fd = open(path, O_RDWR);
|
||||||
|
if (config_fd < 0) {
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lseek(config_fd, pos, SEEK_SET) != pos) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
rc = read(config_fd, (uint8_t *)&val, len);
|
||||||
|
} while (rc < 0 && (errno == EINTR || errno == EAGAIN));
|
||||||
|
if (rc != len) {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int igd_pt_i440fx_initfn(struct PCIDevice *pci_dev)
|
||||||
|
{
|
||||||
|
uint32_t val = 0;
|
||||||
|
int rc, i, num;
|
||||||
|
int pos, len;
|
||||||
|
|
||||||
|
num = ARRAY_SIZE(igd_host_bridge_infos);
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
pos = igd_host_bridge_infos[i].offset;
|
||||||
|
len = igd_host_bridge_infos[i].len;
|
||||||
|
rc = host_pci_config_read(pos, len, val);
|
||||||
|
if (rc) {
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
pci_default_write_config(pci_dev, pos, val, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||||
|
|
||||||
|
k->init = igd_pt_i440fx_initfn;
|
||||||
|
dc->desc = "IGD Passthrough Host bridge";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo igd_passthrough_i440fx_info = {
|
||||||
|
.name = TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE,
|
||||||
|
.parent = TYPE_I440FX_PCI_DEVICE,
|
||||||
|
.instance_size = sizeof(PCII440FXState),
|
||||||
|
.class_init = igd_passthrough_i440fx_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
|
static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
|
||||||
PCIBus *rootbus)
|
PCIBus *rootbus)
|
||||||
{
|
{
|
||||||
|
@ -781,6 +864,7 @@ static const TypeInfo i440fx_pcihost_info = {
|
||||||
static void i440fx_register_types(void)
|
static void i440fx_register_types(void)
|
||||||
{
|
{
|
||||||
type_register_static(&i440fx_info);
|
type_register_static(&i440fx_info);
|
||||||
|
type_register_static(&igd_passthrough_i440fx_info);
|
||||||
type_register_static(&piix3_pci_type_info);
|
type_register_static(&piix3_pci_type_info);
|
||||||
type_register_static(&piix3_info);
|
type_register_static(&piix3_info);
|
||||||
type_register_static(&piix3_xen_info);
|
type_register_static(&piix3_xen_info);
|
||||||
|
|
|
@ -3,3 +3,4 @@ common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o
|
||||||
|
|
||||||
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
|
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
|
||||||
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o
|
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o
|
||||||
|
obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o xen_pt_graphics.o
|
||||||
|
|
|
@ -376,6 +376,11 @@ int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
d->irq = v;
|
d->irq = v;
|
||||||
|
rc = xen_host_pci_get_hex_value(d, "class", &v);
|
||||||
|
if (rc) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
d->class_code = v;
|
||||||
d->is_virtfn = xen_host_pci_dev_is_virtfn(d);
|
d->is_virtfn = xen_host_pci_dev_is_virtfn(d);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -387,6 +392,11 @@ error:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool xen_host_pci_device_closed(XenHostPCIDevice *d)
|
||||||
|
{
|
||||||
|
return d->config_fd == -1;
|
||||||
|
}
|
||||||
|
|
||||||
void xen_host_pci_device_put(XenHostPCIDevice *d)
|
void xen_host_pci_device_put(XenHostPCIDevice *d)
|
||||||
{
|
{
|
||||||
if (d->config_fd >= 0) {
|
if (d->config_fd >= 0) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ typedef struct XenHostPCIDevice {
|
||||||
|
|
||||||
uint16_t vendor_id;
|
uint16_t vendor_id;
|
||||||
uint16_t device_id;
|
uint16_t device_id;
|
||||||
|
uint32_t class_code;
|
||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1];
|
XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1];
|
||||||
|
@ -38,6 +39,7 @@ typedef struct XenHostPCIDevice {
|
||||||
int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
|
int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain,
|
||||||
uint8_t bus, uint8_t dev, uint8_t func);
|
uint8_t bus, uint8_t dev, uint8_t func);
|
||||||
void xen_host_pci_device_put(XenHostPCIDevice *pci_dev);
|
void xen_host_pci_device_put(XenHostPCIDevice *pci_dev);
|
||||||
|
bool xen_host_pci_device_closed(XenHostPCIDevice *d);
|
||||||
|
|
||||||
int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p);
|
int xen_host_pci_get_byte(XenHostPCIDevice *d, int pos, uint8_t *p);
|
||||||
int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p);
|
int xen_host_pci_get_word(XenHostPCIDevice *d, int pos, uint16_t *p);
|
||||||
|
|
192
hw/xen/xen_pt.c
192
hw/xen/xen_pt.c
|
@ -56,6 +56,7 @@
|
||||||
|
|
||||||
#include "hw/pci/pci.h"
|
#include "hw/pci/pci.h"
|
||||||
#include "hw/xen/xen.h"
|
#include "hw/xen/xen.h"
|
||||||
|
#include "hw/i386/pc.h"
|
||||||
#include "hw/xen/xen_backend.h"
|
#include "hw/xen/xen_backend.h"
|
||||||
#include "xen_pt.h"
|
#include "xen_pt.h"
|
||||||
#include "qemu/range.h"
|
#include "qemu/range.h"
|
||||||
|
@ -378,7 +379,7 @@ static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* need to shift back before passing them to xen_host_pci_device */
|
/* need to shift back before passing them to xen_host_pci_set_block. */
|
||||||
val >>= (addr & 3) << 3;
|
val >>= (addr & 3) << 3;
|
||||||
|
|
||||||
memory_region_transaction_commit();
|
memory_region_transaction_commit();
|
||||||
|
@ -406,7 +407,7 @@ out:
|
||||||
(uint8_t *)&val + index, len);
|
(uint8_t *)&val + index, len);
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc);
|
XEN_PT_ERR(d, "xen_host_pci_set_block failed. return value: %d.\n", rc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -502,6 +503,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd)
|
||||||
d->rom.size, d->rom.base_addr);
|
d->rom.size, d->rom.base_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xen_pt_register_vga_regions(d);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -683,13 +685,86 @@ static const MemoryListener xen_pt_io_listener = {
|
||||||
.priority = 10,
|
.priority = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s,
|
||||||
|
XenHostPCIDevice *dev)
|
||||||
|
{
|
||||||
|
uint16_t gpu_dev_id;
|
||||||
|
PCIDevice *d = &s->dev;
|
||||||
|
|
||||||
|
gpu_dev_id = dev->device_id;
|
||||||
|
igd_passthrough_isa_bridge_create(d->bus, gpu_dev_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* destroy. */
|
||||||
|
static void xen_pt_destroy(PCIDevice *d) {
|
||||||
|
|
||||||
|
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
|
||||||
|
XenHostPCIDevice *host_dev = &s->real_device;
|
||||||
|
uint8_t machine_irq = s->machine_irq;
|
||||||
|
uint8_t intx;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (machine_irq && !xen_host_pci_device_closed(&s->real_device)) {
|
||||||
|
intx = xen_pt_pci_intx(s);
|
||||||
|
rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq,
|
||||||
|
PT_IRQ_TYPE_PCI,
|
||||||
|
pci_bus_num(d->bus),
|
||||||
|
PCI_SLOT(s->dev.devfn),
|
||||||
|
intx,
|
||||||
|
0 /* isa_irq */);
|
||||||
|
if (rc < 0) {
|
||||||
|
XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
|
||||||
|
" (machine irq: %i, err: %d)"
|
||||||
|
" But bravely continuing on..\n",
|
||||||
|
'a' + intx, machine_irq, errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* N.B. xen_pt_config_delete takes care of freeing them. */
|
||||||
|
if (s->msi) {
|
||||||
|
xen_pt_msi_disable(s);
|
||||||
|
}
|
||||||
|
if (s->msix) {
|
||||||
|
xen_pt_msix_disable(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (machine_irq) {
|
||||||
|
xen_pt_mapped_machine_irq[machine_irq]--;
|
||||||
|
|
||||||
|
if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
|
||||||
|
rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
|
||||||
|
" But bravely continuing on..\n",
|
||||||
|
machine_irq, errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s->machine_irq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* delete all emulated config registers */
|
||||||
|
xen_pt_config_delete(s);
|
||||||
|
|
||||||
|
xen_pt_unregister_vga_regions(host_dev);
|
||||||
|
|
||||||
|
if (s->listener_set) {
|
||||||
|
memory_listener_unregister(&s->memory_listener);
|
||||||
|
memory_listener_unregister(&s->io_listener);
|
||||||
|
s->listener_set = false;
|
||||||
|
}
|
||||||
|
if (!xen_host_pci_device_closed(&s->real_device)) {
|
||||||
|
xen_host_pci_device_put(&s->real_device);
|
||||||
|
}
|
||||||
|
}
|
||||||
/* init */
|
/* init */
|
||||||
|
|
||||||
static int xen_pt_initfn(PCIDevice *d)
|
static int xen_pt_initfn(PCIDevice *d)
|
||||||
{
|
{
|
||||||
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
|
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
uint8_t machine_irq = 0;
|
uint8_t machine_irq = 0, scratch;
|
||||||
uint16_t cmd = 0;
|
uint16_t cmd = 0;
|
||||||
int pirq = XEN_PT_UNASSIGNED_PIRQ;
|
int pirq = XEN_PT_UNASSIGNED_PIRQ;
|
||||||
|
|
||||||
|
@ -715,27 +790,48 @@ static int xen_pt_initfn(PCIDevice *d)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize virtualized PCI configuration (Extended 256 Bytes) */
|
/* Initialize virtualized PCI configuration (Extended 256 Bytes) */
|
||||||
if (xen_host_pci_get_block(&s->real_device, 0, d->config,
|
memset(d->config, 0, PCI_CONFIG_SPACE_SIZE);
|
||||||
PCI_CONFIG_SPACE_SIZE) == -1) {
|
|
||||||
xen_host_pci_device_put(&s->real_device);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->memory_listener = xen_pt_memory_listener;
|
s->memory_listener = xen_pt_memory_listener;
|
||||||
s->io_listener = xen_pt_io_listener;
|
s->io_listener = xen_pt_io_listener;
|
||||||
|
|
||||||
|
/* Setup VGA bios for passthrough GFX */
|
||||||
|
if ((s->real_device.domain == 0) && (s->real_device.bus == 0) &&
|
||||||
|
(s->real_device.dev == 2) && (s->real_device.func == 0)) {
|
||||||
|
if (!is_igd_vga_passthrough(&s->real_device)) {
|
||||||
|
XEN_PT_ERR(d, "Need to enable igd-passthru if you're trying"
|
||||||
|
" to passthrough IGD GFX.\n");
|
||||||
|
xen_host_pci_device_put(&s->real_device);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xen_pt_setup_vga(s, &s->real_device) < 0) {
|
||||||
|
XEN_PT_ERR(d, "Setup VGA BIOS of passthrough GFX failed!\n");
|
||||||
|
xen_host_pci_device_put(&s->real_device);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register ISA bridge for passthrough GFX. */
|
||||||
|
xen_igd_passthrough_isa_bridge_create(s, &s->real_device);
|
||||||
|
}
|
||||||
|
|
||||||
/* Handle real device's MMIO/PIO BARs */
|
/* Handle real device's MMIO/PIO BARs */
|
||||||
xen_pt_register_regions(s, &cmd);
|
xen_pt_register_regions(s, &cmd);
|
||||||
|
|
||||||
/* reinitialize each config register to be emulated */
|
/* reinitialize each config register to be emulated */
|
||||||
if (xen_pt_config_init(s)) {
|
rc = xen_pt_config_init(s);
|
||||||
|
if (rc) {
|
||||||
XEN_PT_ERR(d, "PCI Config space initialisation failed.\n");
|
XEN_PT_ERR(d, "PCI Config space initialisation failed.\n");
|
||||||
xen_host_pci_device_put(&s->real_device);
|
goto err_out;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bind interrupt */
|
/* Bind interrupt */
|
||||||
if (!s->dev.config[PCI_INTERRUPT_PIN]) {
|
rc = xen_host_pci_get_byte(&s->real_device, PCI_INTERRUPT_PIN, &scratch);
|
||||||
|
if (rc) {
|
||||||
|
XEN_PT_ERR(d, "Failed to read PCI_INTERRUPT_PIN! (rc:%d)\n", rc);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
if (!scratch) {
|
||||||
XEN_PT_LOG(d, "no pin interrupt\n");
|
XEN_PT_LOG(d, "no pin interrupt\n");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -785,69 +881,41 @@ static int xen_pt_initfn(PCIDevice *d)
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
xen_host_pci_set_word(&s->real_device, PCI_COMMAND,
|
uint16_t val;
|
||||||
pci_get_word(d->config + PCI_COMMAND) | cmd);
|
|
||||||
|
rc = xen_host_pci_get_word(&s->real_device, PCI_COMMAND, &val);
|
||||||
|
if (rc) {
|
||||||
|
XEN_PT_ERR(d, "Failed to read PCI_COMMAND! (rc: %d)\n", rc);
|
||||||
|
goto err_out;
|
||||||
|
} else {
|
||||||
|
val |= cmd;
|
||||||
|
rc = xen_host_pci_set_word(&s->real_device, PCI_COMMAND, val);
|
||||||
|
if (rc) {
|
||||||
|
XEN_PT_ERR(d, "Failed to write PCI_COMMAND val=0x%x!(rc: %d)\n",
|
||||||
|
val, rc);
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_listener_register(&s->memory_listener, &s->dev.bus_master_as);
|
memory_listener_register(&s->memory_listener, &s->dev.bus_master_as);
|
||||||
memory_listener_register(&s->io_listener, &address_space_io);
|
memory_listener_register(&s->io_listener, &address_space_io);
|
||||||
|
s->listener_set = true;
|
||||||
XEN_PT_LOG(d,
|
XEN_PT_LOG(d,
|
||||||
"Real physical device %02x:%02x.%d registered successfully!\n",
|
"Real physical device %02x:%02x.%d registered successfully!\n",
|
||||||
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function);
|
s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_out:
|
||||||
|
xen_pt_destroy(d);
|
||||||
|
assert(rc);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xen_pt_unregister_device(PCIDevice *d)
|
static void xen_pt_unregister_device(PCIDevice *d)
|
||||||
{
|
{
|
||||||
XenPCIPassthroughState *s = XEN_PT_DEVICE(d);
|
xen_pt_destroy(d);
|
||||||
uint8_t machine_irq = s->machine_irq;
|
|
||||||
uint8_t intx = xen_pt_pci_intx(s);
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
if (machine_irq) {
|
|
||||||
rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq,
|
|
||||||
PT_IRQ_TYPE_PCI,
|
|
||||||
pci_bus_num(d->bus),
|
|
||||||
PCI_SLOT(s->dev.devfn),
|
|
||||||
intx,
|
|
||||||
0 /* isa_irq */);
|
|
||||||
if (rc < 0) {
|
|
||||||
XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
|
|
||||||
" (machine irq: %i, err: %d)"
|
|
||||||
" But bravely continuing on..\n",
|
|
||||||
'a' + intx, machine_irq, errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->msi) {
|
|
||||||
xen_pt_msi_disable(s);
|
|
||||||
}
|
|
||||||
if (s->msix) {
|
|
||||||
xen_pt_msix_disable(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (machine_irq) {
|
|
||||||
xen_pt_mapped_machine_irq[machine_irq]--;
|
|
||||||
|
|
||||||
if (xen_pt_mapped_machine_irq[machine_irq] == 0) {
|
|
||||||
rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq);
|
|
||||||
|
|
||||||
if (rc < 0) {
|
|
||||||
XEN_PT_ERR(d, "unmapping of interrupt %i failed. (err: %d)"
|
|
||||||
" But bravely continuing on..\n",
|
|
||||||
machine_irq, errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* delete all emulated config registers */
|
|
||||||
xen_pt_config_delete(s);
|
|
||||||
|
|
||||||
memory_listener_unregister(&s->memory_listener);
|
|
||||||
memory_listener_unregister(&s->io_listener);
|
|
||||||
|
|
||||||
xen_host_pci_device_put(&s->real_device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Property xen_pci_passthrough_properties[] = {
|
static Property xen_pci_passthrough_properties[] = {
|
||||||
|
|
|
@ -40,6 +40,9 @@ typedef struct XenPCIPassthroughState XenPCIPassthroughState;
|
||||||
#define XEN_PT_DEVICE(obj) \
|
#define XEN_PT_DEVICE(obj) \
|
||||||
OBJECT_CHECK(XenPCIPassthroughState, (obj), TYPE_XEN_PT_DEVICE)
|
OBJECT_CHECK(XenPCIPassthroughState, (obj), TYPE_XEN_PT_DEVICE)
|
||||||
|
|
||||||
|
uint32_t igd_read_opregion(XenPCIPassthroughState *s);
|
||||||
|
void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val);
|
||||||
|
|
||||||
/* function type for config reg */
|
/* function type for config reg */
|
||||||
typedef int (*xen_pt_conf_reg_init)
|
typedef int (*xen_pt_conf_reg_init)
|
||||||
(XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset,
|
(XenPCIPassthroughState *, XenPTRegInfo *, uint32_t real_offset,
|
||||||
|
@ -66,8 +69,9 @@ typedef int (*xen_pt_conf_byte_read)
|
||||||
#define XEN_PT_BAR_ALLF 0xFFFFFFFF
|
#define XEN_PT_BAR_ALLF 0xFFFFFFFF
|
||||||
#define XEN_PT_BAR_UNMAPPED (-1)
|
#define XEN_PT_BAR_UNMAPPED (-1)
|
||||||
|
|
||||||
#define PCI_CAP_MAX 48
|
#define XEN_PCI_CAP_MAX 48
|
||||||
|
|
||||||
|
#define XEN_PCI_INTEL_OPREGION 0xfc
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
XEN_PT_GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */
|
XEN_PT_GRP_TYPE_HARDWIRED = 0, /* 0 Hardwired reg group */
|
||||||
|
@ -134,7 +138,11 @@ struct XenPTRegInfo {
|
||||||
struct XenPTReg {
|
struct XenPTReg {
|
||||||
QLIST_ENTRY(XenPTReg) entries;
|
QLIST_ENTRY(XenPTReg) entries;
|
||||||
XenPTRegInfo *reg;
|
XenPTRegInfo *reg;
|
||||||
uint32_t data; /* emulated value */
|
union {
|
||||||
|
uint8_t *byte;
|
||||||
|
uint16_t *half_word;
|
||||||
|
uint32_t *word;
|
||||||
|
} ptr; /* pointer to dev.config. */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef const struct XenPTRegGroupInfo XenPTRegGroupInfo;
|
typedef const struct XenPTRegGroupInfo XenPTRegGroupInfo;
|
||||||
|
@ -217,6 +225,7 @@ struct XenPCIPassthroughState {
|
||||||
|
|
||||||
MemoryListener memory_listener;
|
MemoryListener memory_listener;
|
||||||
MemoryListener io_listener;
|
MemoryListener io_listener;
|
||||||
|
bool listener_set;
|
||||||
};
|
};
|
||||||
|
|
||||||
int xen_pt_config_init(XenPCIPassthroughState *s);
|
int xen_pt_config_init(XenPCIPassthroughState *s);
|
||||||
|
@ -282,6 +291,7 @@ static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s)
|
||||||
" value=%i, acceptable range is 1 - 4\n", r_val);
|
" value=%i, acceptable range is 1 - 4\n", r_val);
|
||||||
r_val = 0;
|
r_val = 0;
|
||||||
} else {
|
} else {
|
||||||
|
/* Note that if s.real_device.config_fd is closed we make 0xff. */
|
||||||
r_val -= 1;
|
r_val -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +299,6 @@ static inline uint8_t xen_pt_pci_intx(XenPCIPassthroughState *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MSI/MSI-X */
|
/* MSI/MSI-X */
|
||||||
int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool en);
|
|
||||||
int xen_pt_msi_setup(XenPCIPassthroughState *s);
|
int xen_pt_msi_setup(XenPCIPassthroughState *s);
|
||||||
int xen_pt_msi_update(XenPCIPassthroughState *d);
|
int xen_pt_msi_update(XenPCIPassthroughState *d);
|
||||||
void xen_pt_msi_disable(XenPCIPassthroughState *s);
|
void xen_pt_msi_disable(XenPCIPassthroughState *s);
|
||||||
|
@ -305,5 +314,18 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar)
|
||||||
return s->msix && s->msix->bar_index == bar;
|
return s->msix && s->msix->bar_index == bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void *pci_assign_dev_load_option_rom(PCIDevice *dev,
|
||||||
|
struct Object *owner, int *size,
|
||||||
|
unsigned int domain,
|
||||||
|
unsigned int bus, unsigned int slot,
|
||||||
|
unsigned int function);
|
||||||
|
extern bool has_igd_gfx_passthru;
|
||||||
|
static inline bool is_igd_vga_passthrough(XenHostPCIDevice *dev)
|
||||||
|
{
|
||||||
|
return (has_igd_gfx_passthru
|
||||||
|
&& ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA));
|
||||||
|
}
|
||||||
|
int xen_pt_register_vga_regions(XenHostPCIDevice *dev);
|
||||||
|
int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev);
|
||||||
|
int xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev);
|
||||||
#endif /* !XEN_PT_H */
|
#endif /* !XEN_PT_H */
|
||||||
|
|
|
@ -128,10 +128,11 @@ static int xen_pt_byte_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
|
||||||
{
|
{
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
uint8_t valid_emu_mask = 0;
|
uint8_t valid_emu_mask = 0;
|
||||||
|
uint8_t *data = cfg_entry->ptr.byte;
|
||||||
|
|
||||||
/* emulate byte register */
|
/* emulate byte register */
|
||||||
valid_emu_mask = reg->emu_mask & valid_mask;
|
valid_emu_mask = reg->emu_mask & valid_mask;
|
||||||
*value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
|
*value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -140,10 +141,11 @@ static int xen_pt_word_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
|
||||||
{
|
{
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
uint16_t valid_emu_mask = 0;
|
uint16_t valid_emu_mask = 0;
|
||||||
|
uint16_t *data = cfg_entry->ptr.half_word;
|
||||||
|
|
||||||
/* emulate word register */
|
/* emulate word register */
|
||||||
valid_emu_mask = reg->emu_mask & valid_mask;
|
valid_emu_mask = reg->emu_mask & valid_mask;
|
||||||
*value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
|
*value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -152,10 +154,11 @@ static int xen_pt_long_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
|
||||||
{
|
{
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
uint32_t valid_emu_mask = 0;
|
uint32_t valid_emu_mask = 0;
|
||||||
|
uint32_t *data = cfg_entry->ptr.word;
|
||||||
|
|
||||||
/* emulate long register */
|
/* emulate long register */
|
||||||
valid_emu_mask = reg->emu_mask & valid_mask;
|
valid_emu_mask = reg->emu_mask & valid_mask;
|
||||||
*value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
|
*value = XEN_PT_MERGE_VALUE(*value, *data, ~valid_emu_mask);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -169,10 +172,11 @@ static int xen_pt_byte_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
uint8_t writable_mask = 0;
|
uint8_t writable_mask = 0;
|
||||||
uint8_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
uint8_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
||||||
|
uint8_t *data = cfg_entry->ptr.byte;
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
||||||
|
@ -186,10 +190,11 @@ static int xen_pt_word_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
uint16_t writable_mask = 0;
|
uint16_t writable_mask = 0;
|
||||||
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
||||||
|
uint16_t *data = cfg_entry->ptr.half_word;
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
||||||
|
@ -203,10 +208,11 @@ static int xen_pt_long_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
uint32_t writable_mask = 0;
|
uint32_t writable_mask = 0;
|
||||||
uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
||||||
|
uint32_t *data = cfg_entry->ptr.word;
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
||||||
|
@ -255,7 +261,7 @@ static int xen_pt_status_reg_init(XenPCIPassthroughState *s,
|
||||||
reg_entry = xen_pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST);
|
reg_entry = xen_pt_find_reg(reg_grp_entry, PCI_CAPABILITY_LIST);
|
||||||
if (reg_entry) {
|
if (reg_entry) {
|
||||||
/* check Capabilities Pointer register */
|
/* check Capabilities Pointer register */
|
||||||
if (reg_entry->data) {
|
if (*reg_entry->ptr.half_word) {
|
||||||
reg_field |= PCI_STATUS_CAP_LIST;
|
reg_field |= PCI_STATUS_CAP_LIST;
|
||||||
} else {
|
} else {
|
||||||
reg_field &= ~PCI_STATUS_CAP_LIST;
|
reg_field &= ~PCI_STATUS_CAP_LIST;
|
||||||
|
@ -301,10 +307,11 @@ static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
uint16_t writable_mask = 0;
|
uint16_t writable_mask = 0;
|
||||||
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
||||||
|
uint16_t *data = cfg_entry->ptr.half_word;
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = ~reg->ro_mask & valid_mask;
|
writable_mask = ~reg->ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
if (*val & PCI_COMMAND_INTX_DISABLE) {
|
if (*val & PCI_COMMAND_INTX_DISABLE) {
|
||||||
|
@ -447,7 +454,7 @@ static int xen_pt_bar_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
|
||||||
|
|
||||||
/* emulate BAR */
|
/* emulate BAR */
|
||||||
valid_emu_mask = bar_emu_mask & valid_mask;
|
valid_emu_mask = bar_emu_mask & valid_mask;
|
||||||
*value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask);
|
*value = XEN_PT_MERGE_VALUE(*value, *cfg_entry->ptr.word, ~valid_emu_mask);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -464,6 +471,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
|
||||||
uint32_t bar_ro_mask = 0;
|
uint32_t bar_ro_mask = 0;
|
||||||
uint32_t r_size = 0;
|
uint32_t r_size = 0;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
uint32_t *data = cfg_entry->ptr.word;
|
||||||
|
|
||||||
index = xen_pt_bar_offset_to_index(reg->offset);
|
index = xen_pt_bar_offset_to_index(reg->offset);
|
||||||
if (index < 0 || index >= PCI_NUM_REGIONS) {
|
if (index < 0 || index >= PCI_NUM_REGIONS) {
|
||||||
|
@ -500,7 +508,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry,
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = bar_emu_mask & ~bar_ro_mask & valid_mask;
|
writable_mask = bar_emu_mask & ~bar_ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
|
|
||||||
/* check whether we need to update the virtual region address or not */
|
/* check whether we need to update the virtual region address or not */
|
||||||
switch (s->bases[index].bar_flag) {
|
switch (s->bases[index].bar_flag) {
|
||||||
|
@ -533,6 +541,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
|
||||||
uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
||||||
pcibus_t r_size = 0;
|
pcibus_t r_size = 0;
|
||||||
uint32_t bar_ro_mask = 0;
|
uint32_t bar_ro_mask = 0;
|
||||||
|
uint32_t *data = cfg_entry->ptr.word;
|
||||||
|
|
||||||
r_size = d->io_regions[PCI_ROM_SLOT].size;
|
r_size = d->io_regions[PCI_ROM_SLOT].size;
|
||||||
base = &s->bases[PCI_ROM_SLOT];
|
base = &s->bases[PCI_ROM_SLOT];
|
||||||
|
@ -544,7 +553,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = ~bar_ro_mask & valid_mask;
|
writable_mask = ~bar_ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
||||||
|
@ -552,6 +561,22 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int xen_pt_intel_opregion_read(XenPCIPassthroughState *s,
|
||||||
|
XenPTReg *cfg_entry,
|
||||||
|
uint32_t *value, uint32_t valid_mask)
|
||||||
|
{
|
||||||
|
*value = igd_read_opregion(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xen_pt_intel_opregion_write(XenPCIPassthroughState *s,
|
||||||
|
XenPTReg *cfg_entry, uint32_t *value,
|
||||||
|
uint32_t dev_value, uint32_t valid_mask)
|
||||||
|
{
|
||||||
|
igd_write_opregion(s, *value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Header Type0 reg static information table */
|
/* Header Type0 reg static information table */
|
||||||
static XenPTRegInfo xen_pt_emu_reg_header0[] = {
|
static XenPTRegInfo xen_pt_emu_reg_header0[] = {
|
||||||
/* Vendor ID reg */
|
/* Vendor ID reg */
|
||||||
|
@ -800,15 +825,21 @@ static XenPTRegInfo xen_pt_emu_reg_vendor[] = {
|
||||||
static inline uint8_t get_capability_version(XenPCIPassthroughState *s,
|
static inline uint8_t get_capability_version(XenPCIPassthroughState *s,
|
||||||
uint32_t offset)
|
uint32_t offset)
|
||||||
{
|
{
|
||||||
uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS);
|
uint8_t flag;
|
||||||
return flags & PCI_EXP_FLAGS_VERS;
|
if (xen_host_pci_get_byte(&s->real_device, offset + PCI_EXP_FLAGS, &flag)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return flag & PCI_EXP_FLAGS_VERS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint8_t get_device_type(XenPCIPassthroughState *s,
|
static inline uint8_t get_device_type(XenPCIPassthroughState *s,
|
||||||
uint32_t offset)
|
uint32_t offset)
|
||||||
{
|
{
|
||||||
uint8_t flags = pci_get_byte(s->dev.config + offset + PCI_EXP_FLAGS);
|
uint8_t flag;
|
||||||
return (flags & PCI_EXP_FLAGS_TYPE) >> 4;
|
if (xen_host_pci_get_byte(&s->real_device, offset + PCI_EXP_FLAGS, &flag)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (flag & PCI_EXP_FLAGS_TYPE) >> 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize Link Control register */
|
/* initialize Link Control register */
|
||||||
|
@ -857,8 +888,14 @@ static int xen_pt_linkctrl2_reg_init(XenPCIPassthroughState *s,
|
||||||
reg_field = XEN_PT_INVALID_REG;
|
reg_field = XEN_PT_INVALID_REG;
|
||||||
} else {
|
} else {
|
||||||
/* set Supported Link Speed */
|
/* set Supported Link Speed */
|
||||||
uint8_t lnkcap = pci_get_byte(s->dev.config + real_offset - reg->offset
|
uint8_t lnkcap;
|
||||||
+ PCI_EXP_LNKCAP);
|
int rc;
|
||||||
|
rc = xen_host_pci_get_byte(&s->real_device,
|
||||||
|
real_offset - reg->offset + PCI_EXP_LNKCAP,
|
||||||
|
&lnkcap);
|
||||||
|
if (rc) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
reg_field |= PCI_EXP_LNKCAP_SLS & lnkcap;
|
reg_field |= PCI_EXP_LNKCAP_SLS & lnkcap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -971,10 +1008,11 @@ static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s,
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
uint16_t writable_mask = 0;
|
uint16_t writable_mask = 0;
|
||||||
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
||||||
|
uint16_t *data = cfg_entry->ptr.half_word;
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
*val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS,
|
*val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS,
|
||||||
|
@ -1039,13 +1077,15 @@ static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s,
|
||||||
XenPTRegInfo *reg, uint32_t real_offset,
|
XenPTRegInfo *reg, uint32_t real_offset,
|
||||||
uint32_t *data)
|
uint32_t *data)
|
||||||
{
|
{
|
||||||
PCIDevice *d = &s->dev;
|
|
||||||
XenPTMSI *msi = s->msi;
|
XenPTMSI *msi = s->msi;
|
||||||
uint16_t reg_field = 0;
|
uint16_t reg_field;
|
||||||
|
int rc;
|
||||||
|
|
||||||
/* use I/O device register's value as initial value */
|
/* use I/O device register's value as initial value */
|
||||||
reg_field = pci_get_word(d->config + real_offset);
|
rc = xen_host_pci_get_word(&s->real_device, real_offset, ®_field);
|
||||||
|
if (rc) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
if (reg_field & PCI_MSI_FLAGS_ENABLE) {
|
if (reg_field & PCI_MSI_FLAGS_ENABLE) {
|
||||||
XEN_PT_LOG(&s->dev, "MSI already enabled, disabling it first\n");
|
XEN_PT_LOG(&s->dev, "MSI already enabled, disabling it first\n");
|
||||||
xen_host_pci_set_word(&s->real_device, real_offset,
|
xen_host_pci_set_word(&s->real_device, real_offset,
|
||||||
|
@ -1067,6 +1107,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
|
||||||
XenPTMSI *msi = s->msi;
|
XenPTMSI *msi = s->msi;
|
||||||
uint16_t writable_mask = 0;
|
uint16_t writable_mask = 0;
|
||||||
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
||||||
|
uint16_t *data = cfg_entry->ptr.half_word;
|
||||||
|
|
||||||
/* Currently no support for multi-vector */
|
/* Currently no support for multi-vector */
|
||||||
if (*val & PCI_MSI_FLAGS_QSIZE) {
|
if (*val & PCI_MSI_FLAGS_QSIZE) {
|
||||||
|
@ -1075,8 +1116,8 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE;
|
msi->flags |= *data & ~PCI_MSI_FLAGS_ENABLE;
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
||||||
|
@ -1086,7 +1127,7 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
|
||||||
/* setup MSI pirq for the first time */
|
/* setup MSI pirq for the first time */
|
||||||
if (!msi->initialized) {
|
if (!msi->initialized) {
|
||||||
/* Init physical one */
|
/* Init physical one */
|
||||||
XEN_PT_LOG(&s->dev, "setup MSI\n");
|
XEN_PT_LOG(&s->dev, "setup MSI (register: %x).\n", *val);
|
||||||
if (xen_pt_msi_setup(s)) {
|
if (xen_pt_msi_setup(s)) {
|
||||||
/* We do not broadcast the error to the framework code, so
|
/* We do not broadcast the error to the framework code, so
|
||||||
* that MSI errors are contained in MSI emulation code and
|
* that MSI errors are contained in MSI emulation code and
|
||||||
|
@ -1094,12 +1135,12 @@ static int xen_pt_msgctrl_reg_write(XenPCIPassthroughState *s,
|
||||||
* Guest MSI would be actually not working.
|
* Guest MSI would be actually not working.
|
||||||
*/
|
*/
|
||||||
*val &= ~PCI_MSI_FLAGS_ENABLE;
|
*val &= ~PCI_MSI_FLAGS_ENABLE;
|
||||||
XEN_PT_WARN(&s->dev, "Can not map MSI.\n");
|
XEN_PT_WARN(&s->dev, "Can not map MSI (register: %x)!\n", *val);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (xen_pt_msi_update(s)) {
|
if (xen_pt_msi_update(s)) {
|
||||||
*val &= ~PCI_MSI_FLAGS_ENABLE;
|
*val &= ~PCI_MSI_FLAGS_ENABLE;
|
||||||
XEN_PT_WARN(&s->dev, "Can not bind MSI\n");
|
XEN_PT_WARN(&s->dev, "Can not bind MSI (register: %x)!\n", *val);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
msi->initialized = true;
|
msi->initialized = true;
|
||||||
|
@ -1190,18 +1231,19 @@ static int xen_pt_msgaddr32_reg_write(XenPCIPassthroughState *s,
|
||||||
{
|
{
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
uint32_t writable_mask = 0;
|
uint32_t writable_mask = 0;
|
||||||
uint32_t old_addr = cfg_entry->data;
|
uint32_t old_addr = *cfg_entry->ptr.word;
|
||||||
|
uint32_t *data = cfg_entry->ptr.word;
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
s->msi->addr_lo = cfg_entry->data;
|
s->msi->addr_lo = *data;
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
|
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
|
||||||
|
|
||||||
/* update MSI */
|
/* update MSI */
|
||||||
if (cfg_entry->data != old_addr) {
|
if (*data != old_addr) {
|
||||||
if (s->msi->mapped) {
|
if (s->msi->mapped) {
|
||||||
xen_pt_msi_update(s);
|
xen_pt_msi_update(s);
|
||||||
}
|
}
|
||||||
|
@ -1216,7 +1258,8 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s,
|
||||||
{
|
{
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
uint32_t writable_mask = 0;
|
uint32_t writable_mask = 0;
|
||||||
uint32_t old_addr = cfg_entry->data;
|
uint32_t old_addr = *cfg_entry->ptr.word;
|
||||||
|
uint32_t *data = cfg_entry->ptr.word;
|
||||||
|
|
||||||
/* check whether the type is 64 bit or not */
|
/* check whether the type is 64 bit or not */
|
||||||
if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) {
|
if (!(s->msi->flags & PCI_MSI_FLAGS_64BIT)) {
|
||||||
|
@ -1227,15 +1270,15 @@ static int xen_pt_msgaddr64_reg_write(XenPCIPassthroughState *s,
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
/* update the msi_info too */
|
/* update the msi_info too */
|
||||||
s->msi->addr_hi = cfg_entry->data;
|
s->msi->addr_hi = *data;
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
|
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
|
||||||
|
|
||||||
/* update MSI */
|
/* update MSI */
|
||||||
if (cfg_entry->data != old_addr) {
|
if (*data != old_addr) {
|
||||||
if (s->msi->mapped) {
|
if (s->msi->mapped) {
|
||||||
xen_pt_msi_update(s);
|
xen_pt_msi_update(s);
|
||||||
}
|
}
|
||||||
|
@ -1254,8 +1297,9 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
|
||||||
XenPTRegInfo *reg = cfg_entry->reg;
|
XenPTRegInfo *reg = cfg_entry->reg;
|
||||||
XenPTMSI *msi = s->msi;
|
XenPTMSI *msi = s->msi;
|
||||||
uint16_t writable_mask = 0;
|
uint16_t writable_mask = 0;
|
||||||
uint16_t old_data = cfg_entry->data;
|
uint16_t old_data = *cfg_entry->ptr.half_word;
|
||||||
uint32_t offset = reg->offset;
|
uint32_t offset = reg->offset;
|
||||||
|
uint16_t *data = cfg_entry->ptr.half_word;
|
||||||
|
|
||||||
/* check the offset whether matches the type or not */
|
/* check the offset whether matches the type or not */
|
||||||
if (!xen_pt_msi_check_type(offset, msi->flags, DATA)) {
|
if (!xen_pt_msi_check_type(offset, msi->flags, DATA)) {
|
||||||
|
@ -1266,15 +1310,15 @@ static int xen_pt_msgdata_reg_write(XenPCIPassthroughState *s,
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
/* update the msi_info too */
|
/* update the msi_info too */
|
||||||
msi->data = cfg_entry->data;
|
msi->data = *data;
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
|
*val = XEN_PT_MERGE_VALUE(*val, dev_value, 0);
|
||||||
|
|
||||||
/* update MSI */
|
/* update MSI */
|
||||||
if (cfg_entry->data != old_data) {
|
if (*data != old_data) {
|
||||||
if (msi->mapped) {
|
if (msi->mapped) {
|
||||||
xen_pt_msi_update(s);
|
xen_pt_msi_update(s);
|
||||||
}
|
}
|
||||||
|
@ -1411,14 +1455,16 @@ static int xen_pt_msixctrl_reg_init(XenPCIPassthroughState *s,
|
||||||
XenPTRegInfo *reg, uint32_t real_offset,
|
XenPTRegInfo *reg, uint32_t real_offset,
|
||||||
uint32_t *data)
|
uint32_t *data)
|
||||||
{
|
{
|
||||||
PCIDevice *d = &s->dev;
|
uint16_t reg_field;
|
||||||
uint16_t reg_field = 0;
|
int rc;
|
||||||
|
|
||||||
/* use I/O device register's value as initial value */
|
/* use I/O device register's value as initial value */
|
||||||
reg_field = pci_get_word(d->config + real_offset);
|
rc = xen_host_pci_get_word(&s->real_device, real_offset, ®_field);
|
||||||
|
if (rc) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
if (reg_field & PCI_MSIX_FLAGS_ENABLE) {
|
if (reg_field & PCI_MSIX_FLAGS_ENABLE) {
|
||||||
XEN_PT_LOG(d, "MSIX already enabled, disabling it first\n");
|
XEN_PT_LOG(&s->dev, "MSIX already enabled, disabling it first\n");
|
||||||
xen_host_pci_set_word(&s->real_device, real_offset,
|
xen_host_pci_set_word(&s->real_device, real_offset,
|
||||||
reg_field & ~PCI_MSIX_FLAGS_ENABLE);
|
reg_field & ~PCI_MSIX_FLAGS_ENABLE);
|
||||||
}
|
}
|
||||||
|
@ -1436,10 +1482,11 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s,
|
||||||
uint16_t writable_mask = 0;
|
uint16_t writable_mask = 0;
|
||||||
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask);
|
||||||
int debug_msix_enabled_old;
|
int debug_msix_enabled_old;
|
||||||
|
uint16_t *data = cfg_entry->ptr.half_word;
|
||||||
|
|
||||||
/* modify emulate register */
|
/* modify emulate register */
|
||||||
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask;
|
||||||
cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask);
|
*data = XEN_PT_MERGE_VALUE(*val, *data, writable_mask);
|
||||||
|
|
||||||
/* create value for writing to I/O device register */
|
/* create value for writing to I/O device register */
|
||||||
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
*val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask);
|
||||||
|
@ -1492,6 +1539,19 @@ static XenPTRegInfo xen_pt_emu_reg_msix[] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static XenPTRegInfo xen_pt_emu_reg_igd_opregion[] = {
|
||||||
|
/* Intel IGFX OpRegion reg */
|
||||||
|
{
|
||||||
|
.offset = 0x0,
|
||||||
|
.size = 4,
|
||||||
|
.init_val = 0,
|
||||||
|
.u.dw.read = xen_pt_intel_opregion_read,
|
||||||
|
.u.dw.write = xen_pt_intel_opregion_write,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.size = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/****************************
|
/****************************
|
||||||
* Capabilities
|
* Capabilities
|
||||||
|
@ -1511,8 +1571,7 @@ static int xen_pt_vendor_size_init(XenPCIPassthroughState *s,
|
||||||
const XenPTRegGroupInfo *grp_reg,
|
const XenPTRegGroupInfo *grp_reg,
|
||||||
uint32_t base_offset, uint8_t *size)
|
uint32_t base_offset, uint8_t *size)
|
||||||
{
|
{
|
||||||
*size = pci_get_byte(s->dev.config + base_offset + 0x02);
|
return xen_host_pci_get_byte(&s->real_device, base_offset + 0x02, size);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
/* get PCI Express Capability Structure register group size */
|
/* get PCI Express Capability Structure register group size */
|
||||||
static int xen_pt_pcie_size_init(XenPCIPassthroughState *s,
|
static int xen_pt_pcie_size_init(XenPCIPassthroughState *s,
|
||||||
|
@ -1591,12 +1650,15 @@ static int xen_pt_msi_size_init(XenPCIPassthroughState *s,
|
||||||
const XenPTRegGroupInfo *grp_reg,
|
const XenPTRegGroupInfo *grp_reg,
|
||||||
uint32_t base_offset, uint8_t *size)
|
uint32_t base_offset, uint8_t *size)
|
||||||
{
|
{
|
||||||
PCIDevice *d = &s->dev;
|
|
||||||
uint16_t msg_ctrl = 0;
|
uint16_t msg_ctrl = 0;
|
||||||
uint8_t msi_size = 0xa;
|
uint8_t msi_size = 0xa;
|
||||||
|
int rc;
|
||||||
|
|
||||||
msg_ctrl = pci_get_word(d->config + (base_offset + PCI_MSI_FLAGS));
|
rc = xen_host_pci_get_word(&s->real_device, base_offset + PCI_MSI_FLAGS,
|
||||||
|
&msg_ctrl);
|
||||||
|
if (rc) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
/* check if 64-bit address is capable of per-vector masking */
|
/* check if 64-bit address is capable of per-vector masking */
|
||||||
if (msg_ctrl & PCI_MSI_FLAGS_64BIT) {
|
if (msg_ctrl & PCI_MSI_FLAGS_64BIT) {
|
||||||
msi_size += 4;
|
msi_size += 4;
|
||||||
|
@ -1729,6 +1791,14 @@ static const XenPTRegGroupInfo xen_pt_emu_reg_grps[] = {
|
||||||
.size_init = xen_pt_msix_size_init,
|
.size_init = xen_pt_msix_size_init,
|
||||||
.emu_regs = xen_pt_emu_reg_msix,
|
.emu_regs = xen_pt_emu_reg_msix,
|
||||||
},
|
},
|
||||||
|
/* Intel IGD Opregion group */
|
||||||
|
{
|
||||||
|
.grp_id = XEN_PCI_INTEL_OPREGION,
|
||||||
|
.grp_type = XEN_PT_GRP_TYPE_EMU,
|
||||||
|
.grp_size = 0x4,
|
||||||
|
.size_init = xen_pt_reg_grp_size_init,
|
||||||
|
.emu_regs = xen_pt_emu_reg_igd_opregion,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.grp_size = 0,
|
.grp_size = 0,
|
||||||
},
|
},
|
||||||
|
@ -1739,11 +1809,14 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s,
|
||||||
XenPTRegInfo *reg, uint32_t real_offset,
|
XenPTRegInfo *reg, uint32_t real_offset,
|
||||||
uint32_t *data)
|
uint32_t *data)
|
||||||
{
|
{
|
||||||
int i;
|
int i, rc;
|
||||||
uint8_t *config = s->dev.config;
|
uint8_t reg_field;
|
||||||
uint32_t reg_field = pci_get_byte(config + real_offset);
|
|
||||||
uint8_t cap_id = 0;
|
uint8_t cap_id = 0;
|
||||||
|
|
||||||
|
rc = xen_host_pci_get_byte(&s->real_device, real_offset, ®_field);
|
||||||
|
if (rc) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
/* find capability offset */
|
/* find capability offset */
|
||||||
while (reg_field) {
|
while (reg_field) {
|
||||||
for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) {
|
for (i = 0; xen_pt_emu_reg_grps[i].grp_size != 0; i++) {
|
||||||
|
@ -1752,7 +1825,13 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
cap_id = pci_get_byte(config + reg_field + PCI_CAP_LIST_ID);
|
rc = xen_host_pci_get_byte(&s->real_device,
|
||||||
|
reg_field + PCI_CAP_LIST_ID, &cap_id);
|
||||||
|
if (rc) {
|
||||||
|
XEN_PT_ERR(&s->dev, "Failed to read capability @0x%x (rc:%d)\n",
|
||||||
|
reg_field + PCI_CAP_LIST_ID, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
if (xen_pt_emu_reg_grps[i].grp_id == cap_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) {
|
if (xen_pt_emu_reg_grps[i].grp_type == XEN_PT_GRP_TYPE_EMU) {
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1763,7 +1842,11 @@ static int xen_pt_ptr_reg_init(XenPCIPassthroughState *s,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* next capability */
|
/* next capability */
|
||||||
reg_field = pci_get_byte(config + reg_field + PCI_CAP_LIST_NEXT);
|
rc = xen_host_pci_get_byte(&s->real_device,
|
||||||
|
reg_field + PCI_CAP_LIST_NEXT, ®_field);
|
||||||
|
if (rc) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -1779,7 +1862,7 @@ out:
|
||||||
static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap)
|
static uint8_t find_cap_offset(XenPCIPassthroughState *s, uint8_t cap)
|
||||||
{
|
{
|
||||||
uint8_t id;
|
uint8_t id;
|
||||||
unsigned max_cap = PCI_CAP_MAX;
|
unsigned max_cap = XEN_PCI_CAP_MAX;
|
||||||
uint8_t pos = PCI_CAPABILITY_LIST;
|
uint8_t pos = PCI_CAPABILITY_LIST;
|
||||||
uint8_t status = 0;
|
uint8_t status = 0;
|
||||||
|
|
||||||
|
@ -1827,6 +1910,10 @@ static int xen_pt_config_reg_init(XenPCIPassthroughState *s,
|
||||||
reg_entry->reg = reg;
|
reg_entry->reg = reg;
|
||||||
|
|
||||||
if (reg->init) {
|
if (reg->init) {
|
||||||
|
uint32_t host_mask, size_mask;
|
||||||
|
unsigned int offset;
|
||||||
|
uint32_t val;
|
||||||
|
|
||||||
/* initialize emulate register */
|
/* initialize emulate register */
|
||||||
rc = reg->init(s, reg_entry->reg,
|
rc = reg->init(s, reg_entry->reg,
|
||||||
reg_grp->base_offset + reg->offset, &data);
|
reg_grp->base_offset + reg->offset, &data);
|
||||||
|
@ -1839,8 +1926,67 @@ static int xen_pt_config_reg_init(XenPCIPassthroughState *s,
|
||||||
g_free(reg_entry);
|
g_free(reg_entry);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* set register value */
|
/* Sync up the data to dev.config */
|
||||||
reg_entry->data = data;
|
offset = reg_grp->base_offset + reg->offset;
|
||||||
|
size_mask = 0xFFFFFFFF >> ((4 - reg->size) << 3);
|
||||||
|
|
||||||
|
switch (reg->size) {
|
||||||
|
case 1: rc = xen_host_pci_get_byte(&s->real_device, offset, (uint8_t *)&val);
|
||||||
|
break;
|
||||||
|
case 2: rc = xen_host_pci_get_word(&s->real_device, offset, (uint16_t *)&val);
|
||||||
|
break;
|
||||||
|
case 4: rc = xen_host_pci_get_long(&s->real_device, offset, &val);
|
||||||
|
break;
|
||||||
|
default: assert(1);
|
||||||
|
}
|
||||||
|
if (rc) {
|
||||||
|
/* Serious issues when we cannot read the host values! */
|
||||||
|
g_free(reg_entry);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/* Set bits in emu_mask are the ones we emulate. The dev.config shall
|
||||||
|
* contain the emulated view of the guest - therefore we flip the mask
|
||||||
|
* to mask out the host values (which dev.config initially has) . */
|
||||||
|
host_mask = size_mask & ~reg->emu_mask;
|
||||||
|
|
||||||
|
if ((data & host_mask) != (val & host_mask)) {
|
||||||
|
uint32_t new_val;
|
||||||
|
|
||||||
|
/* Mask out host (including past size). */
|
||||||
|
new_val = val & host_mask;
|
||||||
|
/* Merge emulated ones (excluding the non-emulated ones). */
|
||||||
|
new_val |= data & host_mask;
|
||||||
|
/* Leave intact host and emulated values past the size - even though
|
||||||
|
* we do not care as we write per reg->size granularity, but for the
|
||||||
|
* logging below lets have the proper value. */
|
||||||
|
new_val |= ((val | data)) & ~size_mask;
|
||||||
|
XEN_PT_LOG(&s->dev,"Offset 0x%04x mismatch! Emulated=0x%04x, host=0x%04x, syncing to 0x%04x.\n",
|
||||||
|
offset, data, val, new_val);
|
||||||
|
val = new_val;
|
||||||
|
} else
|
||||||
|
val = data;
|
||||||
|
|
||||||
|
if (val & ~size_mask) {
|
||||||
|
XEN_PT_ERR(&s->dev,"Offset 0x%04x:0x%04x expands past register size(%d)!\n",
|
||||||
|
offset, val, reg->size);
|
||||||
|
g_free(reg_entry);
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
/* This could be just pci_set_long as we don't modify the bits
|
||||||
|
* past reg->size, but in case this routine is run in parallel or the
|
||||||
|
* init value is larger, we do not want to over-write registers. */
|
||||||
|
switch (reg->size) {
|
||||||
|
case 1: pci_set_byte(s->dev.config + offset, (uint8_t)val);
|
||||||
|
break;
|
||||||
|
case 2: pci_set_word(s->dev.config + offset, (uint16_t)val);
|
||||||
|
break;
|
||||||
|
case 4: pci_set_long(s->dev.config + offset, val);
|
||||||
|
break;
|
||||||
|
default: assert(1);
|
||||||
|
}
|
||||||
|
/* set register value pointer to the data. */
|
||||||
|
reg_entry->ptr.byte = s->dev.config + offset;
|
||||||
|
|
||||||
}
|
}
|
||||||
/* list add register entry */
|
/* list add register entry */
|
||||||
QLIST_INSERT_HEAD(®_grp->reg_tbl_list, reg_entry, entries);
|
QLIST_INSERT_HEAD(®_grp->reg_tbl_list, reg_entry, entries);
|
||||||
|
@ -1858,7 +2004,8 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
|
||||||
uint32_t reg_grp_offset = 0;
|
uint32_t reg_grp_offset = 0;
|
||||||
XenPTRegGroup *reg_grp_entry = NULL;
|
XenPTRegGroup *reg_grp_entry = NULL;
|
||||||
|
|
||||||
if (xen_pt_emu_reg_grps[i].grp_id != 0xFF) {
|
if (xen_pt_emu_reg_grps[i].grp_id != 0xFF
|
||||||
|
&& xen_pt_emu_reg_grps[i].grp_id != XEN_PCI_INTEL_OPREGION) {
|
||||||
if (xen_pt_hide_dev_cap(&s->real_device,
|
if (xen_pt_hide_dev_cap(&s->real_device,
|
||||||
xen_pt_emu_reg_grps[i].grp_id)) {
|
xen_pt_emu_reg_grps[i].grp_id)) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -1871,6 +2018,15 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* By default we will trap up to 0x40 in the cfg space.
|
||||||
|
* If an intel device is pass through we need to trap 0xfc,
|
||||||
|
* therefore the size should be 0xff.
|
||||||
|
*/
|
||||||
|
if (xen_pt_emu_reg_grps[i].grp_id == XEN_PCI_INTEL_OPREGION) {
|
||||||
|
reg_grp_offset = XEN_PCI_INTEL_OPREGION;
|
||||||
|
}
|
||||||
|
|
||||||
reg_grp_entry = g_new0(XenPTRegGroup, 1);
|
reg_grp_entry = g_new0(XenPTRegGroup, 1);
|
||||||
QLIST_INIT(®_grp_entry->reg_tbl_list);
|
QLIST_INIT(®_grp_entry->reg_tbl_list);
|
||||||
QLIST_INSERT_HEAD(&s->reg_grps, reg_grp_entry, entries);
|
QLIST_INSERT_HEAD(&s->reg_grps, reg_grp_entry, entries);
|
||||||
|
@ -1883,6 +2039,9 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
|
||||||
reg_grp_offset,
|
reg_grp_offset,
|
||||||
®_grp_entry->size);
|
®_grp_entry->size);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
XEN_PT_LOG(&s->dev, "Failed to initialize %d/%ld, type=0x%x, rc:%d\n",
|
||||||
|
i, ARRAY_SIZE(xen_pt_emu_reg_grps),
|
||||||
|
xen_pt_emu_reg_grps[i].grp_type, rc);
|
||||||
xen_pt_config_delete(s);
|
xen_pt_config_delete(s);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -1897,6 +2056,10 @@ int xen_pt_config_init(XenPCIPassthroughState *s)
|
||||||
/* initialize capability register */
|
/* initialize capability register */
|
||||||
rc = xen_pt_config_reg_init(s, reg_grp_entry, regs);
|
rc = xen_pt_config_reg_init(s, reg_grp_entry, regs);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
|
XEN_PT_LOG(&s->dev, "Failed to initialize %d/%ld reg 0x%x in grp_type=0x%x (%d/%ld), rc=%d\n",
|
||||||
|
j, ARRAY_SIZE(xen_pt_emu_reg_grps[i].emu_regs),
|
||||||
|
regs->offset, xen_pt_emu_reg_grps[i].grp_type,
|
||||||
|
i, ARRAY_SIZE(xen_pt_emu_reg_grps), rc);
|
||||||
xen_pt_config_delete(s);
|
xen_pt_config_delete(s);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
272
hw/xen/xen_pt_graphics.c
Normal file
272
hw/xen/xen_pt_graphics.c
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
/*
|
||||||
|
* graphics passthrough
|
||||||
|
*/
|
||||||
|
#include "xen_pt.h"
|
||||||
|
#include "xen-host-pci-device.h"
|
||||||
|
#include "hw/xen/xen_backend.h"
|
||||||
|
|
||||||
|
static unsigned long igd_guest_opregion;
|
||||||
|
static unsigned long igd_host_opregion;
|
||||||
|
|
||||||
|
#define XEN_PCI_INTEL_OPREGION_MASK 0xfff
|
||||||
|
|
||||||
|
typedef struct VGARegion {
|
||||||
|
int type; /* Memory or port I/O */
|
||||||
|
uint64_t guest_base_addr;
|
||||||
|
uint64_t machine_base_addr;
|
||||||
|
uint64_t size; /* size of the region */
|
||||||
|
int rc;
|
||||||
|
} VGARegion;
|
||||||
|
|
||||||
|
#define IORESOURCE_IO 0x00000100
|
||||||
|
#define IORESOURCE_MEM 0x00000200
|
||||||
|
|
||||||
|
static struct VGARegion vga_args[] = {
|
||||||
|
{
|
||||||
|
.type = IORESOURCE_IO,
|
||||||
|
.guest_base_addr = 0x3B0,
|
||||||
|
.machine_base_addr = 0x3B0,
|
||||||
|
.size = 0xC,
|
||||||
|
.rc = -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = IORESOURCE_IO,
|
||||||
|
.guest_base_addr = 0x3C0,
|
||||||
|
.machine_base_addr = 0x3C0,
|
||||||
|
.size = 0x20,
|
||||||
|
.rc = -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = IORESOURCE_MEM,
|
||||||
|
.guest_base_addr = 0xa0000 >> XC_PAGE_SHIFT,
|
||||||
|
.machine_base_addr = 0xa0000 >> XC_PAGE_SHIFT,
|
||||||
|
.size = 0x20,
|
||||||
|
.rc = -1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* register VGA resources for the domain with assigned gfx
|
||||||
|
*/
|
||||||
|
int xen_pt_register_vga_regions(XenHostPCIDevice *dev)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (!is_igd_vga_passthrough(dev)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) {
|
||||||
|
if (vga_args[i].type == IORESOURCE_IO) {
|
||||||
|
vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid,
|
||||||
|
vga_args[i].guest_base_addr,
|
||||||
|
vga_args[i].machine_base_addr,
|
||||||
|
vga_args[i].size, DPCI_ADD_MAPPING);
|
||||||
|
} else {
|
||||||
|
vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid,
|
||||||
|
vga_args[i].guest_base_addr,
|
||||||
|
vga_args[i].machine_base_addr,
|
||||||
|
vga_args[i].size, DPCI_ADD_MAPPING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vga_args[i].rc) {
|
||||||
|
XEN_PT_ERR(NULL, "VGA %s mapping failed! (rc: %i)\n",
|
||||||
|
vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory",
|
||||||
|
vga_args[i].rc);
|
||||||
|
return vga_args[i].rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* unregister VGA resources for the domain with assigned gfx
|
||||||
|
*/
|
||||||
|
int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!is_igd_vga_passthrough(dev)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) {
|
||||||
|
if (vga_args[i].type == IORESOURCE_IO) {
|
||||||
|
vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid,
|
||||||
|
vga_args[i].guest_base_addr,
|
||||||
|
vga_args[i].machine_base_addr,
|
||||||
|
vga_args[i].size, DPCI_REMOVE_MAPPING);
|
||||||
|
} else {
|
||||||
|
vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid,
|
||||||
|
vga_args[i].guest_base_addr,
|
||||||
|
vga_args[i].machine_base_addr,
|
||||||
|
vga_args[i].size, DPCI_REMOVE_MAPPING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vga_args[i].rc) {
|
||||||
|
XEN_PT_ERR(NULL, "VGA %s unmapping failed! (rc: %i)\n",
|
||||||
|
vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory",
|
||||||
|
vga_args[i].rc);
|
||||||
|
return vga_args[i].rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (igd_guest_opregion) {
|
||||||
|
ret = xc_domain_memory_mapping(xen_xc, xen_domid,
|
||||||
|
(unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT),
|
||||||
|
(unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
|
||||||
|
3,
|
||||||
|
DPCI_REMOVE_MAPPING);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *get_vgabios(XenPCIPassthroughState *s, int *size,
|
||||||
|
XenHostPCIDevice *dev)
|
||||||
|
{
|
||||||
|
return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size,
|
||||||
|
dev->domain, dev->bus,
|
||||||
|
dev->dev, dev->func);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Refer to Seabios. */
|
||||||
|
struct rom_header {
|
||||||
|
uint16_t signature;
|
||||||
|
uint8_t size;
|
||||||
|
uint8_t initVector[4];
|
||||||
|
uint8_t reserved[17];
|
||||||
|
uint16_t pcioffset;
|
||||||
|
uint16_t pnpoffset;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct pci_data {
|
||||||
|
uint32_t signature;
|
||||||
|
uint16_t vendor;
|
||||||
|
uint16_t device;
|
||||||
|
uint16_t vitaldata;
|
||||||
|
uint16_t dlen;
|
||||||
|
uint8_t drevision;
|
||||||
|
uint8_t class_lo;
|
||||||
|
uint16_t class_hi;
|
||||||
|
uint16_t ilen;
|
||||||
|
uint16_t irevision;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t indicator;
|
||||||
|
uint16_t reserved;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
int xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev)
|
||||||
|
{
|
||||||
|
unsigned char *bios = NULL;
|
||||||
|
struct rom_header *rom;
|
||||||
|
int bios_size;
|
||||||
|
char *c = NULL;
|
||||||
|
char checksum = 0;
|
||||||
|
uint32_t len = 0;
|
||||||
|
struct pci_data *pd = NULL;
|
||||||
|
|
||||||
|
if (!is_igd_vga_passthrough(dev)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bios = get_vgabios(s, &bios_size, dev);
|
||||||
|
if (!bios) {
|
||||||
|
XEN_PT_ERR(&s->dev, "VGA: Can't getting VBIOS!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Currently we fixed this address as a primary. */
|
||||||
|
rom = (struct rom_header *)bios;
|
||||||
|
pd = (void *)(bios + (unsigned char)rom->pcioffset);
|
||||||
|
|
||||||
|
/* We may need to fixup Device Identification. */
|
||||||
|
if (pd->device != s->real_device.device_id) {
|
||||||
|
pd->device = s->real_device.device_id;
|
||||||
|
|
||||||
|
len = rom->size * 512;
|
||||||
|
/* Then adjust the bios checksum */
|
||||||
|
for (c = (char *)bios; c < ((char *)bios + len); c++) {
|
||||||
|
checksum += *c;
|
||||||
|
}
|
||||||
|
if (checksum) {
|
||||||
|
bios[len - 1] -= checksum;
|
||||||
|
XEN_PT_LOG(&s->dev, "vga bios checksum is adjusted %x!\n",
|
||||||
|
checksum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Currently we fixed this address as a primary for legacy BIOS. */
|
||||||
|
cpu_physical_memory_rw(0xc0000, bios, bios_size, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t igd_read_opregion(XenPCIPassthroughState *s)
|
||||||
|
{
|
||||||
|
uint32_t val = 0;
|
||||||
|
|
||||||
|
if (!igd_guest_opregion) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = igd_guest_opregion;
|
||||||
|
|
||||||
|
XEN_PT_LOG(&s->dev, "Read opregion val=%x\n", val);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define XEN_PCI_INTEL_OPREGION_PAGES 0x3
|
||||||
|
#define XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED 0x1
|
||||||
|
void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (igd_guest_opregion) {
|
||||||
|
XEN_PT_LOG(&s->dev, "opregion register already been set, ignoring %x\n",
|
||||||
|
val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We just work with LE. */
|
||||||
|
xen_host_pci_get_block(&s->real_device, XEN_PCI_INTEL_OPREGION,
|
||||||
|
(uint8_t *)&igd_host_opregion, 4);
|
||||||
|
igd_guest_opregion = (unsigned long)(val & ~XEN_PCI_INTEL_OPREGION_MASK)
|
||||||
|
| (igd_host_opregion & XEN_PCI_INTEL_OPREGION_MASK);
|
||||||
|
|
||||||
|
ret = xc_domain_iomem_permission(xen_xc, xen_domid,
|
||||||
|
(unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
|
||||||
|
XEN_PCI_INTEL_OPREGION_PAGES,
|
||||||
|
XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
XEN_PT_ERR(&s->dev, "[%d]:Can't enable to access IGD host opregion:"
|
||||||
|
" 0x%lx.\n", ret,
|
||||||
|
(unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT)),
|
||||||
|
igd_guest_opregion = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = xc_domain_memory_mapping(xen_xc, xen_domid,
|
||||||
|
(unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT),
|
||||||
|
(unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
|
||||||
|
XEN_PCI_INTEL_OPREGION_PAGES,
|
||||||
|
DPCI_ADD_MAPPING);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
XEN_PT_ERR(&s->dev, "[%d]:Can't map IGD host opregion:0x%lx to"
|
||||||
|
" guest opregion:0x%lx.\n", ret,
|
||||||
|
(unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
|
||||||
|
(unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT));
|
||||||
|
igd_guest_opregion = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XEN_PT_LOG(&s->dev, "Map OpRegion: 0x%lx -> 0x%lx\n",
|
||||||
|
(unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
|
||||||
|
(unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT));
|
||||||
|
}
|
|
@ -75,19 +75,29 @@ static int msi_msix_enable(XenPCIPassthroughState *s,
|
||||||
bool enable)
|
bool enable)
|
||||||
{
|
{
|
||||||
uint16_t val = 0;
|
uint16_t val = 0;
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (!address) {
|
if (!address) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
xen_host_pci_get_word(&s->real_device, address, &val);
|
rc = xen_host_pci_get_word(&s->real_device, address, &val);
|
||||||
|
if (rc) {
|
||||||
|
XEN_PT_ERR(&s->dev, "Failed to read MSI/MSI-X register (0x%x), rc:%d\n",
|
||||||
|
address, rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
if (enable) {
|
if (enable) {
|
||||||
val |= flag;
|
val |= flag;
|
||||||
} else {
|
} else {
|
||||||
val &= ~flag;
|
val &= ~flag;
|
||||||
}
|
}
|
||||||
xen_host_pci_set_word(&s->real_device, address, val);
|
rc = xen_host_pci_set_word(&s->real_device, address, val);
|
||||||
return 0;
|
if (rc) {
|
||||||
|
XEN_PT_ERR(&s->dev, "Failed to write MSI/MSI-X register (0x%x), rc:%d\n",
|
||||||
|
address, rc);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int msi_msix_setup(XenPCIPassthroughState *s,
|
static int msi_msix_setup(XenPCIPassthroughState *s,
|
||||||
|
@ -220,7 +230,7 @@ static int msi_msix_disable(XenPCIPassthroughState *s,
|
||||||
* MSI virtualization functions
|
* MSI virtualization functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable)
|
static int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable)
|
||||||
{
|
{
|
||||||
XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling");
|
XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling");
|
||||||
|
|
||||||
|
@ -276,7 +286,7 @@ void xen_pt_msi_disable(XenPCIPassthroughState *s)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
xen_pt_msi_set_enable(s, false);
|
(void)xen_pt_msi_set_enable(s, false);
|
||||||
|
|
||||||
msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false,
|
msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false,
|
||||||
msi->initialized);
|
msi->initialized);
|
||||||
|
|
|
@ -137,6 +137,7 @@ struct MachineState {
|
||||||
bool mem_merge;
|
bool mem_merge;
|
||||||
bool usb;
|
bool usb;
|
||||||
bool usb_disabled;
|
bool usb_disabled;
|
||||||
|
bool igd_gfx_passthru;
|
||||||
char *firmware;
|
char *firmware;
|
||||||
bool iommu;
|
bool iommu;
|
||||||
bool suppress_vmdesc;
|
bool suppress_vmdesc;
|
||||||
|
|
|
@ -220,7 +220,13 @@ extern int no_hpet;
|
||||||
struct PCII440FXState;
|
struct PCII440FXState;
|
||||||
typedef struct PCII440FXState PCII440FXState;
|
typedef struct PCII440FXState PCII440FXState;
|
||||||
|
|
||||||
PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn,
|
#define TYPE_I440FX_PCI_HOST_BRIDGE "i440FX-pcihost"
|
||||||
|
#define TYPE_I440FX_PCI_DEVICE "i440FX"
|
||||||
|
|
||||||
|
#define TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE "igd-passthrough-i440FX"
|
||||||
|
|
||||||
|
PCIBus *i440fx_init(const char *host_type, const char *pci_type,
|
||||||
|
PCII440FXState **pi440fx_state, int *piix_devfn,
|
||||||
ISABus **isa_bus, qemu_irq *pic,
|
ISABus **isa_bus, qemu_irq *pic,
|
||||||
MemoryRegion *address_space_mem,
|
MemoryRegion *address_space_mem,
|
||||||
MemoryRegion *address_space_io,
|
MemoryRegion *address_space_io,
|
||||||
|
@ -721,4 +727,5 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
|
||||||
(m)->compat_props = props; \
|
(m)->compat_props = props; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
27
include/hw/pci/pci-assign.h
Normal file
27
include/hw/pci/pci-assign.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2. See
|
||||||
|
* the COPYING file in the top-level directory.
|
||||||
|
*
|
||||||
|
* Just split from hw/i386/kvm/pci-assign.c.
|
||||||
|
*/
|
||||||
|
#ifndef PCI_ASSIGN_H
|
||||||
|
#define PCI_ASSIGN_H
|
||||||
|
|
||||||
|
#include "hw/pci/pci.h"
|
||||||
|
|
||||||
|
//#define DEVICE_ASSIGNMENT_DEBUG
|
||||||
|
|
||||||
|
#ifdef DEVICE_ASSIGNMENT_DEBUG
|
||||||
|
#define DEBUG(fmt, ...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \
|
||||||
|
} while (0)
|
||||||
|
#else
|
||||||
|
#define DEBUG(fmt, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner,
|
||||||
|
int *size, unsigned int domain,
|
||||||
|
unsigned int bus, unsigned int slot,
|
||||||
|
unsigned int function);
|
||||||
|
#endif /* PCI_ASSIGN_H */
|
|
@ -186,6 +186,15 @@ static inline int xen_get_vmport_regs_pfn(XenXC xc, domid_t dom,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Xen before 4.6 */
|
||||||
|
#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 460
|
||||||
|
|
||||||
|
#ifndef HVM_IOREQSRV_BUFIOREQ_ATOMIC
|
||||||
|
#define HVM_IOREQSRV_BUFIOREQ_ATOMIC 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Xen before 4.5 */
|
/* Xen before 4.5 */
|
||||||
#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 450
|
#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 450
|
||||||
|
|
||||||
|
@ -370,7 +379,8 @@ static inline void xen_unmap_pcidev(XenXC xc, domid_t dom,
|
||||||
static inline int xen_create_ioreq_server(XenXC xc, domid_t dom,
|
static inline int xen_create_ioreq_server(XenXC xc, domid_t dom,
|
||||||
ioservid_t *ioservid)
|
ioservid_t *ioservid)
|
||||||
{
|
{
|
||||||
int rc = xc_hvm_create_ioreq_server(xc, dom, 1, ioservid);
|
int rc = xc_hvm_create_ioreq_server(xc, dom, HVM_IOREQSRV_BUFIOREQ_ATOMIC,
|
||||||
|
ioservid);
|
||||||
|
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
trace_xen_ioreq_server_create(*ioservid);
|
trace_xen_ioreq_server_create(*ioservid);
|
||||||
|
@ -407,4 +417,26 @@ static inline int xen_set_ioreq_server_state(XenXC xc, domid_t dom,
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 460
|
||||||
|
static inline int xen_xc_domain_add_to_physmap(XenXC xch, uint32_t domid,
|
||||||
|
unsigned int space,
|
||||||
|
unsigned long idx,
|
||||||
|
xen_pfn_t gpfn)
|
||||||
|
{
|
||||||
|
return xc_domain_add_to_physmap(xch, domid, space, idx, gpfn);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int xen_xc_domain_add_to_physmap(XenXC xch, uint32_t domid,
|
||||||
|
unsigned int space,
|
||||||
|
unsigned long idx,
|
||||||
|
xen_pfn_t gpfn)
|
||||||
|
{
|
||||||
|
/* In Xen 4.6 rc is -1 and errno contains the error value. */
|
||||||
|
int rc = xc_domain_add_to_physmap(xch, domid, space, idx, gpfn);
|
||||||
|
if (rc == -1)
|
||||||
|
return errno;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* QEMU_HW_XEN_COMMON_H */
|
#endif /* QEMU_HW_XEN_COMMON_H */
|
||||||
|
|
|
@ -38,6 +38,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \
|
||||||
" dump-guest-core=on|off include guest memory in a core dump (default=on)\n"
|
" dump-guest-core=on|off include guest memory in a core dump (default=on)\n"
|
||||||
" mem-merge=on|off controls memory merge support (default: on)\n"
|
" mem-merge=on|off controls memory merge support (default: on)\n"
|
||||||
" iommu=on|off controls emulated Intel IOMMU (VT-d) support (default=off)\n"
|
" iommu=on|off controls emulated Intel IOMMU (VT-d) support (default=off)\n"
|
||||||
|
" igd-passthru=on|off controls IGD GFX passthrough support (default=off)\n"
|
||||||
" aes-key-wrap=on|off controls support for AES key wrapping (default=on)\n"
|
" aes-key-wrap=on|off controls support for AES key wrapping (default=on)\n"
|
||||||
" dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n"
|
" dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n"
|
||||||
" suppress-vmdesc=on|off disables self-describing migration (default=off)\n",
|
" suppress-vmdesc=on|off disables self-describing migration (default=off)\n",
|
||||||
|
@ -55,6 +56,8 @@ than one accelerator specified, the next one is used if the previous one fails
|
||||||
to initialize.
|
to initialize.
|
||||||
@item kernel_irqchip=on|off
|
@item kernel_irqchip=on|off
|
||||||
Enables in-kernel irqchip support for the chosen accelerator when available.
|
Enables in-kernel irqchip support for the chosen accelerator when available.
|
||||||
|
@item gfx_passthru=on|off
|
||||||
|
Enables IGD GFX passthrough support for the chosen machine when available.
|
||||||
@item vmport=on|off|auto
|
@item vmport=on|off|auto
|
||||||
Enables emulation of VMWare IO port, for vmmouse etc. auto says to select the
|
Enables emulation of VMWare IO port, for vmmouse etc. auto says to select the
|
||||||
value based on accel. For accel=xen the default is off otherwise the default
|
value based on accel. For accel=xen the default is off otherwise the default
|
||||||
|
|
|
@ -936,6 +936,13 @@ xen_map_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %
|
||||||
xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64
|
xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: %u start: %#"PRIx64" end: %#"PRIx64
|
||||||
xen_map_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
|
xen_map_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
|
||||||
xen_unmap_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
|
xen_unmap_pcidev(uint32_t id, uint8_t bus, uint8_t dev, uint8_t func) "id: %u bdf: %02x.%02x.%02x"
|
||||||
|
handle_ioreq(void *req, uint32_t type, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p type=%d dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
|
||||||
|
handle_ioreq_read(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p read type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
|
||||||
|
handle_ioreq_write(void *req, uint32_t type, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p write type=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
|
||||||
|
cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p pio dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
|
||||||
|
cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=%#"PRIx64" port=%#"PRIx64" size=%d"
|
||||||
|
cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=%#"PRIx64" port=%#"PRIx64" size=%d"
|
||||||
|
cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=%#"PRIx64" data=%#"PRIx64" count=%d size=%d"
|
||||||
|
|
||||||
# xen-mapcache.c
|
# xen-mapcache.c
|
||||||
xen_map_cache(uint64_t phys_addr) "want %#"PRIx64
|
xen_map_cache(uint64_t phys_addr) "want %#"PRIx64
|
||||||
|
|
10
vl.c
10
vl.c
|
@ -1338,6 +1338,13 @@ static inline void semihosting_arg_fallback(const char *file, const char *cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Now we still need this for compatibility with XEN. */
|
||||||
|
bool has_igd_gfx_passthru;
|
||||||
|
static void igd_gfx_passthru(void)
|
||||||
|
{
|
||||||
|
has_igd_gfx_passthru = current_machine->igd_gfx_passthru;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************/
|
/***********************************************************/
|
||||||
/* USB devices */
|
/* USB devices */
|
||||||
|
|
||||||
|
@ -4528,6 +4535,9 @@ int main(int argc, char **argv, char **envp)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if IGD GFX passthrough. */
|
||||||
|
igd_gfx_passthru();
|
||||||
|
|
||||||
/* init generic devices */
|
/* init generic devices */
|
||||||
if (qemu_opts_foreach(qemu_find_opts("device"),
|
if (qemu_opts_foreach(qemu_find_opts("device"),
|
||||||
device_init_func, NULL, NULL)) {
|
device_init_func, NULL, NULL)) {
|
||||||
|
|
55
xen-hvm.c
55
xen-hvm.c
|
@ -344,10 +344,10 @@ go_physmap:
|
||||||
unsigned long idx = pfn + i;
|
unsigned long idx = pfn + i;
|
||||||
xen_pfn_t gpfn = start_gpfn + i;
|
xen_pfn_t gpfn = start_gpfn + i;
|
||||||
|
|
||||||
rc = xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn);
|
rc = xen_xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
DPRINTF("add_to_physmap MFN %"PRI_xen_pfn" to PFN %"
|
DPRINTF("add_to_physmap MFN %"PRI_xen_pfn" to PFN %"
|
||||||
PRI_xen_pfn" failed: %d\n", idx, gpfn, rc);
|
PRI_xen_pfn" failed: %d (errno: %d)\n", idx, gpfn, rc, errno);
|
||||||
return -rc;
|
return -rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -421,10 +421,10 @@ static int xen_remove_from_physmap(XenIOState *state,
|
||||||
xen_pfn_t idx = start_addr + i;
|
xen_pfn_t idx = start_addr + i;
|
||||||
xen_pfn_t gpfn = phys_offset + i;
|
xen_pfn_t gpfn = phys_offset + i;
|
||||||
|
|
||||||
rc = xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn);
|
rc = xen_xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
fprintf(stderr, "add_to_physmap MFN %"PRI_xen_pfn" to PFN %"
|
fprintf(stderr, "add_to_physmap MFN %"PRI_xen_pfn" to PFN %"
|
||||||
PRI_xen_pfn" failed: %d\n", idx, gpfn, rc);
|
PRI_xen_pfn" failed: %d (errno: %d)\n", idx, gpfn, rc, errno);
|
||||||
return -rc;
|
return -rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -813,9 +813,14 @@ static void cpu_ioreq_pio(ioreq_t *req)
|
||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
|
trace_cpu_ioreq_pio(req, req->dir, req->df, req->data_is_ptr, req->addr,
|
||||||
|
req->data, req->count, req->size);
|
||||||
|
|
||||||
if (req->dir == IOREQ_READ) {
|
if (req->dir == IOREQ_READ) {
|
||||||
if (!req->data_is_ptr) {
|
if (!req->data_is_ptr) {
|
||||||
req->data = do_inp(req->addr, req->size);
|
req->data = do_inp(req->addr, req->size);
|
||||||
|
trace_cpu_ioreq_pio_read_reg(req, req->data, req->addr,
|
||||||
|
req->size);
|
||||||
} else {
|
} else {
|
||||||
uint32_t tmp;
|
uint32_t tmp;
|
||||||
|
|
||||||
|
@ -826,6 +831,8 @@ static void cpu_ioreq_pio(ioreq_t *req)
|
||||||
}
|
}
|
||||||
} else if (req->dir == IOREQ_WRITE) {
|
} else if (req->dir == IOREQ_WRITE) {
|
||||||
if (!req->data_is_ptr) {
|
if (!req->data_is_ptr) {
|
||||||
|
trace_cpu_ioreq_pio_write_reg(req, req->data, req->addr,
|
||||||
|
req->size);
|
||||||
do_outp(req->addr, req->size, req->data);
|
do_outp(req->addr, req->size, req->data);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < req->count; i++) {
|
for (i = 0; i < req->count; i++) {
|
||||||
|
@ -842,6 +849,9 @@ static void cpu_ioreq_move(ioreq_t *req)
|
||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
|
trace_cpu_ioreq_move(req, req->dir, req->df, req->data_is_ptr, req->addr,
|
||||||
|
req->data, req->count, req->size);
|
||||||
|
|
||||||
if (!req->data_is_ptr) {
|
if (!req->data_is_ptr) {
|
||||||
if (req->dir == IOREQ_READ) {
|
if (req->dir == IOREQ_READ) {
|
||||||
for (i = 0; i < req->count; i++) {
|
for (i = 0; i < req->count; i++) {
|
||||||
|
@ -914,11 +924,18 @@ static void handle_vmport_ioreq(XenIOState *state, ioreq_t *req)
|
||||||
|
|
||||||
static void handle_ioreq(XenIOState *state, ioreq_t *req)
|
static void handle_ioreq(XenIOState *state, ioreq_t *req)
|
||||||
{
|
{
|
||||||
|
trace_handle_ioreq(req, req->type, req->dir, req->df, req->data_is_ptr,
|
||||||
|
req->addr, req->data, req->count, req->size);
|
||||||
|
|
||||||
if (!req->data_is_ptr && (req->dir == IOREQ_WRITE) &&
|
if (!req->data_is_ptr && (req->dir == IOREQ_WRITE) &&
|
||||||
(req->size < sizeof (target_ulong))) {
|
(req->size < sizeof (target_ulong))) {
|
||||||
req->data &= ((target_ulong) 1 << (8 * req->size)) - 1;
|
req->data &= ((target_ulong) 1 << (8 * req->size)) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (req->dir == IOREQ_WRITE)
|
||||||
|
trace_handle_ioreq_write(req, req->type, req->df, req->data_is_ptr,
|
||||||
|
req->addr, req->data, req->count, req->size);
|
||||||
|
|
||||||
switch (req->type) {
|
switch (req->type) {
|
||||||
case IOREQ_TYPE_PIO:
|
case IOREQ_TYPE_PIO:
|
||||||
cpu_ioreq_pio(req);
|
cpu_ioreq_pio(req);
|
||||||
|
@ -958,23 +975,38 @@ static void handle_ioreq(XenIOState *state, ioreq_t *req)
|
||||||
default:
|
default:
|
||||||
hw_error("Invalid ioreq type 0x%x\n", req->type);
|
hw_error("Invalid ioreq type 0x%x\n", req->type);
|
||||||
}
|
}
|
||||||
|
if (req->dir == IOREQ_READ) {
|
||||||
|
trace_handle_ioreq_read(req, req->type, req->df, req->data_is_ptr,
|
||||||
|
req->addr, req->data, req->count, req->size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_buffered_iopage(XenIOState *state)
|
static int handle_buffered_iopage(XenIOState *state)
|
||||||
{
|
{
|
||||||
|
buffered_iopage_t *buf_page = state->buffered_io_page;
|
||||||
buf_ioreq_t *buf_req = NULL;
|
buf_ioreq_t *buf_req = NULL;
|
||||||
ioreq_t req;
|
ioreq_t req;
|
||||||
int qw;
|
int qw;
|
||||||
|
|
||||||
if (!state->buffered_io_page) {
|
if (!buf_page) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&req, 0x00, sizeof(req));
|
memset(&req, 0x00, sizeof(req));
|
||||||
|
|
||||||
while (state->buffered_io_page->read_pointer != state->buffered_io_page->write_pointer) {
|
for (;;) {
|
||||||
buf_req = &state->buffered_io_page->buf_ioreq[
|
uint32_t rdptr = buf_page->read_pointer, wrptr;
|
||||||
state->buffered_io_page->read_pointer % IOREQ_BUFFER_SLOT_NUM];
|
|
||||||
|
xen_rmb();
|
||||||
|
wrptr = buf_page->write_pointer;
|
||||||
|
xen_rmb();
|
||||||
|
if (rdptr != buf_page->read_pointer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (rdptr == wrptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf_req = &buf_page->buf_ioreq[rdptr % IOREQ_BUFFER_SLOT_NUM];
|
||||||
req.size = 1UL << buf_req->size;
|
req.size = 1UL << buf_req->size;
|
||||||
req.count = 1;
|
req.count = 1;
|
||||||
req.addr = buf_req->addr;
|
req.addr = buf_req->addr;
|
||||||
|
@ -986,15 +1018,14 @@ static int handle_buffered_iopage(XenIOState *state)
|
||||||
req.data_is_ptr = 0;
|
req.data_is_ptr = 0;
|
||||||
qw = (req.size == 8);
|
qw = (req.size == 8);
|
||||||
if (qw) {
|
if (qw) {
|
||||||
buf_req = &state->buffered_io_page->buf_ioreq[
|
buf_req = &buf_page->buf_ioreq[(rdptr + 1) %
|
||||||
(state->buffered_io_page->read_pointer + 1) % IOREQ_BUFFER_SLOT_NUM];
|
IOREQ_BUFFER_SLOT_NUM];
|
||||||
req.data |= ((uint64_t)buf_req->data) << 32;
|
req.data |= ((uint64_t)buf_req->data) << 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_ioreq(state, &req);
|
handle_ioreq(state, &req);
|
||||||
|
|
||||||
xen_mb();
|
atomic_add(&buf_page->read_pointer, qw + 1);
|
||||||
state->buffered_io_page->read_pointer += qw ? 2 : 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return req.count;
|
return req.count;
|
||||||
|
|
Loading…
Reference in a new issue