From 2630541ac58b328d43112c0126334f9c7f7ccf31 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Jun 02 2015 18:55:17 +0000 Subject: work around a gcc 5 bug, Potential unintended writes to host MSI message data field via qemu [XSA-128, CVE-2015-4103], PCI MSI mask bits inadvertently exposed to guests [XSA-129, CVE-2015-4104], Guest triggerable qemu MSI-X pass-through error messages [XSA-130, CVE-2015-4105], Unmediated PCI register access in qemu [XSA-131, CVE-2015-4106] --- diff --git a/xen.gcc5.bug.workaround.patch b/xen.gcc5.bug.workaround.patch new file mode 100644 index 0000000..57d3a19 --- /dev/null +++ b/xen.gcc5.bug.workaround.patch @@ -0,0 +1,7 @@ +--- xen-4.5.1/xen/arch/x86/Rules.mk.orig 2015-05-18 18:20:57.000000000 +0100 ++++ xen-4.5.1/xen/arch/x86/Rules.mk 2015-05-30 21:24:31.886886843 +0100 +@@ -48,3 +48,4 @@ + ifneq ($(call cc-option,$(CC),-fvisibility=hidden,n),n) + CFLAGS += -DGCC_HAS_VISIBILITY_ATTRIBUTE + endif ++CFLAGS += -fno-caller-saves diff --git a/xen.spec b/xen.spec index acd3f4e..01aa2c6 100644 --- a/xen.spec +++ b/xen.spec @@ -51,7 +51,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.5.0 -Release: 9%{?dist} +Release: 10%{?dist} Group: Development/Libraries License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ @@ -101,6 +101,29 @@ Patch32: xsa132.patch Patch33: xsa133-qemut.patch Patch34: xsa133-qemuu.patch Patch35: qemu.trad.build.patch +Patch36: xen.gcc5.bug.workaround.patch +Patch37: xsa128-qemut.patch +Patch38: xsa128-qemuu.patch +Patch39: xsa129-qemut.patch +Patch40: xsa129-qemuu.patch +Patch41: xsa130-qemut.patch +Patch42: xsa130-qemuu.patch +Patch43: xsa131-qemut-1.patch +Patch44: xsa131-qemut-2.patch +Patch45: xsa131-qemut-3.patch +Patch46: xsa131-qemut-4.patch +Patch47: xsa131-qemut-5.patch +Patch48: xsa131-qemut-6.patch +Patch49: xsa131-qemut-7.patch +Patch50: xsa131-qemut-8.patch +Patch51: xsa131-qemuu-1.patch +Patch52: xsa131-qemuu-2.patch +Patch53: xsa131-qemuu-3.patch +Patch54: xsa131-qemuu-4.patch +Patch55: xsa131-qemuu-5.patch +Patch56: xsa131-qemuu-6.patch +Patch57: xsa131-qemuu-7.patch +Patch58: xsa131-qemuu-8.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root BuildRequires: transfig libidn-devel zlib-devel texi2html SDL-devel curl-devel @@ -301,6 +324,29 @@ manage Xen virtual machines. %patch33 -p1 %patch34 -p1 %patch35 -p1 +%patch36 -p1 +%patch37 -p1 +%patch38 -p1 +%patch39 -p1 +%patch40 -p1 +%patch41 -p1 +%patch42 -p1 +%patch43 -p1 +%patch44 -p1 +%patch45 -p1 +%patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 +%patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom @@ -805,9 +851,16 @@ rm -rf %{buildroot} %endif %changelog -* Sat May 16 2015 Michael Young +* Tue Jun 02 2015 Michael Young - 4.5.0-10 - replace deprecated gnutls use in qemu-xen-traditional based on qemu-xen patches +- work around a gcc 5 bug +- Potential unintended writes to host MSI message data field via qemu + [XSA-128, CVE-2015-4103] +- PCI MSI mask bits inadvertently exposed to guests [XSA-129, CVE-2015-4104] +- Guest triggerable qemu MSI-X pass-through error messages [XSA-130, + CVE-2015-4105] +- Unmediated PCI register access in qemu [XSA-131, CVE-2015-4106] * Wed May 13 2015 Michael Young - 4.5.0-9 - Privilege escalation via emulated floppy disk drive [XSA-133, diff --git a/xsa128-qemut.patch b/xsa128-qemut.patch new file mode 100644 index 0000000..3ba9d31 --- /dev/null +++ b/xsa128-qemut.patch @@ -0,0 +1,125 @@ +xen: properly gate host writes of modified PCI CFG contents + +The old logic didn't work as intended when an access spanned multiple +fields (for example a 32-bit access to the location of the MSI Message +Data field with the high 16 bits not being covered by any known field). +Remove it and derive which fields not to write to from the accessed +fields' emulation masks: When they're all ones, there's no point in +doing any host write. + +This fixes a secondary issue at once: We obviously shouldn't make any +host write attempt when already the host read failed. + +This is XSA-128. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- a/tools/qemu-xen-traditional/hw/pass-through.c ++++ b/tools/qemu-xen-traditional/hw/pass-through.c +@@ -454,7 +454,7 @@ static struct pt_reg_info_tbl pt_emu_reg + .offset = PCI_INTEL_OPREGION, + .size = 4, + .init_val = 0, +- .no_wb = 1, ++ .emu_mask = 0xFFFFFFFF, + .u.dw.read = pt_intel_opregion_read, + .u.dw.write = pt_intel_opregion_write, + .u.dw.restore = NULL, +@@ -657,7 +657,6 @@ static struct pt_reg_info_tbl pt_emu_reg + .init_val = 0x00000000, + .ro_mask = 0x00000003, + .emu_mask = 0xFFFFFFFF, +- .no_wb = 1, + .init = pt_common_reg_init, + .u.dw.read = pt_long_reg_read, + .u.dw.write = pt_msgaddr32_reg_write, +@@ -670,7 +669,6 @@ static struct pt_reg_info_tbl pt_emu_reg + .init_val = 0x00000000, + .ro_mask = 0x00000000, + .emu_mask = 0xFFFFFFFF, +- .no_wb = 1, + .init = pt_msgaddr64_reg_init, + .u.dw.read = pt_long_reg_read, + .u.dw.write = pt_msgaddr64_reg_write, +@@ -683,7 +681,6 @@ static struct pt_reg_info_tbl pt_emu_reg + .init_val = 0x0000, + .ro_mask = 0x0000, + .emu_mask = 0xFFFF, +- .no_wb = 1, + .init = pt_msgdata_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_msgdata_reg_write, +@@ -696,7 +693,6 @@ static struct pt_reg_info_tbl pt_emu_reg + .init_val = 0x0000, + .ro_mask = 0x0000, + .emu_mask = 0xFFFF, +- .no_wb = 1, + .init = pt_msgdata_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_msgdata_reg_write, +@@ -1524,7 +1520,7 @@ static void pt_pci_write_config(PCIDevic + uint32_t find_addr = address; + uint32_t real_offset = 0; + uint32_t valid_mask = 0xFFFFFFFF; +- uint32_t read_val = 0; ++ uint32_t read_val = 0, wb_mask; + uint8_t *ptr_val = NULL; + int emul_len = 0; + int index = 0; +@@ -1597,7 +1593,10 @@ static void pt_pci_write_config(PCIDevic + { + PT_LOG("Error: pci_read_block failed. return value[%d].\n", ret); + memset((uint8_t *)&read_val, 0xff, len); ++ wb_mask = 0; + } ++ else ++ wb_mask = 0xFFFFFFFF >> ((4 - len) << 3); + + /* pass directly to libpci for passthrough type register group */ + if (reg_grp_entry == NULL) +@@ -1620,6 +1619,11 @@ static void pt_pci_write_config(PCIDevic + valid_mask = (0xFFFFFFFF >> ((4 - emul_len) << 3)); + valid_mask <<= ((find_addr - real_offset) << 3); + ptr_val = ((uint8_t *)&val + (real_offset & 3)); ++ if (reg->emu_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) { ++ wb_mask &= ~((reg->emu_mask ++ >> ((find_addr - real_offset) << 3)) ++ << ((len - emul_len) << 3)); ++ } + + /* do emulation depend on register size */ + switch (reg->size) { +@@ -1677,8 +1681,19 @@ static void pt_pci_write_config(PCIDevic + val >>= ((address & 3) << 3); + + out: +- if (!(reg && reg->no_wb)) { /* unknown regs are passed through */ +- ret = pci_write_block(pci_dev, address, (uint8_t *)&val, len); ++ for (index = 0; wb_mask; index += len) { ++ /* unknown regs are passed through */ ++ while (!(wb_mask & 0xff)) { ++ index++; ++ wb_mask >>= 8; ++ } ++ len = 0; ++ do { ++ len++; ++ wb_mask >>= 8; ++ } while (wb_mask & 0xff); ++ ret = pci_write_block(pci_dev, address + index, ++ (uint8_t *)&val + index, len); + + if (!ret) + PT_LOG("Error: pci_write_block failed. return value[%d].\n", ret); +--- a/tools/qemu-xen-traditional/hw/pass-through.h ++++ b/tools/qemu-xen-traditional/hw/pass-through.h +@@ -372,8 +372,6 @@ struct pt_reg_info_tbl { + uint32_t ro_mask; + /* reg emulate field mask (ON:emu, OFF:passthrough) */ + uint32_t emu_mask; +- /* no write back allowed */ +- uint32_t no_wb; + /* emul reg initialize method */ + conf_reg_init init; + union { diff --git a/xsa128-qemuu.patch b/xsa128-qemuu.patch new file mode 100644 index 0000000..53ca5a2 --- /dev/null +++ b/xsa128-qemuu.patch @@ -0,0 +1,118 @@ +xen: properly gate host writes of modified PCI CFG contents + +The old logic didn't work as intended when an access spanned multiple +fields (for example a 32-bit access to the location of the MSI Message +Data field with the high 16 bits not being covered by any known field). +Remove it and derive which fields not to write to from the accessed +fields' emulation masks: When they're all ones, there's no point in +doing any host write. + +This fixes a secondary issue at once: We obviously shouldn't make any +host write attempt when already the host read failed. + +This is XSA-128. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- a/tools/qemu-xen/hw/xen/xen_pt.c ++++ b/tools/qemu-xen/hw/xen/xen_pt.c +@@ -234,7 +234,7 @@ static void xen_pt_pci_write_config(PCID + int index = 0; + XenPTRegGroup *reg_grp_entry = NULL; + int rc = 0; +- uint32_t read_val = 0; ++ uint32_t read_val = 0, wb_mask; + int emul_len = 0; + XenPTReg *reg_entry = NULL; + uint32_t find_addr = addr; +@@ -271,6 +271,9 @@ static void xen_pt_pci_write_config(PCID + if (rc < 0) { + XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc); + memset(&read_val, 0xff, len); ++ wb_mask = 0; ++ } else { ++ wb_mask = 0xFFFFFFFF >> ((4 - len) << 3); + } + + /* pass directly to the real device for passthrough type register group */ +@@ -298,6 +301,11 @@ static void xen_pt_pci_write_config(PCID + + valid_mask <<= (find_addr - real_offset) << 3; + ptr_val = (uint8_t *)&val + (real_offset & 3); ++ if (reg->emu_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) { ++ wb_mask &= ~((reg->emu_mask ++ >> ((find_addr - real_offset) << 3)) ++ << ((len - emul_len) << 3)); ++ } + + /* do emulation based on register size */ + switch (reg->size) { +@@ -350,10 +358,19 @@ static void xen_pt_pci_write_config(PCID + memory_region_transaction_commit(); + + out: +- if (!(reg && reg->no_wb)) { ++ for (index = 0; wb_mask; index += len) { + /* unknown regs are passed through */ +- rc = xen_host_pci_set_block(&s->real_device, addr, +- (uint8_t *)&val, len); ++ while (!(wb_mask & 0xff)) { ++ index++; ++ wb_mask >>= 8; ++ } ++ len = 0; ++ do { ++ len++; ++ wb_mask >>= 8; ++ } while (wb_mask & 0xff); ++ rc = xen_host_pci_set_block(&s->real_device, addr + index, ++ (uint8_t *)&val + index, len); + + if (rc < 0) { + XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc); +--- a/tools/qemu-xen/hw/xen/xen_pt.h ++++ b/tools/qemu-xen/hw/xen/xen_pt.h +@@ -105,8 +105,6 @@ struct XenPTRegInfo { + uint32_t ro_mask; + /* reg emulate field mask (ON:emu, OFF:passthrough) */ + uint32_t emu_mask; +- /* no write back allowed */ +- uint32_t no_wb; + xen_pt_conf_reg_init init; + /* read/write function pointer + * for double_word/word/byte size */ +--- a/tools/qemu-xen/hw/xen/xen_pt_config_init.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_config_init.c +@@ -1281,7 +1281,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] + .init_val = 0x00000000, + .ro_mask = 0x00000003, + .emu_mask = 0xFFFFFFFF, +- .no_wb = 1, + .init = xen_pt_common_reg_init, + .u.dw.read = xen_pt_long_reg_read, + .u.dw.write = xen_pt_msgaddr32_reg_write, +@@ -1293,7 +1292,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] + .init_val = 0x00000000, + .ro_mask = 0x00000000, + .emu_mask = 0xFFFFFFFF, +- .no_wb = 1, + .init = xen_pt_msgaddr64_reg_init, + .u.dw.read = xen_pt_long_reg_read, + .u.dw.write = xen_pt_msgaddr64_reg_write, +@@ -1305,7 +1303,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] + .init_val = 0x0000, + .ro_mask = 0x0000, + .emu_mask = 0xFFFF, +- .no_wb = 1, + .init = xen_pt_msgdata_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_msgdata_reg_write, +@@ -1317,7 +1314,6 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] + .init_val = 0x0000, + .ro_mask = 0x0000, + .emu_mask = 0xFFFF, +- .no_wb = 1, + .init = xen_pt_msgdata_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_msgdata_reg_write, diff --git a/xsa129-qemut.patch b/xsa129-qemut.patch new file mode 100644 index 0000000..8667662 --- /dev/null +++ b/xsa129-qemut.patch @@ -0,0 +1,142 @@ +xen: don't allow guest to control MSI mask register + +It's being used by the hypervisor. For now simply mimic a device not +capable of masking, and fully emulate any accesses a guest may issue +nevertheless as simple reads/writes without side effects. + +This is XSA-129. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- a/tools/qemu-xen-traditional/hw/pass-through.c ++++ b/tools/qemu-xen-traditional/hw/pass-through.c +@@ -147,6 +147,10 @@ static uint32_t pt_msgaddr64_reg_init(st + struct pt_reg_info_tbl *reg, uint32_t real_offset); + static uint32_t pt_msgdata_reg_init(struct pt_dev *ptdev, + struct pt_reg_info_tbl *reg, uint32_t real_offset); ++static uint32_t pt_mask_reg_init(struct pt_dev *ptdev, ++ struct pt_reg_info_tbl *reg, uint32_t real_offset); ++static uint32_t pt_pending_reg_init(struct pt_dev *ptdev, ++ struct pt_reg_info_tbl *reg, uint32_t real_offset); + static uint32_t pt_msixctrl_reg_init(struct pt_dev *ptdev, + struct pt_reg_info_tbl *reg, uint32_t real_offset); + static uint32_t pt_header_type_reg_init(struct pt_dev *ptdev, +@@ -644,7 +648,7 @@ static struct pt_reg_info_tbl pt_emu_reg + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFF8E, +- .emu_mask = 0x007F, ++ .emu_mask = 0x017F, + .init = pt_msgctrl_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_msgctrl_reg_write, +@@ -698,6 +702,50 @@ static struct pt_reg_info_tbl pt_emu_reg + .u.w.write = pt_msgdata_reg_write, + .u.w.restore = NULL, + }, ++ /* Mask reg (if PCI_MSI_FLAGS_MASK_BIT set, for 32-bit devices) */ ++ { ++ .offset = PCI_MSI_MASK_32, ++ .size = 4, ++ .init_val = 0x00000000, ++ .ro_mask = 0xFFFFFFFF, ++ .emu_mask = 0xFFFFFFFF, ++ .init = pt_mask_reg_init, ++ .u.dw.read = pt_long_reg_read, ++ .u.dw.write = pt_long_reg_write, ++ }, ++ /* Mask reg (if PCI_MSI_FLAGS_MASK_BIT set, for 64-bit devices) */ ++ { ++ .offset = PCI_MSI_MASK_64, ++ .size = 4, ++ .init_val = 0x00000000, ++ .ro_mask = 0xFFFFFFFF, ++ .emu_mask = 0xFFFFFFFF, ++ .init = pt_mask_reg_init, ++ .u.dw.read = pt_long_reg_read, ++ .u.dw.write = pt_long_reg_write, ++ }, ++ /* Pending reg (if PCI_MSI_FLAGS_MASK_BIT set, for 32-bit devices) */ ++ { ++ .offset = PCI_MSI_MASK_32 + 4, ++ .size = 4, ++ .init_val = 0x00000000, ++ .ro_mask = 0xFFFFFFFF, ++ .emu_mask = 0x00000000, ++ .init = pt_pending_reg_init, ++ .u.dw.read = pt_long_reg_read, ++ .u.dw.write = pt_long_reg_write, ++ }, ++ /* Pending reg (if PCI_MSI_FLAGS_MASK_BIT set, for 64-bit devices) */ ++ { ++ .offset = PCI_MSI_MASK_64 + 4, ++ .size = 4, ++ .init_val = 0x00000000, ++ .ro_mask = 0xFFFFFFFF, ++ .emu_mask = 0x00000000, ++ .init = pt_pending_reg_init, ++ .u.dw.read = pt_long_reg_read, ++ .u.dw.write = pt_long_reg_write, ++ }, + { + .size = 0, + }, +@@ -3023,6 +3071,42 @@ static uint32_t pt_msgdata_reg_init(stru + return PT_INVALID_REG; + } + ++/* this function will be called twice (for 32 bit and 64 bit type) */ ++/* initialize Mask register */ ++static uint32_t pt_mask_reg_init(struct pt_dev *ptdev, ++ struct pt_reg_info_tbl *reg, uint32_t real_offset) ++{ ++ uint32_t flags = ptdev->msi->flags; ++ uint32_t offset = reg->offset; ++ ++ if (!(flags & PCI_MSI_FLAGS_MASK_BIT)) ++ return PT_INVALID_REG; ++ ++ if (offset == (flags & PCI_MSI_FLAGS_64BIT ? ++ PCI_MSI_MASK_64 : PCI_MSI_MASK_32)) ++ return reg->init_val; ++ ++ return PT_INVALID_REG; ++} ++ ++/* this function will be called twice (for 32 bit and 64 bit type) */ ++/* initialize Pending register */ ++static uint32_t pt_pending_reg_init(struct pt_dev *ptdev, ++ struct pt_reg_info_tbl *reg, uint32_t real_offset) ++{ ++ uint32_t flags = ptdev->msi->flags; ++ uint32_t offset = reg->offset; ++ ++ if (!(flags & PCI_MSI_FLAGS_MASK_BIT)) ++ return PT_INVALID_REG; ++ ++ if (offset == (flags & PCI_MSI_FLAGS_64BIT ? ++ PCI_MSI_MASK_64 + 4 : PCI_MSI_MASK_32 + 4)) ++ return reg->init_val; ++ ++ return PT_INVALID_REG; ++} ++ + /* initialize Message Control register for MSI-X */ + static uint32_t pt_msixctrl_reg_init(struct pt_dev *ptdev, + struct pt_reg_info_tbl *reg, uint32_t real_offset) +--- a/tools/qemu-xen-traditional/hw/pass-through.h ++++ b/tools/qemu-xen-traditional/hw/pass-through.h +@@ -84,6 +84,12 @@ + #define PCI_MSI_FLAGS_MASK_BIT 0x0100 + #endif + ++#ifndef PCI_MSI_MASK_32 ++/* interrupt masking register */ ++#define PCI_MSI_MASK_32 12 ++#define PCI_MSI_MASK_64 16 ++#endif ++ + #ifndef PCI_EXP_TYPE_PCIE_BRIDGE + /* PCI/PCI-X to PCIE Bridge */ + #define PCI_EXP_TYPE_PCIE_BRIDGE 0x8 diff --git a/xsa129-qemuu.patch b/xsa129-qemuu.patch new file mode 100644 index 0000000..efd4696 --- /dev/null +++ b/xsa129-qemuu.patch @@ -0,0 +1,172 @@ +xen: don't allow guest to control MSI mask register + +It's being used by the hypervisor. For now simply mimic a device not +capable of masking, and fully emulate any accesses a guest may issue +nevertheless as simple reads/writes without side effects. + +This is XSA-129. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- a/tools/qemu-xen/hw/pci/msi.c ++++ b/tools/qemu-xen/hw/pci/msi.c +@@ -21,10 +21,6 @@ + #include "hw/pci/msi.h" + #include "qemu/range.h" + +-/* Eventually those constants should go to Linux pci_regs.h */ +-#define PCI_MSI_PENDING_32 0x10 +-#define PCI_MSI_PENDING_64 0x14 +- + /* PCI_MSI_ADDRESS_LO */ + #define PCI_MSI_ADDRESS_LO_MASK (~0x3) + +--- a/tools/qemu-xen/hw/xen/xen_pt_config_init.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_config_init.c +@@ -1018,13 +1018,9 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] + */ + + /* Helper */ +-static bool xen_pt_msgdata_check_type(uint32_t offset, uint16_t flags) +-{ +- /* check the offset whether matches the type or not */ +- bool is_32 = (offset == PCI_MSI_DATA_32) && !(flags & PCI_MSI_FLAGS_64BIT); +- bool is_64 = (offset == PCI_MSI_DATA_64) && (flags & PCI_MSI_FLAGS_64BIT); +- return is_32 || is_64; +-} ++#define xen_pt_msi_check_type(offset, flags, what) \ ++ ((offset) == ((flags) & PCI_MSI_FLAGS_64BIT ? \ ++ PCI_MSI_##what##_64 : PCI_MSI_##what##_32)) + + /* Message Control register */ + static int xen_pt_msgctrl_reg_init(XenPCIPassthroughState *s, +@@ -1136,7 +1132,45 @@ static int xen_pt_msgdata_reg_init(XenPC + uint32_t offset = reg->offset; + + /* check the offset whether matches the type or not */ +- if (xen_pt_msgdata_check_type(offset, flags)) { ++ if (xen_pt_msi_check_type(offset, flags, DATA)) { ++ *data = reg->init_val; ++ } else { ++ *data = XEN_PT_INVALID_REG; ++ } ++ return 0; ++} ++ ++/* this function will be called twice (for 32 bit and 64 bit type) */ ++/* initialize Mask register */ ++static int xen_pt_mask_reg_init(XenPCIPassthroughState *s, ++ XenPTRegInfo *reg, uint32_t real_offset, ++ uint32_t *data) ++{ ++ uint32_t flags = s->msi->flags; ++ ++ /* check the offset whether matches the type or not */ ++ if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { ++ *data = XEN_PT_INVALID_REG; ++ } else if (xen_pt_msi_check_type(reg->offset, flags, MASK)) { ++ *data = reg->init_val; ++ } else { ++ *data = XEN_PT_INVALID_REG; ++ } ++ return 0; ++} ++ ++/* this function will be called twice (for 32 bit and 64 bit type) */ ++/* initialize Pending register */ ++static int xen_pt_pending_reg_init(XenPCIPassthroughState *s, ++ XenPTRegInfo *reg, uint32_t real_offset, ++ uint32_t *data) ++{ ++ uint32_t flags = s->msi->flags; ++ ++ /* check the offset whether matches the type or not */ ++ if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { ++ *data = XEN_PT_INVALID_REG; ++ } else if (xen_pt_msi_check_type(reg->offset, flags, PENDING)) { + *data = reg->init_val; + } else { + *data = XEN_PT_INVALID_REG; +@@ -1224,7 +1258,7 @@ static int xen_pt_msgdata_reg_write(XenP + uint32_t offset = reg->offset; + + /* check the offset whether matches the type or not */ +- if (!xen_pt_msgdata_check_type(offset, msi->flags)) { ++ if (!xen_pt_msi_check_type(offset, msi->flags, DATA)) { + /* exit I/O emulator */ + XEN_PT_ERR(&s->dev, "the offset does not match the 32/64 bit type!\n"); + return -1; +@@ -1269,7 +1303,7 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFF8E, +- .emu_mask = 0x007F, ++ .emu_mask = 0x017F, + .init = xen_pt_msgctrl_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_msgctrl_reg_write, +@@ -1318,6 +1352,50 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_msgdata_reg_write, + }, ++ /* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */ ++ { ++ .offset = PCI_MSI_MASK_32, ++ .size = 4, ++ .init_val = 0x00000000, ++ .ro_mask = 0xFFFFFFFF, ++ .emu_mask = 0xFFFFFFFF, ++ .init = xen_pt_mask_reg_init, ++ .u.dw.read = xen_pt_long_reg_read, ++ .u.dw.write = xen_pt_long_reg_write, ++ }, ++ /* Mask reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */ ++ { ++ .offset = PCI_MSI_MASK_64, ++ .size = 4, ++ .init_val = 0x00000000, ++ .ro_mask = 0xFFFFFFFF, ++ .emu_mask = 0xFFFFFFFF, ++ .init = xen_pt_mask_reg_init, ++ .u.dw.read = xen_pt_long_reg_read, ++ .u.dw.write = xen_pt_long_reg_write, ++ }, ++ /* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 32-bit devices) */ ++ { ++ .offset = PCI_MSI_MASK_32 + 4, ++ .size = 4, ++ .init_val = 0x00000000, ++ .ro_mask = 0xFFFFFFFF, ++ .emu_mask = 0x00000000, ++ .init = xen_pt_pending_reg_init, ++ .u.dw.read = xen_pt_long_reg_read, ++ .u.dw.write = xen_pt_long_reg_write, ++ }, ++ /* Pending reg (if PCI_MSI_FLAGS_MASKBIT set, for 64-bit devices) */ ++ { ++ .offset = PCI_MSI_MASK_64 + 4, ++ .size = 4, ++ .init_val = 0x00000000, ++ .ro_mask = 0xFFFFFFFF, ++ .emu_mask = 0x00000000, ++ .init = xen_pt_pending_reg_init, ++ .u.dw.read = xen_pt_long_reg_read, ++ .u.dw.write = xen_pt_long_reg_write, ++ }, + { + .size = 0, + }, +--- a/tools/qemu-xen/include/hw/pci/pci_regs.h ++++ b/tools/qemu-xen/include/hw/pci/pci_regs.h +@@ -298,8 +298,10 @@ + #define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ + #define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ + #define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */ ++#define PCI_MSI_PENDING_32 16 /* Pending bits register for 32-bit devices */ + #define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ + #define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ ++#define PCI_MSI_PENDING_64 20 /* Pending bits register for 32-bit devices */ + + /* MSI-X registers */ + #define PCI_MSIX_FLAGS 2 diff --git a/xsa130-qemut.patch b/xsa130-qemut.patch new file mode 100644 index 0000000..ec9b57a --- /dev/null +++ b/xsa130-qemut.patch @@ -0,0 +1,21 @@ +xen/MSI-X: disable logging by default + +... to avoid allowing the guest to cause the control domain's disk to +fill. + +This is XSA-130. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- a/tools/qemu-xen-traditional/hw/pass-through.h ++++ b/tools/qemu-xen-traditional/hw/pass-through.h +@@ -27,7 +27,7 @@ + #include "qemu-timer.h" + + /* Log acesss */ +-#define PT_LOGGING_ENABLED ++/* #define PT_LOGGING_ENABLED */ + + /* Print errors even if logging is disabled */ + #define PT_ERR(_f, _a...) fprintf(logfile, "%s: " _f, __func__, ##_a) diff --git a/xsa130-qemuu.patch b/xsa130-qemuu.patch new file mode 100644 index 0000000..3123e29 --- /dev/null +++ b/xsa130-qemuu.patch @@ -0,0 +1,71 @@ +xen/MSI-X: limit error messages resulting from bad guest behavior + +... to avoid allowing the guest to cause the control domain's disk to +fill. + +The first message in pci_msix_write() can simply be deleted, as this +is indeed bad guest behavior, but such out of bounds writes don't +really need to be logged. + +The second one is more problematic, as there guest behavior may only +appear to be wrong: For one, the old logic didn't take the mask-all bit +into account. And then this shouldn't depend on host device state (i.e. +the host may have masked the entry without the guest having done so). +Plus these writes shouldn't be dropped even when an entry is unmasked. +Instead, if they can't be made take effect right away, they should take +effect on the next unmasking or enabling operation - the specification +explicitly describes such caching behavior. Until we can validly drop +the message (implementing such caching/latching behavior), issue the +message just once per MSI-X table entry. + +Note that the log message in pci_msix_read() similar to the one being +removed here is not an issue: "addr" being of unsigned type, and the +maximum size of the MSI-X table being 32k, entry_nr simply can't be +negative and hence the conditonal guarding issuing of the message will +never be true. + +This is XSA-130. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- a/tools/qemu-xen/hw/xen/xen_pt.h ++++ b/tools/qemu-xen/hw/xen/xen_pt.h +@@ -175,6 +175,7 @@ typedef struct XenPTMSIXEntry { + uint32_t data; + uint32_t vector_ctrl; + bool updated; /* indicate whether MSI ADDR or DATA is updated */ ++ bool warned; /* avoid issuing (bogus) warning more than once */ + } XenPTMSIXEntry; + typedef struct XenPTMSIX { + uint32_t ctrl_offset; +--- a/tools/qemu-xen/hw/xen/xen_pt_msi.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_msi.c +@@ -434,11 +434,10 @@ static void pci_msix_write(void *opaque, + XenPCIPassthroughState *s = opaque; + XenPTMSIX *msix = s->msix; + XenPTMSIXEntry *entry; +- int entry_nr, offset; ++ unsigned int entry_nr, offset; + + entry_nr = addr / PCI_MSIX_ENTRY_SIZE; +- if (entry_nr < 0 || entry_nr >= msix->total_entries) { +- XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr); ++ if (entry_nr >= msix->total_entries) { + return; + } + entry = &msix->msix_entry[entry_nr]; +@@ -460,8 +459,11 @@ static void pci_msix_write(void *opaque, + + PCI_MSIX_ENTRY_VECTOR_CTRL; + + if (msix->enabled && !(*vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) { +- XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is" +- " already enabled.\n", entry_nr); ++ if (!entry->warned) { ++ entry->warned = true; ++ XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is" ++ " already enabled.\n", entry_nr); ++ } + return; + } + diff --git a/xsa131-qemut-1.patch b/xsa131-qemut-1.patch new file mode 100644 index 0000000..9d186f1 --- /dev/null +++ b/xsa131-qemut-1.patch @@ -0,0 +1,60 @@ +xen/MSI: don't open-code pass-through of enable bit modifications + +Without this the actual XSA-131 fix would cause the enable bit to not +get set anymore (due to the write back getting suppressed there based +on the OR of emu_mask, ro_mask, and res_mask). + +Note that the fiddling with the enable bit shouldn't really be done by +qemu, but making this work right (via libxc and the hypervisor) will +require more extensive changes, which can be postponed until after the +security issue got addressed. + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +Acked-by: Stefano Stabellini + +--- a/tools/qemu-xen-traditional/hw/pass-through.c ++++ b/tools/qemu-xen-traditional/hw/pass-through.c +@@ -648,7 +648,7 @@ static struct pt_reg_info_tbl pt_emu_reg + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFF8E, +- .emu_mask = 0x017F, ++ .emu_mask = 0x017E, + .init = pt_msgctrl_reg_init, + .u.w.read = pt_word_reg_read, + .u.w.write = pt_msgctrl_reg_write, +@@ -3901,6 +3901,9 @@ static int pt_msgctrl_reg_write(struct p + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; ++ /* also emulate MSI_ENABLE bit for MSI-INTx translation */ ++ if (ptdev->msi_trans_en) ++ writable_mask |= PCI_MSI_FLAGS_ENABLE & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + /* update the msi_info too */ + ptdev->msi->flags |= cfg_entry->data & +@@ -3909,6 +3912,9 @@ static int pt_msgctrl_reg_write(struct p + /* create value for writing to I/O device register */ + val = *value; + throughable_mask = ~reg->emu_mask & valid_mask; ++ /* don't pass through MSI_ENABLE bit for MSI-INTx translation */ ++ if (ptdev->msi_trans_en) ++ throughable_mask &= ~PCI_MSI_FLAGS_ENABLE; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* update MSI */ +@@ -3952,12 +3958,6 @@ static int pt_msgctrl_reg_write(struct p + } + } + +- /* pass through MSI_ENABLE bit when no MSI-INTx translation */ +- if (!ptdev->msi_trans_en) { +- *value &= ~PCI_MSI_FLAGS_ENABLE; +- *value |= val & PCI_MSI_FLAGS_ENABLE; +- } +- + return 0; + } + diff --git a/xsa131-qemut-2.patch b/xsa131-qemut-2.patch new file mode 100644 index 0000000..ca6ff57 --- /dev/null +++ b/xsa131-qemut-2.patch @@ -0,0 +1,140 @@ +xen/pt: consolidate PM capability emu_mask + +There's no point in xen_pt_pmcsr_reg_{read,write}() each ORing +PCI_PM_CTRL_STATE_MASK and PCI_PM_CTRL_NO_SOFT_RESET into a local +emu_mask variable - we can have the same effect by setting the field +descriptor's emu_mask member suitably right away. Note that +xen_pt_pmcsr_reg_write() is being retained in order to allow later +patches to be less intrusive. + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +Acked-by: Stefano Stabellini +Acked-by: Ian Campbell + +--- a/tools/qemu-xen-traditional/hw/pass-through.c ++++ b/tools/qemu-xen-traditional/hw/pass-through.c +@@ -179,9 +179,6 @@ static int pt_long_reg_read(struct pt_de + static int pt_bar_reg_read(struct pt_dev *ptdev, + struct pt_reg_tbl *cfg_entry, + uint32_t *value, uint32_t valid_mask); +-static int pt_pmcsr_reg_read(struct pt_dev *ptdev, +- struct pt_reg_tbl *cfg_entry, +- uint16_t *value, uint16_t valid_mask); + static int pt_byte_reg_write(struct pt_dev *ptdev, + struct pt_reg_tbl *cfg_entry, + uint8_t *value, uint8_t dev_value, uint8_t valid_mask); +@@ -494,7 +491,7 @@ static struct pt_reg_info_tbl pt_emu_reg + .u.w.write = pt_word_reg_write, + .u.w.restore = NULL, + }, +- /* PCI Power Management Control/Status reg */ ++ /* PCI Power Management Control/Status reg (->power_mgmt on) */ + { + .offset = PCI_PM_CTRL, + .size = 2, +@@ -502,7 +499,19 @@ static struct pt_reg_info_tbl pt_emu_reg + .ro_mask = 0xE1FC, + .emu_mask = 0x8100, + .init = pt_pmcsr_reg_init, +- .u.w.read = pt_pmcsr_reg_read, ++ .u.w.read = pt_word_reg_read, ++ .u.w.write = pt_pmcsr_reg_write, ++ .u.w.restore = pt_pmcsr_reg_restore, ++ }, ++ /* PCI Power Management Control/Status reg (->power_mgmt off) */ ++ { ++ .offset = PCI_PM_CTRL, ++ .size = 2, ++ .init_val = 0x0008, ++ .ro_mask = 0xE1FC, ++ .emu_mask = 0x810B, ++ .init = pt_pmcsr_reg_init, ++ .u.w.read = pt_word_reg_read, + .u.w.write = pt_pmcsr_reg_write, + .u.w.restore = pt_pmcsr_reg_restore, + }, +@@ -2919,6 +2928,7 @@ static uint32_t pt_pmc_reg_init(struct p + return reg->init_val; + } + ++/* this function will be called twice (for ->power_mgmt on and off cases) */ + /* initialize PCI Power Management Control/Status register */ + static uint32_t pt_pmcsr_reg_init(struct pt_dev *ptdev, + struct pt_reg_info_tbl *reg, uint32_t real_offset) +@@ -2926,8 +2936,23 @@ static uint32_t pt_pmcsr_reg_init(struct + PCIDevice *d = &ptdev->dev; + uint16_t cap_ver = 0; + +- if (!ptdev->power_mgmt) +- return reg->init_val; ++ switch (reg->emu_mask & (PCI_PM_CTRL_STATE_MASK | ++ PCI_PM_CTRL_NO_SOFT_RESET)) ++ { ++ case 0: ++ if (!ptdev->power_mgmt) ++ return PT_INVALID_REG; ++ break; ++ case PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET: ++ if (!ptdev->power_mgmt) ++ return reg->init_val; ++ return PT_INVALID_REG; ++ default: ++ /* exit I/O emulator */ ++ PT_LOG("Internal error: Invalid PMCSR emulation mask %04x." ++ " I/O emulator exit.\n", reg->emu_mask); ++ exit(1); ++ } + + /* check PCI Power Management support version */ + cap_ver = ptdev->pm_state->pmc_field & PCI_PM_CAP_VER_MASK; +@@ -3417,24 +3442,6 @@ static int pt_bar_reg_read(struct pt_dev + } + + +-/* read Power Management Control/Status register */ +-static int pt_pmcsr_reg_read(struct pt_dev *ptdev, +- struct pt_reg_tbl *cfg_entry, +- uint16_t *value, uint16_t valid_mask) +-{ +- struct pt_reg_info_tbl *reg = cfg_entry->reg; +- uint16_t valid_emu_mask = reg->emu_mask; +- +- if (!ptdev->power_mgmt) +- valid_emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; +- +- valid_emu_mask = valid_emu_mask & valid_mask ; +- *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); +- +- return 0; +-} +- +- + /* write byte size emulate register */ + static int pt_byte_reg_write(struct pt_dev *ptdev, + struct pt_reg_tbl *cfg_entry, +@@ -3768,21 +3775,17 @@ static int pt_pmcsr_reg_write(struct pt_ + { + struct pt_reg_info_tbl *reg = cfg_entry->reg; + PCIDevice *d = &ptdev->dev; +- uint16_t emu_mask = reg->emu_mask; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + struct pt_pm_info *pm_state = ptdev->pm_state; + uint16_t read_val = 0; + +- if (!ptdev->power_mgmt) +- emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; +- + /* modify emulate register */ +- writable_mask = emu_mask & ~reg->ro_mask & valid_mask; ++ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~emu_mask & valid_mask; ++ throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + if (!ptdev->power_mgmt) diff --git a/xsa131-qemut-3.patch b/xsa131-qemut-3.patch new file mode 100644 index 0000000..c5383c3 --- /dev/null +++ b/xsa131-qemut-3.patch @@ -0,0 +1,22 @@ +xen/pt: correctly handle PM status bit + +xen_pt_pmcsr_reg_write() needs an adjustment to deal with the RW1C +nature of the not passed through bit 15 (PCI_PM_CTRL_PME_STATUS). + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- a/tools/qemu-xen-traditional/hw/pass-through.c ++++ b/tools/qemu-xen-traditional/hw/pass-through.c +@@ -3786,7 +3786,8 @@ static int pt_pmcsr_reg_write(struct pt_ + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; +- *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); ++ *value = PT_MERGE_VALUE(*value, dev_value & ~PCI_PM_CTRL_PME_STATUS, ++ throughable_mask); + + if (!ptdev->power_mgmt) + return 0; diff --git a/xsa131-qemut-4.patch b/xsa131-qemut-4.patch new file mode 100644 index 0000000..1933505 --- /dev/null +++ b/xsa131-qemut-4.patch @@ -0,0 +1,266 @@ +xen/pt: split out calculation of throughable mask in PCI config space handling + +This is just to avoid having to adjust that calculation later in +multiple places. + +Note that including ->ro_mask in get_throughable_mask()'s calculation +is only an apparent (i.e. benign) behavioral change: For r/o fields it +doesn't matter > whether they get passed through - either the same flag +is also set in emu_mask (then there's no change at all) or the field is +r/o in hardware (and hence a write won't change it anyway). + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +Acked-by: Stefano Stabellini +Reviewed-by: Anthony PERARD + +--- a/tools/qemu-xen-traditional/hw/pass-through.c ++++ b/tools/qemu-xen-traditional/hw/pass-through.c +@@ -3442,6 +3442,15 @@ static int pt_bar_reg_read(struct pt_dev + } + + ++static uint32_t get_throughable_mask(const struct pt_dev *ptdev, ++ const struct pt_reg_info_tbl *reg, ++ uint32_t valid_mask) ++{ ++ uint32_t throughable_mask = ~(reg->emu_mask | reg->ro_mask); ++ ++ return throughable_mask & valid_mask; ++} ++ + /* write byte size emulate register */ + static int pt_byte_reg_write(struct pt_dev *ptdev, + struct pt_reg_tbl *cfg_entry, +@@ -3449,14 +3458,13 @@ static int pt_byte_reg_write(struct pt_d + { + struct pt_reg_info_tbl *reg = cfg_entry->reg; + uint8_t writable_mask = 0; +- uint8_t throughable_mask = 0; ++ uint8_t throughable_mask = get_throughable_mask(ptdev, reg, valid_mask); + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + return 0; +@@ -3469,14 +3477,13 @@ static int pt_word_reg_write(struct pt_d + { + struct pt_reg_info_tbl *reg = cfg_entry->reg; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; ++ uint16_t throughable_mask = get_throughable_mask(ptdev, reg, valid_mask); + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + return 0; +@@ -3489,14 +3496,13 @@ static int pt_long_reg_write(struct pt_d + { + struct pt_reg_info_tbl *reg = cfg_entry->reg; + uint32_t writable_mask = 0; +- uint32_t throughable_mask = 0; ++ uint32_t throughable_mask = get_throughable_mask(ptdev, reg, valid_mask); + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + return 0; +@@ -3509,7 +3515,7 @@ static int pt_cmd_reg_write(struct pt_de + { + struct pt_reg_info_tbl *reg = cfg_entry->reg; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; ++ uint16_t throughable_mask = get_throughable_mask(ptdev, reg, valid_mask); + uint16_t wr_value = *value; + + /* modify emulate register */ +@@ -3517,8 +3523,6 @@ static int pt_cmd_reg_write(struct pt_de + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; +- + if (*value & PCI_COMMAND_DISABLE_INTx) + { + if (ptdev->msi_trans_en) +@@ -3564,7 +3568,6 @@ static int pt_bar_reg_write(struct pt_de + PCIDevice *d = (PCIDevice *)&ptdev->dev; + PCIIORegion *r; + uint32_t writable_mask = 0; +- uint32_t throughable_mask = 0; + uint32_t bar_emu_mask = 0; + uint32_t bar_ro_mask = 0; + uint32_t new_addr, last_addr; +@@ -3691,8 +3694,7 @@ static int pt_bar_reg_write(struct pt_de + + exit: + /* create value for writing to I/O device register */ +- throughable_mask = ~bar_emu_mask & valid_mask; +- *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); ++ *value = PT_MERGE_VALUE(*value, dev_value, 0); + + /* After BAR reg update, we need to remap BAR*/ + reg_grp_entry = pt_find_reg_grp(ptdev, PCI_COMMAND); +@@ -3719,9 +3721,8 @@ static int pt_exp_rom_bar_reg_write(stru + PCIDevice *d = (PCIDevice *)&ptdev->dev; + PCIIORegion *r; + uint32_t writable_mask = 0; +- uint32_t throughable_mask = 0; ++ uint32_t throughable_mask = get_throughable_mask(ptdev, reg, valid_mask); + uint32_t r_size = 0; +- uint32_t bar_emu_mask = 0; + uint32_t bar_ro_mask = 0; + + r = &d->io_regions[PCI_ROM_SLOT]; +@@ -3731,7 +3732,6 @@ static int pt_exp_rom_bar_reg_write(stru + PT_GET_EMUL_SIZE(base->bar_flag, r_size); + + /* set emulate mask and read-only mask */ +- bar_emu_mask = reg->emu_mask; + bar_ro_mask = (reg->ro_mask | (r_size - 1)) & ~PCI_ROM_ADDRESS_ENABLE; + + /* modify emulate register */ +@@ -3751,7 +3751,6 @@ static int pt_exp_rom_bar_reg_write(stru + r->addr = cfg_entry->data; + + /* create value for writing to I/O device register */ +- throughable_mask = ~bar_emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* After BAR reg update, we need to remap BAR*/ +@@ -3776,7 +3775,7 @@ static int pt_pmcsr_reg_write(struct pt_ + struct pt_reg_info_tbl *reg = cfg_entry->reg; + PCIDevice *d = &ptdev->dev; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; ++ uint16_t throughable_mask = get_throughable_mask(ptdev, reg, valid_mask); + struct pt_pm_info *pm_state = ptdev->pm_state; + uint16_t read_val = 0; + +@@ -3785,7 +3784,6 @@ static int pt_pmcsr_reg_write(struct pt_ + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value & ~PCI_PM_CTRL_PME_STATUS, + throughable_mask); + +@@ -3894,7 +3892,7 @@ static int pt_msgctrl_reg_write(struct p + { + struct pt_reg_info_tbl *reg = cfg_entry->reg; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; ++ uint16_t throughable_mask = get_throughable_mask(ptdev, reg, valid_mask); + uint16_t old_ctrl = cfg_entry->data; + PCIDevice *pd = (PCIDevice *)ptdev; + uint16_t val; +@@ -3906,8 +3904,10 @@ static int pt_msgctrl_reg_write(struct p + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + /* also emulate MSI_ENABLE bit for MSI-INTx translation */ +- if (ptdev->msi_trans_en) ++ if (ptdev->msi_trans_en) { + writable_mask |= PCI_MSI_FLAGS_ENABLE & valid_mask; ++ throughable_mask &= ~PCI_MSI_FLAGS_ENABLE; ++ } + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + /* update the msi_info too */ + ptdev->msi->flags |= cfg_entry->data & +@@ -3915,10 +3915,6 @@ static int pt_msgctrl_reg_write(struct p + + /* create value for writing to I/O device register */ + val = *value; +- throughable_mask = ~reg->emu_mask & valid_mask; +- /* don't pass through MSI_ENABLE bit for MSI-INTx translation */ +- if (ptdev->msi_trans_en) +- throughable_mask &= ~PCI_MSI_FLAGS_ENABLE; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* update MSI */ +@@ -3972,7 +3968,6 @@ static int pt_msgaddr32_reg_write(struct + { + struct pt_reg_info_tbl *reg = cfg_entry->reg; + uint32_t writable_mask = 0; +- uint32_t throughable_mask = 0; + uint32_t old_addr = cfg_entry->data; + + /* modify emulate register */ +@@ -3982,8 +3977,7 @@ static int pt_msgaddr32_reg_write(struct + ptdev->msi->addr_lo = cfg_entry->data; + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; +- *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); ++ *value = PT_MERGE_VALUE(*value, dev_value, 0); + + /* update MSI */ + if (cfg_entry->data != old_addr) +@@ -4002,7 +3996,6 @@ static int pt_msgaddr64_reg_write(struct + { + struct pt_reg_info_tbl *reg = cfg_entry->reg; + uint32_t writable_mask = 0; +- uint32_t throughable_mask = 0; + uint32_t old_addr = cfg_entry->data; + + /* check whether the type is 64 bit or not */ +@@ -4020,8 +4013,7 @@ static int pt_msgaddr64_reg_write(struct + ptdev->msi->addr_hi = cfg_entry->data; + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; +- *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); ++ *value = PT_MERGE_VALUE(*value, dev_value, 0); + + /* update MSI */ + if (cfg_entry->data != old_addr) +@@ -4041,7 +4033,6 @@ static int pt_msgdata_reg_write(struct p + { + struct pt_reg_info_tbl *reg = cfg_entry->reg; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; + uint16_t old_data = cfg_entry->data; + uint32_t flags = ptdev->msi->flags; + uint32_t offset = reg->offset; +@@ -4062,8 +4053,7 @@ static int pt_msgdata_reg_write(struct p + ptdev->msi->data = cfg_entry->data; + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; +- *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); ++ *value = PT_MERGE_VALUE(*value, dev_value, 0); + + /* update MSI */ + if (cfg_entry->data != old_data) +@@ -4082,7 +4072,7 @@ static int pt_msixctrl_reg_write(struct + { + struct pt_reg_info_tbl *reg = cfg_entry->reg; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; ++ uint16_t throughable_mask = get_throughable_mask(ptdev, reg, valid_mask); + uint16_t old_ctrl = cfg_entry->data; + + /* modify emulate register */ +@@ -4090,7 +4080,6 @@ static int pt_msixctrl_reg_write(struct + cfg_entry->data = PT_MERGE_VALUE(*value, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); + + /* update MSI-X */ diff --git a/xsa131-qemut-5.patch b/xsa131-qemut-5.patch new file mode 100644 index 0000000..6dfcefe --- /dev/null +++ b/xsa131-qemut-5.patch @@ -0,0 +1,22 @@ +xen/pt: mark all PCIe capability bits read-only + +xen_pt_emu_reg_pcie[]'s PCI_EXP_DEVCAP needs to cover all bits as read- +only to avoid unintended write-back (just a precaution, the field ought +to be read-only in hardware). + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- a/tools/qemu-xen-traditional/hw/pass-through.c ++++ b/tools/qemu-xen-traditional/hw/pass-through.c +@@ -577,7 +577,7 @@ static struct pt_reg_info_tbl pt_emu_reg + .offset = PCI_EXP_DEVCAP, + .size = 4, + .init_val = 0x00000000, +- .ro_mask = 0x1FFCFFFF, ++ .ro_mask = 0xFFFFFFFF, + .emu_mask = 0x10000000, + .init = pt_common_reg_init, + .u.dw.read = pt_long_reg_read, diff --git a/xsa131-qemut-6.patch b/xsa131-qemut-6.patch new file mode 100644 index 0000000..45bdc87 --- /dev/null +++ b/xsa131-qemut-6.patch @@ -0,0 +1,85 @@ +xen/pt: mark reserved bits in PCI config space fields + +The adjustments are solely to make the subsequent patches work right +(and hence make the patch set consistent), namely if permissive mode +(introduced by the last patch) gets used (as both reserved registers +and reserved fields must be similarly protected from guest access in +default mode, but the guest should be allowed access to them in +permissive mode). + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich + +--- a/tools/qemu-xen-traditional/hw/pass-through.c ++++ b/tools/qemu-xen-traditional/hw/pass-through.c +@@ -283,7 +283,7 @@ static struct pt_reg_info_tbl pt_emu_reg + .offset = PCI_COMMAND, + .size = 2, + .init_val = 0x0000, +- .ro_mask = 0xF880, ++ .res_mask = 0xF880, + .emu_mask = 0x0743, + .init = pt_common_reg_init, + .u.w.read = pt_word_reg_read, +@@ -310,7 +310,8 @@ static struct pt_reg_info_tbl pt_emu_reg + .offset = PCI_STATUS, + .size = 2, + .init_val = 0x0000, +- .ro_mask = 0x06FF, ++ .res_mask = 0x0007, ++ .ro_mask = 0x06F8, + .emu_mask = 0x0010, + .init = pt_status_reg_init, + .u.w.read = pt_word_reg_read, +@@ -496,7 +497,8 @@ static struct pt_reg_info_tbl pt_emu_reg + .offset = PCI_PM_CTRL, + .size = 2, + .init_val = 0x0008, +- .ro_mask = 0xE1FC, ++ .res_mask = 0x00F0, ++ .ro_mask = 0xE10C, + .emu_mask = 0x8100, + .init = pt_pmcsr_reg_init, + .u.w.read = pt_word_reg_read, +@@ -508,7 +510,8 @@ static struct pt_reg_info_tbl pt_emu_reg + .offset = PCI_PM_CTRL, + .size = 2, + .init_val = 0x0008, +- .ro_mask = 0xE1FC, ++ .res_mask = 0x00F0, ++ .ro_mask = 0xE10C, + .emu_mask = 0x810B, + .init = pt_pmcsr_reg_init, + .u.w.read = pt_word_reg_read, +@@ -656,7 +659,8 @@ static struct pt_reg_info_tbl pt_emu_reg + .offset = PCI_MSI_FLAGS, // 2 + .size = 2, + .init_val = 0x0000, +- .ro_mask = 0xFF8E, ++ .res_mask = 0xFE00, ++ .ro_mask = 0x018E, + .emu_mask = 0x017E, + .init = pt_msgctrl_reg_init, + .u.w.read = pt_word_reg_read, +@@ -779,7 +783,8 @@ static struct pt_reg_info_tbl pt_emu_reg + .offset = PCI_MSI_FLAGS, // 2 + .size = 2, + .init_val = 0x0000, +- .ro_mask = 0x3FFF, ++ .res_mask = 0x3800, ++ .ro_mask = 0x07FF, + .emu_mask = 0x0000, + .init = pt_msixctrl_reg_init, + .u.w.read = pt_word_reg_read, +--- a/tools/qemu-xen-traditional/hw/pass-through.h ++++ b/tools/qemu-xen-traditional/hw/pass-through.h +@@ -376,6 +376,8 @@ struct pt_reg_info_tbl { + uint32_t size; + /* reg initial value */ + uint32_t init_val; ++ /* reg reserved field mask (ON:reserved, OFF:defined) */ ++ uint32_t res_mask; + /* reg read only field mask (ON:RO/ROS, OFF:other) */ + uint32_t ro_mask; + /* reg emulate field mask (ON:emu, OFF:passthrough) */ diff --git a/xsa131-qemut-7.patch b/xsa131-qemut-7.patch new file mode 100644 index 0000000..f887214 --- /dev/null +++ b/xsa131-qemut-7.patch @@ -0,0 +1,81 @@ +xen/pt: add a few PCI config space field descriptions + +Since the next patch will turn all not explicitly described fields +read-only by default, those fields that have guest writable bits need +to be given explicit descriptors. + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich + +--- a/tools/qemu-xen-traditional/hw/pass-through.c ++++ b/tools/qemu-xen-traditional/hw/pass-through.c +@@ -538,6 +538,16 @@ static struct pt_reg_info_tbl pt_emu_reg + .u.b.restore = NULL, + }, + { ++ .offset = PCI_VPD_ADDR, ++ .size = 2, ++ .ro_mask = 0x0003, ++ .emu_mask = 0x0003, ++ .init = pt_common_reg_init, ++ .u.w.read = pt_word_reg_read, ++ .u.w.write = pt_word_reg_write, ++ .u.w.restore = pt_word_reg_restore, ++ }, ++ { + .size = 0, + }, + }; +@@ -599,6 +609,17 @@ static struct pt_reg_info_tbl pt_emu_reg + .u.w.write = pt_word_reg_write, + .u.w.restore = pt_word_reg_restore, + }, ++ /* Device Status reg */ ++ { ++ .offset = PCI_EXP_DEVSTA, ++ .size = 2, ++ .res_mask = 0xFFC0, ++ .ro_mask = 0x0030, ++ .init = pt_common_reg_init, ++ .u.w.read = pt_word_reg_read, ++ .u.w.write = pt_word_reg_write, ++ .u.w.restore = pt_word_reg_restore, ++ }, + /* Link Control reg */ + { + .offset = PCI_EXP_LNKCTL, +@@ -611,6 +632,16 @@ static struct pt_reg_info_tbl pt_emu_reg + .u.w.write = pt_word_reg_write, + .u.w.restore = pt_word_reg_restore, + }, ++ /* Link Status reg */ ++ { ++ .offset = PCI_EXP_LNKSTA, ++ .size = 2, ++ .ro_mask = 0x3FFF, ++ .init = pt_common_reg_init, ++ .u.w.read = pt_word_reg_read, ++ .u.w.write = pt_word_reg_write, ++ .u.w.restore = pt_word_reg_restore, ++ }, + /* Device Control 2 reg */ + { + .offset = 0x28, +--- a/tools/qemu-xen-traditional/hw/pass-through.h ++++ b/tools/qemu-xen-traditional/hw/pass-through.h +@@ -105,6 +105,14 @@ + #define PCI_EXP_TYPE_ROOT_EC 0xa + #endif + ++#ifndef PCI_VPD_ADDR ++/* Vital Product Data */ ++#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ ++#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ ++#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ ++#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ ++#endif ++ + #ifndef PCI_ERR_UNCOR_MASK + /* Uncorrectable Error Mask */ + #define PCI_ERR_UNCOR_MASK 8 diff --git a/xsa131-qemut-8.patch b/xsa131-qemut-8.patch new file mode 100644 index 0000000..d8d90b6 --- /dev/null +++ b/xsa131-qemut-8.patch @@ -0,0 +1,139 @@ +xen/pt: unknown PCI config space fields should be read-only + +... by default. Add a per-device "permissive" mode similar to pciback's +to allow restoring previous behavior (and hence break security again, +i.e. should be used only for trusted guests). + +This is part of XSA-131. + +Signed-off-by: Jan Beulich +Acked-by: Stefano Stabellini +Reviewed-by: Anthony PERARD ) + +--- a/tools/qemu-xen-traditional/hw/pass-through.c ++++ b/tools/qemu-xen-traditional/hw/pass-through.c +@@ -1613,10 +1613,10 @@ static void pt_pci_write_config(PCIDevic + uint32_t find_addr = address; + uint32_t real_offset = 0; + uint32_t valid_mask = 0xFFFFFFFF; +- uint32_t read_val = 0, wb_mask; ++ uint32_t read_val = 0, wb_mask, wp_mask; + uint8_t *ptr_val = NULL; + int emul_len = 0; +- int index = 0; ++ int index = 0, wp_flag = 0; + int ret = 0; + + #ifdef PT_DEBUG_PCI_CONFIG_ACCESS +@@ -1695,7 +1695,14 @@ static void pt_pci_write_config(PCIDevic + + /* pass directly to libpci for passthrough type register group */ + if (reg_grp_entry == NULL) ++ { ++ if (!assigned_device->permissive) ++ { ++ wb_mask = 0; ++ wp_flag = 1; ++ } + goto out; ++ } + + /* adjust the read and write value to appropriate CFC-CFF window */ + read_val <<= ((address & 3) << 3); +@@ -1714,11 +1721,12 @@ static void pt_pci_write_config(PCIDevic + valid_mask = (0xFFFFFFFF >> ((4 - emul_len) << 3)); + valid_mask <<= ((find_addr - real_offset) << 3); + ptr_val = ((uint8_t *)&val + (real_offset & 3)); +- if (reg->emu_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) { +- wb_mask &= ~((reg->emu_mask +- >> ((find_addr - real_offset) << 3)) ++ wp_mask = reg->emu_mask | reg->ro_mask; ++ if (!assigned_device->permissive) ++ wp_mask |= reg->res_mask; ++ if (wp_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) ++ wb_mask &= ~((wp_mask >> ((find_addr - real_offset) << 3)) + << ((len - emul_len) << 3)); +- } + + /* do emulation depend on register size */ + switch (reg->size) { +@@ -1767,6 +1775,16 @@ static void pt_pci_write_config(PCIDevic + /* nothing to do with passthrough type register, + * continue to find next byte + */ ++ if (!assigned_device->permissive) ++ { ++ wb_mask &= ~(0xff << ((len - emul_len) << 3)); ++ /* Unused BARs will make it here, but we don't want to issue ++ * warnings for writes to them (bogus writes get dealt with ++ * above). ++ */ ++ if (index < 0) ++ wp_flag = 1; ++ } + emul_len--; + find_addr++; + } +@@ -1776,6 +1794,15 @@ static void pt_pci_write_config(PCIDevic + val >>= ((address & 3) << 3); + + out: ++ if (wp_flag && !assigned_device->permissive_warned) ++ { ++ assigned_device->permissive_warned = 1; ++ PT_LOG("Write-back to unknown field 0x%02x (partially) inhibited (0x%0*x)\n", ++ addr, len * 2, wb_mask); ++ PT_LOG("If device %02x:%02x.%o doesn't work, try enabling permissive\n", ++ pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); ++ PT_LOG("mode (unsafe) and if it helps report the problem to xen-devel\n"); ++ } + for (index = 0; wb_mask; index += len) { + /* unknown regs are passed through */ + while (!(wb_mask & 0xff)) { +@@ -3484,6 +3511,9 @@ static uint32_t get_throughable_mask(con + { + uint32_t throughable_mask = ~(reg->emu_mask | reg->ro_mask); + ++ if (!ptdev->permissive) ++ throughable_mask &= ~reg->res_mask; ++ + return throughable_mask & valid_mask; + } + +@@ -4322,7 +4352,7 @@ static struct pt_dev * register_real_dev + uint8_t e_device, e_intx; + uint16_t cmd = 0; + char *key, *val; +- int msi_translate, power_mgmt; ++ int msi_translate, power_mgmt, permissive = 0; + + PT_LOG("Assigning real physical device %02x:%02x.%x ...\n", + r_bus, r_dev, r_func); +@@ -4366,6 +4396,8 @@ static struct pt_dev * register_real_dev + else + PT_LOG("Error: unrecognized value for msitranslate=\n"); + } ++ else if (strcmp(key, "permissive") == 0) ++ permissive = 1; + else if (strcmp(key, "power_mgmt") == 0) + { + if (strcmp(val, "0") == 0) +@@ -4403,6 +4435,7 @@ static struct pt_dev * register_real_dev + assigned_device->msi_trans_cap = msi_translate; + assigned_device->power_mgmt = power_mgmt; + assigned_device->is_virtfn = pt_dev_is_virtfn(pci_dev); ++ assigned_device->permissive = permissive; + pt_iomul_init(assigned_device, r_bus, r_dev, r_func); + + /* Initialize virtualized PCI configuration (Extended 256 Bytes) */ +--- a/tools/qemu-xen-traditional/hw/pass-through.h ++++ b/tools/qemu-xen-traditional/hw/pass-through.h +@@ -242,6 +242,8 @@ struct pt_dev { + unsigned power_mgmt:1; + struct pt_pm_info *pm_state; /* PM virtualization */ + unsigned is_virtfn:1; ++ unsigned permissive:1; ++ unsigned permissive_warned:1; + + /* io port multiplexing */ + #define PCI_IOMUL_INVALID_FD (-1) diff --git a/xsa131-qemuu-1.patch b/xsa131-qemuu-1.patch new file mode 100644 index 0000000..65b1f68 --- /dev/null +++ b/xsa131-qemuu-1.patch @@ -0,0 +1,60 @@ +xen/MSI: don't open-code pass-through of enable bit modifications + +Without this the actual XSA-131 fix would cause the enable bit to not +get set anymore (due to the write back getting suppressed there based +on the OR of emu_mask, ro_mask, and res_mask). + +Note that the fiddling with the enable bit shouldn't really be done by +qemu, but making this work right (via libxc and the hypervisor) will +require more extensive changes, which can be postponed until after the +security issue got addressed. + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +Acked-by: Stefano Stabellini + +--- a/tools/qemu-xen/hw/xen/xen_pt_config_init.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_config_init.c +@@ -1059,7 +1059,6 @@ static int xen_pt_msgctrl_reg_write(XenP + XenPTMSI *msi = s->msi; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; +- uint16_t raw_val; + + /* Currently no support for multi-vector */ + if (*val & PCI_MSI_FLAGS_QSIZE) { +@@ -1072,12 +1071,11 @@ static int xen_pt_msgctrl_reg_write(XenP + msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE; + + /* create value for writing to I/O device register */ +- raw_val = *val; + throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + /* update MSI */ +- if (raw_val & PCI_MSI_FLAGS_ENABLE) { ++ if (*val & PCI_MSI_FLAGS_ENABLE) { + /* setup MSI pirq for the first time */ + if (!msi->initialized) { + /* Init physical one */ +@@ -1105,10 +1103,6 @@ static int xen_pt_msgctrl_reg_write(XenP + xen_pt_msi_disable(s); + } + +- /* pass through MSI_ENABLE bit */ +- *val &= ~PCI_MSI_FLAGS_ENABLE; +- *val |= raw_val & PCI_MSI_FLAGS_ENABLE; +- + return 0; + } + +@@ -1311,7 +1305,7 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xFF8E, +- .emu_mask = 0x017F, ++ .emu_mask = 0x017E, + .init = xen_pt_msgctrl_reg_init, + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_msgctrl_reg_write, diff --git a/xsa131-qemuu-2.patch b/xsa131-qemuu-2.patch new file mode 100644 index 0000000..261a55b --- /dev/null +++ b/xsa131-qemuu-2.patch @@ -0,0 +1,70 @@ +xen/pt: consolidate PM capability emu_mask + +There's no point in xen_pt_pmcsr_reg_{read,write}() each ORing +PCI_PM_CTRL_STATE_MASK and PCI_PM_CTRL_NO_SOFT_RESET into a local +emu_mask variable - we can have the same effect by setting the field +descriptor's emu_mask member suitably right away. Note that +xen_pt_pmcsr_reg_write() is being retained in order to allow later +patches to be less intrusive. + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +Acked-by: Stefano Stabellini +Acked-by: Ian Campbell + +--- a/tools/qemu-xen/hw/xen/xen_pt_config_init.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_config_init.c +@@ -935,38 +935,21 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[ + * Power Management Capability + */ + +-/* read Power Management Control/Status register */ +-static int xen_pt_pmcsr_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, +- uint16_t *value, uint16_t valid_mask) +-{ +- XenPTRegInfo *reg = cfg_entry->reg; +- uint16_t valid_emu_mask = reg->emu_mask; +- +- valid_emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; +- +- valid_emu_mask = valid_emu_mask & valid_mask; +- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); +- +- return 0; +-} + /* write Power Management Control/Status register */ + static int xen_pt_pmcsr_reg_write(XenPCIPassthroughState *s, + XenPTReg *cfg_entry, uint16_t *val, + uint16_t dev_value, uint16_t valid_mask) + { + XenPTRegInfo *reg = cfg_entry->reg; +- uint16_t emu_mask = reg->emu_mask; + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + +- emu_mask |= PCI_PM_CTRL_STATE_MASK | PCI_PM_CTRL_NO_SOFT_RESET; +- + /* modify emulate register */ +- writable_mask = emu_mask & ~reg->ro_mask & valid_mask; ++ writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~emu_mask & valid_mask; ++ throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +@@ -1002,9 +985,9 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] + .size = 2, + .init_val = 0x0008, + .ro_mask = 0xE1FC, +- .emu_mask = 0x8100, ++ .emu_mask = 0x810B, + .init = xen_pt_common_reg_init, +- .u.w.read = xen_pt_pmcsr_reg_read, ++ .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_pmcsr_reg_write, + }, + { diff --git a/xsa131-qemuu-3.patch b/xsa131-qemuu-3.patch new file mode 100644 index 0000000..697ee87 --- /dev/null +++ b/xsa131-qemuu-3.patch @@ -0,0 +1,22 @@ +xen/pt: correctly handle PM status bit + +xen_pt_pmcsr_reg_write() needs an adjustment to deal with the RW1C +nature of the not passed through bit 15 (PCI_PM_CTRL_PME_STATUS). + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- a/tools/qemu-xen/hw/xen/xen_pt_config_init.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_config_init.c +@@ -950,7 +950,8 @@ static int xen_pt_pmcsr_reg_write(XenPCI + + /* create value for writing to I/O device register */ + throughable_mask = ~reg->emu_mask & valid_mask; +- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); ++ *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS, ++ throughable_mask); + + return 0; + } diff --git a/xsa131-qemuu-4.patch b/xsa131-qemuu-4.patch new file mode 100644 index 0000000..ce64487 --- /dev/null +++ b/xsa131-qemuu-4.patch @@ -0,0 +1,248 @@ +xen/pt: split out calculation of throughable mask in PCI config space handling + +This is just to avoid having to adjust that calculation later in +multiple places. + +Note that including ->ro_mask in get_throughable_mask()'s calculation +is only an apparent (i.e. benign) behavioral change: For r/o fields it +doesn't matter > whether they get passed through - either the same flag +is also set in emu_mask (then there's no change at all) or the field is +r/o in hardware (and hence a write won't change it anyway). + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +Acked-by: Stefano Stabellini +Reviewed-by: Anthony PERARD + +--- a/tools/qemu-xen/hw/xen/xen_pt_config_init.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_config_init.c +@@ -95,6 +95,14 @@ XenPTReg *xen_pt_find_reg(XenPTRegGroup + return NULL; + } + ++static uint32_t get_throughable_mask(const XenPCIPassthroughState *s, ++ const XenPTRegInfo *reg, ++ uint32_t valid_mask) ++{ ++ uint32_t throughable_mask = ~(reg->emu_mask | reg->ro_mask); ++ ++ return throughable_mask & valid_mask; ++} + + /**************** + * general register functions +@@ -157,14 +165,13 @@ static int xen_pt_byte_reg_write(XenPCIP + { + XenPTRegInfo *reg = cfg_entry->reg; + uint8_t writable_mask = 0; +- uint8_t throughable_mask = 0; ++ uint8_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +@@ -175,14 +182,13 @@ static int xen_pt_word_reg_write(XenPCIP + { + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; ++ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +@@ -193,14 +199,13 @@ static int xen_pt_long_reg_write(XenPCIP + { + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t writable_mask = 0; +- uint32_t throughable_mask = 0; ++ uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +@@ -292,15 +297,13 @@ static int xen_pt_cmd_reg_write(XenPCIPa + { + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; ++ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + + /* modify emulate register */ + writable_mask = ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; +- + if (*val & PCI_COMMAND_INTX_DISABLE) { + throughable_mask |= PCI_COMMAND_INTX_DISABLE; + } else { +@@ -456,7 +459,6 @@ static int xen_pt_bar_reg_write(XenPCIPa + PCIDevice *d = &s->dev; + const PCIIORegion *r; + uint32_t writable_mask = 0; +- uint32_t throughable_mask = 0; + uint32_t bar_emu_mask = 0; + uint32_t bar_ro_mask = 0; + uint32_t r_size = 0; +@@ -513,8 +515,7 @@ static int xen_pt_bar_reg_write(XenPCIPa + } + + /* create value for writing to I/O device register */ +- throughable_mask = ~bar_emu_mask & valid_mask; +- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); ++ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); + + return 0; + } +@@ -528,9 +529,8 @@ static int xen_pt_exp_rom_bar_reg_write( + XenPTRegion *base = NULL; + PCIDevice *d = (PCIDevice *)&s->dev; + uint32_t writable_mask = 0; +- uint32_t throughable_mask = 0; ++ uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + pcibus_t r_size = 0; +- uint32_t bar_emu_mask = 0; + uint32_t bar_ro_mask = 0; + + r_size = d->io_regions[PCI_ROM_SLOT].size; +@@ -539,7 +539,6 @@ static int xen_pt_exp_rom_bar_reg_write( + r_size = xen_pt_get_emul_size(base->bar_flag, r_size); + + /* set emulate mask and read-only mask */ +- bar_emu_mask = reg->emu_mask; + bar_ro_mask = (reg->ro_mask | (r_size - 1)) & ~PCI_ROM_ADDRESS_ENABLE; + + /* modify emulate register */ +@@ -547,7 +546,6 @@ static int xen_pt_exp_rom_bar_reg_write( + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~bar_emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + return 0; +@@ -942,14 +940,13 @@ static int xen_pt_pmcsr_reg_write(XenPCI + { + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; ++ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + + /* modify emulate register */ + writable_mask = reg->emu_mask & ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value & ~PCI_PM_CTRL_PME_STATUS, + throughable_mask); + +@@ -1042,7 +1039,7 @@ static int xen_pt_msgctrl_reg_write(XenP + XenPTRegInfo *reg = cfg_entry->reg; + XenPTMSI *msi = s->msi; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; ++ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + + /* Currently no support for multi-vector */ + if (*val & PCI_MSI_FLAGS_QSIZE) { +@@ -1055,7 +1052,6 @@ static int xen_pt_msgctrl_reg_write(XenP + msi->flags |= cfg_entry->data & ~PCI_MSI_FLAGS_ENABLE; + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + /* update MSI */ +@@ -1171,7 +1167,6 @@ static int xen_pt_msgaddr32_reg_write(Xe + { + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t writable_mask = 0; +- uint32_t throughable_mask = 0; + uint32_t old_addr = cfg_entry->data; + + /* modify emulate register */ +@@ -1180,8 +1175,7 @@ static int xen_pt_msgaddr32_reg_write(Xe + s->msi->addr_lo = cfg_entry->data; + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; +- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); ++ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); + + /* update MSI */ + if (cfg_entry->data != old_addr) { +@@ -1199,7 +1193,6 @@ static int xen_pt_msgaddr64_reg_write(Xe + { + XenPTRegInfo *reg = cfg_entry->reg; + uint32_t writable_mask = 0; +- uint32_t throughable_mask = 0; + uint32_t old_addr = cfg_entry->data; + + /* check whether the type is 64 bit or not */ +@@ -1216,8 +1209,7 @@ static int xen_pt_msgaddr64_reg_write(Xe + s->msi->addr_hi = cfg_entry->data; + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; +- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); ++ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); + + /* update MSI */ + if (cfg_entry->data != old_addr) { +@@ -1239,7 +1231,6 @@ static int xen_pt_msgdata_reg_write(XenP + XenPTRegInfo *reg = cfg_entry->reg; + XenPTMSI *msi = s->msi; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; + uint16_t old_data = cfg_entry->data; + uint32_t offset = reg->offset; + +@@ -1257,8 +1248,7 @@ static int xen_pt_msgdata_reg_write(XenP + msi->data = cfg_entry->data; + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; +- *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); ++ *val = XEN_PT_MERGE_VALUE(*val, dev_value, 0); + + /* update MSI */ + if (cfg_entry->data != old_data) { +@@ -1420,7 +1410,7 @@ static int xen_pt_msixctrl_reg_write(Xen + { + XenPTRegInfo *reg = cfg_entry->reg; + uint16_t writable_mask = 0; +- uint16_t throughable_mask = 0; ++ uint16_t throughable_mask = get_throughable_mask(s, reg, valid_mask); + int debug_msix_enabled_old; + + /* modify emulate register */ +@@ -1428,7 +1418,6 @@ static int xen_pt_msixctrl_reg_write(Xen + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~reg->emu_mask & valid_mask; + *val = XEN_PT_MERGE_VALUE(*val, dev_value, throughable_mask); + + /* update MSI-X */ diff --git a/xsa131-qemuu-5.patch b/xsa131-qemuu-5.patch new file mode 100644 index 0000000..1082b13 --- /dev/null +++ b/xsa131-qemuu-5.patch @@ -0,0 +1,22 @@ +xen/pt: mark all PCIe capability bits read-only + +xen_pt_emu_reg_pcie[]'s PCI_EXP_DEVCAP needs to cover all bits as read- +only to avoid unintended write-back (just a precaution, the field ought +to be read-only in hardware). + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini + +--- a/tools/qemu-xen/hw/xen/xen_pt_config_init.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_config_init.c +@@ -873,7 +873,7 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[ + .offset = PCI_EXP_DEVCAP, + .size = 4, + .init_val = 0x00000000, +- .ro_mask = 0x1FFCFFFF, ++ .ro_mask = 0xFFFFFFFF, + .emu_mask = 0x10000000, + .init = xen_pt_common_reg_init, + .u.dw.read = xen_pt_long_reg_read, diff --git a/xsa131-qemuu-6.patch b/xsa131-qemuu-6.patch new file mode 100644 index 0000000..5c00a1f --- /dev/null +++ b/xsa131-qemuu-6.patch @@ -0,0 +1,75 @@ +xen/pt: mark reserved bits in PCI config space fields + +The adjustments are solely to make the subsequent patches work right +(and hence make the patch set consistent), namely if permissive mode +(introduced by the last patch) gets used (as both reserved registers +and reserved fields must be similarly protected from guest access in +default mode, but the guest should be allowed access to them in +permissive mode). + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich + +--- a/tools/qemu-xen/hw/xen/xen_pt.h ++++ b/tools/qemu-xen/hw/xen/xen_pt.h +@@ -101,6 +101,8 @@ struct XenPTRegInfo { + uint32_t offset; + uint32_t size; + uint32_t init_val; ++ /* reg reserved field mask (ON:reserved, OFF:defined) */ ++ uint32_t res_mask; + /* reg read only field mask (ON:RO/ROS, OFF:other) */ + uint32_t ro_mask; + /* reg emulate field mask (ON:emu, OFF:passthrough) */ +--- a/tools/qemu-xen/hw/xen/xen_pt_config_init.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_config_init.c +@@ -580,7 +580,7 @@ static XenPTRegInfo xen_pt_emu_reg_heade + .offset = PCI_COMMAND, + .size = 2, + .init_val = 0x0000, +- .ro_mask = 0xF880, ++ .res_mask = 0xF880, + .emu_mask = 0x0743, + .init = xen_pt_common_reg_init, + .u.w.read = xen_pt_word_reg_read, +@@ -605,7 +605,8 @@ static XenPTRegInfo xen_pt_emu_reg_heade + .offset = PCI_STATUS, + .size = 2, + .init_val = 0x0000, +- .ro_mask = 0x06FF, ++ .res_mask = 0x0007, ++ .ro_mask = 0x06F8, + .emu_mask = 0x0010, + .init = xen_pt_status_reg_init, + .u.w.read = xen_pt_word_reg_read, +@@ -982,7 +983,8 @@ static XenPTRegInfo xen_pt_emu_reg_pm[] + .offset = PCI_PM_CTRL, + .size = 2, + .init_val = 0x0008, +- .ro_mask = 0xE1FC, ++ .res_mask = 0x00F0, ++ .ro_mask = 0xE10C, + .emu_mask = 0x810B, + .init = xen_pt_common_reg_init, + .u.w.read = xen_pt_word_reg_read, +@@ -1278,7 +1280,8 @@ static XenPTRegInfo xen_pt_emu_reg_msi[] + .offset = PCI_MSI_FLAGS, + .size = 2, + .init_val = 0x0000, +- .ro_mask = 0xFF8E, ++ .res_mask = 0xFE00, ++ .ro_mask = 0x018E, + .emu_mask = 0x017E, + .init = xen_pt_msgctrl_reg_init, + .u.w.read = xen_pt_word_reg_read, +@@ -1456,7 +1459,8 @@ static XenPTRegInfo xen_pt_emu_reg_msix[ + .offset = PCI_MSI_FLAGS, + .size = 2, + .init_val = 0x0000, +- .ro_mask = 0x3FFF, ++ .res_mask = 0x3800, ++ .ro_mask = 0x07FF, + .emu_mask = 0x0000, + .init = xen_pt_msixctrl_reg_init, + .u.w.read = xen_pt_word_reg_read, diff --git a/xsa131-qemuu-7.patch b/xsa131-qemuu-7.patch new file mode 100644 index 0000000..61eab08 --- /dev/null +++ b/xsa131-qemuu-7.patch @@ -0,0 +1,70 @@ +xen/pt: add a few PCI config space field descriptions + +Since the next patch will turn all not explicitly described fields +read-only by default, those fields that have guest writable bits need +to be given explicit descriptors. + +This is a preparatory patch for XSA-131. + +Signed-off-by: Jan Beulich +--- +Notes: +- blindly allowing all VPD reads may still be a problem (out of bounds + addresses aren't allowed, but the spec doesn't say what the effect + would be) ==> also an issue in pciback? +- Vendor Specific cap regs aren't in the table (will become r/o by + default with this change) +- many PCIe cap regs aren't in the table (will again become r/o) +- same for PM cap regs at offsets 6 and 7 + +--- a/tools/qemu-xen/hw/xen/xen_pt_config_init.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_config_init.c +@@ -756,6 +756,15 @@ static XenPTRegInfo xen_pt_emu_reg_vpd[] + .u.b.write = xen_pt_byte_reg_write, + }, + { ++ .offset = PCI_VPD_ADDR, ++ .size = 2, ++ .ro_mask = 0x0003, ++ .emu_mask = 0x0003, ++ .init = xen_pt_common_reg_init, ++ .u.w.read = xen_pt_word_reg_read, ++ .u.w.write = xen_pt_word_reg_write, ++ }, ++ { + .size = 0, + }, + }; +@@ -891,6 +900,16 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[ + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, ++ /* Device Status reg */ ++ { ++ .offset = PCI_EXP_DEVSTA, ++ .size = 2, ++ .res_mask = 0xFFC0, ++ .ro_mask = 0x0030, ++ .init = xen_pt_common_reg_init, ++ .u.w.read = xen_pt_word_reg_read, ++ .u.w.write = xen_pt_word_reg_write, ++ }, + /* Link Control reg */ + { + .offset = PCI_EXP_LNKCTL, +@@ -902,6 +921,15 @@ static XenPTRegInfo xen_pt_emu_reg_pcie[ + .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_word_reg_write, + }, ++ /* Link Status reg */ ++ { ++ .offset = PCI_EXP_LNKSTA, ++ .size = 2, ++ .ro_mask = 0x3FFF, ++ .init = xen_pt_common_reg_init, ++ .u.w.read = xen_pt_word_reg_read, ++ .u.w.write = xen_pt_word_reg_write, ++ }, + /* Device Control 2 reg */ + { + .offset = 0x28, diff --git a/xsa131-qemuu-8.patch b/xsa131-qemuu-8.patch new file mode 100644 index 0000000..8b2a475 --- /dev/null +++ b/xsa131-qemuu-8.patch @@ -0,0 +1,122 @@ +xen/pt: unknown PCI config space fields should be read-only + +... by default. Add a per-device "permissive" mode similar to pciback's +to allow restoring previous behavior (and hence break security again, +i.e. should be used only for trusted guests). + +This is part of XSA-131. + +Signed-off-by: Jan Beulich +Acked-by: Stefano Stabellini +Reviewed-by: Anthony PERARD ) +--- +Notes: +- What purpose does xen_pt_header_type_reg_init() serve (with .emu_mask + being zero)? +- In the qemu-trad case no equivalent logic to that setting/using + direct_pci_{msi_translate,power_mgmt} is being added, as that logic + seems broken (setting globals from device 0 xenstore settings). + +--- a/tools/qemu-xen/hw/xen/xen_pt.c ++++ b/tools/qemu-xen/hw/xen/xen_pt.c +@@ -239,6 +239,7 @@ static void xen_pt_pci_write_config(PCID + XenPTReg *reg_entry = NULL; + uint32_t find_addr = addr; + XenPTRegInfo *reg = NULL; ++ bool wp_flag = false; + + if (xen_pt_pci_config_access_check(d, addr, len)) { + return; +@@ -280,6 +281,10 @@ static void xen_pt_pci_write_config(PCID + + /* pass directly to the real device for passthrough type register group */ + if (reg_grp_entry == NULL) { ++ if (!s->permissive) { ++ wb_mask = 0; ++ wp_flag = true; ++ } + goto out; + } + +@@ -300,12 +305,15 @@ static void xen_pt_pci_write_config(PCID + uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; + uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); + uint8_t *ptr_val = NULL; ++ uint32_t wp_mask = reg->emu_mask | reg->ro_mask; + + valid_mask <<= (find_addr - real_offset) << 3; + ptr_val = (uint8_t *)&val + (real_offset & 3); +- if (reg->emu_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) { +- wb_mask &= ~((reg->emu_mask +- >> ((find_addr - real_offset) << 3)) ++ if (!s->permissive) { ++ wp_mask |= reg->res_mask; ++ } ++ if (wp_mask == (0xFFFFFFFF >> ((4 - reg->size) << 3))) { ++ wb_mask &= ~((wp_mask >> ((find_addr - real_offset) << 3)) + << ((len - emul_len) << 3)); + } + +@@ -349,6 +357,16 @@ static void xen_pt_pci_write_config(PCID + } else { + /* nothing to do with passthrough type register, + * continue to find next byte */ ++ if (!s->permissive) { ++ wb_mask &= ~(0xff << ((len - emul_len) << 3)); ++ /* Unused BARs will make it here, but we don't want to issue ++ * warnings for writes to them (bogus writes get dealt with ++ * above). ++ */ ++ if (index < 0) { ++ wp_flag = true; ++ } ++ } + emul_len--; + find_addr++; + } +@@ -360,6 +378,13 @@ static void xen_pt_pci_write_config(PCID + memory_region_transaction_commit(); + + out: ++ if (wp_flag && !s->permissive_warned) { ++ s->permissive_warned = true; ++ xen_pt_log(d, "Write-back to unknown field 0x%02x (partially) inhibited (0x%0*x)\n", ++ addr, len * 2, wb_mask); ++ xen_pt_log(d, "If the device doesn't work, try enabling permissive mode\n"); ++ xen_pt_log(d, "(unsafe) and if it helps report the problem to xen-devel\n"); ++ } + for (index = 0; wb_mask; index += len) { + /* unknown regs are passed through */ + while (!(wb_mask & 0xff)) { +@@ -821,6 +846,7 @@ static void xen_pt_unregister_device(PCI + + static Property xen_pci_passthrough_properties[] = { + DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr), ++ DEFINE_PROP_BOOL("permissive", XenPCIPassthroughState, permissive, false), + DEFINE_PROP_END_OF_LIST(), + }; + +--- a/tools/qemu-xen/hw/xen/xen_pt.h ++++ b/tools/qemu-xen/hw/xen/xen_pt.h +@@ -197,6 +197,8 @@ struct XenPCIPassthroughState { + + PCIHostDeviceAddress hostaddr; + bool is_virtfn; ++ bool permissive; ++ bool permissive_warned; + XenHostPCIDevice real_device; + XenPTRegion bases[PCI_NUM_REGIONS]; /* Access regions */ + QLIST_HEAD(, XenPTRegGroup) reg_grps; +--- a/tools/qemu-xen/hw/xen/xen_pt_config_init.c ++++ b/tools/qemu-xen/hw/xen/xen_pt_config_init.c +@@ -101,6 +101,10 @@ static uint32_t get_throughable_mask(con + { + uint32_t throughable_mask = ~(reg->emu_mask | reg->ro_mask); + ++ if (!s->permissive) { ++ throughable_mask &= ~reg->res_mask; ++ } ++ + return throughable_mask & valid_mask; + } +