diff options
author | Baptiste Daroussin <bapt@FreeBSD.org> | 2015-06-11 18:26:45 +0000 |
---|---|---|
committer | Baptiste Daroussin <bapt@FreeBSD.org> | 2015-06-11 18:26:45 +0000 |
commit | 03196bcbc3ea90f9c6c7f64e7d8ae4f158bcd67f (patch) | |
tree | f6acc8779f63852063ca0c68590979f8a80f8f2d /sysutils/xen-tools | |
parent | fed01f227f8c879099624d5c534b64209a909d60 (diff) | |
download | ports-03196bcbc3ea90f9c6c7f64e7d8ae4f158bcd67f.tar.gz ports-03196bcbc3ea90f9c6c7f64e7d8ae4f158bcd67f.zip |
Notes
Diffstat (limited to 'sysutils/xen-tools')
33 files changed, 3138 insertions, 1 deletions
diff --git a/sysutils/xen-tools/Makefile b/sysutils/xen-tools/Makefile index 3ed994e08806..03a24ac23c63 100644 --- a/sysutils/xen-tools/Makefile +++ b/sysutils/xen-tools/Makefile @@ -3,7 +3,7 @@ PORTNAME= xen PKGNAMESUFFIX= -tools PORTVERSION= 4.5.0 -PORTREVISION= 5 +PORTREVISION= 6 CATEGORIES= sysutils emulators MASTER_SITES= http://bits.xensource.com/oss-xen/release/${PORTVERSION}/ \ http://code.coreboot.org/p/seabios/downloads/get/:seabios @@ -46,6 +46,10 @@ QEMU_ARGS= --disable-gtk \ --disable-tools \ --disable-curl \ --cxx=c++ + +EXTRA_PATCHES= ${FILESDIR}/xsa119-unstable.patch:-p1 \ + ${FILESDIR}/xsa125.patch:-p1 + CONFIGURE_ARGS+= --with-extra-qemuu-configure-args="${QEMU_ARGS}" SHEBANG_FILES= tools/misc/xencov_split \ tools/misc/xen-ringwatch @@ -71,6 +75,14 @@ post-patch: ${WRKSRC}/tools/libxl/libxl_dm.c \ ${WRKSRC}/tools/qemu-xen-traditional/i386-dm/helper2.c \ ${WRKSRC}/docs/man/* + @for p in ${FILESDIR}/*qemut*.patch; do \ + ${ECHO_CMD} "====> Applying $${p##*/}" ; \ + patch -s -p1 -i $${p} -d ${WRKSRC}/tools/qemu-xen-traditional ; \ + done + @for p in ${FILESDIR}/*qemuu*.patch; do \ + ${ECHO_CMD} "====> Applying $${p##*/}" ; \ + patch -s -p1 -i $${p} -d ${WRKSRC}/tools/qemu-xen ; \ + done post-install: ${MKDIR} ${STAGEDIR}/var/run/xen diff --git a/sysutils/xen-tools/files/xsa119-unstable.patch b/sysutils/xen-tools/files/xsa119-unstable.patch new file mode 100644 index 000000000000..f696eb5b6eb6 --- /dev/null +++ b/sysutils/xen-tools/files/xsa119-unstable.patch @@ -0,0 +1,99 @@ +From f433bfafbaf7d8a41c4c27aa3e8e78b1ab900b69 Mon Sep 17 00:00:00 2001 +From: Ian Campbell <ian.campbell@citrix.com> +Date: Fri, 20 Feb 2015 14:41:09 +0000 +Subject: [PATCH] tools: libxl: Explicitly disable graphics backends on qemu + cmdline + +By default qemu will try to create some sort of backend for the +emulated VGA device, either SDL or VNC. + +However when the user specifies sdl=0 and vnc=0 in their configuration +libxl was not explicitly disabling either backend, which could lead to +one unexpectedly running. + +If either sdl=1 or vnc=1 is configured then both before and after this +change only the backends which are explicitly enabled are configured, +i.e. this issue only occurs when all backends are supposed to have +been disabled. + +This affects qemu-xen and qemu-xen-traditional differently. + +If qemu-xen was compiled with SDL support then this would result in an +SDL window being opened if $DISPLAY is valid, or a failure to start +the guest if not. Passing "-display none" to qemu before any further +-sdl options disables this default behaviour and ensures that SDL is +only started if the libxl configuration demands it. + +If qemu-xen was compiled without SDL support then qemu would instead +start a VNC server listening on ::1 (IPv6 localhost) or 127.0.0.1 +(IPv4 localhost) with IPv6 preferred if available. Explicitly pass +"-vnc none" when vnc is not enabled in the libxl configuration to +remove this possibility. + +qemu-xen-traditional would never start a vnc backend unless asked. +However by default it will start an SDL backend, the way to disable +this is to pass a -vnc option. In other words passing "-vnc none" will +disable both vnc and sdl by default. sdl can then be reenabled if +configured by subsequent use of the -sdl option. + +Tested with both qemu-xen and qemu-xen-traditional built with SDL +support and: + xl cr # defaults + xl cr sdl=0 vnc=0 + xl cr sdl=1 vnc=0 + xl cr sdl=0 vnc=1 + xl cr sdl=0 vnc=0 vga=\"none\" + xl cr sdl=0 vnc=0 nographic=1 +with both valid and invalid $DISPLAY. + +This is XSA-119. + +Reported-by: Sander Eikelenboom <linux@eikelenboom.it> +Signed-off-by: Ian Campbell <ian.campbell@citrix.com> +Acked-by: Ian Jackson <ian.jackson@eu.citrix.com> +--- + tools/libxl/libxl_dm.c | 21 +++++++++++++++++++-- + 1 file changed, 19 insertions(+), 2 deletions(-) + +diff --git a/tools/libxl/libxl_dm.c b/tools/libxl/libxl_dm.c +index 8599a6a..3b918c6 100644 +--- a/tools/libxl/libxl_dm.c ++++ b/tools/libxl/libxl_dm.c +@@ -180,7 +180,14 @@ static char ** libxl__build_device_model_args_old(libxl__gc *gc, + if (libxl_defbool_val(vnc->findunused)) { + flexarray_append(dm_args, "-vncunused"); + } +- } ++ } else ++ /* ++ * VNC is not enabled by default by qemu-xen-traditional, ++ * however passing -vnc none causes SDL to not be ++ * (unexpectedly) enabled by default. This is overridden by ++ * explicitly passing -sdl below as required. ++ */ ++ flexarray_append_pair(dm_args, "-vnc", "none"); + + if (sdl) { + flexarray_append(dm_args, "-sdl"); +@@ -522,7 +529,17 @@ static char ** libxl__build_device_model_args_new(libxl__gc *gc, + } + + flexarray_append(dm_args, vncarg); +- } ++ } else ++ /* ++ * Ensure that by default no vnc server is created. ++ */ ++ flexarray_append_pair(dm_args, "-vnc", "none"); ++ ++ /* ++ * Ensure that by default no display backend is created. Further ++ * options given below might then enable more. ++ */ ++ flexarray_append_pair(dm_args, "-display", "none"); + + if (sdl) { + flexarray_append(dm_args, "-sdl"); +-- +2.1.4 + diff --git a/sysutils/xen-tools/files/xsa125.patch b/sysutils/xen-tools/files/xsa125.patch new file mode 100644 index 000000000000..ad5dbb31c204 --- /dev/null +++ b/sysutils/xen-tools/files/xsa125.patch @@ -0,0 +1,154 @@ +From 98670acc98cad5aee0e0714694a64d3b96675c36 Mon Sep 17 00:00:00 2001 +From: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> +Date: Wed, 19 Nov 2014 12:57:11 -0500 +Subject: [PATCH] Limit XEN_DOMCTL_memory_mapping hypercall to only process up + to 64 GFNs (or less) + +Said hypercall for large BARs can take quite a while. As such +we can require that the hypercall MUST break up the request +in smaller values. + +Another approach is to add preemption to it - whether we do the +preemption using hypercall_create_continuation or returning +EAGAIN to userspace (and have it re-invocate the call) - either +way the issue we cannot easily solve is that in 'map_mmio_regions' +if we encounter an error we MUST call 'unmap_mmio_regions' for the +whole BAR region. + +Since the preemption would re-use input fields such as nr_mfns, +first_gfn, first_mfn - we would lose the original values - +and only undo what was done in the current round (i.e. ignoring +anything that was done prior to earlier preemptions). + +Unless we re-used the return value as 'EAGAIN|nr_mfns_done<<10' but +that puts a limit (since the return value is a long) on the amount +of nr_mfns that can provided. + +This patch sidesteps this problem by: + - Setting an hard limit of nr_mfns having to be 64 or less. + - Toolstack adjusts correspondingly to the nr_mfn limit. + - If the there is an error when adding the toolstack will call the + remove operation to remove the whole region. + +The need to break this hypercall down is for large BARs can take +more than the guest (initial domain usually) time-slice. This has +the negative result in that the guest is locked out for a long +duration and is unable to act on any pending events. + +We also augment the code to return zero if nr_mfns instead +of trying to the hypercall. + +Suggested-by: Jan Beulich <jbeulich@suse.com> +Acked-by: Jan Beulich <jbeulich@suse.com> +Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> +Acked-by: Ian Campbell <ian.campbell@citrix.com> +--- +[v50: Simplify loop] +[v51: If max_batch_sz 1 (or less) we would return zero. Fix that] +[v52: Handle nr_mfns being zero] +[v53: Fix up return value] +--- + tools/libxc/xc_domain.c | 46 +++++++++++++++++++++++++++++++++++++++++---- + xen/common/domctl.c | 5 +++++ + xen/include/public/domctl.h | 1 + + 3 files changed, 48 insertions(+), 4 deletions(-) + +diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c +index 845d1d7..bba7672 100644 +--- a/tools/libxc/xc_domain.c ++++ b/tools/libxc/xc_domain.c +@@ -1988,6 +1988,8 @@ int xc_domain_memory_mapping( + { + DECLARE_DOMCTL; + xc_dominfo_t info; ++ int ret = 0, err; ++ unsigned long done = 0, nr, max_batch_sz; + + if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 || + info.domid != domid ) +@@ -1998,14 +2000,50 @@ int xc_domain_memory_mapping( + if ( !xc_core_arch_auto_translated_physmap(&info) ) + return 0; + ++ if ( !nr_mfns ) ++ return 0; ++ + domctl.cmd = XEN_DOMCTL_memory_mapping; + domctl.domain = domid; +- domctl.u.memory_mapping.first_gfn = first_gfn; +- domctl.u.memory_mapping.first_mfn = first_mfn; +- domctl.u.memory_mapping.nr_mfns = nr_mfns; + domctl.u.memory_mapping.add_mapping = add_mapping; ++ max_batch_sz = nr_mfns; ++ do ++ { ++ nr = min(nr_mfns - done, max_batch_sz); ++ domctl.u.memory_mapping.nr_mfns = nr; ++ domctl.u.memory_mapping.first_gfn = first_gfn + done; ++ domctl.u.memory_mapping.first_mfn = first_mfn + done; ++ err = do_domctl(xch, &domctl); ++ if ( err && errno == E2BIG ) ++ { ++ if ( max_batch_sz <= 1 ) ++ break; ++ max_batch_sz >>= 1; ++ continue; ++ } ++ /* Save the first error... */ ++ if ( !ret ) ++ ret = err; ++ /* .. and ignore the rest of them when removing. */ ++ if ( err && add_mapping != DPCI_REMOVE_MAPPING ) ++ break; + +- return do_domctl(xch, &domctl); ++ done += nr; ++ } while ( done < nr_mfns ); ++ ++ /* ++ * Undo what we have done unless unmapping, by unmapping the entire region. ++ * Errors here are ignored. ++ */ ++ if ( ret && add_mapping != DPCI_REMOVE_MAPPING ) ++ xc_domain_memory_mapping(xch, domid, first_gfn, first_mfn, nr_mfns, ++ DPCI_REMOVE_MAPPING); ++ ++ /* We might get E2BIG so many times that we never advance. */ ++ if ( !done && !ret ) ++ ret = -1; ++ ++ return ret; + } + + int xc_domain_ioport_mapping( +diff --git a/xen/common/domctl.c b/xen/common/domctl.c +index d396cc4..c2e60a7 100644 +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -1027,6 +1027,11 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) + (gfn + nr_mfns - 1) < gfn ) /* wrap? */ + break; + ++ ret = -E2BIG; ++ /* Must break hypercall up as this could take a while. */ ++ if ( nr_mfns > 64 ) ++ break; ++ + ret = -EPERM; + if ( !iomem_access_permitted(current->domain, mfn, mfn_end) || + !iomem_access_permitted(d, mfn, mfn_end) ) +diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h +index ca0e51e..0c9f474 100644 +--- a/xen/include/public/domctl.h ++++ b/xen/include/public/domctl.h +@@ -543,6 +543,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_bind_pt_irq_t); + + + /* Bind machine I/O address range -> HVM address range. */ ++/* If this returns -E2BIG lower nr_mfns value. */ + /* XEN_DOMCTL_memory_mapping */ + #define DPCI_ADD_MAPPING 1 + #define DPCI_REMOVE_MAPPING 0 +-- +2.1.0 + diff --git a/sysutils/xen-tools/files/xsa126-qemut.patch b/sysutils/xen-tools/files/xsa126-qemut.patch new file mode 100644 index 000000000000..599ff172e485 --- /dev/null +++ b/sysutils/xen-tools/files/xsa126-qemut.patch @@ -0,0 +1,151 @@ +xen: limit guest control of PCI command register + +Otherwise the guest can abuse that control to cause e.g. PCIe +Unsupported Request responses (by disabling memory and/or I/O decoding +and subsequently causing [CPU side] accesses to the respective address +ranges), which (depending on system configuration) may be fatal to the +host. + +This is CVE-2015-2756 / XSA-126. + +Signed-off-by: Jan Beulich <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> +Acked-by: Ian Campbell <ian.campbell@citrix.com> + +--- a/hw/pass-through.c ++++ b/hw/pass-through.c +@@ -172,9 +172,6 @@ static int pt_word_reg_read(struct pt_de + static int pt_long_reg_read(struct pt_dev *ptdev, + struct pt_reg_tbl *cfg_entry, + uint32_t *value, uint32_t valid_mask); +-static int pt_cmd_reg_read(struct pt_dev *ptdev, +- struct pt_reg_tbl *cfg_entry, +- uint16_t *value, uint16_t valid_mask); + static int pt_bar_reg_read(struct pt_dev *ptdev, + struct pt_reg_tbl *cfg_entry, + uint32_t *value, uint32_t valid_mask); +@@ -286,9 +283,9 @@ static struct pt_reg_info_tbl pt_emu_reg + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xF880, +- .emu_mask = 0x0740, ++ .emu_mask = 0x0743, + .init = pt_common_reg_init, +- .u.w.read = pt_cmd_reg_read, ++ .u.w.read = pt_word_reg_read, + .u.w.write = pt_cmd_reg_write, + .u.w.restore = pt_cmd_reg_restore, + }, +@@ -1905,7 +1902,7 @@ static int pt_dev_is_virtfn(struct pci_d + return rc; + } + +-static int pt_register_regions(struct pt_dev *assigned_device) ++static int pt_register_regions(struct pt_dev *assigned_device, uint16_t *cmd) + { + int i = 0; + uint32_t bar_data = 0; +@@ -1925,17 +1922,26 @@ static int pt_register_regions(struct pt + + /* Register current region */ + if ( pci_dev->base_addr[i] & PCI_ADDRESS_SPACE_IO ) ++ { + pci_register_io_region((PCIDevice *)assigned_device, i, + (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_IO, + pt_ioport_map); ++ *cmd |= PCI_COMMAND_IO; ++ } + else if ( pci_dev->base_addr[i] & PCI_ADDRESS_SPACE_MEM_PREFETCH ) ++ { + pci_register_io_region((PCIDevice *)assigned_device, i, + (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_MEM_PREFETCH, + pt_iomem_map); ++ *cmd |= PCI_COMMAND_MEMORY; ++ } + else ++ { + pci_register_io_region((PCIDevice *)assigned_device, i, + (uint32_t)pci_dev->size[i], PCI_ADDRESS_SPACE_MEM, + pt_iomem_map); ++ *cmd |= PCI_COMMAND_MEMORY; ++ } + + PT_LOG("IO region registered (size=0x%08x base_addr=0x%08x)\n", + (uint32_t)(pci_dev->size[i]), +@@ -3263,27 +3269,6 @@ static int pt_long_reg_read(struct pt_de + return 0; + } + +-/* read Command register */ +-static int pt_cmd_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 = 0; +- uint16_t emu_mask = reg->emu_mask; +- +- if ( ptdev->is_virtfn ) +- emu_mask |= PCI_COMMAND_MEMORY; +- if ( pt_is_iomul(ptdev) ) +- emu_mask |= PCI_COMMAND_IO; +- +- /* emulate word register */ +- valid_emu_mask = emu_mask & valid_mask; +- *value = PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); +- +- return 0; +-} +- + /* read BAR */ + static int pt_bar_reg_read(struct pt_dev *ptdev, + struct pt_reg_tbl *cfg_entry, +@@ -3418,19 +3403,13 @@ static int pt_cmd_reg_write(struct pt_de + uint16_t writable_mask = 0; + uint16_t throughable_mask = 0; + uint16_t wr_value = *value; +- uint16_t emu_mask = reg->emu_mask; +- +- if ( ptdev->is_virtfn ) +- emu_mask |= PCI_COMMAND_MEMORY; +- if ( pt_is_iomul(ptdev) ) +- emu_mask |= PCI_COMMAND_IO; + + /* modify emulate register */ + writable_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; + + if (*value & PCI_COMMAND_DISABLE_INTx) + { +@@ -4211,6 +4190,7 @@ static struct pt_dev * register_real_dev + struct pt_dev *assigned_device = NULL; + struct pci_dev *pci_dev; + uint8_t e_device, e_intx; ++ uint16_t cmd = 0; + char *key, *val; + int msi_translate, power_mgmt; + +@@ -4300,7 +4280,7 @@ static struct pt_dev * register_real_dev + assigned_device->dev.config[i] = pci_read_byte(pci_dev, i); + + /* Handle real device's MMIO/PIO BARs */ +- pt_register_regions(assigned_device); ++ pt_register_regions(assigned_device, &cmd); + + /* Setup VGA bios for passthroughed gfx */ + if ( setup_vga_pt(assigned_device) < 0 ) +@@ -4378,6 +4358,10 @@ static struct pt_dev * register_real_dev + } + + out: ++ if (cmd) ++ pci_write_word(pci_dev, PCI_COMMAND, ++ *(uint16_t *)(&assigned_device->dev.config[PCI_COMMAND]) | cmd); ++ + PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n" + "IRQ type = %s\n", r_bus, r_dev, r_func, + assigned_device->msi_trans_en? "MSI-INTx":"INTx"); diff --git a/sysutils/xen-tools/files/xsa126-qemuu.patch b/sysutils/xen-tools/files/xsa126-qemuu.patch new file mode 100644 index 000000000000..21805a43be51 --- /dev/null +++ b/sysutils/xen-tools/files/xsa126-qemuu.patch @@ -0,0 +1,128 @@ +xen: limit guest control of PCI command register + +Otherwise the guest can abuse that control to cause e.g. PCIe +Unsupported Request responses (by disabling memory and/or I/O decoding +and subsequently causing [CPU side] accesses to the respective address +ranges), which (depending on system configuration) may be fatal to the +host. + +This is CVE-2015-2756 / XSA-126. + +Signed-off-by: Jan Beulich <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> +Acked-by: Ian Campbell <ian.campbell@citrix.com> + +--- a/hw/xen/xen_pt.c ++++ b/hw/xen/xen_pt.c +@@ -388,7 +388,7 @@ static const MemoryRegionOps ops = { + .write = xen_pt_bar_write, + }; + +-static int xen_pt_register_regions(XenPCIPassthroughState *s) ++static int xen_pt_register_regions(XenPCIPassthroughState *s, uint16_t *cmd) + { + int i = 0; + XenHostPCIDevice *d = &s->real_device; +@@ -406,6 +406,7 @@ static int xen_pt_register_regions(XenPC + + if (r->type & XEN_HOST_PCI_REGION_TYPE_IO) { + type = PCI_BASE_ADDRESS_SPACE_IO; ++ *cmd |= PCI_COMMAND_IO; + } else { + type = PCI_BASE_ADDRESS_SPACE_MEMORY; + if (r->type & XEN_HOST_PCI_REGION_TYPE_PREFETCH) { +@@ -414,6 +415,7 @@ static int xen_pt_register_regions(XenPC + if (r->type & XEN_HOST_PCI_REGION_TYPE_MEM_64) { + type |= PCI_BASE_ADDRESS_MEM_TYPE_64; + } ++ *cmd |= PCI_COMMAND_MEMORY; + } + + memory_region_init_io(&s->bar[i], OBJECT(s), &ops, &s->dev, +@@ -638,6 +640,7 @@ static int xen_pt_initfn(PCIDevice *d) + XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); + int rc = 0; + uint8_t machine_irq = 0; ++ uint16_t cmd = 0; + int pirq = XEN_PT_UNASSIGNED_PIRQ; + + /* register real device */ +@@ -672,7 +675,7 @@ static int xen_pt_initfn(PCIDevice *d) + s->io_listener = xen_pt_io_listener; + + /* Handle real device's MMIO/PIO BARs */ +- xen_pt_register_regions(s); ++ xen_pt_register_regions(s, &cmd); + + /* reinitialize each config register to be emulated */ + if (xen_pt_config_init(s)) { +@@ -736,6 +739,11 @@ static int xen_pt_initfn(PCIDevice *d) + } + + out: ++ if (cmd) { ++ xen_host_pci_set_word(&s->real_device, PCI_COMMAND, ++ pci_get_word(d->config + PCI_COMMAND) | cmd); ++ } ++ + memory_listener_register(&s->memory_listener, &address_space_memory); + memory_listener_register(&s->io_listener, &address_space_io); + XEN_PT_LOG(d, +--- a/hw/xen/xen_pt_config_init.c ++++ b/hw/xen/xen_pt_config_init.c +@@ -286,23 +286,6 @@ static int xen_pt_irqpin_reg_init(XenPCI + } + + /* Command register */ +-static int xen_pt_cmd_reg_read(XenPCIPassthroughState *s, XenPTReg *cfg_entry, +- uint16_t *value, uint16_t valid_mask) +-{ +- XenPTRegInfo *reg = cfg_entry->reg; +- uint16_t valid_emu_mask = 0; +- uint16_t emu_mask = reg->emu_mask; +- +- if (s->is_virtfn) { +- emu_mask |= PCI_COMMAND_MEMORY; +- } +- +- /* emulate word register */ +- valid_emu_mask = emu_mask & valid_mask; +- *value = XEN_PT_MERGE_VALUE(*value, cfg_entry->data, ~valid_emu_mask); +- +- return 0; +-} + static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, + uint16_t *val, uint16_t dev_value, + uint16_t valid_mask) +@@ -310,18 +293,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 emu_mask = reg->emu_mask; +- +- if (s->is_virtfn) { +- emu_mask |= PCI_COMMAND_MEMORY; +- } + + /* modify emulate register */ + writable_mask = ~reg->ro_mask & valid_mask; + cfg_entry->data = XEN_PT_MERGE_VALUE(*val, cfg_entry->data, writable_mask); + + /* create value for writing to I/O device register */ +- throughable_mask = ~emu_mask & valid_mask; ++ throughable_mask = ~reg->emu_mask & valid_mask; + + if (*val & PCI_COMMAND_INTX_DISABLE) { + throughable_mask |= PCI_COMMAND_INTX_DISABLE; +@@ -605,9 +583,9 @@ static XenPTRegInfo xen_pt_emu_reg_heade + .size = 2, + .init_val = 0x0000, + .ro_mask = 0xF880, +- .emu_mask = 0x0740, ++ .emu_mask = 0x0743, + .init = xen_pt_common_reg_init, +- .u.w.read = xen_pt_cmd_reg_read, ++ .u.w.read = xen_pt_word_reg_read, + .u.w.write = xen_pt_cmd_reg_write, + }, + /* Capabilities Pointer reg */ diff --git a/sysutils/xen-tools/files/xsa128-qemut.patch b/sysutils/xen-tools/files/xsa128-qemut.patch new file mode 100644 index 000000000000..7530690bdfc4 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/pass-through.c ++++ b/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/hw/pass-through.h ++++ b/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/sysutils/xen-tools/files/xsa128-qemuu.patch b/sysutils/xen-tools/files/xsa128-qemuu.patch new file mode 100644 index 000000000000..d551c6244a76 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/xen/xen_pt.c ++++ b/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/hw/xen/xen_pt.h ++++ b/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/hw/xen/xen_pt_config_init.c ++++ b/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/sysutils/xen-tools/files/xsa129-qemut.patch b/sysutils/xen-tools/files/xsa129-qemut.patch new file mode 100644 index 000000000000..005efa5dd83d --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/pass-through.c ++++ b/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/hw/pass-through.h ++++ b/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/sysutils/xen-tools/files/xsa129-qemuu.patch b/sysutils/xen-tools/files/xsa129-qemuu.patch new file mode 100644 index 000000000000..96715a68d372 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/pci/msi.c ++++ b/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/hw/xen/xen_pt_config_init.c ++++ b/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/include/hw/pci/pci_regs.h ++++ b/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/sysutils/xen-tools/files/xsa130-qemut.patch b/sysutils/xen-tools/files/xsa130-qemut.patch new file mode 100644 index 000000000000..75af778b0172 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/pass-through.h ++++ b/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/sysutils/xen-tools/files/xsa130-qemuu.patch b/sysutils/xen-tools/files/xsa130-qemuu.patch new file mode 100644 index 000000000000..c2f35bd0b902 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/xen/xen_pt.h ++++ b/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/hw/xen/xen_pt_msi.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemut-1.patch b/sysutils/xen-tools/files/xsa131-qemut-1.patch new file mode 100644 index 000000000000..3bfe23698261 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/pass-through.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemut-2.patch b/sysutils/xen-tools/files/xsa131-qemut-2.patch new file mode 100644 index 000000000000..cb2c7b9b2094 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> +Acked-by: Ian Campbell <ian.campbell@citrix.com> + +--- a/hw/pass-through.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemut-3.patch b/sysutils/xen-tools/files/xsa131-qemut-3.patch new file mode 100644 index 000000000000..a50b2392b7ac --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/pass-through.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemut-4.patch b/sysutils/xen-tools/files/xsa131-qemut-4.patch new file mode 100644 index 000000000000..95a7f517003f --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> +Reviewed-by: Anthony PERARD <anthony.perard@citrix.com> + +--- a/hw/pass-through.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemut-5.patch b/sysutils/xen-tools/files/xsa131-qemut-5.patch new file mode 100644 index 000000000000..fb5c4d403d8d --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/pass-through.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemut-6.patch b/sysutils/xen-tools/files/xsa131-qemut-6.patch new file mode 100644 index 000000000000..02d175d424e8 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> + +--- a/hw/pass-through.c ++++ b/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/hw/pass-through.h ++++ b/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/sysutils/xen-tools/files/xsa131-qemut-7.patch b/sysutils/xen-tools/files/xsa131-qemut-7.patch new file mode 100644 index 000000000000..b2430b5143a1 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> + +--- a/hw/pass-through.c ++++ b/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/hw/pass-through.h ++++ b/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/sysutils/xen-tools/files/xsa131-qemut-8.patch b/sysutils/xen-tools/files/xsa131-qemut-8.patch new file mode 100644 index 000000000000..875ae9646f56 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> +Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>) + +--- a/hw/pass-through.c ++++ b/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/hw/pass-through.h ++++ b/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/sysutils/xen-tools/files/xsa131-qemuu-1.patch b/sysutils/xen-tools/files/xsa131-qemuu-1.patch new file mode 100644 index 000000000000..ece230bb381d --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/xen/xen_pt_config_init.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemuu-2.patch b/sysutils/xen-tools/files/xsa131-qemuu-2.patch new file mode 100644 index 000000000000..fc547f493e10 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> +Acked-by: Ian Campbell <ian.campbell@citrix.com> + +--- a/hw/xen/xen_pt_config_init.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemuu-3.patch b/sysutils/xen-tools/files/xsa131-qemuu-3.patch new file mode 100644 index 000000000000..f62a4f804503 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/xen/xen_pt_config_init.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemuu-4.patch b/sysutils/xen-tools/files/xsa131-qemuu-4.patch new file mode 100644 index 000000000000..c36669beeebf --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> +Reviewed-by: Anthony PERARD <anthony.perard@citrix.com> + +--- a/hw/xen/xen_pt_config_init.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemuu-5.patch b/sysutils/xen-tools/files/xsa131-qemuu-5.patch new file mode 100644 index 000000000000..aed87c638a95 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> + +--- a/hw/xen/xen_pt_config_init.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemuu-6.patch b/sysutils/xen-tools/files/xsa131-qemuu-6.patch new file mode 100644 index 000000000000..98313d07275c --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> + +--- a/hw/xen/xen_pt.h ++++ b/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/hw/xen/xen_pt_config_init.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemuu-7.patch b/sysutils/xen-tools/files/xsa131-qemuu-7.patch new file mode 100644 index 000000000000..fa27fec84479 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +--- +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/hw/xen/xen_pt_config_init.c ++++ b/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/sysutils/xen-tools/files/xsa131-qemuu-8.patch b/sysutils/xen-tools/files/xsa131-qemuu-8.patch new file mode 100644 index 000000000000..9537baf27d50 --- /dev/null +++ b/sysutils/xen-tools/files/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 <jbeulich@suse.com> +Acked-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> +Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>) +--- +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/hw/xen/xen_pt.c ++++ b/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/hw/xen/xen_pt.h ++++ b/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/hw/xen/xen_pt_config_init.c ++++ b/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; + } + diff --git a/sysutils/xen-tools/files/xsa133-qemut.patch b/sysutils/xen-tools/files/xsa133-qemut.patch new file mode 100644 index 000000000000..e1b77117df16 --- /dev/null +++ b/sysutils/xen-tools/files/xsa133-qemut.patch @@ -0,0 +1,80 @@ +From ac7ddbe342d7aa2303c39ca731cc6229dbbd739b Mon Sep 17 00:00:00 2001 +From: Petr Matousek <pmatouse@redhat.com> +Date: Wed, 6 May 2015 09:48:59 +0200 +Subject: [PATCH] fdc: force the fifo access to be in bounds of the allocated buffer + +During processing of certain commands such as FD_CMD_READ_ID and +FD_CMD_DRIVE_SPECIFICATION_COMMAND the fifo memory access could +get out of bounds leading to memory corruption with values coming +from the guest. + +Fix this by making sure that the index is always bounded by the +allocated memory. + +This is CVE-2015-3456. + +Signed-off-by: Petr Matousek <pmatouse@redhat.com> +Reviewed-by: John Snow <jsnow@redhat.com> +--- + hw/fdc.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/hw/fdc.c b/hw/fdc.c +index b00a4ec..aba02e4 100644 +--- a/hw/fdc.c ++++ b/hw/fdc.c +@@ -1318,7 +1318,7 @@ static uint32_t fdctrl_read_data (fdctrl_t *fdctrl) + { + fdrive_t *cur_drv; + uint32_t retval = 0; +- int pos; ++ uint32_t pos; + + cur_drv = get_cur_drv(fdctrl); + fdctrl->dsr &= ~FD_DSR_PWRDOWN; +@@ -1327,8 +1327,8 @@ static uint32_t fdctrl_read_data (fdctrl_t *fdctrl) + return 0; + } + pos = fdctrl->data_pos; ++ pos %= FD_SECTOR_LEN; + if (fdctrl->msr & FD_MSR_NONDMA) { +- pos %= FD_SECTOR_LEN; + if (pos == 0) { + if (fdctrl->data_pos != 0) + if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { +@@ -1673,10 +1673,13 @@ static void fdctrl_handle_option (fdctrl_t *fdctrl, int direction) + static void fdctrl_handle_drive_specification_command (fdctrl_t *fdctrl, int direction) + { + fdrive_t *cur_drv = get_cur_drv(fdctrl); ++ uint32_t pos; + +- if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) { ++ pos = fdctrl->data_pos - 1; ++ pos %= FD_SECTOR_LEN; ++ if (fdctrl->fifo[pos] & 0x80) { + /* Command parameters done */ +- if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) { ++ if (fdctrl->fifo[pos] & 0x40) { + fdctrl->fifo[0] = fdctrl->fifo[1]; + fdctrl->fifo[2] = 0; + fdctrl->fifo[3] = 0; +@@ -1771,7 +1774,7 @@ static uint8_t command_to_handler[256]; + static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) + { + fdrive_t *cur_drv; +- int pos; ++ uint32_t pos; + + /* Reset mode */ + if (!(fdctrl->dor & FD_DOR_nRESET)) { +@@ -1817,7 +1820,9 @@ static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) + } + + FLOPPY_DPRINTF("%s: %02x\n", __func__, value); +- fdctrl->fifo[fdctrl->data_pos++] = value; ++ pos = fdctrl->data_pos++; ++ pos %= FD_SECTOR_LEN; ++ fdctrl->fifo[pos] = value; + if (fdctrl->data_pos == fdctrl->data_len) { + /* We now have all parameters + * and will be able to treat the command diff --git a/sysutils/xen-tools/files/xsa133-qemuu.patch b/sysutils/xen-tools/files/xsa133-qemuu.patch new file mode 100644 index 000000000000..95f3dcc21e5b --- /dev/null +++ b/sysutils/xen-tools/files/xsa133-qemuu.patch @@ -0,0 +1,84 @@ +From ac7ddbe342d7aa2303c39ca731cc6229dbbd739b Mon Sep 17 00:00:00 2001 +From: Petr Matousek <pmatouse@redhat.com> +Date: Wed, 6 May 2015 09:48:59 +0200 +Subject: [PATCH] fdc: force the fifo access to be in bounds of the allocated buffer + +During processing of certain commands such as FD_CMD_READ_ID and +FD_CMD_DRIVE_SPECIFICATION_COMMAND the fifo memory access could +get out of bounds leading to memory corruption with values coming +from the guest. + +Fix this by making sure that the index is always bounded by the +allocated memory. + +This is CVE-2015-3456. + +Signed-off-by: Petr Matousek <pmatouse@redhat.com> +Reviewed-by: John Snow <jsnow@redhat.com> +--- + hw/block/fdc.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/hw/block/fdc.c b/hw/block/fdc.c +index f72a392..d8a8edd 100644 +--- a/hw/block/fdc.c ++++ b/hw/block/fdc.c +@@ -1497,7 +1497,7 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl) + { + FDrive *cur_drv; + uint32_t retval = 0; +- int pos; ++ uint32_t pos; + + cur_drv = get_cur_drv(fdctrl); + fdctrl->dsr &= ~FD_DSR_PWRDOWN; +@@ -1506,8 +1506,8 @@ static uint32_t fdctrl_read_data(FDCtrl *fdctrl) + return 0; + } + pos = fdctrl->data_pos; ++ pos %= FD_SECTOR_LEN; + if (fdctrl->msr & FD_MSR_NONDMA) { +- pos %= FD_SECTOR_LEN; + if (pos == 0) { + if (fdctrl->data_pos != 0) + if (!fdctrl_seek_to_next_sect(fdctrl, cur_drv)) { +@@ -1852,10 +1852,13 @@ static void fdctrl_handle_option(FDCtrl *fdctrl, int direction) + static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction) + { + FDrive *cur_drv = get_cur_drv(fdctrl); ++ uint32_t pos; + +- if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) { ++ pos = fdctrl->data_pos - 1; ++ pos %= FD_SECTOR_LEN; ++ if (fdctrl->fifo[pos] & 0x80) { + /* Command parameters done */ +- if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x40) { ++ if (fdctrl->fifo[pos] & 0x40) { + fdctrl->fifo[0] = fdctrl->fifo[1]; + fdctrl->fifo[2] = 0; + fdctrl->fifo[3] = 0; +@@ -1955,7 +1958,7 @@ static uint8_t command_to_handler[256]; + static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) + { + FDrive *cur_drv; +- int pos; ++ uint32_t pos; + + /* Reset mode */ + if (!(fdctrl->dor & FD_DOR_nRESET)) { +@@ -2004,7 +2007,9 @@ static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) + } + + FLOPPY_DPRINTF("%s: %02x\n", __func__, value); +- fdctrl->fifo[fdctrl->data_pos++] = value; ++ pos = fdctrl->data_pos++; ++ pos %= FD_SECTOR_LEN; ++ fdctrl->fifo[pos] = value; + if (fdctrl->data_pos == fdctrl->data_len) { + /* We now have all parameters + * and will be able to treat the command +-- +2.1.0 + + diff --git a/sysutils/xen-tools/files/xsa135-qemut-1.patch b/sysutils/xen-tools/files/xsa135-qemut-1.patch new file mode 100644 index 000000000000..1102ce67fc3e --- /dev/null +++ b/sysutils/xen-tools/files/xsa135-qemut-1.patch @@ -0,0 +1,92 @@ +pcnet: fix Negative array index read + +From: Gonglei <arei.gonglei@huawei.com> + +s->xmit_pos maybe assigned to a negative value (-1), +but in this branch variable s->xmit_pos as an index to +array s->buffer. Let's add a check for s->xmit_pos. + +upstream-commit-id: 7b50d00911ddd6d56a766ac5671e47304c20a21b + +Signed-off-by: Gonglei <arei.gonglei@huawei.com> +Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> +Reviewed-by: Jason Wang <jasowang@redhat.com> +Reviewed-by: Jason Wang <jasowang@redhat.com> +Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> + +diff --git a/hw/pcnet.c b/hw/pcnet.c +index 7cc0637..9f3e1cc 100644 +--- a/hw/pcnet.c ++++ b/hw/pcnet.c +@@ -1250,7 +1250,7 @@ static void pcnet_transmit(PCNetState *s) + target_phys_addr_t xmit_cxda = 0; + int count = CSR_XMTRL(s)-1; + int add_crc = 0; +- ++ int bcnt; + s->xmit_pos = -1; + + if (!CSR_TXON(s)) { +@@ -1276,34 +1276,39 @@ static void pcnet_transmit(PCNetState *s) + if (BCR_SWSTYLE(s) != 1) + add_crc = GET_FIELD(tmd.status, TMDS, ADDFCS); + } ++ ++ if (s->xmit_pos < 0) { ++ goto txdone; ++ } ++ ++ bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); ++ s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), ++ s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); ++ s->xmit_pos += bcnt; ++ + if (!GET_FIELD(tmd.status, TMDS, ENP)) { +- int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); +- s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), +- s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); +- s->xmit_pos += bcnt; +- } else if (s->xmit_pos >= 0) { +- int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); +- s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), +- s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); +- s->xmit_pos += bcnt; ++ goto txdone; ++ } + #ifdef PCNET_DEBUG +- printf("pcnet_transmit size=%d\n", s->xmit_pos); ++ printf("pcnet_transmit size=%d\n", s->xmit_pos); + #endif +- if (CSR_LOOP(s)) { +- if (BCR_SWSTYLE(s) == 1) +- add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); +- s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; +- pcnet_receive(s, s->buffer, s->xmit_pos); +- s->looptest = 0; +- } else +- if (s->vc) +- qemu_send_packet(s->vc, s->buffer, s->xmit_pos); +- +- s->csr[0] &= ~0x0008; /* clear TDMD */ +- s->csr[4] |= 0x0004; /* set TXSTRT */ +- s->xmit_pos = -1; ++ if (CSR_LOOP(s)) { ++ if (BCR_SWSTYLE(s) == 1) ++ add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); ++ s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; ++ pcnet_receive(s, s->buffer, s->xmit_pos); ++ s->looptest = 0; ++ } else { ++ if (s->vc) { ++ qemu_send_packet(s->vc, s->buffer, s->xmit_pos); ++ } + } + ++ s->csr[0] &= ~0x0008; /* clear TDMD */ ++ s->csr[4] |= 0x0004; /* set TXSTRT */ ++ s->xmit_pos = -1; ++ ++ txdone: + SET_FIELD(&tmd.status, TMDS, OWN, 0); + TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s))); + if (!CSR_TOKINTD(s) || (CSR_LTINTEN(s) && GET_FIELD(tmd.status, TMDS, LTINT))) diff --git a/sysutils/xen-tools/files/xsa135-qemut-2.patch b/sysutils/xen-tools/files/xsa135-qemut-2.patch new file mode 100644 index 000000000000..bc3d02f30f5f --- /dev/null +++ b/sysutils/xen-tools/files/xsa135-qemut-2.patch @@ -0,0 +1,45 @@ +From 2630672ab22255de252f877709851c0557a1c647 Mon Sep 17 00:00:00 2001 +From: Petr Matousek <pmatouse@redhat.com> +Date: Sun, 24 May 2015 10:53:44 +0200 +Subject: [PATCH] pcnet: force the buffer access to be in bounds during tx + +4096 is the maximum length per TMD and it is also currently the size of +the relay buffer pcnet driver uses for sending the packet data to QEMU +for further processing. With packet spanning multiple TMDs it can +happen that the overall packet size will be bigger than sizeof(buffer), +which results in memory corruption. + +Fix this by only allowing to queue maximum sizeof(buffer) bytes. + +This is CVE-2015-3209. + +Signed-off-by: Petr Matousek <pmatouse@redhat.com> +Reported-by: Matt Tait <matttait@google.com> +Reviewed-by: Peter Maydell <peter.maydell@linaro.org> +Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> +--- + hw/pcnet.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/hw/pcnet.c b/hw/pcnet.c +index bdfd38f..6d32e4c 100644 +--- a/hw/pcnet.c ++++ b/hw/pcnet.c +@@ -1241,6 +1241,14 @@ static void pcnet_transmit(PCNetState *s) + } + + bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); ++ ++ /* if multi-tmd packet outsizes s->buffer then skip it silently. ++ Note: this is not what real hw does */ ++ if (s->xmit_pos + bcnt > sizeof(s->buffer)) { ++ s->xmit_pos = -1; ++ goto txdone; ++ } ++ + s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), + s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); + s->xmit_pos += bcnt; +-- +2.1.0 + diff --git a/sysutils/xen-tools/files/xsa135-qemuu-4.5-1.patch b/sysutils/xen-tools/files/xsa135-qemuu-4.5-1.patch new file mode 100644 index 000000000000..31b161026f14 --- /dev/null +++ b/sysutils/xen-tools/files/xsa135-qemuu-4.5-1.patch @@ -0,0 +1,94 @@ +pcnet: fix Negative array index read + +From: Gonglei <arei.gonglei@huawei.com> + +s->xmit_pos maybe assigned to a negative value (-1), +but in this branch variable s->xmit_pos as an index to +array s->buffer. Let's add a check for s->xmit_pos. + +upstream-commit-id: 7b50d00911ddd6d56a766ac5671e47304c20a21b + +Signed-off-by: Gonglei <arei.gonglei@huawei.com> +Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> +Reviewed-by: Jason Wang <jasowang@redhat.com> +Reviewed-by: Jason Wang <jasowang@redhat.com> +Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> + +diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c +index d344c15..f409b92 100644 +--- a/hw/net/pcnet.c ++++ b/hw/net/pcnet.c +@@ -1212,7 +1212,7 @@ static void pcnet_transmit(PCNetState *s) + hwaddr xmit_cxda = 0; + int count = CSR_XMTRL(s)-1; + int add_crc = 0; +- ++ int bcnt; + s->xmit_pos = -1; + + if (!CSR_TXON(s)) { +@@ -1247,35 +1247,40 @@ static void pcnet_transmit(PCNetState *s) + s->xmit_pos = -1; + goto txdone; + } ++ ++ if (s->xmit_pos < 0) { ++ goto txdone; ++ } ++ ++ bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); ++ s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), ++ s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); ++ s->xmit_pos += bcnt; ++ + if (!GET_FIELD(tmd.status, TMDS, ENP)) { +- int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); +- s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), +- s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); +- s->xmit_pos += bcnt; +- } else if (s->xmit_pos >= 0) { +- int bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); +- s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), +- s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); +- s->xmit_pos += bcnt; ++ goto txdone; ++ } ++ + #ifdef PCNET_DEBUG +- printf("pcnet_transmit size=%d\n", s->xmit_pos); ++ printf("pcnet_transmit size=%d\n", s->xmit_pos); + #endif +- if (CSR_LOOP(s)) { +- if (BCR_SWSTYLE(s) == 1) +- add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); +- s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; +- pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); +- s->looptest = 0; +- } else +- if (s->nic) +- qemu_send_packet(qemu_get_queue(s->nic), s->buffer, +- s->xmit_pos); +- +- s->csr[0] &= ~0x0008; /* clear TDMD */ +- s->csr[4] |= 0x0004; /* set TXSTRT */ +- s->xmit_pos = -1; ++ if (CSR_LOOP(s)) { ++ if (BCR_SWSTYLE(s) == 1) ++ add_crc = !GET_FIELD(tmd.status, TMDS, NOFCS); ++ s->looptest = add_crc ? PCNET_LOOPTEST_CRC : PCNET_LOOPTEST_NOCRC; ++ pcnet_receive(qemu_get_queue(s->nic), s->buffer, s->xmit_pos); ++ s->looptest = 0; ++ } else { ++ if (s->nic) { ++ qemu_send_packet(qemu_get_queue(s->nic), s->buffer, ++ s->xmit_pos); ++ } + } + ++ s->csr[0] &= ~0x0008; /* clear TDMD */ ++ s->csr[4] |= 0x0004; /* set TXSTRT */ ++ s->xmit_pos = -1; ++ + txdone: + SET_FIELD(&tmd.status, TMDS, OWN, 0); + TMDSTORE(&tmd, PHYSADDR(s,CSR_CXDA(s))); diff --git a/sysutils/xen-tools/files/xsa135-qemuu-4.5-2.patch b/sysutils/xen-tools/files/xsa135-qemuu-4.5-2.patch new file mode 100644 index 000000000000..4fe7df5440fd --- /dev/null +++ b/sysutils/xen-tools/files/xsa135-qemuu-4.5-2.patch @@ -0,0 +1,45 @@ +From 2630672ab22255de252f877709851c0557a1c647 Mon Sep 17 00:00:00 2001 +From: Petr Matousek <pmatouse@redhat.com> +Date: Sun, 24 May 2015 10:53:44 +0200 +Subject: [PATCH] pcnet: force the buffer access to be in bounds during tx + +4096 is the maximum length per TMD and it is also currently the size of +the relay buffer pcnet driver uses for sending the packet data to QEMU +for further processing. With packet spanning multiple TMDs it can +happen that the overall packet size will be bigger than sizeof(buffer), +which results in memory corruption. + +Fix this by only allowing to queue maximum sizeof(buffer) bytes. + +This is CVE-2015-3209. + +Signed-off-by: Petr Matousek <pmatouse@redhat.com> +Reported-by: Matt Tait <matttait@google.com> +Reviewed-by: Peter Maydell <peter.maydell@linaro.org> +Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> +--- + hw/net/pcnet.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c +index bdfd38f..6d32e4c 100644 +--- a/hw/net/pcnet.c ++++ b/hw/net/pcnet.c +@@ -1241,6 +1241,14 @@ static void pcnet_transmit(PCNetState *s) + } + + bcnt = 4096 - GET_FIELD(tmd.length, TMDL, BCNT); ++ ++ /* if multi-tmd packet outsizes s->buffer then skip it silently. ++ Note: this is not what real hw does */ ++ if (s->xmit_pos + bcnt > sizeof(s->buffer)) { ++ s->xmit_pos = -1; ++ goto txdone; ++ } ++ + s->phys_mem_read(s->dma_opaque, PHYSADDR(s, tmd.tbadr), + s->buffer + s->xmit_pos, bcnt, CSR_BSWP(s)); + s->xmit_pos += bcnt; +-- +2.1.0 + |