diff --git a/.gitignore b/.gitignore index 6b74118..d414712 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/mini-os-4.21.0.tar.xz -/xen-4.21.1.tar.xz +/xen-4.13.3.tar.gz diff --git a/CVE-2014-0150.patch b/CVE-2014-0150.patch new file mode 100644 index 0000000..adcbcc7 --- /dev/null +++ b/CVE-2014-0150.patch @@ -0,0 +1,11 @@ +--- xen-4.4.1/tools/qemu-xen-traditional/hw/virtio-net.c.orig 2014-07-02 15:54:37.000000000 +0100 ++++ xen-4.4.1/tools/qemu-xen-traditional/hw/virtio-net.c 2014-11-18 20:50:13.593122915 +0000 +@@ -192,7 +192,7 @@ + return VIRTIO_NET_ERR; + + if (mac_data.entries) { +- if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { ++ if (n->mac_table.in_use <= MAC_TABLE_ENTRIES - mac_data.entries) { + memcpy(n->mac_table.macs + (n->mac_table.in_use * ETH_ALEN), + elem->out_sg[2].iov_base + sizeof(mac_data), + mac_data.entries * ETH_ALEN); diff --git a/qemu.trad.CVE-2015-5278.patch b/qemu.trad.CVE-2015-5278.patch new file mode 100644 index 0000000..950817a --- /dev/null +++ b/qemu.trad.CVE-2015-5278.patch @@ -0,0 +1,11 @@ +--- xen-4.5.1/tools/qemu-xen-traditional/hw/ne2000.c.orig 2015-09-26 17:27:49.494334726 +0100 ++++ xen-4.5.1/tools/qemu-xen-traditional/hw/ne2000.c 2015-09-26 17:31:53.107474932 +0100 +@@ -331,7 +331,7 @@ + if (index <= s->stop) + avail = s->stop - index; + else +- avail = 0; ++ break; + len = size; + if (len > avail) + len = avail; diff --git a/qemu.trad.CVE-2015-5279.patch b/qemu.trad.CVE-2015-5279.patch new file mode 100644 index 0000000..ea08067 --- /dev/null +++ b/qemu.trad.CVE-2015-5279.patch @@ -0,0 +1,48 @@ +--- xen-4.5.1/tools/qemu-xen-traditional/hw/ne2000.c.orig 2015-06-09 16:32:24.000000000 +0100 ++++ xen-4.5.1/tools/qemu-xen-traditional/hw/ne2000.c 2015-09-26 17:27:49.494334726 +0100 +@@ -304,6 +304,9 @@ + } + + index = s->curpag << 8; ++ if (index >= NE2000_PMEM_END) { ++ index = s->start; ++ } + /* 4 bytes for header */ + total_len = size + 4; + /* address for next packet (4 bytes for CRC) */ +@@ -387,15 +390,21 @@ + offset = addr | (page << 4); + switch(offset) { + case EN0_STARTPG: +- s->start = val << 8; ++ if (val << 8 <= NE2000_PMEM_END) { ++ s->start = val << 8; ++ } + s->tainted = 1; + break; + case EN0_STOPPG: +- s->stop = val << 8; ++ if (val << 8 <= NE2000_PMEM_END) { ++ s->stop = val << 8; ++ } + s->tainted = 1; + break; + case EN0_BOUNDARY: +- s->boundary = val; ++ if (val << 8 < NE2000_PMEM_END) { ++ s->boundary = val; ++ } + break; + case EN0_IMR: + s->imr = val; +@@ -436,7 +445,9 @@ + s->phys[offset - EN1_PHYS] = val; + break; + case EN1_CURPAG: +- s->curpag = val; ++ if (val << 8 < NE2000_PMEM_END) { ++ s->curpag = val; ++ } + s->tainted = 1; + break; + case EN1_MULT ... EN1_MULT + 7: diff --git a/qemu.trad.CVE-2015-6815.patch b/qemu.trad.CVE-2015-6815.patch new file mode 100644 index 0000000..7386d6c --- /dev/null +++ b/qemu.trad.CVE-2015-6815.patch @@ -0,0 +1,12 @@ +--- xen-4.5.1/tools/qemu-xen-traditional/hw/e1000.c.orig 2015-06-09 16:32:24.000000000 +0100 ++++ xen-4.5.1/tools/qemu-xen-traditional/hw/e1000.c 2015-09-26 17:16:36.406544380 +0100 +@@ -461,7 +461,8 @@ + memmove(tp->data, tp->header, hdr); + tp->size = hdr; + } +- } while (split_size -= bytes); ++ split_size -= bytes; ++ } while (bytes && split_size); + } else if (!tp->tse && tp->cptse) { + // context descriptor TSE is not set, while data descriptor TSE is set + DBGOUT(TXERR, "TCP segmentaion Error\n"); diff --git a/qemu.trad.CVE-2015-7295.patch b/qemu.trad.CVE-2015-7295.patch new file mode 100644 index 0000000..1c74270 --- /dev/null +++ b/qemu.trad.CVE-2015-7295.patch @@ -0,0 +1,63 @@ +--- xen-4.5.1/tools/qemu-xen-traditional/hw/virtio.c.orig 2015-06-09 16:32:24.000000000 +0100 ++++ xen-4.5.1/tools/qemu-xen-traditional/hw/virtio.c 2015-10-10 16:57:01.806370020 +0100 +@@ -268,8 +268,8 @@ + return vring_avail_idx(vq) == vq->last_avail_idx; + } + +-void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, +- unsigned int len, unsigned int idx) ++static void virtqueue_unmap_sg(VirtQueue *vq, const VirtQueueElement *elem, ++ unsigned int len) + { + unsigned int offset; + int i; +@@ -302,7 +302,19 @@ + + offset += size; + } ++} + ++void virtqueue_discard(VirtQueue *vq, const VirtQueueElement *elem, ++ unsigned int len) ++{ ++ vq->last_avail_idx--; ++ virtqueue_unmap_sg(vq, elem, len); ++} ++ ++void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, ++ unsigned int len, unsigned int idx) ++{ ++ virtqueue_unmap_sg(vq, elem, len); + idx = (idx + vring_used_idx(vq)) % vq->vring.num; + + /* Get a pointer to the next entry in the used ring. */ +--- xen-4.5.1/tools/qemu-xen-traditional/hw/virtio.h.orig 2015-06-09 16:32:24.000000000 +0100 ++++ xen-4.5.1/tools/qemu-xen-traditional/hw/virtio.h 2015-10-10 16:57:53.146216039 +0100 +@@ -105,6 +105,8 @@ + void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len); + void virtqueue_flush(VirtQueue *vq, unsigned int count); ++void virtqueue_discard(VirtQueue *vq, const VirtQueueElement *elem, ++ unsigned int len); + void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, + unsigned int len, unsigned int idx); + +--- xen-4.5.1/tools/qemu-xen-traditional/hw/virtio-net.c.orig 2015-10-10 16:10:05.071786348 +0100 ++++ xen-4.5.1/tools/qemu-xen-traditional/hw/virtio-net.c 2015-10-10 19:05:34.510029916 +0100 +@@ -424,11 +424,15 @@ + len = iov_fill(sg, elem.in_num, + buf + offset, size - offset); + total += len; ++ offset += len; ++ if (!n->mergeable_rx_bufs && offset < size) { ++ virtqueue_discard(n->rx_vq, &elem, total); ++ return; ++ } + + /* signal other side */ + virtqueue_fill(n->rx_vq, &elem, total, i++); + +- offset += len; + } + + if (mhdr) diff --git a/qemu.trad.CVE-2015-7512.patch b/qemu.trad.CVE-2015-7512.patch new file mode 100644 index 0000000..6a1f33f --- /dev/null +++ b/qemu.trad.CVE-2015-7512.patch @@ -0,0 +1,37 @@ +From 8b98a2f07175d46c3f7217639bd5e03f2ec56343 Mon Sep 17 00:00:00 2001 +From: Jason Wang +Date: Mon, 30 Nov 2015 15:00:06 +0800 +Subject: [PATCH] pcnet: fix rx buffer overflow(CVE-2015-7512) + +Backends could provide a packet whose length is greater than buffer +size. Check for this and truncate the packet to avoid rx buffer +overflow in this case. + +Cc: Prasad J Pandit +Cc: qemu-stable@nongnu.org +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Jason Wang +--- + tools/qemu-xen-traditional/hw/pcnet.c | 6 ++++++ + 1 files changed, 6 insertions(+), 0 deletions(-) + +diff --git a/tools/qemu-xen-traditional/hw/pcnet.c b/tools/qemu-xen-traditional/hw/pcnet.c +index 309c40b..1f4a3db 100644 +--- a/tools/qemu-xen-traditional/hw/pcnet.c ++++ b/tools/qemu-xen-traditional/hw/pcnet.c +@@ -1064,6 +1064,12 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) + int pktcount = 0; + + if (!s->looptest) { ++ if (size > 4092) { ++#ifdef PCNET_DEBUG_RMD ++ fprintf(stderr, "pcnet: truncates rx packet.\n"); ++#endif ++ size = 4092; ++ } + memcpy(src, buf, size); + /* no need to compute the CRC */ + src[size] = 0; +-- +1.7.0.4 + diff --git a/qemu.trad.CVE-2015-8345.patch b/qemu.trad.CVE-2015-8345.patch new file mode 100644 index 0000000..73215ca --- /dev/null +++ b/qemu.trad.CVE-2015-8345.patch @@ -0,0 +1,38 @@ +From 00837731d254908a841d69298a4f9f077babaf24 Mon Sep 17 00:00:00 2001 +From: Stefan Weil +Date: Fri, 20 Nov 2015 08:42:33 +0100 +Subject: [PATCH] eepro100: Prevent two endless loops + +http://lists.nongnu.org/archive/html/qemu-devel/2015-11/msg04592.html +shows an example how an endless loop in function action_command can +be achieved. + +During my code review, I noticed a 2nd case which can result in an +endless loop. + +Reported-by: Qinghao Tang +Signed-off-by: Stefan Weil +Signed-off-by: Jason Wang +--- + tools/qemu-xen-traditional/hw/eepro100.c | 16 ++++++++++++++++ + 1 files changed, 16 insertions(+), 0 deletions(-) + +diff --git a/tools/qemu-xen-traditional/hw/eepro100.c b/tools/qemu-xen-traditional/hw/eepro100.c +index 60333b7..685a478 100644 +--- a/tools/qemu-xen-traditional/hw/eepro100.c ++++ b/tools/qemu-xen-traditional/hw/eepro100.c +@@ -774,6 +774,11 @@ static void tx_command(EEPRO100State *s) + uint32_t tx_buffer_address = ldl_phys(tbd_address); + uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); + //~ uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); ++ if (tx_buffer_size == 0) { ++ /* Prevent an endless loop. */ ++ logout("loop in %s:%u\n", __FILE__, __LINE__); ++ break; ++ } + tbd_address += 8; + logout + ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", +-- +1.7.0.4 + diff --git a/qemu.trad.CVE-2015-8504.patch b/qemu.trad.CVE-2015-8504.patch new file mode 100644 index 0000000..3620d40 --- /dev/null +++ b/qemu.trad.CVE-2015-8504.patch @@ -0,0 +1,44 @@ +From 4c65fed8bdf96780735dbdb92a8bd0d6b6526cc3 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Thu, 3 Dec 2015 18:54:17 +0530 +Subject: [PATCH] ui: vnc: avoid floating point exception + +While sending 'SetPixelFormat' messages to a VNC server, +the client could set the 'red-max', 'green-max' and 'blue-max' +values to be zero. This leads to a floating point exception in +write_png_palette while doing frame buffer updates. + +Reported-by: Lian Yihan +Signed-off-by: Prasad J Pandit +Reviewed-by: Gerd Hoffmann +Signed-off-by: Peter Maydell +--- + tools/qemu-xen-traditional/vnc.c | 6 +++--- + 1 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/tools/qemu-xen-traditional/vnc.c b/tools/qemu-xen-traditional/vnc.c +index 7538405..cbe4d33 100644 +--- a/tools/qemu-xen-traditional/vnc.c ++++ b/tools/qemu-xen-traditional/vnc.c +@@ -2198,15 +2198,15 @@ static void set_pixel_format(VncState *vs, + } + + vs->clientds = vs->serverds; +- vs->clientds.pf.rmax = red_max; ++ vs->clientds.pf.rmax = red_max ? red_max : 0xFF; + count_bits(vs->clientds.pf.rbits, red_max); + vs->clientds.pf.rshift = red_shift; + vs->clientds.pf.rmask = red_max << red_shift; +- vs->clientds.pf.gmax = green_max; ++ vs->clientds.pf.gmax = green_max ? green_max : 0xFF; + count_bits(vs->clientds.pf.gbits, green_max); + vs->clientds.pf.gshift = green_shift; + vs->clientds.pf.gmask = green_max << green_shift; +- vs->clientds.pf.bmax = blue_max; ++ vs->clientds.pf.bmax = blue_max ? blue_max : 0xFF; + count_bits(vs->clientds.pf.bbits, blue_max); + vs->clientds.pf.bshift = blue_shift; + vs->clientds.pf.bmask = blue_max << blue_shift; +-- +1.7.0.4 + diff --git a/qemu.trad.CVE-2016-1714.patch b/qemu.trad.CVE-2016-1714.patch new file mode 100644 index 0000000..59b840b --- /dev/null +++ b/qemu.trad.CVE-2016-1714.patch @@ -0,0 +1,30 @@ +--- xen-4.6.1/tools/qemu-xen-traditional/hw/fw_cfg.c.orig 2016-01-04 15:35:42.000000000 +0000 ++++ xen-4.6.1/tools/qemu-xen-traditional/hw/fw_cfg.c 2016-03-06 16:42:33.464296362 +0000 +@@ -54,11 +54,15 @@ + static void fw_cfg_write(FWCfgState *s, uint8_t value) + { + int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); +- FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; ++ FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL : ++ &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; + + FW_CFG_DPRINTF("write %d\n", value); + +- if (s->cur_entry & FW_CFG_WRITE_CHANNEL && s->cur_offset < e->len) { ++ if (s->cur_entry & FW_CFG_WRITE_CHANNEL ++ && e != NULL ++ && e->callback ++ && s->cur_offset < e->len) { + e->data[s->cur_offset++] = value; + if (s->cur_offset == e->len) { + e->callback(e->callback_opaque, e->data); +@@ -88,7 +92,8 @@ + static uint8_t fw_cfg_read(FWCfgState *s) + { + int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL); +- FWCfgEntry *e = &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; ++ FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL : ++ &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK]; + uint8_t ret; + + if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len) diff --git a/qemu.trad.CVE-2016-1981.patch b/qemu.trad.CVE-2016-1981.patch new file mode 100644 index 0000000..cd2a8c1 --- /dev/null +++ b/qemu.trad.CVE-2016-1981.patch @@ -0,0 +1,104 @@ +------------------------------------------------------------------------ +*From*: Laszlo Ersek +*Subject*: [Qemu-devel] [PATCH] e1000: eliminate infinite loops on +out-of-bounds transfer start +*Date*: Tue, 19 Jan 2016 14:17:20 +0100 + +------------------------------------------------------------------------ + +The start_xmit() and e1000_receive_iov() functions implement DMA transfers +iterating over a set of descriptors that the guest's e1000 driver +prepares: + +- the TDLEN and RDLEN registers store the total size of the descriptor + area, + +- while the TDH and RDH registers store the offset (in whole tx / rx + descriptors) into the area where the transfer is supposed to start. + +Each time a descriptor is processed, the TDH and RDH register is bumped +(as appropriate for the transfer direction). + +QEMU already contains logic to deal with bogus transfers submitted by the +guest: + +- Normally, the transmit case wants to increase TDH from its initial value + to TDT. (TDT is allowed to be numerically smaller than the initial TDH + value; wrapping at or above TDLEN bytes to zero is normal.) The failsafe + that QEMU currently has here is a check against reaching the original + TDH value again -- a complete wraparound, which should never happen. + +- In the receive case RDH is increased from its initial value until + "total_size" bytes have been received; preferably in a single step, or + in "s->rxbuf_size" byte steps, if the latter is smaller. However, null + RX descriptors are skipped without receiving data, while RDH is + incremented just the same. QEMU tries to prevent an infinite loop + (processing only null RX descriptors) by detecting whether RDH assumes + its original value during the loop. (Again, wrapping from RDLEN to 0 is + normal.) + +What both directions miss is that the guest could program TDLEN and RDLEN +so low, and the initial TDH and RDH so high, that these registers will +immediately be truncated to zero, and then never reassume their initial +values in the loop -- a full wraparound will never occur. + +The condition that expresses this is: + + xdh_start >= s->mac_reg[XDLEN] / sizeof(desc) + +i.e., TDH or RDH start out after the last whole rx or tx descriptor that +fits into the TDLEN or RDLEN sized area. + +This condition could be checked before we enter the loops, but +pci_dma_read() / pci_dma_write() knows how to fill in buffers safely for +bogus DMA addresses, so we just extend the existing failsafes with the +above condition. + +Cc: "Michael S. Tsirkin" +Cc: Petr Matousek +Cc: Stefano Stabellini +Cc: Prasad Pandit +Cc: Michael Roth +Cc: Jason Wang +RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1296044 +Signed-off-by: Laszlo Ersek +Reviewed-by: Jason Wang +--- + +Notes: + Regarding the public posting: we made an honest effort to vet this + vulnerability, and the impact seems low -- no host side reads/writes, + "just" a DoS (infinite loop). We decided the patch could be posted + publicly, for the usual review process. Jason and Prasad checked the + patch in the internal discussion already, but comments, improvements + etc. are clearly welcome. The CVE request is underway. Thanks. + + hw/net/e1000.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/hw/net/e1000.c b/hw/net/e1000.c +index bec06e9..34d0823 100644 +--- a/tools/qemu-xen-traditional/hw/e1000.c ++++ b/tools/qemu-xen-traditional/hw/e1000.c +@@ -908,7 +908,8 @@ start_xmit(E1000State *s) + * bogus values to TDT/TDLEN. + * there's nothing too intelligent we could do about this. + */ +- if (s->mac_reg[TDH] == tdh_start) { ++ if (s->mac_reg[TDH] == tdh_start || ++ tdh_start >= s->mac_reg[TDLEN] / sizeof(desc)) { + DBGOUT(TXERR, "TDH wraparound @%x, TDT %x, TDLEN %x\n", + tdh_start, s->mac_reg[TDT], s->mac_reg[TDLEN]); + break; +@@ -1165,7 +1166,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) + s->mac_reg[RDH] = 0; + s->check_rxov = 1; + /* see comment in start_xmit; same here */ +- if (s->mac_reg[RDH] == rdh_start) { ++ if (s->mac_reg[RDH] == rdh_start || ++ rdh_start >= s->mac_reg[RDLEN] / sizeof(desc)) { + DBGOUT(RXERR, "RDH wraparound @%x, RDT %x, RDLEN %x\n", + rdh_start, s->mac_reg[RDT], s->mac_reg[RDLEN]); + set_ics(s, 0, E1000_ICS_RXO); +-- +1.8.3.1 diff --git a/qemu.trad.CVE-2016-2538.patch b/qemu.trad.CVE-2016-2538.patch new file mode 100644 index 0000000..be05dd7 --- /dev/null +++ b/qemu.trad.CVE-2016-2538.patch @@ -0,0 +1,56 @@ +From: Prasad J Pandit + +When processing remote NDIS control message packets, +the USB Net device emulator uses a fixed length(4096) data buffer. +The incoming informationBufferOffset & Length combination could +overflow and cross that range. Check control message buffer +offsets and length to avoid it. + +Reported-by: Qinghao Tang +Signed-off-by: Prasad J Pandit +--- + hw/usb/dev-network.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +Update as per review + -> https://lists.gnu.org/archive/html/qemu-devel/2016-02/msg03475.html + +diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c +index 8a4ff49..180adce 100644 +--- a/tools/qemu-xen-traditional/hw/usb-net.c ++++ b/tools/qemu-xen-traditional/hw/usb-net.c +@@ -915,8 +915,9 @@ static int rndis_query_response(USBNetState *s, + + bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; + buflen = le32_to_cpu(buf->InformationBufferLength); +- if (bufoffs + buflen > length) ++ if (buflen > length || bufoffs >= length || bufoffs + buflen > length) { + return USB_RET_STALL; ++ } + + infobuflen = ndis_query(s, le32_to_cpu(buf->OID), + bufoffs + (uint8_t *) buf, buflen, infobuf, +@@ -961,8 +962,9 @@ static int rndis_set_response(USBNetState *s, + + bufoffs = le32_to_cpu(buf->InformationBufferOffset) + 8; + buflen = le32_to_cpu(buf->InformationBufferLength); +- if (bufoffs + buflen > length) ++ if (buflen > length || bufoffs >= length || bufoffs + buflen > length) { + return USB_RET_STALL; ++ } + + ret = ndis_set(s, le32_to_cpu(buf->OID), + bufoffs + (uint8_t *) buf, buflen); +@@ -1212,8 +1214,9 @@ static void usb_net_handle_dataout(USBNetState *s, USBPacket *p) + if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) { + uint32_t offs = 8 + le32_to_cpu(msg->DataOffset); + uint32_t size = le32_to_cpu(msg->DataLength); +- if (offs + size <= len) ++ if (offs < len && size < len && offs + size <= len) { + qemu_send_packet(s->vc, s->out_buf + offs, size); ++ } + } + s->out_ptr -= len; + memmove(s->out_buf, &s->out_buf[len], s->out_ptr); +-- +2.5.0 diff --git a/qemu.trad.CVE-2016-2841.patch b/qemu.trad.CVE-2016-2841.patch new file mode 100644 index 0000000..6979fbc --- /dev/null +++ b/qemu.trad.CVE-2016-2841.patch @@ -0,0 +1,34 @@ +From: Prasad J Pandit + +Ne2000 NIC uses ring buffer of NE2000_MEM_SIZE(49152) +bytes to process network packets. Registers PSTART & PSTOP +define ring buffer size & location. Setting these registers +to invalid values could lead to infinite loop or OOB r/w +access issues. Add check to avoid it. + +Reported-by: Yang Hongke +Signed-off-by: Prasad J Pandit +--- + hw/net/ne2000.c | 4 ++++ + 1 file changed, 4 insertions(+) + +Update per review: + -> https://lists.gnu.org/archive/html/qemu-devel/2016-02/msg05522.html + +diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c +index b032212..ced4666 100644 +--- a/tools/qemu-xen-traditional/hw/ne2000.c ++++ b/tools/qemu-xen-traditional/hw/ne2000.c +@@ -154,6 +154,10 @@ static int ne2000_buffer_full(NE2000State *s) + { + int avail, index, boundary; + ++ if (s->stop <= s->start) { ++ return 1; ++ } ++ + index = s->curpag << 8; + boundary = s->boundary << 8; + if (index < boundary) +-- +2.5.0 diff --git a/qemu.trad.CVE-2016-2857.patch b/qemu.trad.CVE-2016-2857.patch new file mode 100644 index 0000000..5bef1a7 --- /dev/null +++ b/qemu.trad.CVE-2016-2857.patch @@ -0,0 +1,45 @@ +From: Prasad J Pandit + +While computing IP checksum, 'net_checksum_calculate' reads +payload length from the packet. It could exceed the given 'data' +buffer size. Add a check to avoid it. + +Reported-by: Liu Ling +Signed-off-by: Prasad J Pandit +--- + net/checksum.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +Update as per review: + -> https://lists.gnu.org/archive/html/qemu-devel/2016-02/msg06121.html + +diff --git a/net/checksum.c b/net/checksum.c +index 14c0855..0942437 100644 +--- a/tools/qemu-xen-traditional/net-checksum.c ++++ b/tools/qemu-xen-traditional/net-checksum.c +@@ -59,6 +59,11 @@ void net_checksum_calculate(uint8_t *data, int length) + int hlen, plen, proto, csum_offset; + uint16_t csum; + ++ /* Ensure data has complete L2 & L3 headers. */ ++ if (length < 14 + 20) { ++ return; ++ } ++ + if ((data[14] & 0xf0) != 0x40) + return; /* not IPv4 */ + hlen = (data[14] & 0x0f) * 4; +@@ -76,8 +81,9 @@ void net_checksum_calculate(uint8_t *data, int length) + return; + } + +- if (plen < csum_offset+2) +- return; ++ if (plen < csum_offset + 2 || 14 + hlen + plen > length) { ++ return; ++ } + + data[14+hlen+csum_offset] = 0; + data[14+hlen+csum_offset+1] = 0; +-- +2.5.0 diff --git a/qemu.trad.CVE-2016-4001.patch b/qemu.trad.CVE-2016-4001.patch new file mode 100644 index 0000000..9ca362f --- /dev/null +++ b/qemu.trad.CVE-2016-4001.patch @@ -0,0 +1,46 @@ +From 3a15cc0e1ee7168db0782133d2607a6bfa422d66 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Fri, 8 Apr 2016 11:33:48 +0530 +Subject: [PATCH] net: stellaris_enet: check packet length against receive buffer + +When receiving packets over Stellaris ethernet controller, it +uses receive buffer of size 2048 bytes. In case the controller +accepts large(MTU) packets, it could lead to memory corruption. +Add check to avoid it. + +Reported-by: Oleksandr Bazhaniuk +Signed-off-by: Prasad J Pandit +Message-id: 1460095428-22698-1-git-send-email-ppandit@redhat.com +Reviewed-by: Peter Maydell +Signed-off-by: Peter Maydell +--- + tools/qemu-xen-traditional/hw/stellaris_enet.c | 12 +++++++++++- + 1 files changed, 11 insertions(+), 1 deletions(-) + +diff --git a/tools/qemu-xen-traditional/hw/stellaris_enet.c b/tools/qemu-xen-traditional/hw/stellaris_enet.c +index 84cf60b..6880894 100644 +--- a/tools/qemu-xen-traditional/hw/stellaris_enet.c ++++ b/tools/qemu-xen-traditional/hw/stellaris_enet.c +@@ -236,8 +236,18 @@ static ssize_t stellaris_enet_receive(NetClientState *nc, const uint8_t *buf, si + n = s->next_packet + s->np; + if (n >= 31) + n -= 31; +- s->np++; + ++ if (size >= sizeof(s->rx[n].data) - 6) { ++ /* If the packet won't fit into the ++ * emulated 2K RAM, this is reported ++ * as a FIFO overrun error. ++ */ ++ s->ris |= SE_INT_FOV; ++ stellaris_enet_update(s); ++ return -1; ++ } ++ ++ s->np++; + s->rx[n].len = size + 6; + p = s->rx[n].data; + *(p++) = (size + 6); +-- +1.7.0.4 + diff --git a/qemu.trad.CVE-2016-4002.patch b/qemu.trad.CVE-2016-4002.patch new file mode 100644 index 0000000..e122297 --- /dev/null +++ b/qemu.trad.CVE-2016-4002.patch @@ -0,0 +1,31 @@ +From: Prasad J Pandit + +When receiving packets over MIPSnet network device, it uses + receive buffer of size 1514 bytes. In case the controller +accepts large(MTU) packets, it could lead to memory corruption. +Add check to avoid it. + +Reported by: Oleksandr Bazhaniuk + +Signed-off-by: Prasad J Pandit +--- + tools/qemu-xen-traditional/hw/mipsnet.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/tools/qemu-xen-traditional/hw/mipsnet.c b/tools/qemu-xen-traditional/hw/mipsnet.c +index f261011..e134b31 100644 +--- a/tools/qemu-xen-traditional/hw/mipsnet.c ++++ b/tools/qemu-xen-traditional/hw/mipsnet.c +@@ -82,6 +82,9 @@ static ssize_t mipsnet_receive(NetClientState *nc, const uint8_t *buf, size_t si + if (!mipsnet_can_receive(opaque)) + return; + ++ if (size >= sizeof(s->rx_buffer)) { ++ return; ++ } + s->busy = 1; + + /* Just accept everything. */ +-- +2.5.5 + diff --git a/qemu.trad.CVE-2016-4439.patch b/qemu.trad.CVE-2016-4439.patch new file mode 100644 index 0000000..6816695 --- /dev/null +++ b/qemu.trad.CVE-2016-4439.patch @@ -0,0 +1,44 @@ +------------------------------------------------------------------------ +*From*: P J P +*Subject*: [Qemu-devel] [PATCH 1/2] scsi: check command buffer length +before write(CVE-2016-4439) +*Date*: Thu, 19 May 2016 16:09:30 +0530 + +------------------------------------------------------------------------ + +From: Prasad J Pandit + +The 53C9X Fast SCSI Controller(FSC) comes with an internal 16-byte +FIFO buffer. It is used to handle command and data transfer. While +writing to this command buffer 's->cmdbuf[TI_BUFSZ=16]', a check +was missing to validate input length. Add check to avoid OOB write +access. + +Fixes CVE-2016-4439 +Reported-by: Li Qiang + +Signed-off-by: Prasad J Pandit +--- + hw/scsi/esp.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/tools/qemu-xen-traditional/hw/esp.c b/tools/qemu-xen-traditional/hw/esp.c +index 8961be2..01497e6 100644 +--- a/tools/qemu-xen-traditional/hw/esp.c ++++ b/tools/qemu-xen-traditional/hw/esp.c +@@ -448,7 +448,11 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) + break; + case ESP_FIFO: + if (s->do_cmd) { +- s->cmdbuf[s->cmdlen++] = val & 0xff; ++ if (s->cmdlen < TI_BUFSZ) { ++ s->cmdbuf[s->cmdlen++] = val & 0xff; ++ } else { ++ ESP_ERROR("fifo overrun\n"); ++ } + } else if (s->ti_size == TI_BUFSZ - 1) { + ESP_ERROR("fifo overrun\n"); + } else { +-- +2.5.5 + diff --git a/qemu.trad.CVE-2016-4441.patch b/qemu.trad.CVE-2016-4441.patch new file mode 100644 index 0000000..fab6a35 --- /dev/null +++ b/qemu.trad.CVE-2016-4441.patch @@ -0,0 +1,68 @@ +------------------------------------------------------------------------ +*From*: P J P +*Subject*: [Qemu-devel] [PATCH 2/2] scsi: check dma length before +reading scsi command(CVE-2016-4441) +*Date*: Thu, 19 May 2016 16:09:31 +0530 + +------------------------------------------------------------------------ + +From: Prasad J Pandit + +The 53C9X Fast SCSI Controller(FSC) comes with an internal 16-byte +FIFO buffer. It is used to handle command and data transfer. +Routine get_cmd() uses DMA to read scsi commands into this buffer. +Add check to validate DMA length against buffer size to avoid any +overrun. + +Fixes CVE-2016-4441 +Reported-by: Li Qiang + +Signed-off-by: Prasad J Pandit +--- + hw/scsi/esp.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/tools/qemu-xen-traditional/hw/esp.c b/tools/qemu-xen-traditional/hw/esp.c +index 01497e6..591c817 100644 +--- a/tools/qemu-xen-traditional/hw/esp.c ++++ b/tools/qemu-xen-traditional/hw/esp.c +@@ -82,7 +82,7 @@ void esp_request_cancelled(SCSIRequest *req) + } + } + +-static uint32_t get_cmd(ESPState *s, uint8_t *buf) ++static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t buflen) + { + uint32_t dmalen; + int target; +@@ -92,6 +92,9 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf) + target = s->wregs[ESP_WBUSID] & BUSID_DID; + if (s->dma) { + dmalen = s->rregs[ESP_TCLO] | (s->rregs[ESP_TCMID] << 8); ++ if (dmalen > buflen) { ++ return 0; ++ } + s->dma_memory_read(s->dma_opaque, buf, dmalen); + } else { + dmalen = s->ti_size; +@@ -166,7 +169,7 @@ static void handle_satn(ESPState *s) + uint8_t buf[32]; + int len; + +- len = get_cmd(s, buf); ++ len = get_cmd(s, buf, sizeof(buf)); + if (len) + do_cmd(s, buf); + } +@@ -192,7 +195,7 @@ static void handle_satn_stop(ESPState *s) + + static void handle_satn_stop(ESPState *s) + { +- s->cmdlen = get_cmd(s, s->cmdbuf); ++ s->cmdlen = get_cmd(s, s->cmdbuf, sizeof(s->cmdbuf)); + if (s->cmdlen) { + DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen); + s->do_cmd = 1; +-- +2.5.5 + diff --git a/qemu.trad.CVE-2016-5238.patch b/qemu.trad.CVE-2016-5238.patch new file mode 100644 index 0000000..f6767de --- /dev/null +++ b/qemu.trad.CVE-2016-5238.patch @@ -0,0 +1,65 @@ +------------------------------------------------------------------------ +*From*: Paolo Bonzini +*Subject*: Re: [Qemu-devel] [PATCH] scsi: check buffer length before +reading scsi command +*Date*: Wed, 1 Jun 2016 15:10:16 +0200 +*User-agent*: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 +Thunderbird/45.1.0 + +------------------------------------------------------------------------ + + +On 31/05/2016 19:53, P J P wrote: +>/ From: Prasad J Pandit / +>/ / +>/ The 53C9X Fast SCSI Controller(FSC) comes with an internal 16-byte/ +>/ FIFO buffer. It is used to handle command and data transfer./ +>/ Routine get_cmd() in non-DMA mode, uses 'ti_size' to read scsi/ +>/ command into a buffer. Add check to validate command length against/ +>/ buffer size to avoid any overrun./ +>/ / +>/ Reported-by: Li Qiang / +>/ Signed-off-by: Prasad J Pandit / +>/ ---/ +>/ hw/scsi/esp.c | 3 +++/ +>/ 1 file changed, 3 insertions(+)/ +>/ / +>/ diff --git a/tools/qemu-xen-traditional/hw/esp.c b/tools/qemu-xen-traditional/hw/esp.c/ +>/ index 60c1b28..953027a 100644/ +>/ --- a/tools/qemu-xen-traditional/hw/esp.c/ +>/ +++ b/tools/qemu-xen-traditional/hw/esp.c/ +>/ @@ -98,6 +98,9 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf, uint8_t / +>/ buflen)/ +>/ s->dma_memory_read(s->dma_opaque, buf, dmalen);/ +>/ } else {/ +>/ dmalen = s->ti_size;/ +>/ + if (dmalen > TI_BUFSZ) {/ +>/ + return 0;/ +>/ + }/ +>/ memcpy(buf, s->ti_buf, dmalen);/ +>/ buf[0] = buf[2] >> 5;/ +>/ }/ +>/ / + +In theory this shouldn't happen, but I agree that it is better to be +defensive. I'm queuing this patch. + +At least the following patch is needed to ensure that ti_size always +matches ti_rptr/ti_wptr (Hervé, what do you think about it? should I +resubmit it formally?). Also, things are more complicated than +necessary due to ti_size being used for both DMA and FIFO transfers. + +diff --git a/tools/qemu-xen-traditional/hw/esp.c b/tools/qemu-xen-traditional/hw/esp.c +index c2f6f8f..6407844 100644 +--- a/tools/qemu-xen-traditional/hw/esp.c ++++ b/tools/qemu-xen-traditional/hw/esp.c +@@ -222,7 +222,7 @@ static void write_response(ESPState *s) + } else { + s->ti_size = 2; + s->ti_rptr = 0; +- s->ti_wptr = 0; ++ s->ti_wptr = 2; + s->rregs[ESP_RFLAGS] = 2; + } + esp_raise_irq(s); + diff --git a/qemu.trad.CVE-2016-5338.patch b/qemu.trad.CVE-2016-5338.patch new file mode 100644 index 0000000..be36dca --- /dev/null +++ b/qemu.trad.CVE-2016-5338.patch @@ -0,0 +1,76 @@ +------------------------------------------------------------------------ +*From*: P J P +*Subject*: [Qemu-devel] [PATCH v3] scsi: esp: check TI buffer index +before read/write +*Date*: Mon, 6 Jun 2016 22:04:43 +0530 + +------------------------------------------------------------------------ + +From: Prasad J Pandit + +The 53C9X Fast SCSI Controller(FSC) comes with internal 16-byte +FIFO buffers. One is used to handle commands and other is for +information transfer. Three control variables 'ti_rptr', +'ti_wptr' and 'ti_size' are used to control r/w access to the +information transfer buffer ti_buf[TI_BUFSZ=16]. In that, + +'ti_rptr' is used as read index, where read occurs. +'ti_wptr' is a write index, where write would occur. +'ti_size' indicates total bytes to be read from the buffer. + +While reading/writing to this buffer, index could exceed its +size. Add check to avoid OOB r/w access. + +Reported-by: Huawei PSIRT +Reported-by: Li Qiang +Signed-off-by: Prasad J Pandit +--- + hw/scsi/esp.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +Update as per: + -> https://lists.gnu.org/archive/html/qemu-devel/2016-06/msg01326.html + +diff --git a/tools/qemu-xen-traditional/hw/esp.c b/tools/qemu-xen-traditional/hw/esp.c +index c2f6f8f..4b94bbc 100644 +--- a/tools/qemu-xen-traditional/hw/esp.c ++++ b/tools/qemu-xen-traditional/hw/esp.c +@@ -403,18 +403,17 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr) + DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]); + switch (saddr) { + case ESP_FIFO: +- if (s->ti_size > 0) { ++ if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) { ++ /* Data out. */ ++ ESP_ERROR("PIO data read not implemented\n"); ++ s->rregs[ESP_FIFO] = 0; ++ esp_raise_irq(s); ++ } else if (s->ti_rptr < s->ti_wptr) { + s->ti_size--; +- if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) { +- /* Data out. */ +- ESP_ERROR("PIO data read not implemented\n"); +- s->rregs[ESP_FIFO] = 0; +- } else { +- s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++]; +- } ++ s->rregs[ESP_FIFO] = s->ti_buf[s->ti_rptr++]; + esp_raise_irq(s); + } +- if (s->ti_size == 0) { ++ if (s->ti_rptr == s->ti_wptr) { + s->ti_rptr = 0; + s->ti_wptr = 0; + } +@@ -459,7 +457,7 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) + } else { + ESP_ERROR("fifo overrun\n"); + } +- } else if (s->ti_size == TI_BUFSZ - 1) { ++ } else if (s->ti_wptr == TI_BUFSZ - 1) { + ESP_ERROR("fifo overrun\n"); + } else { + s->ti_size++; +-- +2.5.5 + diff --git a/qemu.trad.CVE-2016-6351.patch b/qemu.trad.CVE-2016-6351.patch new file mode 100644 index 0000000..10f1ab3 --- /dev/null +++ b/qemu.trad.CVE-2016-6351.patch @@ -0,0 +1,81 @@ +From 926cde5f3e4d2504ed161ed0cb771ac7cad6fd11 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Thu, 16 Jun 2016 00:22:35 +0200 +Subject: [PATCH] scsi: esp: make cmdbuf big enough for maximum CDB size + +While doing DMA read into ESP command buffer 's->cmdbuf', it could +write past the 's->cmdbuf' area, if it was transferring more than 16 +bytes. Increase the command buffer size to 32, which is maximum when +'s->do_cmd' is set, and add a check on 'len' to avoid OOB access. + +Reported-by: Li Qiang +Signed-off-by: Prasad J Pandit +Signed-off-by: Paolo Bonzini +--- + hw/esp.c | 6 ++++-- + hw/esp.c | 3 ++- + 2 files changed, 6 insertions(+), 3 deletions(-) + +diff --git a/hw/esp.c b/hw/esp.c +index 64680b3..baa0a2c 100644 +--- a/hw/esp.c ++++ b/hw/esp.c +@@ -25,6 +25,7 @@ + #include "hw.h" + #include "scsi-disk.h" + #include "scsi.h" ++#include + + /* debug ESP card */ + //#define DEBUG_ESP +@@ -248,6 +248,8 @@ static void esp_do_dma(ESPState *s) + len = s->dma_left; + if (s->do_cmd) { + DPRINTF("command len %d + %d\n", s->cmdlen, len); ++ assert (s->cmdlen <= sizeof(s->cmdbuf) && ++ len <= sizeof(s->cmdbuf) - s->cmdlen); + s->dma_memory_read(s->dma_opaque, &s->cmdbuf[s->cmdlen], len); + s->ti_size = 0; + s->cmdlen = 0; +@@ -345,7 +347,7 @@ static void handle_ti(ESPState *s) + s->dma_counter = dmalen; + + if (s->do_cmd) +- minlen = (dmalen < 32) ? dmalen : 32; ++ minlen = (dmalen < ESP_CMDBUF_SZ) ? dmalen : ESP_CMDBUF_SZ; + else if (s->ti_size < 0) + minlen = (dmalen < -s->ti_size) ? dmalen : -s->ti_size; + else +@@ -449,7 +451,7 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) + break; + case ESP_FIFO: + if (s->do_cmd) { +- if (s->cmdlen < TI_BUFSZ) { ++ if (s->cmdlen < ESP_CMDBUF_SZ) { + s->cmdbuf[s->cmdlen++] = val & 0xff; + } else { + ESP_ERROR("fifo overrun\n"); +diff --git a/hw/esp.c b/hw/esp.c +index 6c79527..d2c4886 100644 +--- a/hw/esp.c ++++ b/hw/esp.c +@@ -14,6 +14,7 @@ void esp_init(hwaddr espaddr, int it_shift, + + #define ESP_REGS 16 + #define TI_BUFSZ 16 ++#define ESP_CMDBUF_SZ 32 + + typedef struct ESPState ESPState; + +@@ -31,7 +32,7 @@ struct ESPState { + uint32_t dma; + SCSIDevice *scsi_dev[ESP_MAX_DEVS]; + SCSIDevice *current_dev; +- uint8_t cmdbuf[TI_BUFSZ]; ++ uint8_t cmdbuf[ESP_CMDBUF_SZ]; + uint32_t cmdlen; + uint32_t do_cmd; + +-- +1.7.0.4 + diff --git a/qemu.trad.CVE-2016-8669.patch b/qemu.trad.CVE-2016-8669.patch new file mode 100644 index 0000000..05abe36 --- /dev/null +++ b/qemu.trad.CVE-2016-8669.patch @@ -0,0 +1,37 @@ +From 3592fe0c919cf27a81d8e9f9b4f269553418bb01 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Wed, 12 Oct 2016 11:28:08 +0530 +Subject: [PATCH] char: serial: check divider value against baud base + +16550A UART device uses an oscillator to generate frequencies +(baud base), which decide communication speed. This speed could +be changed by dividing it by a divider. If the divider is +greater than the baud base, speed is set to zero, leading to a +divide by zero error. Add check to avoid it. + +Reported-by: Huawei PSIRT +Signed-off-by: Prasad J Pandit +Message-Id: <1476251888-20238-1-git-send-email-ppandit@redhat.com> +Signed-off-by: Paolo Bonzini +--- + hw/char/serial.c | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +diff --git a/hw/serial.c b/hw/serial.c +index 3442f47..eec72b7 100644 +--- a/hw/serial.c ++++ b/hw/serial.c +@@ -153,8 +153,9 @@ static void serial_update_parameters(SerialState *s) + int speed, parity, data_bits, stop_bits, frame_size; + QEMUSerialSetParams ssp; + +- if (s->divider == 0) ++ if (s->divider == 0 || s->divider > s->baudbase) { + return; ++ } + + frame_size = 1; + if (s->lcr & 0x08) { +-- +1.7.0.4 + diff --git a/qemu.trad.CVE-2016-8910.patch b/qemu.trad.CVE-2016-8910.patch new file mode 100644 index 0000000..ddb67b1 --- /dev/null +++ b/qemu.trad.CVE-2016-8910.patch @@ -0,0 +1,29 @@ +From: Prasad J Pandit + +RTL8139 ethernet controller in C+ mode supports multiple +descriptor rings, each with maximum of 64 descriptors. While +processing transmit descriptor ring in 'rtl8139_cplus_transmit', +it does not limit the descriptor count and runs forever. Add +check to avoid it. + +Reported-by: Andrew Henderson +Signed-off-by: Prasad J Pandit +--- + hw/net/rtl8139.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/rtl8139.c b/hw/rtl8139.c +index 3345bc6..f05e59c 100644 +--- a/hw/rtl8139.c ++++ b/hw/rtl8139.c +@@ -2350,7 +2350,7 @@ static void rtl8139_cplus_transmit(RTL8139State *s) + { + int txcount = 0; + +- while (rtl8139_cplus_transmit_one(s)) ++ while (txcount < 64 && rtl8139_cplus_transmit_one(s)) + { + ++txcount; + } +-- +2.7.4 diff --git a/qemu.trad.CVE-2016-9776.patch b/qemu.trad.CVE-2016-9776.patch new file mode 100644 index 0000000..2098ed3 --- /dev/null +++ b/qemu.trad.CVE-2016-9776.patch @@ -0,0 +1,34 @@ +From 77d54985b85a0cb760330ec2bd92505e0a2a97a9 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Tue, 29 Nov 2016 00:38:39 +0530 +Subject: [PATCH] net: mcf: check receive buffer size register value + +ColdFire Fast Ethernet Controller uses a receive buffer size +register(EMRBR) to hold maximum size of all receive buffers. +It is set by a user before any operation. If it was set to be +zero, ColdFire emulator would go into an infinite loop while +receiving data in mcf_fec_receive. Add check to avoid it. + +Reported-by: Wjjzhang +Signed-off-by: Prasad J Pandit +Signed-off-by: Jason Wang +--- + hw/net/mcf_fec.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/hw/mcf_fec.c b/hw/mcf_fec.c +index dc61bac..4025eb3 100644 +--- a/hw/mcf_fec.c ++++ b/hw/mcf_fec.c +@@ -393,7 +393,7 @@ static void mcf_fec_write(void *opaque, hwaddr addr, + s->tx_descriptor = s->etdsr; + break; + case 0x188: +- s->emrbr = value & 0x7f0; ++ s->emrbr = value > 0 ? value & 0x7F0 : 0x7F0; + break; + default: + cpu_abort(cpu_single_env, "mcf_fec_write Bad address 0x%x\n", +-- +1.7.0.4 + diff --git a/qemu.trad.CVE-2017-6505.patch b/qemu.trad.CVE-2017-6505.patch new file mode 100644 index 0000000..b374a3d --- /dev/null +++ b/qemu.trad.CVE-2017-6505.patch @@ -0,0 +1,51 @@ +From 95ed56939eb2eaa4e2f349fe6dcd13ca4edfd8fb Mon Sep 17 00:00:00 2001 +From: Li Qiang +Date: Tue, 7 Feb 2017 02:23:33 -0800 +Subject: [PATCH] usb: ohci: limit the number of link eds + +The guest may builds an infinite loop with link eds. This patch +limit the number of linked ed to avoid this. + +Signed-off-by: Li Qiang +Message-id: 5899a02e.45ca240a.6c373.93c1@mx.google.com +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ohci.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c +index 2cba3e3..21c93e0 100644 +--- a/hw/usb-ohci.c ++++ b/hw/usb-ohci.c +@@ -42,6 +42,8 @@ + + #define OHCI_MAX_PORTS 15 + ++#define ED_LINK_LIMIT 4 ++ + static int64_t usb_frame_time; + static int64_t usb_bit_time; + +@@ -1184,7 +1186,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) + uint32_t next_ed; + uint32_t cur; + int active; +- ++ uint32_t link_cnt = 0; + active = 0; + + if (head == 0) +@@ -1199,6 +1201,10 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) + + next_ed = ed.next & OHCI_DPTR_MASK; + ++ if (++link_cnt > ED_LINK_LIMIT) { ++ return 0; ++ } ++ + if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { + uint32_t addr; + /* Cancel pending packets for ED that have been paused. */ +-- +1.8.3.1 + diff --git a/qemu.trad.CVE-2017-7718.patch b/qemu.trad.CVE-2017-7718.patch new file mode 100644 index 0000000..70382ab --- /dev/null +++ b/qemu.trad.CVE-2017-7718.patch @@ -0,0 +1,51 @@ +From 215902d7b6fb50c6fc216fc74f770858278ed904 Mon Sep 17 00:00:00 2001 +From: hangaohuai +Date: Tue, 14 Mar 2017 14:39:19 +0800 +Subject: [PATCH] fix :cirrus_vga fix OOB read case qemu Segmentation fault + +check the validity of parameters in cirrus_bitblt_rop_fwd_transp_xxx +and cirrus_bitblt_rop_fwd_xxx to avoid the OOB read which causes qemu Segmentation fault. + +After the fix, we will touch the assert in +cirrus_invalidate_region: +assert(off_cur_end >= off_cur); + +Signed-off-by: fangying +Signed-off-by: hangaohuai +Message-id: 20170314063919.16200-1-hangaohuai@huawei.com +Signed-off-by: Gerd Hoffmann +--- + hw/cirrus_vga_rop.h | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/hw/cirrus_vga_rop.h b/hw/cirrus_vga_rop.h +index 0925a00..b7447f8 100644 +--- a/hw/cirrus_vga_rop.h ++++ b/hw/cirrus_vga_rop.h +@@ -97,6 +97,11 @@ glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_8)(CirrusVGAState *s, + src = src_ - src_base; + dstpitch -= bltwidth; + srcpitch -= bltwidth; ++ ++ if (bltheight > 1 && (dstpitch < 0 || srcpitch < 0)) { ++ return; ++ } ++ + for (y = 0; y < bltheight; y++) { + for (x = 0; x < bltwidth; x++) { + p = *(dst_base + m(dst)); +@@ -143,6 +148,11 @@ glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_16)(CirrusVGAState *s, + src = src_ - src_base; + dstpitch -= bltwidth; + srcpitch -= bltwidth; ++ ++ if (bltheight > 1 && (dstpitch < 0 || srcpitch < 0)) { ++ return; ++ } ++ + for (y = 0; y < bltheight; y++) { + for (x = 0; x < bltwidth; x+=2) { + p1 = *(dst_base + m(dst)); +-- +1.8.3.1 + diff --git a/qemu.trad.CVE-2017-8309.patch b/qemu.trad.CVE-2017-8309.patch new file mode 100644 index 0000000..10b5b05 --- /dev/null +++ b/qemu.trad.CVE-2017-8309.patch @@ -0,0 +1,38 @@ +From 3268a845f41253fb55852a8429c32b50f36f349a Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Fri, 28 Apr 2017 09:56:12 +0200 +Subject: [PATCH] audio: release capture buffers + +AUD_add_capture() allocates two buffers which are never released. +Add the missing calls to AUD_del_capture(). + +Impact: Allows vnc clients to exhaust host memory by repeatedly +starting and stopping audio capture. + +Fixes: CVE-2017-8309 +Cc: P J P +Cc: Huawei PSIRT +Reported-by: "Jiangxin (hunter, SCC)" +Signed-off-by: Gerd Hoffmann +Reviewed-by: Prasad J Pandit +Message-id: 20170428075612.9997-1-kraxel@redhat.com +--- + audio/audio.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/audio/audio.c b/audio/audio.c +index c8898d8..beafed2 100644 +--- a/audio/audio.c ++++ b/audio/audio.c +@@ -2028,6 +2028,8 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque) + sw = sw1; + } + LIST_REMOVE (cap, entries); ++ qemu_free (cap->hw.mix_buf); ++ qemu_free (cap->buf); + qemu_free (cap); + } + return; +-- +1.8.3.1 + diff --git a/qemu.trad.CVE-2017-9330.patch b/qemu.trad.CVE-2017-9330.patch new file mode 100644 index 0000000..046e3e0 --- /dev/null +++ b/qemu.trad.CVE-2017-9330.patch @@ -0,0 +1,31 @@ +From 26f670a244982335cc08943fb1ec099a2c81e42d Mon Sep 17 00:00:00 2001 +From: Li Qiang +Date: Tue, 7 Feb 2017 03:15:03 -0800 +Subject: [PATCH] usb: ohci: fix error return code in servicing iso td + +It should return 1 if an error occurs when reading iso td. +This will avoid an infinite loop issue in ohci_service_ed_list. + +Signed-off-by: Li Qiang +Message-id: 5899ac3e.1033240a.944d5.9a2d@mx.google.com +Signed-off-by: Gerd Hoffmann +--- + hw/usb-ohci.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c +index c82a92f..2cba3e3 100644 +--- a/hw/usb-ohci.c ++++ b/hw/usb-ohci.c +@@ -725,7 +725,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, + + if (!ohci_read_iso_td(addr, &iso_td)) { + printf("usb-ohci: ISO_TD read error at %x\n", addr); +- return 0; ++ return 1; + } + + starting_frame = OHCI_BM(iso_td.flags, TD_SF); +-- +1.8.3.1 + diff --git a/qemu.trad.bug1399055.patch b/qemu.trad.bug1399055.patch new file mode 100644 index 0000000..69f8fd6 --- /dev/null +++ b/qemu.trad.bug1399055.patch @@ -0,0 +1,76 @@ +From 4299b90e9ba9ce5ca9024572804ba751aa1a7e70 Mon Sep 17 00:00:00 2001 +From: Prasad J Pandit +Date: Tue, 18 Oct 2016 13:15:17 +0530 +Subject: [PATCH] display: cirrus: check vga bits per pixel(bpp) value + +In Cirrus CLGD 54xx VGA Emulator, if cirrus graphics mode is VGA, +'cirrus_get_bpp' returns zero(0), which could lead to a divide +by zero error in while copying pixel data. The same could occur +via blit pitch values. Add check to avoid it. + +Reported-by: Huawei PSIRT +Signed-off-by: Prasad J Pandit +Message-id: 1476776717-24807-1-git-send-email-ppandit@redhat.com +Signed-off-by: Gerd Hoffmann +--- + hw/cirrus_vga.c | 14 ++++++++++---- + 1 files changed, 10 insertions(+), 4 deletions(-) + +diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c +index 3d712d5..bdb092e 100644 +--- a/hw/cirrus_vga.c ++++ b/hw/cirrus_vga.c +@@ -272,6 +272,9 @@ static void cirrus_update_memory_access(CirrusVGAState *s); + static bool blit_region_is_unsafe(struct CirrusVGAState *s, + int32_t pitch, int32_t addr) + { ++ if (!pitch) { ++ return true; ++ } + if (pitch < 0) { + int64_t min = addr + + ((int64_t)s->cirrus_blt_height - 1) * pitch +@@ -715,7 +718,7 @@ static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s) + s->cirrus_addr_mask)); + } + +-static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) ++static int cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) + { + int sx = 0, sy = 0; + int dx = 0, dy = 0; +@@ -729,6 +732,9 @@ static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) + int width, height; + + depth = s->get_bpp((VGAState *)s) / 8; ++ if (!depth) { ++ return 0; ++ } + s->get_resolution((VGAState *)s, &width, &height); + + /* extra x, y */ +@@ -783,6 +789,8 @@ static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); ++ ++ return 1; + } + + static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s) +@@ -790,11 +798,9 @@ static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s) + if (blit_is_unsafe(s)) + return 0; + +- cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->start_addr, ++ return cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->start_addr, + s->cirrus_blt_srcaddr - s->start_addr, + s->cirrus_blt_width, s->cirrus_blt_height); +- +- return 1; + } + + /*************************************** +-- +1.7.0.4 + diff --git a/sources b/sources index 4b30fea..cc9d5b9 100644 --- a/sources +++ b/sources @@ -4,5 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (mini-os-4.21.0.tar.xz) = 7543774d15da84476d93d04154990923c82209cb3fa125574c0383652c5a310957200f54b63b34502161f9c3afce4907e0060d9036f3eaf3a7cb6b1b3119b546 -SHA512 (xen-4.21.1.tar.xz) = 8dfe65255e202b3dacf9d0d7265636bc1f97627c11b08babc13a5b8e74c7c65e7e2c6a1513e28b3c713fe512edb6702a73b2bf667e2a8f2ce825b196a2cd5aab +SHA512 (xen-4.13.3.tar.gz) = 622127d824b9c49b57282a887fb404e0bad05ff60bccade82e4e0e9b5ad975ff9aa1fba83392e6d8379e9a15340e8ae9785c0913eb11027816e4600432eea6b6 diff --git a/xen-net-disable-iptables-on-bridge.patch b/xen-net-disable-iptables-on-bridge.patch new file mode 100644 index 0000000..bc2de21 --- /dev/null +++ b/xen-net-disable-iptables-on-bridge.patch @@ -0,0 +1,27 @@ +--- xen-4.1.0-orig/tools/hotplug/Linux/vif-bridge 2008-08-22 10:49:07.000000000 +0100 ++++ xen-4.1.0-new/tools/hotplug/Linux/vif-bridge 2008-08-29 11:29:38.000000000 +0100 +@@ -96,8 +96,6 @@ case "$command" in + ;; + esac + +-handle_iptable +- + call_hooks vif post + + log debug "Successful vif-bridge $command for $dev, bridge $bridge." +--- xen-3.3.0-orig/tools/hotplug/Linux/xen-network-common.sh 2008-08-22 10:49:07.000000000 +0100 ++++ xen-3.3.0-new/tools/hotplug/Linux/xen-network-common.sh 2008-08-29 11:29:38.000000000 +0100 +@@ -99,6 +99,13 @@ create_bridge () { + brctl addbr ${bridge} + brctl stp ${bridge} off + brctl setfd ${bridge} 0 ++ # Setting these to zero stops guest<->LAN traffic ++ # traversing the bridge from hitting the *tables ++ # rulesets. guest<->host traffic still gets processed ++ # by the host's iptables rules so this isn't a hole ++ sysctl -q -w "net.bridge.bridge-nf-call-arptables=0" ++ sysctl -q -w "net.bridge.bridge-nf-call-ip6tables=0" ++ sysctl -q -w "net.bridge.bridge-nf-call-iptables=0" + fi + } + diff --git a/xen.canonicalize.patch b/xen.canonicalize.patch index 45fa724..0130355 100644 --- a/xen.canonicalize.patch +++ b/xen.canonicalize.patch @@ -1,54 +1,56 @@ ---- xen-4.18.0-rc1/tools/xenstored/watch.c.orig 2023-09-29 09:09:29.000000000 +0100 -+++ xen-4.18.0-rc1/tools/xenstored/watch.c 2023-10-02 16:12:14.971264769 +0100 -@@ -164,7 +164,7 @@ - const char **path, bool *relative) - { - *relative = !strstarts(*path, "/") && !strstarts(*path, "@"); -- *path = canonicalize(conn, ctx, *path, true); -+ *path = xenstore_canonicalize(conn, ctx, *path, true); - - return *path ? 0 : errno; - } -@@ -250,7 +250,7 @@ +--- xen-4.9.0-rc1.2/tools/xenstore/xenstored_watch.c.orig 2017-04-12 16:18:57.000000000 +0100 ++++ xen-4.9.0-rc1.2/tools/xenstore/xenstored_watch.c 2017-04-13 21:17:12.255231094 +0100 +@@ -166,7 +166,7 @@ + /* check if valid event */ + } else { + relative = !strstarts(vec[0], "/"); +- vec[0] = canonicalize(conn, in, vec[0]); ++ vec[0] = xenstore_canonicalize(conn, in, vec[0]); + if (!vec[0]) + return ENOMEM; + if (!is_valid_nodename(vec[0])) +@@ -219,7 +219,7 @@ if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) return EINVAL; -- node = canonicalize(conn, ctx, vec[0], true); -+ node = xenstore_canonicalize(conn, ctx, vec[0], true); +- node = canonicalize(conn, in, vec[0]); ++ node = xenstore_canonicalize(conn, in, vec[0]); if (!node) - return errno; + return ENOMEM; list_for_each_entry(watch, &conn->watches, list) { ---- xen-4.18.0-rc1/tools/xenstored/core.c.orig 2023-09-29 09:09:29.000000000 +0100 -+++ xen-4.18.0-rc1/tools/xenstored/core.c 2023-10-02 16:12:14.993264626 +0100 -@@ -1249,7 +1249,7 @@ +--- xen-4.9.0-rc1.2/tools/xenstore/xenstored_core.c.orig 2017-04-12 16:18:57.000000000 +0100 ++++ xen-4.9.0-rc1.2/tools/xenstore/xenstored_core.c 2017-04-13 21:19:35.668429881 +0100 +@@ -777,7 +777,7 @@ return strings; } --const char *canonicalize(struct connection *conn, const void *ctx, -+const char *xenstore_canonicalize(struct connection *conn, const void *ctx, - const char *node, bool allow_special) +-char *canonicalize(struct connection *conn, const void *ctx, const char *node) ++char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node) { - const char *name; -@@ -1303,7 +1303,7 @@ - { - struct node *node; + const char *prefix; -- *canonical_name = canonicalize(conn, ctx, name, allow_special); -+ *canonical_name = xenstore_canonicalize(conn, ctx, name, allow_special); - if (!*canonical_name) - return NULL; +@@ -799,7 +799,7 @@ -@@ -1320,7 +1320,7 @@ - const char *tmp_name; - const struct node *node; + if (!canonical_name) + canonical_name = &tmp_name; +- *canonical_name = canonicalize(conn, ctx, name); ++ *canonical_name = xenstore_canonicalize(conn, ctx, name); + return get_node(conn, ctx, *canonical_name, perm); + } -- tmp_name = canonicalize(conn, ctx, name, allow_special); -+ tmp_name = xenstore_canonicalize(conn, ctx, name, allow_special); - if (!tmp_name) - return NULL; +--- xen-4.13.3/tools/xenstore/xenstored_core.h.orig 2021-03-22 16:57:42.000000000 +0000 ++++ xen-4.13.3/tools/xenstore/xenstored_core.h 2021-03-29 19:56:36.642394283 +0100 +@@ -153,7 +153,7 @@ + void send_ack(struct connection *conn, enum xsd_sockmsg_type type); ---- xen-4.18.0-rc1/tools/console/testsuite/console-dom0.c.orig 2023-09-29 09:09:29.000000000 +0100 -+++ xen-4.18.0-rc1/tools/console/testsuite/console-dom0.c 2023-10-02 16:12:15.001264574 +0100 + /* Canonicalize this path if possible. */ +-char *canonicalize(struct connection *conn, const void *ctx, const char *node); ++char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node); + + /* Get access permissions. */ + enum xs_perm_type perm_for_conn(struct connection *conn, +--- xen-4.8.0/tools/console/testsuite/console-dom0.c.orig 2016-12-05 12:03:27.000000000 +0000 ++++ xen-4.8.0/tools/console/testsuite/console-dom0.c 2017-02-26 21:52:24.554678631 +0000 @@ -18,7 +18,7 @@ } } @@ -85,8 +87,8 @@ fprintf(stderr, "%s", line); } while (strcmp(line, "Okay.\n") != 0); ---- xen-4.18.0-rc1/tools/console/testsuite/console-domU.c.orig 2023-09-29 09:09:29.000000000 +0100 -+++ xen-4.18.0-rc1/tools/console/testsuite/console-domU.c 2023-10-02 16:12:15.008264528 +0100 +--- xen-4.8.0/tools/console/testsuite/console-domU.c.orig 2016-12-05 12:03:27.000000000 +0000 ++++ xen-4.8.0/tools/console/testsuite/console-domU.c 2017-02-26 21:52:50.320622804 +0000 @@ -6,7 +6,7 @@ #include #include @@ -105,14 +107,3 @@ seed = strtoul(line, 0, 0); printf("Seed Okay.\n"); fflush(stdout); ---- xen-4.18.0-rc1/tools/xenstored/core.h.orig 2023-09-29 09:09:29.000000000 +0100 -+++ xen-4.18.0-rc1/tools/xenstored/core.h 2023-10-02 16:12:15.015264482 +0100 -@@ -240,7 +240,7 @@ - void send_ack(struct connection *conn, enum xsd_sockmsg_type type); - - /* Canonicalize this path if possible. */ --const char *canonicalize(struct connection *conn, const void *ctx, -+const char *xenstore_canonicalize(struct connection *conn, const void *ctx, - const char *node, bool allow_special); - - /* Get access permissions. */ diff --git a/xen.drop.brctl.patch b/xen.drop.brctl.patch new file mode 100644 index 0000000..8d51b1e --- /dev/null +++ b/xen.drop.brctl.patch @@ -0,0 +1,100 @@ +--- xen-4.11.0-rc7/tools/hotplug/Linux/colo-proxy-setup.orig 2018-06-28 08:39:45.000000000 +0100 ++++ xen-4.11.0-rc7/tools/hotplug/Linux/colo-proxy-setup 2018-07-03 20:09:26.637017216 +0100 +@@ -76,10 +76,10 @@ + + function setup_secondary() + { +- do_without_error brctl delif $bridge $vifname +- do_without_error brctl addbr $forwardbr +- do_without_error brctl addif $forwardbr $vifname +- do_without_error brctl addif $forwardbr $forwarddev ++ do_without_error ip link set $vifname nomaster ++ do_without_error ip link add name $forwardbr type bridge ++ do_without_error ip link set $vifname master $forwardbr ++ do_without_error ip link set $forwarddev master $forwardbr + do_without_error ip link set dev $forwardbr up + do_without_error modprobe xt_SECCOLO + +@@ -91,10 +91,10 @@ + + function teardown_secondary() + { +- do_without_error brctl delif $forwardbr $forwarddev +- do_without_error brctl delif $forwardbr $vifname +- do_without_error brctl delbr $forwardbr +- do_without_error brctl addif $bridge $vifname ++ do_without_error ip link set $forwarddev nomaster ++ do_without_error ip link set $vifname nomaster ++ do_without_error ip link delete $forwardbr type bridge ++ do_without_error ip link set $vifname master $bridge + + do_without_error iptables -t mangle -D PREROUTING -m physdev --physdev-in \ + $vifname -j SECCOLO --index $index +--- xen-4.11.0-rc7/tools/hotplug/Linux/vif2.orig 2018-06-28 08:39:45.000000000 +0100 ++++ xen-4.11.0-rc7/tools/hotplug/Linux/vif2 2018-07-03 20:11:07.558757301 +0100 +@@ -7,13 +7,12 @@ + bridge=$(xenstore_read_default "$XENBUS_PATH/bridge" "$bridge") + if [ -z "$bridge" ] + then +- nr_bridges=$(($(brctl show | cut -f 1 | grep -v "^$" | wc -l) - 1)) ++ nr_bridges=$(bridge link | wc -l) + if [ "$nr_bridges" != 1 ] + then + fatal "no bridge specified, and don't know which one to use ($nr_bridges found)" + fi +- bridge=$(brctl show | cut -d " +-" -f 2 | cut -f 1) ++ bridge=$(bridge link | cut -d" " -f10) + fi + + command="$1" +--- xen-4.11.0-rc7/tools/hotplug/Linux/vif-bridge.orig 2018-07-03 19:59:18.499474117 +0100 ++++ xen-4.11.0-rc7/tools/hotplug/Linux/vif-bridge 2018-07-03 20:12:31.088852864 +0100 +@@ -33,7 +33,7 @@ + + if [ -z "$bridge" ] + then +- bridge=$(brctl show | awk 'NR==2{print$1}') ++ bridge=$(bridge link | cut -d" " -f10) + + if [ -z "$bridge" ] + then +@@ -82,7 +82,7 @@ + ;; + + offline) +- do_without_error brctl delif "$bridge" "$dev" ++ do_without_error ip link set "$dev" nomaster + do_without_error ifconfig "$dev" down + ;; + +--- xen-4.11.0-rc7/tools/hotplug/Linux/xen-network-common.sh.orig 2018-07-03 19:59:18.500474154 +0100 ++++ xen-4.11.0-rc7/tools/hotplug/Linux/xen-network-common.sh 2018-07-03 20:16:16.466205182 +0100 +@@ -111,9 +111,7 @@ + + # Don't create the bridge if it already exists. + if [ ! -e "/sys/class/net/${bridge}/bridge" ]; then +- brctl addbr ${bridge} +- brctl stp ${bridge} off +- brctl setfd ${bridge} 0 ++ ip link add name ${bridge} type bridge stp_state 0 forward_delay 0 + # Setting these to zero stops guest<->LAN traffic + # traversing the bridge from hitting the *tables + # rulesets. guest<->host traffic still gets processed +@@ -134,7 +132,7 @@ + ip link set dev ${dev} up || true + return + fi +- brctl addif ${bridge} ${dev} ++ ip link set ${dev} master ${bridge} + ip link set dev ${dev} up + } + +--- xen-4.11.0-rc7/tools/qemu-xen-traditional/i386-dm/qemu-ifup-Linux.orig 2017-09-15 19:37:27.000000000 +0100 ++++ xen-4.11.0-rc7/tools/qemu-xen-traditional/i386-dm/qemu-ifup-Linux 2018-07-03 20:17:52.934780235 +0100 +@@ -34,4 +34,4 @@ + fi + + ifconfig $1 0.0.0.0 up +-brctl addif $bridge $1 || true ++ip link set $1 master $bridge || true diff --git a/xen.efi.build.patch b/xen.efi.build.patch deleted file mode 100644 index b5455df..0000000 --- a/xen.efi.build.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- xen-4.20.0-rc4/xen/arch/x86/arch.mk.orig 2025-02-07 11:56:01.000000000 +0000 -+++ xen-4.20.0-rc4/xen/arch/x86/arch.mk 2025-02-09 22:56:05.579507311 +0000 -@@ -95,7 +95,9 @@ - -c $(srctree)/$(efi-check).c -o $(efi-check).o,y) - - # Check if the linker supports PE. --EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 --enable-long-section-names -+#EFI_LDFLAGS := $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10 --enable-long-section-names -+# use a reduced set of options from LDFLAGS -+EFI_LDFLAGS = --as-needed --build-id=sha1 -mi386pep --subsystem=10 --enable-long-section-names - LD_PE_check_cmd = $(call ld-option,$(EFI_LDFLAGS) --image-base=0x100000000 -o $(efi-check).efi $(efi-check).o) - XEN_BUILD_PE := $(LD_PE_check_cmd) - diff --git a/xen.fedora.crypt.patch b/xen.fedora.crypt.patch new file mode 100644 index 0000000..7aba2d4 --- /dev/null +++ b/xen.fedora.crypt.patch @@ -0,0 +1,11 @@ +--- xen-4.5.1/tools/qemu-xen-traditional/vnc.c.orig 2015-07-12 21:55:32.875504811 +0100 ++++ xen-4.5.1/tools/qemu-xen-traditional/vnc.c 2015-07-12 22:03:03.860005391 +0100 +@@ -2140,7 +2140,7 @@ + GNUTLS_VERSION_NUMBER >= 0x020200 /* 2.2.0 */ + static int vnc_set_gnutls_priority(gnutls_session_t s, int x509) + { +- const char *priority = x509 ? "NORMAL" : "NORMAL:+ANON-DH"; ++ const char *priority = x509 ? "@SYSTEM" : "@SYSTEM:+ANON-DH"; + int rc; + + rc = gnutls_priority_set_direct(s, priority, NULL); diff --git a/xen.fedora.efi.build.patch b/xen.fedora.efi.build.patch new file mode 100644 index 0000000..36f9608 --- /dev/null +++ b/xen.fedora.efi.build.patch @@ -0,0 +1,10 @@ +--- xen-4.8.0/xen/Makefile.orig 2016-12-05 12:03:27.000000000 +0000 ++++ xen-4.8.0/xen/Makefile 2017-02-28 00:02:54.080529810 +0000 +@@ -20,6 +20,7 @@ + MAKEFLAGS += -rR + + EFI_MOUNTPOINT ?= $(BOOT_DIR)/efi ++EFI_VENDOR=fedora + + ARCH=$(XEN_TARGET_ARCH) + SRCARCH=$(shell echo $(ARCH) | sed -e 's/x86.*/x86/' -e s'/arm\(32\|64\)/arm/g') diff --git a/xen.fedora.systemd.patch b/xen.fedora.systemd.patch index 5b6a7a3..3b75ed0 100644 --- a/xen.fedora.systemd.patch +++ b/xen.fedora.systemd.patch @@ -1,6 +1,7 @@ ---- xen-4.17.0/tools/hotplug/Linux/systemd/Makefile.orig 2022-12-08 18:03:08.000000000 +0000 -+++ xen-4.17.0/tools/hotplug/Linux/systemd/Makefile 2022-12-09 19:47:53.227189371 +0000 -@@ -10,7 +10,8 @@ +diff -uN xen-4.5.0/tools/hotplug/Linux/systemd.orig/Makefile xen-4.5.0/tools/hotplug/Linux/systemd/Makefile +--- xen-4.5.0/tools/hotplug/Linux/systemd.orig/Makefile 2015-01-12 16:53:24.000000000 +0000 ++++ xen-4.5.0/tools/hotplug/Linux/systemd/Makefile 2015-01-25 22:23:26.000000000 +0000 +@@ -14,7 +14,8 @@ XEN_SYSTEMD_SERVICE += xen-qemu-dom0-disk-backend.service XEN_SYSTEMD_SERVICE += xendomains.service XEN_SYSTEMD_SERVICE += xen-watchdog.service @@ -9,7 +10,16 @@ +XEN_SYSTEMD_SERVICE += oxenstored.service XEN_SYSTEMD_SERVICE += xendriverdomain.service - ALL_XEN_SYSTEMD := $(XEN_SYSTEMD_MODULES) \ + ALL_XEN_SYSTEMD = $(XEN_SYSTEMD_MODULES) \ +diff -uN xen-4.5.0/tools/hotplug/Linux/systemd.orig/var-lib-xenstored.mount.in xen-4.5.0/tools/hotplug/Linux/systemd/var-lib-xenstored.mount.in +--- xen-4.5.0/tools/hotplug/Linux/systemd.orig/var-lib-xenstored.mount.in 2015-01-12 16:53:24.000000000 +0000 ++++ xen-4.5.0/tools/hotplug/Linux/systemd/var-lib-xenstored.mount.in 2015-01-25 22:28:59.000000000 +0000 +@@ -9,4 +9,4 @@ + What=xenstore + Where=@XEN_LIB_STORED@ + Type=tmpfs +-Options=mode=755 ++Options=mode=755,context="system_u:object_r:xenstored_var_lib_t:s0" diff -uN xen-4.5.0/tools/hotplug/Linux/systemd.orig/xenconsoled.service.in xen-4.5.0/tools/hotplug/Linux/systemd/xenconsoled.service.in --- xen-4.5.0/tools/hotplug/Linux/systemd.orig/xenconsoled.service.in 2015-01-12 16:53:24.000000000 +0000 +++ xen-4.5.0/tools/hotplug/Linux/systemd/xenconsoled.service.in 2015-01-25 22:30:26.000000000 +0000 @@ -49,26 +59,27 @@ diff -uN xen-4.5.0/tools/hotplug/Linux/systemd.orig/xen-qemu-dom0-disk-backend.s Before=xendomains.service libvirtd.service libvirt-guests.service RefuseManualStop=true ConditionPathExists=/proc/xen/capabilities ---- xen-4.17.0/tools/configure.ac.orig 2022-12-08 18:03:08.000000000 +0000 -+++ xen-4.17.0/tools/configure.ac 2022-12-09 19:50:24.773193862 +0000 -@@ -481,8 +481,8 @@ +--- xen-4.6.0/tools/configure.ac.orig 2015-02-15 16:47:22.000000000 +0000 ++++ xen-4.6.0/tools/configure.ac 2015-03-01 16:18:30.493647587 +0000 +@@ -382,9 +382,9 @@ AS_IF([test "x$systemd" = "xy"], [ AC_CONFIG_FILES([ + hotplug/Linux/systemd/oxenstored.service hotplug/Linux/systemd/proc-xen.mount + hotplug/Linux/systemd/var-lib-xenstored.mount - hotplug/Linux/systemd/xen-init-dom0.service hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service ---- xen-4.17.0/tools/configure.orig 2022-12-08 18:03:08.000000000 +0000 -+++ xen-4.17.0/tools/configure 2022-12-09 19:51:43.278708226 +0000 -@@ -10081,7 +10081,7 @@ - if test "x$systemd" = "xy" - then : +--- xen-4.6.0/tools/configure.orig 2015-02-15 16:47:22.000000000 +0000 ++++ xen-4.6.0/tools/configure 2015-03-01 16:20:10.648285840 +0000 +@@ -8995,7 +8995,7 @@ -- ac_config_files="$ac_config_files hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/xen-init-dom0.service hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" -+ ac_config_files="$ac_config_files hotplug/Linux/systemd/oxenstored.service hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" + if test "x$systemd" = "xy"; then : + +- ac_config_files="$ac_config_files hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/var-lib-xenstored.mount hotplug/Linux/systemd/xen-init-dom0.service hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" ++ ac_config_files="$ac_config_files hotplug/Linux/systemd/oxenstored.service hotplug/Linux/systemd/proc-xen.mount hotplug/Linux/systemd/var-lib-xenstored.mount hotplug/Linux/systemd/xen-qemu-dom0-disk-backend.service hotplug/Linux/systemd/xen-watchdog.service hotplug/Linux/systemd/xenconsoled.service hotplug/Linux/systemd/xendomains.service hotplug/Linux/systemd/xendriverdomain.service hotplug/Linux/systemd/xenstored.service" fi diff --git a/xen.gcc11.fixes.patch b/xen.gcc11.fixes.patch deleted file mode 100644 index 6971080..0000000 --- a/xen.gcc11.fixes.patch +++ /dev/null @@ -1,24 +0,0 @@ ---- xen-4.14.0/xen/include/crypto/vmac.h.orig 2020-07-23 16:07:51.000000000 +0100 -+++ xen-4.14.0/xen/include/crypto/vmac.h 2020-10-24 15:45:49.246467465 +0100 -@@ -142,7 +142,7 @@ - - #define vmac_update vhash_update - --void vhash_update(unsigned char m[], -+void vhash_update(uint8_t *m, - unsigned int mbytes, - vmac_ctx_t *ctx); - -diff --git a/xen/arch/x86/tboot.c b/xen/arch/x86/tboot.c -index 320e06f..618ae92 100644 ---- a/xen/arch/x86/tboot.c -+++ b/xen/arch/x86/tboot.c -@@ -91,7 +91,7 @@ static void __init tboot_copy_memory(unsigned char *va, uint32_t size, - - void __init tboot_probe(void) - { -- tboot_shared_t *tboot_shared; -+ tboot_shared_t * volatile tboot_shared; - static const uuid_t __initconst tboot_shared_uuid = TBOOT_SHARED_UUID; - - /* Look for valid page-aligned address for shared page. */ diff --git a/xen.gcc12.fixes.patch b/xen.gcc12.fixes.patch deleted file mode 100644 index b35440f..0000000 --- a/xen.gcc12.fixes.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- xen-4.16.0/Config.mk.orig 2021-11-30 11:42:42.000000000 +0000 -+++ xen-4.16.0/Config.mk 2022-01-24 20:25:16.687125822 +0000 -@@ -186,6 +186,7 @@ - - $(call cc-option-add,CFLAGS,CC,-Wno-unused-but-set-variable) - $(call cc-option-add,CFLAGS,CC,-Wno-unused-local-typedefs) -+$(call cc-option-add,CFLAGS,CC,-Wno-error=array-bounds) - - LDFLAGS += $(foreach i, $(EXTRA_LIB), -L$(i)) - CFLAGS += $(foreach i, $(EXTRA_INCLUDES), -I$(i)) diff --git a/xen.gcc7.fix.patch b/xen.gcc7.fix.patch new file mode 100644 index 0000000..b18ba2b --- /dev/null +++ b/xen.gcc7.fix.patch @@ -0,0 +1,12 @@ +--- xen-4.8.0/extras/mini-os/Makefile.orig 2016-09-28 12:09:38.000000000 +0100 ++++ xen-4.8.0/extras/mini-os/Makefile 2017-02-15 21:15:19.340197960 +0000 +@@ -142,6 +142,9 @@ + APP_LDLIBS += -lz + APP_LDLIBS += -lm + LDLIBS += -lc ++ifeq ($(MINIOS_TARGET_ARCH),x86_32) ++LDLIBS += -L$(shell dirname `gcc -m32 -print-libgcc-file-name`) -lgcc ++endif + endif + + ifneq ($(APP_OBJS)-$(lwip),-y) diff --git a/xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch b/xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch deleted file mode 100644 index a5e65ba..0000000 --- a/xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3 Mon Sep 17 00:00:00 2001 -From: Andrew Cooper -Date: Fri, 10 Apr 2026 21:55:46 +0100 -Subject: [PATCH] x86/amd: Mitigate AMD-SN-7053 / FP-DSS -MIME-Version: 1.0 -Content-Type: text/plain; charset=utf8 -Content-Transfer-Encoding: 8bit - -This is XSA-488 / CVE-2025-54505 - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné -(cherry picked from commit 99912d346009fda1e7fb1510c9501fbab17e92a0) ---- - xen/arch/x86/cpu/amd.c | 37 ++++++++++++++++++++++++++++ - xen/arch/x86/include/asm/msr-index.h | 1 + - 2 files changed, 38 insertions(+) - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index 8c55d233f3..1bb0766ebf 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -1048,6 +1048,42 @@ void amd_init_de_cfg(const struct cpuinfo_x86 *c) - wrmsrl(MSR_AMD64_DE_CFG, val | new); - } - -+static void amd_init_fp_cfg(const struct cpuinfo_x86 *c) -+{ -+ uint64_t val, new = 0; -+ -+ /* If virtualised, we won't have mutable access even if we can read it. */ -+ if ( cpu_has_hypervisor ) -+ return; -+ -+ /* -+ * On Zen1, mitigate SB-7053 / FP-DSS Floating Point Divider State -+ * Sampling by setting bit 9 as instructed. -+ */ -+ if ( c->family == 0x17 && is_zen1_uarch() ) -+ new |= 1 << 9; -+ -+ /* -+ * Avoid reading FP_CFG if we don't intend to change anything. The -+ * register doesn't exist on all families. -+ */ -+ if ( !new ) -+ return; -+ -+ val = rdmsr(MSR_AMD64_FP_CFG); -+ -+ if ( (val & new) == new ) -+ return; -+ -+ /* -+ * FP_CFG is a Core-scoped MSR, and this write is racy. However, both -+ * threads calculate the new value from state which expected to be -+ * consistent across CPUs and unrelated to the old value, so the result -+ * should be consistent. -+ */ -+ wrmsr(MSR_AMD64_FP_CFG, val | new); -+} -+ - void __init amd_init_lfence_dispatch(void) - { - struct cpuinfo_x86 *c = &boot_cpu_data; -@@ -1120,6 +1156,7 @@ static void cf_check init_amd(struct cpuinfo_x86 *c) - uint64_t value; - - amd_init_de_cfg(c); -+ amd_init_fp_cfg(c); - - if (c == &boot_cpu_data) - amd_init_lfence_dispatch(); /* Needs amd_init_de_cfg() */ -diff --git a/xen/arch/x86/include/asm/msr-index.h b/xen/arch/x86/include/asm/msr-index.h -index df52587c85..6c5b2569e1 100644 ---- a/xen/arch/x86/include/asm/msr-index.h -+++ b/xen/arch/x86/include/asm/msr-index.h -@@ -428,6 +428,7 @@ - #define MSR_AMD64_LS_CFG 0xc0011020U - #define MSR_AMD64_IC_CFG 0xc0011021U - #define MSR_AMD64_DC_CFG 0xc0011022U -+#define MSR_AMD64_FP_CFG 0xc0011028U - #define MSR_AMD64_DE_CFG 0xc0011029U - #define AMD64_DE_CFG_LFENCE_SERIALISE (_AC(1, ULL) << 1) - #define MSR_AMD64_EX_CFG 0xc001102cU --- -2.39.5 - diff --git a/xen.hypervisor.config b/xen.hypervisor.config index 7f11043..4f2bb09 100644 --- a/xen.hypervisor.config +++ b/xen.hypervisor.config @@ -1,172 +1,101 @@ # # Automatically generated file; DO NOT EDIT. -# Xen/x86 4.20 Configuration +# Xen/x86 4.13.0 Configuration # -CONFIG_CC_IS_GCC=y -CONFIG_GCC_VERSION=150001 -CONFIG_CLANG_VERSION=0 -CONFIG_LD_IS_GNU=y -CONFIG_CC_HAS_VISIBILITY_ATTRIBUTE=y -CONFIG_CC_SPLIT_SECTIONS=y -CONFIG_FUNCTION_ALIGNMENT_16B=y -CONFIG_FUNCTION_ALIGNMENT=16 CONFIG_X86_64=y CONFIG_X86=y CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" -CONFIG_CC_HAS_INDIRECT_THUNK=y -CONFIG_HAS_AS_CET_SS=y -CONFIG_HAS_CC_CET_IBT=y # # Architecture Features # -CONFIG_AMD=y -CONFIG_INTEL=y -CONFIG_64BIT=y CONFIG_NR_CPUS=256 -CONFIG_NR_NUMA_NODES=64 CONFIG_PV=y -CONFIG_PV32=y CONFIG_PV_LINEAR_PT=y CONFIG_HVM=y -CONFIG_AMD_SVM=y -CONFIG_INTEL_VMX=y -CONFIG_XEN_SHSTK=y -CONFIG_XEN_IBT=y CONFIG_SHADOW_PAGING=y # CONFIG_BIGMEM is not set -CONFIG_HVM_FEP=y -CONFIG_X86_PSR=y +# CONFIG_HVM_FEP is not set +CONFIG_TBOOT=y CONFIG_XEN_ALIGN_DEFAULT=y # CONFIG_XEN_ALIGN_2M is not set -# CONFIG_X2APIC_PHYSICAL is not set -CONFIG_X2APIC_MIXED=y # CONFIG_XEN_GUEST is not set -# CONFIG_HYPERV_GUEST is not set -# CONFIG_REQUIRE_NX is not set -CONFIG_ALTP2M=y -# end of Architecture Features # # Common Features # CONFIG_COMPAT=y CONFIG_CORE_PARKING=y -CONFIG_GRANT_TABLE=y -CONFIG_ALTERNATIVE_CALL=y -CONFIG_ARCH_MAP_DOMAIN_PAGE=y -CONFIG_GENERIC_BUG_FRAME=y CONFIG_HAS_ALTERNATIVE=y -CONFIG_HAS_COMPAT=y -CONFIG_HAS_DIT=y CONFIG_HAS_EX_TABLE=y -CONFIG_HAS_FAST_MULTIPLY=y -CONFIG_HAS_IOPORTS=y -CONFIG_HAS_KEXEC=y -CONFIG_HAS_PIRQ=y -CONFIG_HAS_SCHED_GRANULARITY=y -CONFIG_HAS_UBSAN=y -CONFIG_HAS_VMAP=y CONFIG_MEM_ACCESS_ALWAYS_ON=y CONFIG_MEM_ACCESS=y +CONFIG_HAS_MEM_PAGING=y +CONFIG_HAS_MEM_SHARING=y +CONFIG_HAS_PDX=y +CONFIG_HAS_UBSAN=y +CONFIG_HAS_KEXEC=y +CONFIG_HAS_GDBSX=y +CONFIG_HAS_IOPORTS=y CONFIG_NEEDS_LIBELF=y -CONFIG_NUMA=y # # Speculative hardening # -CONFIG_INDIRECT_THUNK=y -CONFIG_RETURN_THUNK=y CONFIG_SPECULATIVE_HARDEN_ARRAY=y CONFIG_SPECULATIVE_HARDEN_BRANCH=y -CONFIG_SPECULATIVE_HARDEN_GUEST_ACCESS=y -CONFIG_SPECULATIVE_HARDEN_LOCK=y -# end of Speculative hardening - -# CONFIG_DIT_DEFAULT is not set -CONFIG_HYPFS=y -CONFIG_HYPFS_CONFIG=y -CONFIG_IOREQ_SERVER=y CONFIG_KEXEC=y +CONFIG_XENOPROF=y # CONFIG_XSM is not set CONFIG_SCHED_CREDIT=y CONFIG_SCHED_CREDIT2=y CONFIG_SCHED_RTDS=y -CONFIG_SCHED_ARINC653=y +# CONFIG_SCHED_ARINC653 is not set CONFIG_SCHED_NULL=y CONFIG_SCHED_DEFAULT="credit2" -# CONFIG_BOOT_TIME_CPUPOOLS is not set +CONFIG_CRYPTO=y CONFIG_LIVEPATCH=y CONFIG_FAST_SYMBOL_LOOKUP=y CONFIG_ENFORCE_UNIQUE_SYMBOLS=y CONFIG_CMDLINE="" CONFIG_DOM0_MEM="" -CONFIG_DTB_FILE="" -CONFIG_TRACEBUFFER=y -# end of Common Features # # Device Drivers # CONFIG_ACPI=y CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y -CONFIG_ACPI_NUMA=y +CONFIG_NUMA=y CONFIG_HAS_NS16550=y CONFIG_HAS_EHCI=y -CONFIG_SERIAL_TX_BUFSIZE=32768 -# CONFIG_XHCI is not set CONFIG_HAS_CPUFREQ=y CONFIG_HAS_PASSTHROUGH=y -CONFIG_AMD_IOMMU=y -CONFIG_INTEL_IOMMU=y -# CONFIG_IOMMU_QUARANTINE_NONE is not set -CONFIG_IOMMU_QUARANTINE_BASIC=y -# CONFIG_IOMMU_QUARANTINE_SCRATCH_PAGE is not set CONFIG_HAS_PCI=y -CONFIG_HAS_PCI_MSI=y CONFIG_VIDEO=y CONFIG_VGA=y CONFIG_HAS_VPCI=y -# end of Device Drivers -# CONFIG_EXPERT is not set -# CONFIG_UNSUPPORTED is not set +# +# Deprecated Functionality +# +# CONFIG_PV_LDT_PAGING is not set +CONFIG_DEFCONFIG_LIST="$ARCH_DEFCONFIG" CONFIG_ARCH_SUPPORTS_INT128=y -CONFIG_ARCH_VCPU_IOREQ_COMPLETION=y # # Debugging Options # # CONFIG_DEBUG is not set -CONFIG_GDBSX=y -CONFIG_FRAME_POINTER=y -CONFIG_SELF_TESTS=y -# CONFIG_DEBUG_LOCK_PROFILE is not set -CONFIG_DEBUG_LOCKS=y -# CONFIG_PERF_COUNTERS is not set -CONFIG_VERBOSE_DEBUG=y -CONFIG_SCRUB_DEBUG=y -# CONFIG_UBSAN is not set -# CONFIG_DEBUG_TRACE is not set -CONFIG_XMEM_POOL_POISON=y -CONFIG_DEBUG_INFO=y -# end of Debugging Options # ARM64 settings -CONFIG_MMU=y +CONFIG_64BIT=y CONFIG_ARM_64=y CONFIG_ARM=y -CONFIG_ARM_EFI=y -CONFIG_GICV2=y CONFIG_GICV3=y -CONFIG_VGICV2=y # CONFIG_NEW_VGIC is not set CONFIG_SBSA_VUART_CONSOLE=y -CONFIG_HWDOM_VUART=y CONFIG_ARM_SSBD=y CONFIG_HARDEN_BRANCH_PREDICTOR=y -CONFIG_STATIC_EVTCHN=y -CONFIG_PARTIAL_EMULATION=y # # ARM errata workaround via the alternative framework @@ -178,11 +107,6 @@ CONFIG_ARM64_ERRATUM_843419=y CONFIG_ARM64_ERRATUM_832075=y CONFIG_ARM64_ERRATUM_834220=y CONFIG_ARM_ERRATUM_858921=y -CONFIG_ARM64_WORKAROUND_REPEAT_TLBI=y -CONFIG_ARM64_ERRATUM_1286807=y -CONFIG_ARM64_ERRATUM_1508412=y - -# end of ARM errata workaround via the alternative framework CONFIG_ARM64_HARDEN_BRANCH_PREDICTOR=y CONFIG_ALL_PLAT=y # CONFIG_QEMU is not set @@ -191,18 +115,17 @@ CONFIG_ALL_PLAT=y # CONFIG_NO_PLAT is not set CONFIG_ALL64_PLAT=y CONFIG_MPSOC_PLATFORM=y - -# -# Common Features -# CONFIG_HAS_DEVICE_TREE=y CONFIG_HAS_CADENCE_UART=y -CONFIG_HAS_LINFLEX=y -CONFIG_HAS_IMX_LPUART=y CONFIG_HAS_MVEBU=y CONFIG_HAS_MESON=y CONFIG_HAS_PL011=y -CONFIG_HAS_OMAP=y CONFIG_HAS_SCIF=y CONFIG_ARM_SMMU=y -# CONFIG_IPMMU_VMSA is not set + +# ARM32 settings +CONFIG_ALL32_PLAT=y +CONFIG_ARM32_HARDEN_BRANCH_PREDICTOR=y +CONFIG_ARM_32=y +CONFIG_HAS_EXYNOS4210=y +CONFIG_HAS_OMAP=y diff --git a/xen.json.nocpuid.patch b/xen.json.nocpuid.patch deleted file mode 100644 index f701f0d..0000000 --- a/xen.json.nocpuid.patch +++ /dev/null @@ -1,27 +0,0 @@ ---- xen-4.21.0/tools/libs/light/libxl_nocpuid.c.orig 2025-11-18 18:02:13.000000000 +0000 -+++ xen-4.21.0/tools/libs/light/libxl_nocpuid.c 2025-11-20 09:03:56.517804514 +0000 -@@ -40,11 +40,24 @@ - return 0; - } - -+#ifdef HAVE_LIBJSONC -+#ifndef _hidden -+#define _hidden -+#endif -+_hidden int libxl_cpuid_policy_list_gen_jso(json_object **jso_r, -+ libxl_cpuid_policy_list *pcpuid) -+{ -+ return 0; -+} -+#endif -+ -+#if defined(HAVE_LIBYAJL) - yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand, - libxl_cpuid_policy_list *pcpuid) - { - return 0; - } -+#endif - - int libxl__cpuid_policy_list_parse_json(libxl__gc *gc, - const libxl__json_object *o, diff --git a/xen.ocaml.4.10.patch b/xen.ocaml.4.10.patch new file mode 100644 index 0000000..f6692d0 --- /dev/null +++ b/xen.ocaml.4.10.patch @@ -0,0 +1,58 @@ +--- xen-4.13.0/tools/ocaml/libs/xc/xenctrl_stubs.c.orig 2019-12-17 14:23:09.000000000 +0000 ++++ xen-4.13.0/tools/ocaml/libs/xc/xenctrl_stubs.c 2020-01-21 19:24:49.508024245 +0000 +@@ -46,7 +46,7 @@ + #define Val_none (Val_int(0)) + + #define string_of_option_array(array, index) \ +- ((Field(array, index) == Val_none) ? NULL : String_val(Field(Field(array, index), 0))) ++ ((Field(array, index) == Val_none) ? NULL : (char *) Bp_val(Field(Field(array, index), 0))) + + /* maybe here we should check the range of the input instead of blindly + * casting it to uint32 */ +@@ -505,7 +505,7 @@ + ret = xc_vcpu_getcontext(_H(xch), _D(domid), Int_val(cpu), &ctxt); + + context = caml_alloc_string(sizeof(ctxt)); +- memcpy((char *) String_val(context), &ctxt.c, sizeof(ctxt.c)); ++ memcpy((char *) Bp_val(context), (char *) &ctxt.c, sizeof(ctxt.c)); + + CAMLreturn(context); + } +@@ -684,7 +684,7 @@ + conring_size = size; + + ring = caml_alloc_string(count); +- memcpy((char *) String_val(ring), str, count); ++ memcpy((char *) Bp_val(ring), str, count); + free(str); + + CAMLreturn(ring); +@@ -695,7 +695,7 @@ + CAMLparam2(xch, keys); + int r; + +- r = xc_send_debug_keys(_H(xch), String_val(keys)); ++ r = xc_send_debug_keys(_H(xch), (char *) Bp_val(keys)); + if (r) + failwith_xc(_H(xch)); + CAMLreturn(Val_unit); +@@ -855,7 +855,7 @@ + } + + for (r = 0; r < 4; r++) +- out_config[r] = (c_config[r]) ? String_val(Field(Field(array, r), 0)) : NULL; ++ out_config[r] = (c_config[r]) ? (char *) Bp_val(Field(Field(array, r), 0)) : NULL; + + r = xc_cpuid_set(_H(xch), _D(domid), + c_input, (const char **)c_config, out_config); +--- xen-4.13.0/tools/ocaml/libs/xb/xenbus_stubs.c.orig 2019-12-17 14:23:09.000000000 +0000 ++++ xen-4.13.0/tools/ocaml/libs/xb/xenbus_stubs.c 2020-01-22 00:04:09.443168991 +0000 +@@ -65,7 +65,7 @@ + }; + + ret = caml_alloc_string(sizeof(struct xsd_sockmsg)); +- memcpy((char *) String_val(ret), &xsd, sizeof(struct xsd_sockmsg)); ++ memcpy((char *) Bp_val(ret), &xsd, sizeof(struct xsd_sockmsg)); + + CAMLreturn(ret); + } diff --git a/xen.python.env.patch b/xen.python.env.patch new file mode 100644 index 0000000..3b8c1e6 --- /dev/null +++ b/xen.python.env.patch @@ -0,0 +1,43 @@ +--- xen-4.11.0/tools/xenmon/Makefile.orig 2018-07-09 14:47:19.000000000 +0100 ++++ xen-4.11.0/tools/xenmon/Makefile 2018-09-10 21:13:15.200655105 +0100 +@@ -32,7 +32,7 @@ + $(INSTALL_DIR) $(DESTDIR)$(sbindir) + $(INSTALL_PROG) xenbaked $(DESTDIR)$(sbindir)/xenbaked + $(INSTALL_PROG) xentrace_setmask $(DESTDIR)$(sbindir)/xentrace_setmask +- $(INSTALL_PROG) xenmon.py $(DESTDIR)$(sbindir)/xenmon ++ $(INSTALL_PYTHON_PROG) xenmon.py $(DESTDIR)$(sbindir)/xenmon + + .PHONY: uninstall + uninstall: +--- xen-4.11.0/tools/python/Makefile.orig 2018-07-09 14:47:19.000000000 +0100 ++++ xen-4.11.0/tools/python/Makefile 2018-09-10 21:21:07.097979007 +0100 +@@ -20,8 +20,8 @@ + setup.py install --record $(INSTALL_LOG) $(PYTHON_PREFIX_ARG) \ + --root="$(DESTDIR)" --force + +- $(INSTALL_PROG) scripts/convert-legacy-stream $(DESTDIR)$(LIBEXEC_BIN) +- $(INSTALL_PROG) scripts/verify-stream-v2 $(DESTDIR)$(LIBEXEC_BIN) ++ $(INSTALL_PYTHON_PROG) scripts/convert-legacy-stream $(DESTDIR)$(LIBEXEC_BIN) ++ $(INSTALL_PYTHON_PROG) scripts/verify-stream-v2 $(DESTDIR)$(LIBEXEC_BIN) + + .PHONY: uninstall + uninstall: +--- xen-4.11.0/tools/python/install-wrap.orig 2018-07-09 14:47:19.000000000 +0100 ++++ xen-4.11.0/tools/python/install-wrap 2018-09-11 20:09:57.803655357 +0100 +@@ -44,7 +44,7 @@ + destf="$dest" + for srcf in ${srcs}; do + if test -d "$dest"; then +- destf="$dest/${srcf%%*/}" ++ destf="$dest/${srcf##*/}" + fi + org="$(sed -n '2q; /^#! *\/usr\/bin\/env python *$/p' $srcf)" + if test "x$org" = x; then +--- xen-4.11.0/tools/misc/xencov_split.orig 2018-07-09 14:47:19.000000000 +0100 ++++ xen-4.11.0/tools/misc/xencov_split 2018-09-18 21:56:07.397893895 +0100 +@@ -1,4 +1,4 @@ +-#!/usr/bin/python ++#!/usr/bin/python3 + + import sys, os, os.path as path, struct, errno + from optparse import OptionParser diff --git a/xen.python3.12.patch b/xen.python3.12.patch deleted file mode 100644 index a6539c2..0000000 --- a/xen.python3.12.patch +++ /dev/null @@ -1,22 +0,0 @@ ---- xen-4.17.1/tools/python/Makefile.orig 2023-04-27 13:53:19.000000000 +0100 -+++ xen-4.17.1/tools/python/Makefile 2023-06-22 22:21:25.287486906 +0100 -@@ -4,7 +4,7 @@ - .PHONY: all - all: build - --PY_CFLAGS = $(CFLAGS) $(PY_NOOPT_CFLAGS) -+PY_CFLAGS = $(CFLAGS) $(PY_NOOPT_CFLAGS) -Wno-error=declaration-after-statement - PY_LDFLAGS = $(SHLIB_LDFLAGS) $(APPEND_LDFLAGS) - INSTALL_LOG = build/installed_files.txt - ---- xen-4.17.1/tools/pygrub/Makefile.orig 2023-04-27 13:53:19.000000000 +0100 -+++ xen-4.17.1/tools/pygrub/Makefile 2023-06-22 22:52:52.803047401 +0100 -@@ -2,7 +2,7 @@ - XEN_ROOT = $(CURDIR)/../.. - include $(XEN_ROOT)/tools/Rules.mk - --PY_CFLAGS = $(CFLAGS) $(PY_NOOPT_CFLAGS) -+PY_CFLAGS = $(CFLAGS) $(PY_NOOPT_CFLAGS) -Wno-error=declaration-after-statement - PY_LDFLAGS = $(SHLIB_LDFLAGS) $(APPEND_LDFLAGS) - INSTALL_LOG = build/installed_files.txt - diff --git a/xen.spec b/xen.spec index 1bea8ef..7a5d044 100644 --- a/xen.spec +++ b/xen.spec @@ -6,12 +6,18 @@ %define build_docs %{?_without_docs: 0} %{?!_without_docs: 1} # Build with stubdom unless rpmbuild was run with --without stubdom %define build_stubdom %{?_without_stubdom: 0} %{?!_without_stubdom: 1} +# Build with qemu-traditional unless rpmbuild was run with --without qemutrad +%define build_qemutrad %{?_without_qemutrad: 0} %{?!_without_qemutrad: 1} # build with ovmf from edk2-ovmf unless rpmbuild was run with --without ovmf %define build_ovmf %{?_without_ovmf: 0} %{?!_without_ovmf: 1} -# set to 0 for archs that don't use ovmf (reduces build dependencies) -%ifnarch x86_64 +# set to 0 for archs that don't use qemu or ovmf (reduces build dependencies) +%ifnarch x86_64 %{ix86} +%define build_qemutrad 0 %define build_ovmf 0 %endif +%if ! %build_qemutrad +%define build_stubdom 0 +%endif # Build with xen hypervisor unless rpmbuild was run with --without hyp %define build_hyp %{?_without_hyp: 0} %{?!_without_hyp: 1} # build xsm support unless rpmbuild was run with --without xsm @@ -36,7 +42,8 @@ # --without efi %define build_efi %{?_without_efi: 0} %{?!_without_efi: 1} # xen only supports efi boot images on x86_64 or aarch64 -%ifnarch x86_64 aarch64 +# i686 builds a x86_64 hypervisor so add that as well +%ifnarch x86_64 aarch64 %{ix86} %define build_efi 0 %endif %if "%dist" >= ".fc20" @@ -46,16 +53,15 @@ %endif # Hypervisor ABI -%define hv_abi 4.21 +%define hv_abi 4.13 Summary: Xen is a virtual machine monitor Name: xen -Version: 4.21.1 -Release: 10%{?dist} -# Automatically converted from old format: GPLv2+ and LGPLv2+ and BSD - review is highly recommended. -License: GPL-2.0-or-later AND LicenseRef-Callaway-LGPLv2+ AND LicenseRef-Callaway-BSD +Version: 4.13.3 +Release: 1%{?dist} +License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ -Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.xz +Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz Source2: %{name}.logrotate # used by stubdoms Source10: lwip-1.3.0.tar.gz @@ -66,60 +72,67 @@ Source14: grub-0.97.tar.gz Source15: polarssl-1.1.4-gpl.tgz # .config file for xen hypervisor Source21: xen.hypervisor.config -# mini-os xen-RELEASE-4.21.0 with .git and .gitignore stripped -Source22: mini-os-4.21.0.tar.xz -Patch1: xen.fedora.systemd.patch -Patch2: xen.ocaml.selinux.fix.patch -Patch3: xen.canonicalize.patch -Patch4: droplibvirtconflict.patch -Patch5: xen.gcc9.fixes.patch -Patch6: xen.gcc11.fixes.patch -Patch7: xen.gcc12.fixes.patch -Patch8: xen.efi.build.patch -Patch9: xen.python3.12.patch -Patch11: xen.json.nocpuid.patch -Patch12: xsa483.patch -Patch13: xsa484.patch -Patch14: xsa486.patch -Patch15: xen.git-90b20547b756a5cf9b0fec9fb0de5b361e8bf4c3.patch -Patch16: xsa490-4.21.patch -Patch17: xsa491-4.21.patch -Patch18: xsa492-4.21-01.patch -Patch19: xsa492-4.21-02.patch -Patch20: xsa492-4.21-03.patch -Patch21: xsa492-4.21-04.patch -Patch22: xsa492-4.21-05.patch -Patch23: xsa492-4.21-06.patch -Patch24: xsa492-4.21-07.patch -Patch25: xsa492-4.21-08.patch -Patch26: xsa492-4.21-09.patch -Patch27: xsa492-4.21-10.patch -Patch28: xsa492-4.21-11.patch -Patch29: xsa492-4.21-12.patch -Patch30: xsa492-4.21-13.patch -Patch31: xsa492-4.21-14.patch -Patch32: xsa492-4.21-15.patch -Patch33: xsa492-4.21-16.patch -Patch34: xsa492-4.21-17.patch -Patch35: xsa492-4.21-18.patch -Patch36: xsa492-4.21-19.patch -Patch37: xsa492-4.21-20.patch -Patch38: xsa493-4.21-01.patch -Patch39: xsa493-4.21-02.patch -Patch40: xsa493-4.21-03.patch -Patch41: xsa493-4.21-04.patch -Patch42: xsa494-4.21.patch +Patch1: xen-net-disable-iptables-on-bridge.patch +Patch3: xen.fedora.efi.build.patch +Patch4: CVE-2014-0150.patch +Patch5: xen.fedora.systemd.patch +Patch6: xen.ocaml.selinux.fix.patch +Patch7: xen.fedora.crypt.patch +Patch8: qemu.trad.CVE-2015-6815.patch +Patch9: qemu.trad.CVE-2015-5279.patch +Patch10: qemu.trad.CVE-2015-5278.patch +Patch11: qemu.trad.CVE-2015-7295.patch +Patch12: qemu.trad.CVE-2015-8345.patch +Patch13: qemu.trad.CVE-2015-7512.patch +Patch14: qemu.trad.CVE-2015-8504.patch +Patch15: qemu.trad.CVE-2016-1714.patch +Patch16: qemu.trad.CVE-2016-1981.patch +Patch17: qemu.trad.CVE-2016-2841.patch +Patch18: qemu.trad.CVE-2016-2538.patch +Patch19: qemu.trad.CVE-2016-2857.patch +Patch20: qemu.trad.CVE-2016-4001.patch +Patch21: qemu.trad.CVE-2016-4002.patch +Patch22: qemu.trad.CVE-2016-4439.patch +Patch23: qemu.trad.CVE-2016-4441.patch +Patch24: qemu.trad.CVE-2016-5238.patch +Patch25: qemu.trad.CVE-2016-5338.patch +Patch27: qemu.trad.CVE-2016-6351.patch +Patch29: qemu.trad.CVE-2016-8669.patch +Patch30: qemu.trad.CVE-2016-8910.patch +Patch31: qemu.trad.bug1399055.patch +Patch32: qemu.trad.CVE-2016-9776.patch +Patch33: xen.gcc7.fix.patch +Patch34: xen.canonicalize.patch +Patch35: qemu.trad.CVE-2017-6505.patch +Patch36: qemu.trad.CVE-2017-7718.patch +Patch37: droplibvirtconflict.patch +Patch38: qemu.trad.CVE-2017-8309.patch +Patch39: qemu.trad.CVE-2017-9330.patch +Patch40: xen.drop.brctl.patch +Patch41: xen.python.env.patch +Patch42: xen.gcc9.fixes.patch +Patch44: xen.ocaml.4.10.patch +Patch65: zstd-dom0.patch +Patch100: xsa363.patch +%if %build_qemutrad +BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel +BuildRequires: libX11-devel gtk2-devel libaio-devel # build using Fedora seabios and ipxe packages for roms BuildRequires: seabios-bin ipxe-roms-qemu %ifarch %{ix86} x86_64 # for the VMX "bios" BuildRequires: dev86 %endif -BuildRequires: python3-devel ncurses-devel python3-setuptools +%endif +BuildRequires: python3-devel ncurses-devel BuildRequires: perl-interpreter perl-generators +%ifarch %{ix86} x86_64 +# so that x86_64 builds pick up glibc32 correctly +BuildRequires: /usr/include/gnu/stubs-32.h +%endif BuildRequires: gettext BuildRequires: gnutls-devel BuildRequires: openssl-devel @@ -130,13 +143,11 @@ BuildRequires: libuuid-devel # iasl needed to build hvmloader BuildRequires: acpica-tools # modern compressed kernels -BuildRequires: bzip2-devel xz-devel libzstd-devel +BuildRequires: bzip2-devel xz-devel # libfsimage BuildRequires: e2fsprogs-devel -# tools now require wget -BuildRequires: wget -# use json-c instead of yajl -BuildRequires: json-c-devel +# tools now require yajl and wget +BuildRequires: yajl-devel wget # remus support now needs libnl3 BuildRequires: libnl3-devel %if %with_xsm @@ -147,7 +158,7 @@ BuildRequires: checkpolicy m4 # cross compiler for building 64-bit hypervisor on ix86 BuildRequires: gcc-x86_64-linux-gnu %endif -BuildRequires: gcc make +BuildRequires: gcc Requires: iproute Requires: python3-lxml Requires: xen-runtime = %{version}-%{release} @@ -155,10 +166,10 @@ Requires: xen-runtime = %{version}-%{release} # now for accessing domU data from within a dom0 so bring it in when the user # installs xen. Requires: kpartx -ExclusiveArch: x86_64 aarch64 +ExclusiveArch: %{ix86} x86_64 armv7hl aarch64 +#ExclusiveArch: %#{ix86} x86_64 ia64 noarch %if %with_ocaml BuildRequires: ocaml, ocaml-findlib -BuildRequires: perl(Data::Dumper) %endif %if %with_systemd_presets Requires(post): systemd @@ -166,13 +177,12 @@ Requires(preun): systemd BuildRequires: systemd %endif BuildRequires: systemd-devel -%ifarch aarch64 +%ifarch armv7hl aarch64 BuildRequires: libfdt-devel %endif -%if %build_hyp -BuildRequires: bison flex +%if %build_ovmf +BuildRequires: edk2-ovmf %endif -BuildRequires: hostname %description This package contains the XenD daemon and xm command line @@ -198,12 +208,11 @@ Requires: /usr/bin/qemu-img Requires: xen-hypervisor-abi = %{hv_abi} # perl is used in /etc/xen/scripts/locking.sh Recommends: perl -%ifnarch aarch64 +%ifnarch armv7hl aarch64 # use /usr/bin/qemu-system-i386 in Fedora instead of qemu-xen Recommends: qemu-system-x86-core -%endif -%if %build_ovmf -Recommends: edk2-ovmf-xen +# rom file for qemu-xen-traditional +Recommends: ipxe-roms-qemu %endif %description runtime @@ -215,14 +224,6 @@ form the core Xen userspace environment. Summary: Libraries for Xen tools Provides: xen-hypervisor-abi = %{hv_abi} Requires: xen-licenses -%if %build_hyp -%ifarch %{ix86} -Recommends: grub2-pc-modules -%endif -%ifarch x86_64 -Recommends: grub2-pc-modules grub2-efi-x64-modules -%endif -%endif %description hypervisor This package contains the Xen hypervisor @@ -234,8 +235,14 @@ Summary: Xen documentation BuildArch: noarch Requires: xen-licenses # for the docs -BuildRequires: perl(Pod::Man) perl(Pod::Text) perl(File::Find) -BuildRequires: transfig pandoc perl(Pod::Html) +%if "%dist" >= ".fc18" +BuildRequires: texlive-times texlive-courier texlive-helvetic texlive-ntgclass +%endif +BuildRequires: transfig texi2html ghostscript texlive-latex +BuildRequires: perl(Pod::Man) perl(Pod::Text) texinfo graphviz +# optional requires for more documentation +#BuildRequires: pandoc discount +BuildRequires: discount %description doc This package contains the Xen documentation. @@ -279,62 +286,65 @@ This package contains libraries for developing ocaml tools to manage Xen virtual machines. %endif -%package test -Summary: internal xen tests -%description test -This package contains files used in testing the xen builds %prep %setup -q -%patch 1 -p1 -%patch 2 -p1 -%patch 3 -p1 -%patch 4 -p1 -%patch 5 -p1 -%patch 6 -p1 -%patch 7 -p1 -%patch 8 -p1 -%patch 9 -p1 -%patch 11 -p1 -%patch 12 -p1 -%patch 13 -p1 -%patch 14 -p1 -%patch 15 -p1 -%patch 16 -p1 -%patch 17 -p1 -%patch 18 -p1 -%patch 19 -p1 -%patch 20 -p1 -%patch 21 -p1 -%patch 22 -p1 -%patch 23 -p1 -%patch 24 -p1 -%patch 25 -p1 -%patch 26 -p1 -%patch 27 -p1 -%patch 28 -p1 -%patch 29 -p1 -%patch 30 -p1 -%patch 31 -p1 -%patch 32 -p1 -%patch 33 -p1 -%patch 34 -p1 -%patch 35 -p1 -%patch 36 -p1 -%patch 37 -p1 -%patch 38 -p1 -%patch 39 -p1 -%patch 40 -p1 -%patch 41 -p1 -%patch 42 -p1 +%patch1 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch33 -p1 +%patch34 -p1 +%patch37 -p1 +%patch3 -p1 +%patch40 -p1 +%patch41 -p1 +%patch42 -p1 +%patch44 -p1 +%patch65 -p1 +%patch100 -p1 + +# qemu-xen-traditional patches +pushd tools/qemu-xen-traditional +%patch27 -p1 +%patch29 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch35 -p1 +%patch36 -p1 +%patch38 -p1 +%patch39 -p1 +popd + +# qemu-xen patches +pushd tools/qemu-xen +popd # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom # copy xen hypervisor .config file to change settings cp -v %{SOURCE21} xen/.config -# mini-os is now separate file -mkdir extras -tar -C extras -xf %{SOURCE22} + %build # This package calls binutils components directly and would need to pass @@ -351,40 +361,51 @@ mkdir -p dist/install/boot/efi/efi/fedora %if %build_ocaml mkdir -p dist/install%{_libdir}/ocaml/stublibs %endif -export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS -Wno-error=use-after-free $LDFLAGS" -export PYTHON="/usr/bin/python3" -export LDFLAGS_SAVE=`echo $LDFLAGS | sed -e 's/-Wl,//g' -e 's/,/ /g' -e 's? -specs=[-a-z/0-9]*??g'` -export CFLAGS_SAVE="$CFLAGS" -CONFIG_EXTRA="" -%if %build_ovmf -CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=/usr/share/edk2/xen/OVMF.fd" +%if %(test -f /usr/share/seabios/bios-256k.bin && echo 1|| echo 0) +%define seabiosloc /usr/share/seabios/bios-256k.bin +%else +%define seabiosloc /usr/share/seabios/bios.bin %endif +#export XEN_VENDORVERSION="-%{release}" +export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS -Wno-error=declaration-after-statement" +export EXTRA_CFLAGS_QEMU_TRADITIONAL="$RPM_OPT_FLAGS" +export EXTRA_CFLAGS_QEMU_XEN="$RPM_OPT_FLAGS" +export PYTHON="/usr/bin/python3" +%if %build_hyp +%if %build_crosshyp +XEN_TARGET_ARCH=x86_64 make %{?_smp_mflags} prefix=/usr xen CC="/usr/bin/x86_64-linux-gnu-gcc `echo $RPM_OPT_FLAGS | sed -e 's/-m32//g' -e 's/-march=i686//g' -e 's/-mtune=atom//g' -e 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-annobin-cc1//g' -e 's/-fstack-clash-protection//g' -e 's/-mcet//g' -e 's/-fcf-protection//g'`" +%else +%ifarch armv7hl +make %{?_smp_mflags} prefix=/usr xen CC="gcc `echo $RPM_OPT_FLAGS | sed -e 's/-mfloat-abi=hard//g' -e 's/-march=armv7-a//g'`" +%else %ifarch aarch64 +make %{?_smp_mflags} prefix=/usr xen CC="gcc $RPM_OPT_FLAGS -mno-outline-atomics" +%else +make %{?_smp_mflags} prefix=/usr xen CC="gcc `echo $RPM_OPT_FLAGS | sed -e 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-annobin-cc1//g' -e 's/-fcf-protection//g'`" +%endif +%endif +%endif +%endif +%if ! %build_qemutrad +CONFIG_EXTRA="--disable-qemu-traditional" +%else +CONFIG_EXTRA="" +%endif +%if %build_ovmf +CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=%{_libexecdir}/%{name}/boot/ovmf.bin" +%endif +%ifnarch armv7hl aarch64 CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ipxe=/usr/share/ipxe/10ec8139.rom" %endif -%if %(test -f /usr/share/seabios/bios-256k.bin && echo 1|| echo 0) -CONFIG_EXTRA="$CONFIG_EXTRA --with-system-seabios=/usr/share/seabios/bios-256k.bin" -%else -CONFIG_EXTRA="$CONFIG_EXTRA --disable-seabios" -%endif -%if %with_systemd_presets -CONFIG_EXTRA="$CONFIG_EXTRA --enable-systemd" -%endif -./configure --prefix=%{_prefix} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --with-system-qemu=/usr/bin/qemu-system-i386 --with-linux-backend-modules="xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback xen-scsiback xen-acpi-processor" $CONFIG_EXTRA -unset CFLAGS CXXFLAGS FFLAGS LDFLAGS -export LDFLAGS="$LDFLAGS_SAVE" -export CFLAGS=`echo "$CFLAGS_SAVE -Wno-error=address" | sed -e 's/-specs=\/usr\/lib\/rpm\/redhat/redhat-annobin-cc1//g'` - -%if %build_hyp -%make_build prefix=/usr xen -%endif -unset CFLAGS CXXFLAGS FFLAGS LDFLAGS - -%make_build %{?ocaml_flags} prefix=/usr tools +./configure --prefix=%{_prefix} --libdir=%{_libdir} --libexecdir=%{_libexecdir} --with-system-seabios=%{seabiosloc} --with-system-qemu=/usr/bin/qemu-system-i386 --with-linux-backend-modules="xen-evtchn xen-gntdev xen-gntalloc xen-blkback xen-netback xen-pciback xen-scsiback xen-acpi-processor" $CONFIG_EXTRA +make %{?_smp_mflags} %{?ocaml_flags} prefix=/usr tools %if %build_docs make prefix=/usr docs %endif export RPM_OPT_FLAGS_RED=`echo $RPM_OPT_FLAGS | sed -e 's/-m64//g' -e 's/--param=ssp-buffer-size=4//g' -e's/-fstack-protector-strong//'` +%ifarch %{ix86} +export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS_RED" +%endif %if %build_stubdom %ifnarch armv7hl aarch64 make mini-os-dir @@ -392,7 +413,7 @@ make -C stubdom build %endif %ifarch x86_64 export EXTRA_CFLAGS_XEN_TOOLS="$RPM_OPT_FLAGS_RED" -XEN_TARGET_ARCH=x86_32 make -C stubdom pv-grub-if-enabled +XEN_TARGET_ARCH=x86_32 make -C stubdom pv-grub %endif %endif @@ -427,18 +448,30 @@ find %{buildroot} -print | xargs ls -ld | sed -e 's|.*%{buildroot}||' > f1.list rm -rf %{buildroot}/usr/*-xen-elf # hypervisor symlinks -rm -rf %{buildroot}/boot/xen-%{hv_abi}.gz +rm -rf %{buildroot}/boot/xen-4.0.gz rm -rf %{buildroot}/boot/xen-4.gz -rm -rf %{buildroot}/boot/xen.gz %if !%build_hyp rm -rf %{buildroot}/boot %endif # silly doc dir fun rm -fr %{buildroot}%{_datadir}/doc/xen +rm -rf %{buildroot}%{_datadir}/doc/qemu # Pointless helper -rm -f %{buildroot}%{_bindir}/xen-python-path +rm -f %{buildroot}%{_sbindir}/xen-python-path + +# qemu stuff (unused or available from upstream) +rm -rf %{buildroot}/usr/share/xen/man +rm -rf %{buildroot}/usr/bin/qemu-*-xen +ln -s qemu-img %{buildroot}/%{_bindir}/qemu-img-xen +ln -s qemu-img %{buildroot}/%{_bindir}/qemu-nbd-xen +for file in bios.bin openbios-sparc32 openbios-sparc64 ppc_rom.bin \ + pxe-e1000.bin pxe-ne2k_pci.bin pxe-pcnet.bin pxe-rtl8139.bin \ + vgabios.bin vgabios-cirrus.bin video.x openbios-ppc bamboo.dtb +do + rm -f %{buildroot}/%{_datadir}/xen/qemu/$file +done # README's not intended for end users rm -f %{buildroot}/%{_sysconfdir}/xen/README* @@ -451,17 +484,20 @@ rm -rf %{buildroot}/%{_libdir}/*.a %if %build_efi # clean up extra efi files -rm -f %{buildroot}/%{_libdir}/efi/xen-%{hv_abi}.efi -rm -f %{buildroot}/%{_libdir}/efi/xen-4.efi -rm -f %{buildroot}/%{_libdir}/efi/xen.efi -cp -p %{buildroot}/%{_libdir}/efi/xen-%{version}{,.notstripped}.efi -strip -s %{buildroot}/%{_libdir}/efi/xen-%{version}.efi +rm -rf %{buildroot}/%{_libdir}/efi +%ifarch %{ix86} +rm -rf %{buildroot}/usr/lib64/efi +%endif %endif %if ! %build_ocaml rm -rf %{buildroot}/%{_unitdir}/oxenstored.service %endif +%if %build_ovmf +cat /usr/share/OVMF/OVMF_{VARS,CODE}.fd >%{buildroot}%{_libexecdir}/%{name}/boot/ovmf.bin +%endif + ############ fixup files in /etc ############ # logrotate @@ -469,12 +505,10 @@ mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d/ install -m 644 %{SOURCE2} %{buildroot}%{_sysconfdir}/logrotate.d/%{name} # init scripts -%define initdloc %(test -d /etc/rc.d/init.d/ && echo rc.d/init.d || echo init.d ) - -rm %{buildroot}%{_sysconfdir}/%{initdloc}/xen-watchdog -rm %{buildroot}%{_sysconfdir}/%{initdloc}/xencommons -rm %{buildroot}%{_sysconfdir}/%{initdloc}/xendomains -rm %{buildroot}%{_sysconfdir}/%{initdloc}/xendriverdomain +rm %{buildroot}%{_sysconfdir}/rc.d/init.d/xen-watchdog +rm %{buildroot}%{_sysconfdir}/rc.d/init.d/xencommons +rm %{buildroot}%{_sysconfdir}/rc.d/init.d/xendomains +rm %{buildroot}%{_sysconfdir}/rc.d/init.d/xendriverdomain ############ create dirs in /var ############ @@ -488,7 +522,7 @@ ln -s %{_libexecdir}/%{name} %{buildroot}/%{_libdir}/%{name} %endif ############ create symlink to qemu-system-i386 in /usr/bin ############ -ln -s ../../../bin/qemu-system-i386 %{buildroot}/%{_libexecdir}/%{name}/bin/qemu-system-i386 +ln -s /usr/bin/qemu-system-i386 %{buildroot}/%{_libexecdir}/%{name}/bin/qemu-system-i386 ############ debug packaging: list files ############ @@ -506,17 +540,6 @@ find . -path licensedir -prune -o -path stubdom/ioemu -prune -o \ install -m 644 $file licensedir/$file done -############ move sbin files to bin - -mv %{buildroot}/usr/sbin/* %{buildroot}/usr/bin/ - -############ remove xen*.efi.elf files to avoid debuginfo failure - -%ifarch x86_64 -rm dist/install/usr/lib/debug/xen-*.efi.elf -rm %{buildroot}/usr/lib/debug/xen-*.efi.elf -%endif - ############ all done now ############ %post @@ -558,7 +581,7 @@ fi %endif %posttrans runtime -if [ ! -L /usr/lib/xen -a -d /usr/lib/xen ] && [ -z "$(ls -A /usr/lib/xen)" ]; then +if [ ! -L /usr/lib/xen -a -d /usr/lib/xen -a -z "$(ls -A /usr/lib/xen)" ]; then rmdir /usr/lib/xen fi if [ ! -e /usr/lib/xen ]; then @@ -569,49 +592,55 @@ fi %if %build_hyp %post hypervisor -do_it() { - DIR=$1 - TARGET=$2 - if [ -d $DIR ]; then - if [ ! -d $TARGET ]; then - mkdir $TARGET - fi - for m in relocator.mod multiboot2.mod elf.mod; do - if [ -f $DIR/$m ]; then - if [ ! -f $TARGET/$m ] || ! cmp -s $DIR/$m $TARGET/$m; then - cp -p $DIR/$m $TARGET/$m - fi - fi - done - fi -} if [ $1 == 1 -a -f /sbin/grub2-mkconfig ]; then - for f in /boot/grub2/grub.cfg; do - if [ -f $f ]; then - /sbin/grub2-mkconfig -o $f - sed -i -e '/insmod module2/d' $f - fi - done + if [ -f /boot/grub2/grub.cfg ]; then + /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg + sed -i -e '/insmod module2/d' /boot/grub2/grub.cfg + fi + if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then + /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg + sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg + fi fi if [ -f /sbin/grub2-mkconfig ]; then if [ -f /boot/grub2/grub.cfg ]; then - DIR=/usr/lib/grub/i386-pc - TARGET=/boot/grub2/i386-pc - do_it $DIR $TARGET - DIR=/usr/lib/grub/x86_64-efi - TARGET=/boot/grub2/x86_64-efi - do_it $DIR $TARGET + if [ -d /usr/lib/grub/i386-pc ]; then + if [ ! -d /boot/grub2/i386-pc ]; then + mkdir /boot/grub2/i386-pc + fi + if [ -f /usr/lib/grub/i386-pc/relocator.mod -a ! -f /boot/grub2/i386-pc/relocator.mod ]; then + cp -p /usr/lib/grub/i386-pc/relocator.mod /boot/grub2/i386-pc/relocator.mod + fi + if [ -f /usr/lib/grub/i386-pc/multiboot2.mod -a ! -f /boot/grub2/i386-pc/multiboot2.mod ]; then + cp -p /usr/lib/grub/i386-pc/multiboot2.mod /boot/grub2/i386-pc/multiboot2.mod + fi + fi + fi + if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then + if [ -d /usr/lib/grub/x86_64-efi ]; then + if [ ! -d /boot/efi/EFI/fedora/x86_64-efi ]; then + mkdir /boot/efi/EFI/fedora/x86_64-efi + fi + if [ -f /usr/lib/grub/x86_64-efi/relocator.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/relocator.mod ]; then + cp -p /usr/lib/grub/x86_64-efi/relocator.mod /boot/efi/EFI/fedora/x86_64-efi/relocator.mod + fi + if [ -f /usr/lib/grub/x86_64-efi/multiboot2.mod -a ! -f /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod ]; then + cp -p /usr/lib/grub/x86_64-efi/multiboot2.mod /boot/efi/EFI/fedora/x86_64-efi/multiboot2.mod + fi + fi fi fi %postun hypervisor if [ -f /sbin/grub2-mkconfig ]; then - for f in /boot/grub2/grub.cfg; do - if [ -f $f ]; then - /sbin/grub2-mkconfig -o $f - sed -i -e '/insmod module2/d' $f - fi - done + if [ -f /boot/grub2/grub.cfg ]; then + /sbin/grub2-mkconfig -o /boot/grub2/grub.cfg + sed -i -e '/insmod module2/d' /boot/grub2/grub.cfg + fi + if [ -f /boot/efi/EFI/fedora/grub.cfg ]; then + /sbin/grub2-mkconfig -o /boot/efi/EFI/fedora/grub.cfg + sed -i -e '/insmod module2/d' /boot/efi/EFI/fedora/grub.cfg + fi fi %endif @@ -639,6 +668,7 @@ fi #files -f xen-xm.lang %files %doc COPYING README +%{_bindir}/xencons %{python3_sitearch}/%{name} %{python3_sitearch}/xen-*.egg-info @@ -651,22 +681,22 @@ fi %files libs %{_libdir}/libxencall.so.1 -%{_libdir}/libxencall.so.1.3 +%{_libdir}/libxencall.so.1.2 %{_libdir}/libxenctrl.so.4.* %{_libdir}/libxendevicemodel.so.1 -%{_libdir}/libxendevicemodel.so.1.4 +%{_libdir}/libxendevicemodel.so.1.3 %{_libdir}/libxenevtchn.so.1 -%{_libdir}/libxenevtchn.so.1.2 +%{_libdir}/libxenevtchn.so.1.1 %{_libdir}/libxenforeignmemory.so.1 -%{_libdir}/libxenforeignmemory.so.1.4 +%{_libdir}/libxenforeignmemory.so.1.3 %{_libdir}/libxenfsimage.so.4.* %{_libdir}/libxengnttab.so.1 %{_libdir}/libxengnttab.so.1.2 %{_libdir}/libxenguest.so.4.* %{_libdir}/libxenlight.so.4.* %{_libdir}/libxenstat.so.4.* -%{_libdir}/libxenstore.so.4 -%{_libdir}/libxenstore.so.4.1 +%{_libdir}/libxenstore.so.3.0 +%{_libdir}/libxenstore.so.3.0.3 %{_libdir}/libxentoolcore.so.1 %{_libdir}/libxentoolcore.so.1.0 %{_libdir}/libxentoollog.so.1 @@ -674,10 +704,6 @@ fi %{_libdir}/libxenvchan.so.4.* %{_libdir}/libxlutil.so.4.* %{_libdir}/xenfsimage -%{_libdir}/libxenhypfs.so.1 -%{_libdir}/libxenhypfs.so.1.0 -%{_libdir}/libxenmanage.so.1 -%{_libdir}/libxenmanage.so.1.0 # All runtime stuff except for XenD/xm python stuff %files runtime @@ -687,16 +713,16 @@ fi %dir %attr(0700,root,root) %{_sysconfdir}/%{name}/scripts/ %config %attr(0700,root,root) %{_sysconfdir}/%{name}/scripts/* -%{_sysconfdir}/bash_completion.d/xl +%{_sysconfdir}/bash_completion.d/xl.sh %{_unitdir}/proc-xen.mount +%{_unitdir}/var-lib-xenstored.mount %{_unitdir}/xenstored.service %{_unitdir}/xenconsoled.service %{_unitdir}/xen-watchdog.service %{_unitdir}/xen-qemu-dom0-disk-backend.service %{_unitdir}/xendriverdomain.service -%{_modulesloaddir}/xen.conf -%{_systemd_util_dir}/system-sleep/xen-watchdog-sleep.sh +/usr/lib/modules-load.d/xen.conf %config(noreplace) %{_sysconfdir}/sysconfig/xencommons %config(noreplace) %{_sysconfdir}/xen/xl.conf @@ -710,10 +736,19 @@ fi %dir %{_libexecdir}/%{name} %dir %{_libexecdir}/%{name}/bin %attr(0700,root,root) %{_libexecdir}/%{name}/bin/* +# QEMU runtime files +%if %build_qemutrad +%ifnarch armv7hl aarch64 +%dir %{_datadir}/%{name}/qemu +%dir %{_datadir}/%{name}/qemu/keymaps +%{_datadir}/%{name}/qemu/keymaps/* +%endif +%endif # man pages %if %build_docs %{_mandir}/man1/xentop.1* +%{_mandir}/man1/xentrace_format.1* %{_mandir}/man8/xentrace.8* %{_mandir}/man1/xl.1* %{_mandir}/man5/xl.cfg.5* @@ -728,10 +763,6 @@ fi %{_mandir}/man5/xl-network-configuration.5.gz %{_mandir}/man7/xen-pv-channel.7.gz %{_mandir}/man7/xl-numa-placement.7.gz -%{_mandir}/man1/xenhypfs.1.gz -%{_mandir}/man7/xen-vbd-interface.7.gz -%{_mandir}/man5/xl-pci-configuration.5.gz -%{_mandir}/man8/xenwatchdogd.8.gz %endif %{python3_sitearch}/xenfsimage*.so @@ -739,14 +770,20 @@ fi %{python3_sitearch}/pygrub-*.egg-info # The firmware -%ifarch x86_64 +%ifarch %{ix86} x86_64 %dir %{_libexecdir}/%{name}/boot %{_libexecdir}/xen/boot/hvmloader +%ifnarch %{ix86} %{_libexecdir}/%{name}/boot/xen-shim /usr/lib/debug%{_libexecdir}/xen/boot/xen-shim-syms +%endif +%if %build_ovmf +%{_libexecdir}/xen/boot/ovmf.bin +%endif %if %build_stubdom +%{_libexecdir}/xen/boot/ioemu-stubdom.gz %{_libexecdir}/xen/boot/xenstore-stubdom.gz -%{_libexecdir}/xen/boot/xenstorepvh-stubdom.gz +%{_libexecdir}/xen/boot/pv-grub*.gz %endif %endif %if "%{_libdir}" != "/usr/lib" @@ -757,65 +794,64 @@ fi %dir %{_localstatedir}/lib/%{name} %dir %{_localstatedir}/lib/%{name}/dump %dir %{_localstatedir}/lib/%{name}/images +# Xenstore persistent state +%dir %{_localstatedir}/lib/xenstored # Xenstore runtime state %ghost %{_localstatedir}/run/xenstored # All xenstore CLI tools +%{_bindir}/qemu-*-xen %{_bindir}/xenstore %{_bindir}/xenstore-* +%{_bindir}/pygrub +%{_bindir}/xentrace* #%#{_bindir}/remus # XSM -%{_bindir}/flask-* +%{_sbindir}/flask-* # Misc stuff -%ifnarch aarch64 +%ifnarch armv7hl aarch64 %{_bindir}/xen-detect %endif %{_bindir}/xencov_split -%ifnarch aarch64 -%{_bindir}/gdbsx -%{_bindir}/xen-kdd +%ifnarch armv7hl aarch64 +%{_sbindir}/gdbsx +%{_sbindir}/xen-kdd %endif -%ifnarch aarch64 -%{_bindir}/xen-hptool -%{_bindir}/xen-hvmcrash -%{_bindir}/xen-hvmctx +%ifnarch armv7hl aarch64 +%{_sbindir}/xen-hptool +%{_sbindir}/xen-hvmcrash +%{_sbindir}/xen-hvmctx %endif -%{_bindir}/xenconsoled -%{_bindir}/xenlockprof -%{_bindir}/xenmon -%{_bindir}/xentop -%{_bindir}/xentrace_setmask -%{_bindir}/xenbaked -%{_bindir}/xenstored -%{_bindir}/xenpm -%{_bindir}/xenpmd -%{_bindir}/xenperf -%{_bindir}/xenwatchdogd -%{_bindir}/xl -%ifnarch aarch64 -%{_bindir}/xen-lowmemd +%{_sbindir}/xenconsoled +%{_sbindir}/xenlockprof +%{_sbindir}/xenmon +%{_sbindir}/xentop +%{_sbindir}/xentrace_setmask +%{_sbindir}/xenbaked +%{_sbindir}/xenstored +%{_sbindir}/xenpm +%{_sbindir}/xenpmd +%{_sbindir}/xenperf +%{_sbindir}/xenwatchdogd +%{_sbindir}/xl +%ifnarch armv7hl aarch64 +%{_sbindir}/xen-lowmemd %endif -%{_bindir}/xencov -%ifnarch aarch64 -%{_bindir}/xen-mfndump +%{_sbindir}/xencov +%ifnarch armv7hl aarch64 +%{_sbindir}/xen-mfndump %endif %{_bindir}/xenalyze -%{_bindir}/xentrace -%{_bindir}/xentrace_setsize -%ifnarch aarch64 +%{_sbindir}/xentrace +%{_sbindir}/xentrace_setsize +%ifnarch armv7hl aarch64 %{_bindir}/xen-cpuid %endif -%{_bindir}/xen-livepatch -%{_bindir}/xen-diag +%{_sbindir}/xen-livepatch +%{_sbindir}/xen-diag %ifnarch armv7hl aarch64 -%{_bindir}/xen-ucode -%{_bindir}/xen-memshare -%{_bindir}/xen-mceinj -%{_bindir}/xen-vmtrace +%{_sbindir}/xen-ucode %endif -%{_bindir}/vchan-socket-proxy -%{_bindir}/xenhypfs -%{_bindir}/xen-access # Xen logfiles %dir %attr(0700,root,root) %{_localstatedir}/log/xen @@ -824,8 +860,9 @@ fi %files hypervisor %if %build_hyp -%ifnarch aarch64 +%ifnarch armv7hl aarch64 /boot/xen-*.gz +/boot/xen.gz /boot/xen*.config %else /boot/xen* @@ -834,10 +871,10 @@ fi %dir %attr(0755,root,root) /boot/flask /boot/flask/xenpolicy* %endif -/usr/lib/debug/xen* -%endif %if %build_efi -%{_libdir}/efi/*.efi +/boot/efi/EFI/fedora/*.efi +%endif +/usr/lib/debug/xen* %endif %if %build_docs @@ -866,7 +903,7 @@ fi %exclude %{_libdir}/ocaml/xen*/*.cmx %{_libdir}/ocaml/stublibs/*.so %{_libdir}/ocaml/stublibs/*.so.owner -%{_bindir}/oxenstored +%{_sbindir}/oxenstored %config(noreplace) %{_sysconfdir}/xen/oxenstored.conf %{_unitdir}/oxenstored.service @@ -874,582 +911,27 @@ fi %{_libdir}/ocaml/xen*/*.a %{_libdir}/ocaml/xen*/*.cmxa %{_libdir}/ocaml/xen*/*.cmx -%{_libdir}/ocaml/xsd_glue/* -%{_libexecdir}/xen/ocaml/xsd_glue/xenctrl_plugin/domain_getinfo_v1.cmxs %endif -%files test -%{_libexecdir}/xen/tests/* - %changelog -* Wed Jul 22 2026 Python Maint - 4.21.1-10 -- Rebuilt for Python 3.15.0b4 ABI change - -* Fri Jul 17 2026 Fedora Release Engineering - 4.21.1-9 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild - -* Thu Jul 09 2026 Jerry James - 4.21.1-8 -- OCaml 5.5.0 rebuild - -* Thu Jun 18 2026 Yaakov Selkowitz - 4.21.1-7 -- Rebuilt for openssl 4.0 - -* Thu Jun 18 2026 Michael Young - 4.21.1-6 -- x86 HVM I/O port list traversal [XSA-491, CVE-2026-42487] -- domctl lock open to abuse [XSA-492, CVE-2026-42489, CVE-2026-42490] -- Arm: Completion of memory accesses not guaranteed by completion of a TLBI - [XSA-493, CVE-2025-10263] -- x86: mismatched mapcache metadata [XSA-494, CVE-2026-42488] - -* Sat Jun 13 2026 Yaakov Selkowitz - 4.21.1-5 -- Rebuilt for openssl 4.0 - -* Wed Jun 03 2026 Python Maint - 4.21.1-4 -- Rebuilt for Python 3.15 - -* Tue May 12 2026 Michael Young - 4.21.1-3 -- x86: CPU Opcode Cache corruption [XSA-490,CVE-2025-54518] - -* Tue Apr 28 2026 Michael Young - 4.21.1-2 -- oxenstored keeps quota related use counts across domain destruction - [XSA-483, CVE-2026-23556] -- Xenstored DoS via XS_RESET_WATCHES command [XSA-484, CVE-2026-23557] -- grant table v2 race in status page mapping [XSA-486, CVE-2026-23558] -- x86: Floating Point Divider State Sampling [XSA-488, CVE-2025-54505] - -* Thu Mar 26 2026 Michael Young - 4.21.1-1 -- update to xen 4.21.1 +* Mon Mar 29 2021 Michael Young - 4.13.3-1 +- update to 4.13.3 remove patches now included or superceded upstream + adjust xen.hypervisor.config -* Tue Mar 17 2026 Michael Young - 4.21.0-5 -- Use after free of paging structures in EPT [XSA-480, CVE-2026-23554] -- Xenstored DoS by unprivileged domain [XSA-481, CVE-2026-23555] - -* Fri Feb 20 2026 Richard W.M. Jones - 4.21.0-4 -- OCaml 5.4.1 rebuild - -* Wed Jan 28 2026 Michael Young - 4.21.0-3 - x86: buffer overrun with shadow paging + tracing [XSA-477, CVE-2025-58150] - x86: incomplete IBPB for vCPU isolation [XSA-479, CVE-2026-23553] - -* Sat Jan 17 2026 Fedora Release Engineering - 4.21.0-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild - -* Wed Jan 07 2026 Michael Young - 4.21.0-1 -- update to xen 4.21.0 - rebase mini-os - use .xz xen tarball instead of .gz - fix quotes around sed command - update libxenstore version - package libxenmanage and xen-watchdog-sleep.sh files - add a new package for test files - renumber patches - use json-c instead of yajl -- fix bug in xen code when using json-c -- fix code issues detected by gcc16 - -* Thu Nov 13 2025 Michael Young - 4.20.2-2.fc44 -- update to xen 4.20.2 - remove patches now included or superceded upstream - -* Sun Oct 26 2025 Michael Young - 4.20.1-9 -- teecr32_el1 and teehbr32_el1 support dropped in binutils 2.45.50-5.fc44 - -* Fri Oct 24 2025 Michael Young -- Incorrect removal of permissions on PCI device unplug [XSA-476, - CVE-2025-58149] - -* Tue Oct 21 2025 Michael Young -- x86: Incorrect input sanitisation in Viridian hypercalls [XSA-475, - CVE-2025-58147, CVE-2025-58148] - -* Wed Oct 15 2025 Richard W.M. Jones - 4.20.1-7 -- OCaml 5.4.0 rebuild - -* Fri Sep 19 2025 Python Maint - 4.20.1-6 -- Rebuilt for Python 3.14.0rc3 bytecode - -* Wed Sep 10 2025 Michael Young - 4.20.1-5 -- Mutiple vulnerabilities in the Viridian interface [XSA-472, - CVE-2025-27466, CVE-2025-58142, CVE-2025-58143] -- Arm issues with page refcounting [XSA-473, CVE-2025-58144, - CVE-2025-58145] - -* Tue Sep 02 2025 Michael Young - 4.20.1-4 -- tools/xl: don't crash on NULL command line - -* Fri Aug 15 2025 Python Maint - 4.20.1-3 -- Rebuilt for Python 3.14.0rc2 bytecode - -* Fri Jul 25 2025 Fedora Release Engineering - 4.20.1-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild - -* Sun Jul 13 2025 Michael Young - 4.20.1-1 -- update to xen 4.20.1 - remove old qemu code for spac file - remove armv7hl and ix86 code from spec file - update configuration in xen.hypervisor.config - minios is now a separate file - package extra ocaml files - unset -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 for hypervisor build - rebase xen.efi.build.patch - includes fixes for security vulnerabilites - x86: Incorrect stubs exception handling for flags recovery [XSA-470, - CVE-2025-27465] - x86: Transitive Scheduler Attacks [XSA-471, CVE-2024-36350, - CVE-2024-36357] - -* Fri Jul 11 2025 Jerry James - 4.19.2-6 -- Rebuild to fix OCaml dependencies - -* Mon Jun 02 2025 Python Maint - 4.19.2-5 -- Rebuilt for Python 3.14 - -* Mon May 12 2025 Michael Young - 4.19.2-4 -- x86: Indirect Target Selection [XSA-469, CVE-2024-28956] - -* Mon Apr 07 2025 Michael Young - 4.19.2-2 -- update to xen-4.19.2 - remove patches now included or superceded upstream - remove xen*.efi.elf files to avoid debuginfo failure - -* Thu Feb 27 2025 Michael Young - 4.19.1-7 -- deadlock potential with VT-d and legacy PCI device pass-through - [XSA-467, CVE-2025-1713] - -* Thu Jan 23 2025 Michael Young - 4.19.1-6 -- adjust file locations now /usr/sbin is a symlink to /usr/bin -- remove debugedit fix as no longer needed - -* Sun Jan 19 2025 Fedora Release Engineering - 4.19.1-5 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild - -* Fri Jan 10 2025 Jerry James - 4.19.1-4 -- OCaml 5.3.0 rebuild for Fedora 42 - -* Thu Jan 09 2025 Michael Young - 4.19.1-3 -- work around debugedit bug to fix aarch64 builds - -* Sat Jan 04 2025 Andrea Perotti - 4.19.1-2 -- xen-hypervisor %post doesn't load all needed grub2 modules - (#2335558) - -* Thu Dec 05 2024 Michael Young - 4.19.1-1 -- update to xen-4.19.1 - remove patches now included or superceded upstream - -* Tue Nov 12 2024 Michael Young - 4.19.0-5 -- Deadlock in x86 HVM standard VGA handling [XSA-463, CVE-2024-45818] -- libxl leaks data to PVH guests via ACPI tables [XSA-464, CVE-2024-45819] -- additional patches so above applies cleanly - -* Tue Sep 24 2024 Michael Young - 4.19.0-4 -- x86: Deadlock in vlapic_error() [XSA-462, CVE-2024-45817] (#2314782) - -* Wed Sep 04 2024 Miroslav Suchý - 4.19.0-3 -- convert license to SPDX - -* Wed Aug 14 2024 Michael Young - 4.19.0-2 -- error handling in x86 IOMMU identity mapping [XSA-460, CVE-2024-31145] - (#2314784) -- PCI device pass-through with shared resources [XSA-461, CVE-2024-31146] - (#2314783) - -* Sat Aug 03 2024 Michael Young - 4.19.0-1 -- update to xen-4.19.0 - rebase xen.fedora.systemd.patch, xen.efi.build.patch - xen.ocaml5.fixes.patch and xen.gcc14.fixes.patch - remove patches now included or superceded upstream - now need to enable systemd explicitly - xentrace_format has gone, pygrub is now only in /usr/libexec/xen/bin/ - package xenwatchdogd.8.gz - use relative links for /usr/bin/qemu-system-i386 - -* Sat Jul 20 2024 Fedora Release Engineering - 4.18.2-5 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild - -* Tue Jul 16 2024 Michael Young - 4.18.2-4 -- double unlock in x86 guest IRQ handling [XSA-458, CVE-2024-31143] - (#2298690) - -* Fri Jun 07 2024 Python Maint - 4.18.2-3 -- Rebuilt for Python 3.13 - -* Mon Jun 03 2024 Michael Young - 4.18.2-2 -- x86: Native Branch History Injection [XSA-456 version 3, CVE-2024-2201] - -* Tue Apr 09 2024 Michael Young - 4.18.2-1 -- x86: Native Branch History Injection [XSA-456, CVE-2024-2201] -- update to xen 4.18.2, remove patches now included upstream - -* Tue Apr 09 2024 Michael Young - 4.18.1-2 -- x86 HVM hypercalls may trigger Xen bug check [XSA-454, CVE-2023-46842] -- x86: Incorrect logic for BTC/SRSO mitigations [XSA-455, CVE-2024-31142] - -* Wed Mar 20 2024 Michael Young - 4.18.1-1 -- update to xen-4.18.1 - rebase xen.gcc12.fixes.patch - remove patches now included or superceded upstream - -* Wed Mar 13 2024 Michael Young - 4.18.0-7 -- x86: Register File Data Sampling [XSA-452, CVE-2023-28746] -- GhostRace: Speculative Race Conditions [XSA-453, CVE-2024-2193] -- additional patches so above applies cleanly - -* Tue Feb 27 2024 Michael Young - 4.18.0-6 -- x86: shadow stack vs exceptions from emulation stubs - [XSA-451, - CVE-2023-46841] (#2266326) - -* Sun Feb 04 2024 Michael Young - 4.18.0-5 -- pci: phantom functions assigned to incorrect contexts [XSA-449, - CVE-2023-46839] -- VT-d: Failure to quarantine devices in !HVM build [XSA-450, - CVE-2023-46840] -- the glibc32 doesn't seem to add anything to the build so drop it - -* Sat Feb 03 2024 Michael Young - 4.18.0-4 -- build fixes for gcc14, replace stubs-32.h requirement with glibc32 - -* Sat Jan 27 2024 Fedora Release Engineering - 4.18.0-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Wed Dec 13 2023 Michael Young - 4.18.0-2 -- arm32: The cache may not be properly cleaned/invalidated (take two) - [XSA-447, CVE-2023-46837] -- rebuild for OCaml-5.1.1 - -* Wed Nov 29 2023 Michael Young - 4.18.0-1 -- update to xen-4.18.0 - rebase xen.canonicalize.patch and xen.ocaml5.fixes.patch - remove or adjust patches now included or superceded upstream -- xencons has been dropped - - -* Tue Nov 14 2023 Michael Young - 4.17.2-5 -- x86/AMD: mismatch in IOMMU quarantine page table levels [XSA-445, - CVE-2023-46835] -- x86: BTC/SRSO fixes not fully effective [XSA-446, CVE-2023-46836] - -* Tue Oct 10 2023 Michael Young - 4.17.2-4 -- xenstored: A transaction conflict can crash C Xenstored [XSA-440, - CVE-2023-34323] -- x86/AMD: missing IOMMU TLB flushing [XSA-442, CVE-2023-34326] -- Multiple vulnerabilities in libfsimage disk handling [XSA-443, - CVE-2023-34325] -- x86/AMD: Debug Mask handling [XSA-444, CVE-2023-34327, - CVE-2023-34328] - -* Sun Oct 08 2023 Michael Young - 4.17.2-3 -- rebuild (f40) for OCaml 5.1 - -* Tue Sep 26 2023 Michael Young - 4.17.2-2 -- arm32: The cache may not be properly cleaned/invalidated [XSA-437, - CVE-2023-34321] -- top-level shadow reference dropped too early for 64-bit PV guests - [XSA-438, CVE-2023-34322] -- x86/AMD: Divide speculative information leak [XSA-439, CVE-2023-20588] - -* Thu Aug 10 2023 Michael Young - 4.17.2-1 -- update to xen-4.17.2 which includes - x86/AMD: Speculative Return Stack Overflow [XSA-434, CVE-2023-20569] - x86/Intel: Gather Data Sampling [XSA-435, CVE-2022-40982] -- remove patches now included upstream - -* Tue Aug 01 2023 Michael Young - 4.17.1-9 -- arm: Guests can trigger a deadlock on Cortex-A77 [XSA-436, CVE-2023-34320] - (#2228238) - -* Mon Jul 31 2023 Michael Young - 4.17.1-8 -- bugfix for x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] - -* Tue Jul 25 2023 Michael Young -- adjust OCaml patch condition so eln builds work - -* Mon Jul 24 2023 Michael Young - 4.17.1-7 -- x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] -- omit OCaml 5 patch on fc38 - -* Sat Jul 22 2023 Fedora Release Engineering - 4.17.1-6 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild - -* Mon Jul 10 2023 Jerry James - 4.17.1-5 -- Add patch for OCaml 5.0.0 - -* Tue Jun 27 2023 Michael Young - 4.17.1-4 -- work around a build problem with python 3.12 - -* Tue Jun 13 2023 Python Maint - 4.17.1-3 -- Rebuilt for Python 3.12 - -* Tue May 16 2023 Michael Young - 4.17.1-2 -- Mishandling of guest SSBD selection on AMD hardware - [XSA-431, CVE-2022-42336] - -* Tue May 02 2023 Michael Young - 4.17.1-1 -- update to xen-4.17.1 - remove patches now included upstream - switch from patchN to patch N format for applying patches - -* Tue Apr 25 2023 Michael Young - 4.17.0-9 -- x86 shadow paging arbitrary pointer dereference [XSA-430, CVE-2022-42335] - -* Tue Mar 21 2023 Michael Young - 4.17.0-8 -- 3 security issues (#2180425) - x86 shadow plus log-dirty mode use-after-free [XSA-427, CVE-2022-42332] - x86/HVM pinned cache attributes mis-handling [XSA-428, CVE-2022-42333, - CVE-2022-42334] - x86: speculative vulnerability in 32bit SYSCALL path [XSA-429, - CVE-2022-42331] - -* Sat Feb 18 2023 Michael Young - 4.17.0-7 -- use OVMF.fd from new edk2-ovmf-xen package as ovmf.bin file - built from edk2-ovmf package no longer supports xen (#2170930) - -* Tue Feb 14 2023 Michael Young - 4.17.0-6 -- x86: Cross-Thread Return Address Predictions [XSA-426, CVE-2022-27672] - -* Wed Jan 25 2023 Michael Young - 4.17.0-5 -- Guests can cause Xenstore crash via soft reset [XSA-425, CVE-2022-42330] - (#2164520) - -* Tue Jan 24 2023 Michael Young -- now need BuildRequires for hostname - -* Sat Jan 21 2023 Fedora Release Engineering - 4.17.0-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild - -* Tue Jan 17 2023 Michael Young - 4.17.0-3 -- build fix for gcc13 - -* Sun Jan 08 2023 Michael Young - 4.17.0-2 -- fix clean up of init scripts if /etc/rc.d/init.d doesn't exist - -* Tue Dec 20 2022 Michael Young -- python3-setuptools BuildRequires is needed for python 3.12 - -* Tue Dec 13 2022 Michael Young - 4.17.0-1 -- update to xen-4.17.0 - rebase xen.fedora.systemd.patch and xen.canonicalize.patch - remove or adjust patches now included or superceded upstream - /var/lib/xenstored has moved to /run/xenstored - -* Tue Nov 08 2022 Michael Young - 4.16.2-4 -- x86: Multiple speculative security issues [XSA-422, CVE-2022-23824] - -* Tue Nov 01 2022 Michael Young - 4.16.2-3 -- x86: unintended memory sharing between guests [XSA-412, CVE-2022-42327] -- Xenstore: Guests can crash xenstored [XSA-414, CVE-2022-42309] -- Xenstore: Guests can create orphaned Xenstore nodes [XSA-415, - CVE-2022-42310] -- Xenstore: guests can let run xenstored out of memory [XSA-326, - CVE-2022-42311, CVE-2022-42312, CVE-2022-42313, CVE-2022-42314, - CVE-2022-42315, CVE-2022-42316, CVE-2022-42317, CVE-2022-42318] -- Xenstore: Guests can cause Xenstore to not free temporary memory - [XSA-416, CVE-2022-42319] -- Xenstore: Guests can get access to Xenstore nodes of deleted domains - [XSA-417, CVE-2022-42320] -- Xenstore: Guests can crash xenstored via exhausting the stack - [XSA-418, CVE-2022-42321] -- Xenstore: Cooperating guests can create arbitrary numbers of nodes - [XSA-419, CVE-2022-42322, CVE-2022-42323] -- Oxenstored 32->31 bit integer truncation issues [XSA-420, CVE-2022-42324] -- Xenstore: Guests can create arbitrary number of nodes via transactions - [XSA-421, CVE-2022-42325, CVE-2022-42326] - -* Fri Oct 14 2022 Michael Young - 4.16.2-2 -- Arm: unbounded memory consumption for 2nd-level page tables [XSA-409, - CVE-2022-33747] (#2135268) -- P2M pool freeing may take excessively long [XSA-410, CVE-2022-33746] - (#2135641) -- lock order inversion in transitive grant copy handling [XSA-411, - CVE-2022-33748] (#2135263) - -* Sat Sep 17 2022 Michael Young - 4.16.2-1 -- update to xen-4.16.2 - remove or adjust patches now included or superceded upstream - -* Tue Jul 26 2022 Michael Young - 4.16.1-8 -- insufficient TLB flush for x86 PV guests in shadow mode [XSA-408, - CVE-2022-33745] (#2112223) - -* Sat Jul 23 2022 Fedora Release Engineering - 4.16.1-7 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild - -* Tue Jul 12 2022 Michael Young - 4.16.1-6 -- Retbleed - arbitrary speculative code execution with return instructions - [XSA-407, CVE-2022-23816, CVE-2022-23825, CVE-2022-29900] - -* Tue Jul 05 2022 Michael Young - 4.16.1-5 -- Linux disk/nic frontends data leaks [XSA-403, CVE-2022-26365, - CVE-2022-33740, CVE-2022-33741, CVE-2022-33742] (#2104747) - -* Tue Jun 21 2022 Michael Young - 4.16.1-4 -- x86: MMIO Stale Data vulnerabilities [XSA-404, CVE-2022-21123, - CVE-2022-21125, CVE-2022-21166] - -* Mon Jun 13 2022 Python Maint - 4.16.1-3 -- Rebuilt for Python 3.11 (F37 build only) - -* Sat Jun 11 2022 Michael Young - 4.16.1-2 -- stop building for ix86 and armv7hl due to missing build dependency -- x86 pv: Race condition in typeref acquisition [XSA-401, CVE-2022-26362] -- x86 pv: Insufficient care with non-coherent mappings [ XSA-402, - CVE-2022-26363, CVE-2022-26364] -- additional patches so above applies cleanly - -* Thu Apr 14 2022 Michael Young - 4.16.1-1 -- update to xen-4.16.1 - remove or adjust patches now included or superceded upstream - renumber patches -- strip .efi file to help EFI partitions with limited space - -* Tue Apr 05 2022 Michael Young - 4.16.0-6 -- Racy interactions between dirty vram tracking and paging log dirty - hypercalls [XSA-397, CVE-2022-26356] -- race in VT-d domain ID cleanup [XSA-399, CVE-2022-26357] -- IOMMU: RMRR (VT-d) and unity map (AMD-Vi) handling issues [XSA-400, - CVE-2022-26358, CVE-2022-26359, CVE-2022-26360, CVE-2022-26361] -- additional patches so above applies cleanly - -* Mon Mar 21 2022 Michael Young - 4.16.0-5 -- fix build of xen*.efi file and package it in /usr/lib*/efi - -* Tue Mar 15 2022 Michael Young - 4.16.0-4 -- Multiple speculative security issues [XSA-398] -- additional patches so above applies cleanly - -* Sat Jan 29 2022 Michael Young - 4.16.0-3 -- adjust build script and patches for gcc12 and package note support - -* Sat Jan 29 2022 Michael Young -- arm: guest_physmap_remove_page not removing the p2m mappings [XSA-393, - CVE-2022-23033] (#2045044) -- A PV guest could DoS Xen while unmapping a grant [XSA-394, CVE-2022-23034] - (#2045042) -- Insufficient cleanup of passed-through device IRQs [XSA-395, - CVE-2022-23035] (#2045040) - -* Sat Jan 22 2022 Fedora Release Engineering - 4.16.0-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild - -* Mon Jan 10 2022 Michael Young - 4.16.0-1 -- update to xen-4.16.0 - rebase xen.canonicalize.patch and xen.gcc11.fixes.patch - drop xen.fedora.efi.build.patch which is no longer useful - remove or adjust patches now included or superceded upstream - update libxenstore libary versions - unpackage /boot/efi/EFI/fedora/xen*.efi - package xen-mceinj and xen-vmtrace -- don't build qemu-traditional or pv-grub by default (following upstream) -- fix some incorrect dependencies on building qemu-traditional -- change grub module package dependencies from Suggests to Recommends - and move to hypervisor package -- rework seabios configure logic (bios.bin is no longer useful) -- frontends vulnerable to backends [XSA-376] (document change only) - -* Tue Nov 23 2021 Michael Young - 4.15.1-4 -- guests may exceed their designated memory limit [XSA-385, CVE-2021-28706] -- PoD operations on misaligned GFNs [XSA-388, CVE-2021-28704, CVE-2021-28707 - CVE-2021-28708] -- issues with partially successful P2M updates on x86 [XSA-389, - CVE-2021-28705, CVE-2021-28709] -- certain VT-d IOMMUs may not work in shared page table mode [XSA-390, - CVE-2021-28710] - -* Wed Oct 06 2021 Michael Young - 4.15.1-3 -- rebuild (f36 only) for OCaml 4.13.1 - -* Tue Oct 05 2021 Michael Young - 4.15.1-2 -- PCI devices with RMRRs not deassigned correctly [XSA-386, CVE-2021-28702] - (#2011248) - -* Sun Sep 12 2021 Michael Young - 4.15.1-1 -- update to xen-4.15.1 - remove or adjust patches now included or superceded upstream - update libxencall version - -* Wed Sep 08 2021 Michael Young - 4.15.0-7 -- Another race in XENMAPSPACE_grant_table handling [XSA-384, CVE-2021-28701] - (#2002786) -- bugfix for XSA-380 -- stop editing grub files in /boot/efi/EFI/fedora - -* Wed Aug 25 2021 Michael Young - 4.15.0-6 -- IOMMU page mapping issues on x86 [XSA-378, CVE-2021-28694, - CVE-2021-28695, CVE-2021-28696] (#1997531) (#1997568) - (#1997537) -- grant table v2 status pages may remain accessible after de-allocation - [XSA-379, CVE-2021-28697] (#1997520) -- long running loops in grant table handling [XSA-380, CVE-2021-28698] - (#1997526) -- inadequate grant-v2 status frames array bounds check [XSA-382, - CVE-2021-28699] (#1997523) -- xen/arm: No memory limit for dom0less domUs [XSA-383, CVE-2021-28700] - (#1997527) -- grub x86_64-efi modules now go into /boot/grub2 - -* Thu Aug 12 2021 Michael Young - 4.15.0-5 - - work around build issue with GNU ld 2.37 (#1990344) - -* Fri Jul 23 2021 Fedora Release Engineering - 4.15.0-4 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild - -* Tue Jun 08 2021 Michael Young - 4.15.0-3 -- xen/arm: Boot modules are not scrubbed [XSA-372, CVE-2021-28693] - (#1970542) -- inappropriate x86 IOMMU timeout detection / handling - [XSA-373, CVE-2021-28692] (#1970540) -- Speculative Code Store Bypass [XSA-375, CVE-2021-0089, CVE-2021-26313] - (#1970531) -- x86: TSX Async Abort protections not restored after S3 - [XSA-377, CVE-2021-28690] (#1970546) - -* Fri Jun 04 2021 Python Maint - 4.15.0-2 -- Rebuilt for Python 3.10 - -* Wed May 05 2021 Michael Young - 4.15.0-1 -- update to xen-4.15.0 - adjust xen.canonicalize.patch - remove or adjust patches now included or superceded upstream - renumber patch - update libxendevicemodel libxenevtchn libxenforeignmemory versions - /etc/bash_completion.d/xl.sh is now xl - package xen-access xen-memshare xenstorepvh-stubdom.gz - xl-pci-configuration.5.gz -- adjust xen.ocaml.4.12.fixes.patch to work with earlier ocaml -- re-copy grub modules if they have changed - -* Fri Mar 19 2021 Michael Young - 4.14.1-8 +* Thu Mar 18 2021 Michael Young - 4.13.2-8 - HVM soft-reset crashes toolstack [XSA-368, CVE-2021-28687] (#1940610) -- adjust efi test to stop build failing -* Tue Mar 02 2021 Michael Young - 4.14.1-6 -- build fixes for OCaml 4.12.0 - -* Tue Feb 16 2021 Michael Young - 4.14.1-5 +* Wed Feb 17 2021 Michael Young - 4.13.2-7 - Linux: display frontend "be-alloc" mode is unsupported (comment only) - [XSA-363, CVE-2021-26934] (#1929549) + [XSA-363, CVE-2021-26934] (#1929549) - arm: The cache may not be cleaned for newly allocated scrubbed pages - [XSA-364, CVE-2021-26933] (#1929547) + [XSA-364, CVE-2021-26933] (#1929547) -* Mon Feb 01 2021 Michael Young - 4.14.1-4 -- backport upstream zstd dom0 and guest patches -- add libzstd-devel BuildRequires -- add weak dependency on grub modules to improve initial boot setup - -* Wed Jan 27 2021 Fedora Release Engineering - 4.14.1-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild - -* Thu Jan 21 2021 Michael Young - 4.14.1-2 +* Thu Jan 21 2021 Michael Young - 4.13.2-6 - IRQ vector leak on x86 [XSA-360] -* Sun Dec 20 2020 Michael Young - 4.14.1-1 -- update to 4.14.1 - adjust xen.canonicalize.patch - remove or adjust patches now included or superceded upstream - renumber patches - -* Tue Dec 15 2020 Michael Young - 4.14.0-14 +* Wed Dec 16 2020 Michael Young - 4.13.2-5 - xenstore watch notifications lacking permission checks [XSA-115, CVE-2020-29480] (#1908091) - Xenstore: new domains inheriting existing node permissions [XSA-322, @@ -1467,48 +949,38 @@ fi [XSA-352, CVE-2020-29486] (#1908003) - oxenstored: permissions not checked on root node [XSA-353, CVE-2020-29479] (#1908002) -- infinite loop when cleaning up IRQ vectors [XSA-356, CVE-2020-29567] - (#1907932) - FIFO event channels control block related ordering [XSA-358, CVE-2020-29570] (#1907931) - FIFO event channels control structure ordering [XSA-359, CVE-2020-29571] (#1908089) -* Sat Dec 05 2020 Jeff Law - 4.14.0-13 -- Work around another gcc-11 stringop-overflow diagnostic - -* Tue Nov 24 2020 Michael Young - 4.14.0-12 +* Tue Nov 24 2020 Michael Young - 4.13.2-4 - stack corruption from XSA-346 change [XSA-355] -* Mon Nov 23 2020 Michael Young - 4.14.0-11 +* Mon Nov 23 2020 Michael Young - 4.13.2-3 - support zstd compressed kernels (dom0 only) based on linux kernel code -* Tue Nov 10 2020 Michael Young - 4.14.0-10 +* Tue Nov 10 2020 Michael Young - 4.13.2-2 - Information leak via power sidechannel [XSA-351, CVE-2020-28368] (#1897146) -- add make as build requires -* Tue Nov 03 2020 Michael Young - 4.14.0-9 -- revised patch for XSA-286 (mitigating performance impact) +* Tue Nov 03 2020 Michael Young - 4.13.2-1 +- update to 4.13.2 + remove patches now included or superceded upstream -* Fri Oct 30 2020 Jeff Law - 4.14.0-8 -- Work around gcc-11 stringop-overflow diagnostics as well - -* Wed Oct 28 2020 Michael Young - 4.14.0-7 +* Wed Oct 28 2020 Michael Young - 4.13.1-8 - x86 PV guest INVLPG-like flushes may leave stale TLB entries - [XSA-286, CVE-2020-27674] (#1891092) -- simplify grub scripts (patches from Thierry Vignaud ) -- some fixes for gcc 11 + [XSA-286, CVE-2020-27674] (#1891092) -* Tue Oct 20 2020 Michael Young - 4.14.0-6 +* Tue Oct 20 2020 Michael Young - 4.13.1-7 - x86: Race condition in Xen mapping code [XSA-345, CVE-2020-27672] (#1891097) - undue deferral of IOMMU TLB flushes [XSA-346, CVE-2020-27671] (#1891093) -- unsafe AMD IOMMU page table updates [XSA-347, CVE-2020-27670] +- unsafe AMD IOMMU page table updates [XSA-347 CVE-2020-27670] (#1891088) -* Tue Sep 22 2020 Michael Young - 4.14.0-5 +* Tue Sep 22 2020 Michael Young - 4.13.1-6 - x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, CVE-2020-25602] (#1881619) - Missing unlock in XENMEM_acquire_resource error path [XSA-334, @@ -1529,34 +1001,10 @@ fi - lack of preemption in evtchn_reset() / evtchn_destroy() [XSA-344, CVE-2020-25601] (#1881586) -* Thu Sep 03 2020 Michael Young - 4.14.0-4 -- rebuild for OCaml 4.11.1 - -* Mon Aug 24 2020 Michael Young - 4.14.0-3 +* Tue Aug 25 2020 Michael Young - 4.13.1-5 - QEMU: usb: out-of-bounds r/w access issue [XSA-335, CVE-2020-14364] (#1871850) -* Wed Jul 29 2020 Fedora Release Engineering - 4.14.0-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild - -* Sun Jul 26 2020 Michael Young - 4.14.0-1 -- update to 4.14.0 - remove or adjust patches now included or superceded upstream - adjust xen.hypervisor.config - bison and flex packages now needed for hypervisor build - /usr/bin/vchan-socket-proxy and /usr/sbin/xenhypfs have been added - with associated libraries and man page -- re-enable pandoc for more documentation - adding xen-vbd-interface.7.gz -- revise documentation build dependencies - drop tex, texinfo, ghostscript, graphviz, discount - add perl(Pod::Html) perl(File::Find) -- additional build dependency for ocaml on perl(Data::Dumper) - -* Tue Jul 14 2020 Tom Stellard - 4.13.1-5 -- Use make macros -- https://fedoraproject.org/wiki/Changes/UseMakeBuildInstallMacro - * Tue Jul 07 2020 Michael Young - 4.13.1-4 - incorrect error handling in event channel port allocation leads to DoS [XSA-317, CVE-2020-15566] (#1854465) diff --git a/xsa363.patch b/xsa363.patch new file mode 100644 index 0000000..c8a3de3 --- /dev/null +++ b/xsa363.patch @@ -0,0 +1,22 @@ +From: Jan Beulich +Subject: SUPPORT.md: PV display frontend is unsupported in "backend allocation" mode + +This wasn't meant to be supported, but wasn't stated this way. + +This is XSA-363. + +Reported-by: Jan Belich +Signed-off-by: Jan Beulich + +--- a/SUPPORT.md ++++ b/SUPPORT.md +@@ -414,7 +414,8 @@ Guest-side driver capable of speaking th + + Guest-side driver capable of speaking the Xen PV display protocol + +- Status, Linux: Supported ++ Status, Linux: Supported (outside of "backend allocation" mode) ++ Status, Linux: Experimental (in "backend allocation" mode) + + ### PV Console (frontend) + diff --git a/xsa483.patch b/xsa483.patch deleted file mode 100644 index 8ecb2e9..0000000 --- a/xsa483.patch +++ /dev/null @@ -1,30 +0,0 @@ -From: Andrii Sultanov -Subject: tools/oxenstored: Reset quota when resetting permissions - -The quota object contains both limits and the current node usage counts. - -When a domain is torn down, the node data itself is cleaned up but the node -usage counts are not. A later domain reusing the same domid can create fewer -nodes before being deemed to be over quota. - -Reset the count when the node permissions are cleaned up. - -This is XSA-483 / CVE-2026-23556. - -Signed-off-by: Andrii Sultanov -Signed-off-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index 9b8dd2812df0..aa9204ead3ec 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -465,7 +465,8 @@ let reset_permissions store domid = - if perms <> node.perms then - Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); - Some { node with Node.perms } -- ) store.root -+ ) store.root; -+ store.quota <- Quota.del store.quota domid - - type ops = { - store: t; diff --git a/xsa484.patch b/xsa484.patch deleted file mode 100644 index 522549e..0000000 --- a/xsa484.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 3d0d19ad17f29c64dde4a7baf392da4fd58f3654 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Mon, 16 Mar 2026 15:06:11 +0100 -Subject: [PATCH] tools/xenstored: make conn_delete_all_transactions() - idempotent - -conn_delete_all_transactions() should be callable in any context, -resetting ALL transaction related data. - -This includes number of active transactions and the transaction -pointer in struct connection. - -So reset conn->trans to NULL in conn_delete_all_transactions() and -do the cleanup for each transaction in destroy_transaction(). - -This avoids triggering the assert() in conn_delete_all_transactions() -in case e.g. ignore_connection() was called while an operation inside -a transaction was performed, or XS_RESET_WATCHES was called in a -transaction. - -This is XSA-484 / CVE-2026-23557. - -Reported-by: Andrii Sultanov -Fixes: 1f9d04fb021c ("xenstored: allow guest to shutdown all its watches/transactions") -Signed-off-by: Juergen Gross ---- - tools/xenstored/transaction.c | 20 +++++++++----------- - 1 file changed, 9 insertions(+), 11 deletions(-) - -diff --git a/tools/xenstored/transaction.c b/tools/xenstored/transaction.c -index 167cd597fd..0825c48859 100644 ---- a/tools/xenstored/transaction.c -+++ b/tools/xenstored/transaction.c -@@ -432,17 +432,23 @@ static int finalize_transaction(struct connection *conn, - static int destroy_transaction(void *_transaction) - { - struct transaction *trans = _transaction; -+ struct connection *conn = trans->conn; - struct accessed_node *i; - - wrl_ntransactions--; - trace_destroy(trans, "transaction"); - while ((i = list_top(&trans->accessed, struct accessed_node, list))) { - if (i->ta_node) -- db_delete(trans->conn, i->trans_name, NULL); -+ db_delete(conn, i->trans_name, NULL); - list_del(&i->list); - talloc_free(i); - } - -+ list_del(&trans->list); -+ domain_transaction_dec(conn); -+ if (list_empty(&conn->transaction_list)) -+ conn->ta_start_time = 0; -+ - return 0; - } - -@@ -523,10 +529,6 @@ int do_transaction_end(const void *ctx, struct connection *conn, - return ENOENT; - - conn->transaction = NULL; -- list_del(&trans->list); -- domain_transaction_dec(conn); -- if (list_empty(&conn->transaction_list)) -- conn->ta_start_time = 0; - - chk_quota = trans->node_created && domain_is_unprivileged(conn); - -@@ -572,14 +574,10 @@ void conn_delete_all_transactions(struct connection *conn) - struct transaction *trans; - - while ((trans = list_top(&conn->transaction_list, -- struct transaction, list))) { -- list_del(&trans->list); -+ struct transaction, list))) - talloc_free(trans); -- } -- -- assert(conn->transaction == NULL); - -- conn->ta_start_time = 0; -+ conn->transaction = NULL; - } - - int check_transactions(struct hashtable *hash) --- -2.53.0 - diff --git a/xsa486.patch b/xsa486.patch deleted file mode 100644 index 654e957..0000000 --- a/xsa486.patch +++ /dev/null @@ -1,181 +0,0 @@ -From: Jan Beulich -Subject: gnttab: split gnttab_map_frame() - -If a domain tries to map status frames in parallel to switching grant -table version from 2 to 1, the mapping operation may put in place P2M -entries referencing MFNs which gnttab_unpopulate_status_frames() is in the -process of freeing. - -Ideally we would refcount pages when entered into P2M tables, but that's a -significant change. Extend the grant-table-locked region instead in -xenmem_add_to_physmap_one() (being the sole caller of gnttab_map_frame()), -such that a race with gnttab_unpopulate_status_frames() is no longer -possible. - -This is XSA-486 / CVE-2026-23558. - -Fixes: 5ce8fafa947c ("Dynamic grant-table sizing") -Fixes: a98dc13703e0 ("Introduce a grant_entry_v2 structure") -Reported-by: Rafal Wojtczuk -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/arm/mm.c -+++ b/xen/arch/arm/mm.c -@@ -174,12 +174,10 @@ int xenmem_add_to_physmap_one( - switch ( space ) - { - case XENMAPSPACE_grant_table: -- rc = gnttab_map_frame(d, idx, gfn, &mfn); -+ rc = gnttab_map_frame_begin(d, idx, gfn, &mfn); - if ( rc ) - return rc; - -- /* Need to take care of the reference obtained in gnttab_map_frame(). */ -- page = mfn_to_page(mfn); - t = p2m_ram_rw; - - break; -@@ -281,10 +279,23 @@ int xenmem_add_to_physmap_one( - * to drop the reference we took earlier. In all other cases we need to - * drop any reference we took earlier (perhaps indirectly). - */ -- if ( space == XENMAPSPACE_gmfn_foreign ? rc : page != NULL ) -+ switch ( space ) - { -+ default: -+ if ( page ) -+ put_page(page); -+ break; -+ -+ case XENMAPSPACE_grant_table: -+ gnttab_map_frame_end(d, mfn); -+ break; -+ -+ case XENMAPSPACE_gmfn_foreign: -+ if ( !rc ) -+ break; - ASSERT(page != NULL); - put_page(page); -+ break; - } - - return rc; ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -2009,11 +2009,9 @@ int xenmem_add_to_physmap_one( - break; - - case XENMAPSPACE_grant_table: -- rc = gnttab_map_frame(d, idx, gfn, &mfn); -+ rc = gnttab_map_frame_begin(d, idx, gfn, &mfn); - if ( rc ) - return rc; -- /* Need to take care of the reference obtained in gnttab_map_frame(). */ -- page = mfn_to_page(mfn); - break; - - case XENMAPSPACE_gmfn: -@@ -2095,19 +2093,28 @@ int xenmem_add_to_physmap_one( - put_gfn(d, gfn_x(gfn)); - - put_both: -- /* -- * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. -- * We also may need to transfer ownership of the page reference to our -- * caller. -- */ -- if ( space == XENMAPSPACE_gmfn ) -+ switch ( space ) - { -+ case XENMAPSPACE_gmfn: -+ /* -+ * We took a ref of the gfn at the top. We also may need to transfer -+ * ownership of the page reference to our caller. -+ */ - put_gfn(d, gmfn); - if ( !rc && extra.ppage ) - { - *extra.ppage = page; - page = NULL; - } -+ break; -+ -+ case XENMAPSPACE_grant_table: -+ /* -+ * We (gnttab_map_frame_begin()) acquired a lock and took a ref of the -+ * page underlying the MFN at the top. -+ */ -+ gnttab_map_frame_end(d, mfn); -+ break; - } - - if ( page ) ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -4250,7 +4250,8 @@ int gnttab_acquire_resource( - return rc; - } - --int gnttab_map_frame(struct domain *d, unsigned long idx, gfn_t gfn, mfn_t *mfn) -+int gnttab_map_frame_begin( -+ struct domain *d, unsigned long idx, gfn_t gfn, mfn_t *mfn) - { - int rc = 0; - struct grant_table *gt = d->grant_table; -@@ -4288,11 +4289,19 @@ int gnttab_map_frame(struct domain *d, u - put_page(pg); - } - -- grant_write_unlock(gt); -+ if ( rc ) -+ grant_write_unlock(d->grant_table); - - return rc; - } - -+void gnttab_map_frame_end(struct domain *d, mfn_t mfn) -+{ -+ put_page(mfn_to_page(mfn)); -+ -+ grant_write_unlock(d->grant_table); -+} -+ - static void gnttab_usage_print(struct domain *rd) - { - int first = 1; ---- a/xen/include/xen/grant_table.h -+++ b/xen/include/xen/grant_table.h -@@ -60,8 +60,13 @@ int gnttab_release_mappings(struct domai - int mem_sharing_gref_to_gfn(struct grant_table *gt, grant_ref_t ref, - gfn_t *gfn, uint16_t *status); - --int gnttab_map_frame(struct domain *d, unsigned long idx, gfn_t gfn, -- mfn_t *mfn); -+/* -+ * These need to be used as a pair, as the first (in the success case) returns -+ * with a lock and page reference held which the second needs to drop. -+ */ -+int gnttab_map_frame_begin(struct domain *d, unsigned long idx, gfn_t gfn, -+ mfn_t *mfn); -+void gnttab_map_frame_end(struct domain *d, mfn_t mfn); - - unsigned int gnttab_resource_max_frames(const struct domain *d, unsigned int id); - -@@ -100,12 +105,14 @@ static inline int mem_sharing_gref_to_gf - return -EINVAL; - } - --static inline int gnttab_map_frame(struct domain *d, unsigned long idx, -- gfn_t gfn, mfn_t *mfn) -+static inline int gnttab_map_frame_begin(struct domain *d, unsigned long idx, -+ gfn_t gfn, mfn_t *mfn) - { - return -EINVAL; - } - -+static inline void gnttab_map_frame_end(struct domain *d, mfn_t mfn) {} -+ - static inline unsigned int gnttab_resource_max_frames( - const struct domain *d, unsigned int id) - { diff --git a/xsa490-4.21.patch b/xsa490-4.21.patch deleted file mode 100644 index 5a560cb..0000000 --- a/xsa490-4.21.patch +++ /dev/null @@ -1,43 +0,0 @@ -From: Andrew Cooper -Subject: x86/amd: Mitigate AMD-SN-7052 - -This is XSA-490 / CVE-2025-54518. - -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index 1bb0766ebf13..b5bf2b732e8f 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -1116,11 +1116,25 @@ static void amd_check_bp_cfg(void) - { - uint64_t val, new = 0; - -- /* -- * AMD Erratum #1485. Set bit 5, as instructed. -- */ -- if (!cpu_has_hypervisor && boot_cpu_data.x86 == 0x19 && is_zen4_uarch()) -- new |= (1 << 5); -+ if (!cpu_has_hypervisor) { -+ /* -+ * AMD Erratum #1485. If SMT is enabled and STIBP disabled, -+ * the CPU may fetch incorrect instruction bytes. -+ * -+ * Set bit 5, as instructed. -+ */ -+ if (boot_cpu_data.x86 == 0x19 && is_zen4_uarch()) -+ new |= (1 << 5); -+ -+ /* -+ * AMD SB-7052. CPU OP Cache corruption, causing instructions -+ * to be executed at a higher privilege. -+ * -+ * Set bit 33, as instructed. -+ */ -+ if (boot_cpu_data.x86 == 0x17 && is_zen2_uarch()) -+ new |= (1UL << 33); -+ } - - /* - * On hardware supporting SRSO_MSR_FIX, activate BP_SPEC_REDUCE by diff --git a/xsa491-4.21.patch b/xsa491-4.21.patch deleted file mode 100644 index d1ebc1a..0000000 --- a/xsa491-4.21.patch +++ /dev/null @@ -1,211 +0,0 @@ -From: Jan Beulich -Subject: x86/HVM: add locking to I/O port translation list traversal - -XEN_DOMCTL_ioport_mapping is usable by DM stubdoms, and hence we can't -assume the list to be left unaltered while the guest (really: the -hypervisor on behalf of the guest) is accessing it. - -This is XSA-491 / CVE-2026-42487. - -Fixes: 192c4dabc344 ("domctl and p2m changes for PCI passthru") -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/domctl.c -+++ b/xen/arch/x86/domctl.c -@@ -663,6 +663,7 @@ long arch_do_domctl( - "ioport_map:add: dom%d gport=%x mport=%x nr=%x\n", - d->domain_id, fgp, fmp, np); - -+ write_lock(&hvm->g2m_ioport_lock); - list_for_each_entry(g2m_ioport, &hvm->g2m_ioport_list, list) - if (g2m_ioport->mport == fmp ) - { -@@ -684,11 +685,14 @@ long arch_do_domctl( - g2m_ioport->np = np; - list_add_tail(&g2m_ioport->list, &hvm->g2m_ioport_list); - } -+ write_unlock(&hvm->g2m_ioport_lock); - if ( !ret ) - ret = ioports_permit_access(d, fmp, fmp + np - 1); - if ( ret && !found && g2m_ioport ) - { -+ write_lock(&hvm->g2m_ioport_lock); - list_del(&g2m_ioport->list); -+ write_unlock(&hvm->g2m_ioport_lock); - xfree(g2m_ioport); - } - } -@@ -697,6 +701,8 @@ long arch_do_domctl( - printk(XENLOG_G_INFO - "ioport_map:remove: dom%d gport=%x mport=%x nr=%x\n", - d->domain_id, fgp, fmp, np); -+ -+ write_lock(&hvm->g2m_ioport_lock); - list_for_each_entry(g2m_ioport, &hvm->g2m_ioport_list, list) - if ( g2m_ioport->mport == fmp ) - { -@@ -704,6 +710,8 @@ long arch_do_domctl( - xfree(g2m_ioport); - break; - } -+ write_unlock(&hvm->g2m_ioport_lock); -+ - ret = ioports_deny_access(d, fmp, fmp + np - 1); - if ( ret && is_hardware_domain(currd) ) - printk(XENLOG_ERR ---- a/xen/arch/x86/hvm/emulate.c -+++ b/xen/arch/x86/hvm/emulate.c -@@ -160,7 +160,6 @@ void hvmemul_cancel(struct vcpu *v) - hvio->mmio_insn_bytes = 0; - hvio->mmio_access = (struct npfec){}; - hvio->mmio_retry = false; -- hvio->g2m_ioport = NULL; - - hvmemul_cache_disable(v); - } ---- a/xen/arch/x86/hvm/hvm.c -+++ b/xen/arch/x86/hvm/hvm.c -@@ -610,6 +610,7 @@ int hvm_domain_initialise(struct domain - spin_lock_init(&d->arch.hvm.irq_lock); - spin_lock_init(&d->arch.hvm.uc_lock); - spin_lock_init(&d->arch.hvm.write_map.lock); -+ rwlock_init(&d->arch.hvm.g2m_ioport_lock); - rwlock_init(&d->arch.hvm.mmcfg_lock); - INIT_LIST_HEAD(&d->arch.hvm.write_map.list); - INIT_LIST_HEAD(&d->arch.hvm.g2m_ioport_list); ---- a/xen/arch/x86/hvm/io.c -+++ b/xen/arch/x86/hvm/io.c -@@ -143,36 +143,56 @@ bool handle_pio(uint16_t port, unsigned - return true; - } - --static bool cf_check g2m_portio_accept( -- const struct hvm_io_handler *handler, const ioreq_t *p) -+/* NB: Returns with the lock held in the success case. */ -+static const struct g2m_ioport *g2m_portio_find_and_lock(struct hvm_domain *hvm, -+ uint64_t addr, -+ uint32_t size) - { -- struct vcpu *curr = current; -- const struct hvm_domain *hvm = &curr->domain->arch.hvm; -- struct hvm_vcpu_io *hvio = &curr->arch.hvm.hvm_io; -- struct g2m_ioport *g2m_ioport; -- unsigned int start, end; -+ const struct g2m_ioport *g2m_ioport; -+ -+ read_lock(&hvm->g2m_ioport_lock); - - list_for_each_entry( g2m_ioport, &hvm->g2m_ioport_list, list ) - { -- start = g2m_ioport->gport; -- end = start + g2m_ioport->np; -- if ( (p->addr >= start) && (p->addr + p->size <= end) ) -- { -- hvio->g2m_ioport = g2m_ioport; -- return 1; -- } -+ unsigned int start = g2m_ioport->gport; -+ -+ if ( addr >= start && addr + size <= start + g2m_ioport->np ) -+ return g2m_ioport; - } - -- return 0; -+ read_unlock(&hvm->g2m_ioport_lock); -+ -+ return NULL; -+} -+ -+static bool cf_check g2m_portio_accept( -+ const struct hvm_io_handler *handler, const ioreq_t *p) -+{ -+ struct hvm_domain *hvm = ¤t->domain->arch.hvm; -+ const struct g2m_ioport *g2m_ioport = -+ g2m_portio_find_and_lock(hvm, p->addr, p->size); -+ -+ if ( !g2m_ioport ) -+ return false; -+ -+ read_unlock(&hvm->g2m_ioport_lock); -+ -+ return true; - } - - static int cf_check g2m_portio_read( - const struct hvm_io_handler *handler, uint64_t addr, uint32_t size, - uint64_t *data) - { -- struct hvm_vcpu_io *hvio = ¤t->arch.hvm.hvm_io; -- const struct g2m_ioport *g2m_ioport = hvio->g2m_ioport; -- unsigned int mport = (addr - g2m_ioport->gport) + g2m_ioport->mport; -+ struct hvm_domain *hvm = ¤t->domain->arch.hvm; -+ const struct g2m_ioport *g2m_ioport = -+ g2m_portio_find_and_lock(hvm, addr, size); -+ unsigned int mport; -+ -+ if ( !g2m_ioport ) -+ return X86EMUL_RETRY; -+ -+ mport = addr - g2m_ioport->gport + g2m_ioport->mport; - - switch ( size ) - { -@@ -189,6 +209,8 @@ static int cf_check g2m_portio_read( - BUG(); - } - -+ read_unlock(&hvm->g2m_ioport_lock); -+ - return X86EMUL_OKAY; - } - -@@ -196,9 +218,15 @@ static int cf_check g2m_portio_write( - const struct hvm_io_handler *handler, uint64_t addr, uint32_t size, - uint64_t data) - { -- struct hvm_vcpu_io *hvio = ¤t->arch.hvm.hvm_io; -- const struct g2m_ioport *g2m_ioport = hvio->g2m_ioport; -- unsigned int mport = (addr - g2m_ioport->gport) + g2m_ioport->mport; -+ struct hvm_domain *hvm = ¤t->domain->arch.hvm; -+ const struct g2m_ioport *g2m_ioport = -+ g2m_portio_find_and_lock(hvm, addr, size); -+ unsigned int mport; -+ -+ if ( !g2m_ioport ) -+ return X86EMUL_RETRY; -+ -+ mport = addr - g2m_ioport->gport + g2m_ioport->mport; - - switch ( size ) - { -@@ -215,6 +243,8 @@ static int cf_check g2m_portio_write( - BUG(); - } - -+ read_unlock(&hvm->g2m_ioport_lock); -+ - return X86EMUL_OKAY; - } - ---- a/xen/arch/x86/include/asm/hvm/domain.h -+++ b/xen/arch/x86/include/asm/hvm/domain.h -@@ -125,6 +125,7 @@ struct hvm_domain { - - /* List of guest to machine IO ports mapping. */ - struct list_head g2m_ioport_list; -+ rwlock_t g2m_ioport_lock; - - /* List of MMCFG regions trapped by Xen. */ - struct list_head mmcfg_regions; ---- a/xen/arch/x86/include/asm/hvm/vcpu.h -+++ b/xen/arch/x86/include/asm/hvm/vcpu.h -@@ -54,8 +54,6 @@ struct hvm_vcpu_io { - unsigned long msix_unmask_address; - unsigned long msix_snoop_address; - unsigned long msix_snoop_gpa; -- -- const struct g2m_ioport *g2m_ioport; - }; - - struct nestedvcpu { diff --git a/xsa492-4.21-01.patch b/xsa492-4.21-01.patch deleted file mode 100644 index 7244ebd..0000000 --- a/xsa492-4.21-01.patch +++ /dev/null @@ -1,264 +0,0 @@ -From: Jan Beulich -Subject: sched: use sequence counter to enlighten vcpu_runstate_get() - -Subsequently XEN_DOMCTL_getdomaininfo will want to invoke the function -without holding a lock, thus allowing parallel execution of potentially -many instances. As was learned from 228ab9992ffb ("domctl: improve -locking during domain destruction"), reverted by d0887cc6b16e, such -parallelism can result in severe lock contention on any (previously) -inner lock. To avoid taking that risk replace the use of the scheduler -lock in vcpu_runstate_get() by a newly introduced sequence counter. -Convert the "no lock if current" property to "use a local counter -instance", thus guaranteeing the loop to exit after the first iteration. - -Skeleton and commentary of the seqcount implementation based on / -derived from Linux 6.11-rc. - -To have runstate_seq placed next to runstate in struct vcpu, without -introducing a new obvious padding hole, yet while keeping the latter -adjacent to runstate_guest{,_area} as well, move runstate down a little. - -This is part of XSA-492. - -Requested-by: Andrew Cooper -Signed-off-by: Jan Beulich -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné -Reviewed-by: Juergen Gross - ---- a/xen/common/sched/core.c -+++ b/xen/common/sched/core.c -@@ -281,13 +281,18 @@ static inline void vcpu_runstate_change( - } - - delta = new_entry_time - v->runstate.state_entry_time; -- if ( delta > 0 ) -+ -+ /* Serialization: ->schedule_lock (see ASSERT() above). */ -+ with_seq_write(&v->runstate_seq) - { -- v->runstate.time[v->runstate.state] += delta; -- v->runstate.state_entry_time = new_entry_time; -- } -+ if ( delta > 0 ) -+ { -+ v->runstate.time[v->runstate.state] += delta; -+ v->runstate.state_entry_time = new_entry_time; -+ } - -- v->runstate.state = new_state; -+ v->runstate.state = new_state; -+ } - } - - void sched_guest_idle(void (*idle) (void), unsigned int cpu) -@@ -307,30 +312,18 @@ void sched_guest_idle(void (*idle) (void - void vcpu_runstate_get(const struct vcpu *v, - struct vcpu_runstate_info *runstate) - { -- spinlock_t *lock; -- s_time_t delta; -- struct sched_unit *unit; -+ struct seqcount seq = SEQCNT_ZERO(); -+ const struct seqcount *s = likely(v == current) ? &seq : &v->runstate_seq; - -- rcu_read_lock(&sched_res_rculock); -- -- /* -- * Be careful in case of an idle vcpu: the assignment to a unit might -- * change even with the scheduling lock held, so be sure to use the -- * correct unit for locking in order to avoid triggering an ASSERT() in -- * the unlock function. -- */ -- unit = is_idle_vcpu(v) ? get_sched_res(v->processor)->sched_unit_idle -- : v->sched_unit; -- lock = likely(v == current) ? NULL : unit_schedule_lock_irq(unit); -- memcpy(runstate, &v->runstate, sizeof(*runstate)); -- delta = NOW() - runstate->state_entry_time; -- if ( delta > 0 ) -- runstate->time[runstate->state] += delta; -- -- if ( unlikely(lock != NULL) ) -- unit_schedule_unlock_irq(lock, unit); -+ until_seq_read(s) -+ { -+ s_time_t delta; - -- rcu_read_unlock(&sched_res_rculock); -+ *runstate = v->runstate; -+ delta = NOW() - runstate->state_entry_time; -+ if ( delta > 0 ) -+ runstate->time[runstate->state] += delta; -+ } - } - - uint64_t get_cpu_idle_time(unsigned int cpu) ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -198,7 +199,6 @@ struct vcpu - - struct sched_unit *sched_unit; - -- struct vcpu_runstate_info runstate; - #ifndef CONFIG_COMPAT - # define runstate_guest(v) ((v)->runstate_guest) - XEN_GUEST_HANDLE(vcpu_runstate_info_t) runstate_guest; /* guest address */ -@@ -210,6 +210,8 @@ struct vcpu - } runstate_guest; /* guest address */ - #endif - struct guest_area runstate_guest_area; -+ struct vcpu_runstate_info runstate; -+ struct seqcount runstate_seq; - unsigned int new_state; - - /* Has the FPU been initialised? */ ---- /dev/null -+++ b/xen/include/xen/seqcount.h -@@ -0,0 +1,139 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+#ifndef XEN_SEQCOUNT_H -+#define XEN_SEQCOUNT_H -+ -+#include -+#include -+ -+#include -+#include -+ -+/* -+ * Sequence counters (seqcount_t) -+ * -+ * This is the raw counting mechanism, without any writer protection. -+ * -+ * Write side critical sections must be serialized (and non-preemptible). -+ * -+ * If readers can be invoked from interrupt contexts, interrupts must also -+ * be respectively disabled before entering the write section. -+ * -+ * This mechanism can't be used if the protected data contains pointers, -+ * as the writer can invalidate a pointer that a reader is following. -+ */ -+struct seqcount { -+ unsigned int sequence; -+}; -+ -+/* -+ * SEQCNT_ZERO() - initializer for seqcount_t -+ * @name: Name of the struct seqcount instance -+ */ -+#define SEQCNT_ZERO() { .sequence = 0 } -+ -+static inline unsigned int seqprop_sequence(const struct seqcount *s) -+{ -+ return ACCESS_ONCE(s->sequence); -+} -+ -+/* -+ * read_seqcount_begin() - begin a seqcount read critical section -+ * @s: Pointer to struct seqcount -+ * -+ * Return: count to be passed to read_seqcount_retry() -+ */ -+static inline unsigned int _read_seqcount_begin(const struct seqcount *s) -+{ -+ unsigned int seq; -+ -+ while ((seq = seqprop_sequence(s)) & 1) -+ cpu_relax(); -+ -+ smp_rmb(); -+ -+ return seq; -+} -+ -+static always_inline unsigned int read_seqcount_begin(const struct seqcount *s) -+{ -+ unsigned int seq = _read_seqcount_begin(s); -+ -+ block_lock_speculation(); -+ -+ return seq; -+} -+ -+/* -+ * read_seqcount_retry() - end a seqcount read critical section -+ * @s: Pointer to struct seqcount -+ * @start: count, from read_seqcount_begin() -+ * -+ * read_seqcount_retry closes the read critical section of given struct -+ * seqcount. If the critical section was invalid, it must be ignored -+ * (and typically retried). -+ * -+ * Return: true if a read section retry is required, else false -+ */ -+static inline bool _read_seqcount_retry(const struct seqcount *s, -+ unsigned int start) -+{ -+ smp_rmb(); -+ return unlikely(seqprop_sequence(s) != start); -+} -+ -+static always_inline bool read_seqcount_retry(const struct seqcount *s, -+ unsigned int start) -+{ -+ return lock_evaluate_nospec(_read_seqcount_retry(s, start)); -+} -+ -+/* Loops until a consistent count has been observed across the loop body. */ -+#define until_seq_read(seq) \ -+ for ( unsigned int retry_ = 1, count_; \ -+ retry_ && (count_ = read_seqcount_begin(seq), true); \ -+ retry_ = read_seqcount_retry(seq, count_) ) -+ -+/* -+ * write_seqcount_begin() - start a struct seqcount write side critical section -+ * @s: Pointer to struct seqcount -+ * -+ * Context: sequence counter write side sections must be serialized. -+ * If readers can be invoked from interrupt context, interrupts must be -+ * respectively disabled. -+ */ -+static inline void write_seqcount_begin(struct seqcount *s) -+{ -+ add_sized(&s->sequence, 1); -+ smp_wmb(); -+} -+ -+/* -+ * write_seqcount_end() - end a struct seqcount write side critical section -+ * @s: Pointer to seqcount -+ */ -+static inline void write_seqcount_end(struct seqcount *s) -+{ -+ smp_wmb(); -+ add_sized(&s->sequence, 1); -+} -+ -+/* -+ * Not really a loop, but we need write_seqcount_{begin,end}() in the correct -+ * position. -+ */ -+#define with_seq_write(seq) \ -+ for ( bool once_ = true; \ -+ once_ && (write_seqcount_begin(seq), true); \ -+ (write_seqcount_end(seq), once_ = false) ) -+ -+#endif /* XEN_SEQCOUNT_H */ -+ -+/* -+ * Local variables: -+ * mode: C -+ * c-file-style: "BSD" -+ * c-basic-offset: 4 -+ * tab-width: 4 -+ * indent-tabs-mode: nil -+ * End: -+ */ diff --git a/xsa492-4.21-02.patch b/xsa492-4.21-02.patch deleted file mode 100644 index 75ca8ca..0000000 --- a/xsa492-4.21-02.patch +++ /dev/null @@ -1,104 +0,0 @@ -From: Jan Beulich -Subject: domctl: handle XEN_DOMCTL_getdomaininfo without acquiring domctl lock - -getdomaininfo() is not called under consistently the same lock. Thus, -with caller side locking irrelevant, it can as well be called with the -domctl lock not held. (Callers not pausing the domain they want to -retrieve information for already need to be aware that not all of the -data returned can be relied on as being consistent; most data will also -be stale by the time the caller gets to look at it.) - -Move the handling not only ahead of acquiring the lock, but also ahead -of the XSM check, leveraging that the sub-op has its own hook. - -While moving, convert an assignment to an assertion: The domain in -question was determined from the field which previously was "updated". - -This is part of XSA-492. - -Fixes: 5513bd0b4675 ("add xenstore domain flag to hypervisor") -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Acked-by: Daniel P. Smith - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -318,6 +318,26 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - break; - } - -+ /* Handle sub-ops not requiring the domctl lock. */ -+ switch ( op->cmd ) -+ { -+ case XEN_DOMCTL_getdomaininfo: -+ ret = xsm_getdomaininfo(XSM_XS_PRIV, d); -+ if ( !ret ) -+ { -+ getdomaininfo(d, &op->u.getdomaininfo); -+ -+ ASSERT(op->domain == op->u.getdomaininfo.domain); -+ copyback = true; -+ } -+ -+ goto domctl_out_unlock_domonly; -+ -+ default: -+ /* Everything else handled further down. */ -+ break; -+ } -+ - ret = xsm_domctl(XSM_OTHER, d, op->cmd, - /* SSIDRef only applicable for cmd == createdomain */ - op->u.createdomain.ssidref); -@@ -516,17 +536,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - copyback = 1; - break; - -- case XEN_DOMCTL_getdomaininfo: -- ret = xsm_getdomaininfo(XSM_XS_PRIV, d); -- if ( ret ) -- break; -- -- getdomaininfo(d, &op->u.getdomaininfo); -- -- op->domain = op->u.getdomaininfo.domain; -- copyback = 1; -- break; -- - case XEN_DOMCTL_getvcpucontext: - { - vcpu_guest_context_u c = { .nat = NULL }; ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -172,9 +172,13 @@ static XSM_INLINE int cf_check xsm_domct - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_unbind_pt_irq: - return xsm_default_action(XSM_DM_PRIV, current->domain, d); -- case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: - return xsm_default_action(XSM_XS_PRIV, current->domain, d); -+ -+ case XEN_DOMCTL_getdomaininfo: -+ ASSERT_UNREACHABLE(); -+ return -EILSEQ; -+ - default: - return xsm_default_action(XSM_PRIV, current->domain, d); - } ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -682,8 +682,12 @@ static int cf_check flask_domctl(struct - */ - return avc_current_has_perm(ssidref, SECCLASS_DOMAIN, DOMAIN__CREATE, NULL); - -- /* These have individual XSM hooks (common/domctl.c) */ -+ /* These have individual XSM hooks and don't make it here. */ - case XEN_DOMCTL_getdomaininfo: -+ ASSERT_UNREACHABLE(); -+ return -EILSEQ; -+ -+ /* These have individual XSM hooks (common/domctl.c) */ - case XEN_DOMCTL_scheduler_op: - case XEN_DOMCTL_irq_permission: - case XEN_DOMCTL_iomem_permission: diff --git a/xsa492-4.21-03.patch b/xsa492-4.21-03.patch deleted file mode 100644 index 5a0db22..0000000 --- a/xsa492-4.21-03.patch +++ /dev/null @@ -1,87 +0,0 @@ -From: Daniel P. Smith -Subject: domctl: protect locking for get_domain_state - -When DOMID_INVALID is passed, the dom exec handler lock is being taken -without any check that the domain is even allowed to take the lock. This -allows for an unauthorized domain to DoS the get_domain_state domctl op. -Move to consider the op effectively being called against the hypervisor. -Thus it is the target of the call being invoked to identify the last -domain with a state change. The subsequent check of whether the source -domain is allowed the state of the last domain to change state is still -relevant. - -This is part of XSA-492. - -Signed-off-by: Daniel P. Smith -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/tools/flask/policy/modules/xenstore.te -+++ b/tools/flask/policy/modules/xenstore.te -@@ -14,6 +14,7 @@ allow xenstore_t xen_t:xen writeconsole; - # Xenstore queries domaininfo on all domains - allow xenstore_t domain_type:domain getdomaininfo; - allow xenstore_t domain_type:domain2 get_domain_state; -+allow xenstore_t domxen_t:domain2 get_domain_state; - - # As a shortcut, the following 3 rules are used instead of adding a domain_comms - # rule between xenstore_t and every domain type that talks to xenstore ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -216,12 +216,8 @@ int get_domain_state(struct xen_domctl_g - if ( info->pad0 ) - return -EINVAL; - -- if ( d ) -+ if ( d != dom_xen ) - { -- rc = xsm_get_domain_state(XSM_XS_PRIV, d); -- if ( rc ) -- return rc; -- - set_domain_state_info(info, d); - - return 0; ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -304,13 +304,19 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - fallthrough; - case XEN_DOMCTL_test_assign_device: - case XEN_DOMCTL_vm_event_op: -- case XEN_DOMCTL_get_domain_state: - if ( op->domain == DOMID_INVALID ) - { - d = NULL; - break; - } - fallthrough; -+ case XEN_DOMCTL_get_domain_state: -+ if ( op->domain == DOMID_INVALID ) -+ { -+ d = dom_xen; -+ break; -+ } -+ fallthrough; - default: - d = rcu_lock_domain_by_id(op->domain); - if ( !d ) -@@ -863,7 +869,9 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - break; - - case XEN_DOMCTL_get_domain_state: -- ret = get_domain_state(&op->u.get_domain_state, d, &op->domain); -+ ret = xsm_get_domain_state(XSM_XS_PRIV, d); -+ if ( !ret ) -+ ret = get_domain_state(&op->u.get_domain_state, d, &op->domain); - if ( !ret ) - copyback = true; - break; -@@ -876,7 +884,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - domctl_lock_release(); - - domctl_out_unlock_domonly: -- if ( d && d != dom_io ) -+ if ( d && !is_system_domain(d) ) - rcu_unlock_domain(d); - - if ( copyback && __copy_to_guest(u_domctl, op, 1) ) diff --git a/xsa492-4.21-04.patch b/xsa492-4.21-04.patch deleted file mode 100644 index 481ff5d..0000000 --- a/xsa492-4.21-04.patch +++ /dev/null @@ -1,81 +0,0 @@ -From: Jan Beulich -Subject: domctl: handle XEN_DOMCTL_get_domain_state without acquiring domctl lock - -get_domain_state() uses its own locking. Thus, with caller side locking -irrelevant, it can as well be called with the domctl lock not held. - -Move the handling not only ahead of acquiring the lock, but also ahead -of the XSM check, leveraging that the sub-op has its own hook. - -This is part of XSA-492. - -Fixes: 3ad3df1bd0aa ("xen: add new domctl get_domain_state") -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith -Reviewed-by: Roger Pau Monné - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -339,6 +339,14 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - - goto domctl_out_unlock_domonly; - -+ case XEN_DOMCTL_get_domain_state: -+ ret = xsm_get_domain_state(XSM_XS_PRIV, d); -+ if ( !ret ) -+ ret = get_domain_state(&op->u.get_domain_state, d, &op->domain); -+ if ( !ret ) -+ copyback = true; -+ goto domctl_out_unlock_domonly; -+ - default: - /* Everything else handled further down. */ - break; -@@ -868,14 +876,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - ret = -EOPNOTSUPP; - break; - -- case XEN_DOMCTL_get_domain_state: -- ret = xsm_get_domain_state(XSM_XS_PRIV, d); -- if ( !ret ) -- ret = get_domain_state(&op->u.get_domain_state, d, &op->domain); -- if ( !ret ) -- copyback = true; -- break; -- - default: - ret = arch_do_domctl(op, d, u_domctl); - break; ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -172,10 +172,9 @@ static XSM_INLINE int cf_check xsm_domct - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_unbind_pt_irq: - return xsm_default_action(XSM_DM_PRIV, current->domain, d); -- case XEN_DOMCTL_get_domain_state: -- return xsm_default_action(XSM_XS_PRIV, current->domain, d); - - case XEN_DOMCTL_getdomaininfo: -+ case XEN_DOMCTL_get_domain_state: - ASSERT_UNREACHABLE(); - return -EILSEQ; - ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -684,6 +684,7 @@ static int cf_check flask_domctl(struct - - /* These have individual XSM hooks and don't make it here. */ - case XEN_DOMCTL_getdomaininfo: -+ case XEN_DOMCTL_get_domain_state: - ASSERT_UNREACHABLE(); - return -EILSEQ; - -@@ -694,7 +695,6 @@ static int cf_check flask_domctl(struct - case XEN_DOMCTL_memory_mapping: - case XEN_DOMCTL_set_target: - case XEN_DOMCTL_vm_event_op: -- case XEN_DOMCTL_get_domain_state: - - /* These have individual XSM hooks (arch/../domctl.c) */ - case XEN_DOMCTL_bind_pt_irq: diff --git a/xsa492-4.21-05.patch b/xsa492-4.21-05.patch deleted file mode 100644 index cb7beaa..0000000 --- a/xsa492-4.21-05.patch +++ /dev/null @@ -1,156 +0,0 @@ -From: Jan Beulich -Subject: domain: locking for iomem_caps accesses - -In order to be able to pull at least the XEN_DOMCTL_iomem_mapping handling -out of the domctl-locked region, a separate (per-domain) lock is needed to -synchronize in particular with XEN_DOMCTL_iomem_permission. - -Locking is added only as far as domctl-s are affected. Uses presently -outside of the domctl lock may want dealing with subsequently (perhaps -limited to non-__init code). - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -518,10 +518,15 @@ static int late_hwdom_init(struct domain - * may be modified after this hypercall returns if a more complex - * device model is desired. - */ -+ write_lock(&dom0->caps_lock); - rangeset_swap(d->irq_caps, dom0->irq_caps); - rangeset_swap(d->iomem_caps, dom0->iomem_caps); - #ifdef CONFIG_X86 - rangeset_swap(d->arch.ioport_caps, dom0->arch.ioport_caps); -+#endif -+ write_unlock(&dom0->caps_lock); -+ -+#ifdef CONFIG_X86 - setup_io_bitmap(d); - setup_io_bitmap(dom0); - #endif -@@ -873,6 +878,7 @@ struct domain *domain_create(domid_t dom - rspin_lock_init_prof(d, domain_lock); - rspin_lock_init_prof(d, page_alloc_lock); - spin_lock_init(&d->hypercall_deadlock_mutex); -+ rwlock_init(&d->caps_lock); - INIT_PAGE_LIST_HEAD(&d->page_list); - INIT_PAGE_LIST_HEAD(&d->extra_page_list); - INIT_PAGE_LIST_HEAD(&d->xenpage_list); ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -267,6 +267,35 @@ static struct vnuma_info *vnuma_init(con - return ERR_PTR(ret); - } - -+void iocaps_double_lock(struct domain *d, bool write) -+{ -+ struct domain *currd = current->domain; -+ -+ if ( d->domain_id > currd->domain_id ) -+ read_lock(&currd->caps_lock); -+ -+ if ( write ) -+ write_lock(&d->caps_lock); -+ else -+ read_lock(&d->caps_lock); -+ -+ if ( d->domain_id < currd->domain_id ) -+ read_lock(&currd->caps_lock); -+} -+ -+void iocaps_double_unlock(struct domain *d, bool write) -+{ -+ struct domain *currd = current->domain; -+ -+ if ( d != currd ) -+ read_unlock(&currd->caps_lock); -+ -+ if ( write ) -+ write_unlock(&d->caps_lock); -+ else -+ read_unlock(&d->caps_lock); -+} -+ - static bool is_stable_domctl(uint32_t cmd) - { - return cmd == XEN_DOMCTL_get_domain_state; -@@ -687,6 +716,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - if ( (mfn + nr_mfns - 1) < mfn ) /* wrap? */ - break; - -+ iocaps_double_lock(d, true); -+ - if ( !iomem_access_permitted(current->domain, - mfn, mfn + nr_mfns - 1) || - xsm_iomem_permission(XSM_HOOK, d, mfn, mfn + nr_mfns - 1, allow) ) -@@ -695,6 +726,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - ret = iomem_permit_access(d, mfn, mfn + nr_mfns - 1); - else - ret = iomem_deny_access(d, mfn, mfn + nr_mfns - 1); -+ -+ iocaps_double_unlock(d, true); - break; - } - -@@ -719,19 +752,15 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - break; - #endif - -+ iocaps_double_lock(d, false); -+ - ret = -EPERM; - if ( !iomem_access_permitted(current->domain, mfn, mfn_end) || -- !iomem_access_permitted(d, mfn, mfn_end) ) -- break; -- -- ret = xsm_iomem_mapping(XSM_HOOK, d, mfn, mfn_end, add); -- if ( ret ) -- break; -- -- if ( !paging_mode_translate(d) ) -- break; -- -- if ( add ) -+ !iomem_access_permitted(d, mfn, mfn_end) || -+ (ret = xsm_iomem_mapping(XSM_HOOK, d, mfn, mfn_end, add)) || -+ !paging_mode_translate(d) ) -+ /* Nothing. */; -+ else if ( add ) - { - printk(XENLOG_G_DEBUG - "memory_map:add: dom%d gfn=%lx mfn=%lx nr=%lx\n", -@@ -755,6 +784,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - "memory_map: error %ld removing dom%d access to [%lx,%lx]\n", - ret, d->domain_id, mfn, mfn_end); - } -+ -+ iocaps_double_unlock(d, false); - break; - } - ---- a/xen/include/xen/iocap.h -+++ b/xen/include/xen/iocap.h -@@ -12,6 +12,9 @@ - #include - #include - -+void iocaps_double_lock(struct domain *d, bool write); -+void iocaps_double_unlock(struct domain *d, bool write); -+ - static inline int iomem_permit_access(struct domain *d, unsigned long s, - unsigned long e) - { ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -536,6 +536,7 @@ struct domain - #endif - - /* I/O capabilities (access to IRQs and memory-mapped I/O). */ -+ rwlock_t caps_lock; - struct rangeset *iomem_caps; - struct rangeset *irq_caps; - diff --git a/xsa492-4.21-06.patch b/xsa492-4.21-06.patch deleted file mode 100644 index c9e0061..0000000 --- a/xsa492-4.21-06.patch +++ /dev/null @@ -1,84 +0,0 @@ -From: Jan Beulich -Subject: x86/domain: locking for ioport_caps accesses - -In order to be able to pull at least the XEN_DOMCTL_ioport_mapping -handling out of the domctl-locked region, the new separate (per-domain) -lock is used to synchronize in particular with -XEN_DOMCTL_ioport_permission. - -Locking is added only as far as domctl-s are affected. Uses presently -outside of the domctl lock may want dealing with subsequently (perhaps -limited to non-__init code). - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/domctl.c -+++ b/xen/arch/x86/domctl.c -@@ -233,6 +233,8 @@ long arch_do_domctl( - unsigned int np = domctl->u.ioport_permission.nr_ports; - int allow = domctl->u.ioport_permission.allow_access; - -+ iocaps_double_lock(d, true); -+ - if ( (fp + np) <= fp || (fp + np) > MAX_IOPORTS ) - ret = -EINVAL; - else if ( !ioports_access_permitted(currd, fp, fp + np - 1) || -@@ -242,6 +244,8 @@ long arch_do_domctl( - ret = ioports_permit_access(d, fp, fp + np - 1); - else - ret = ioports_deny_access(d, fp, fp + np - 1); -+ -+ iocaps_double_unlock(d, true); - break; - } - -@@ -648,16 +652,13 @@ long arch_do_domctl( - break; - } - -- ret = -EPERM; -- if ( !ioports_access_permitted(currd, fmp, fmp + np - 1) ) -- break; -- -- ret = xsm_ioport_mapping(XSM_HOOK, d, fmp, fmp + np - 1, add); -- if ( ret ) -- break; -- - hvm = &d->arch.hvm; -- if ( add ) -+ iocaps_double_lock(d, true); -+ -+ if ( !ioports_access_permitted(currd, fmp, fmp + np - 1) || -+ (ret = xsm_ioport_mapping(XSM_HOOK, d, fmp, fmp + np - 1, add)) ) -+ ret = ret ?: -EPERM; -+ else if ( add ) - { - printk(XENLOG_G_INFO - "ioport_map:add: dom%d gport=%x mport=%x nr=%x\n", -@@ -718,6 +720,8 @@ long arch_do_domctl( - "ioport_map: error %ld denying dom%d access to [%x,%x]\n", - ret, d->domain_id, fmp, fmp + np - 1); - } -+ -+ iocaps_double_unlock(d, true); - break; - } - ---- a/xen/arch/x86/setup.c -+++ b/xen/arch/x86/setup.c -@@ -2339,9 +2339,12 @@ void __hwdom_init setup_io_bitmap(struct - return; - - bitmap_fill(d->arch.hvm.io_bitmap, 0x10000); -+ -+ read_lock(&d->caps_lock); - if ( rangeset_report_ranges(d->arch.ioport_caps, 0, 0x10000, - io_bitmap_cb, d) ) - BUG(); -+ read_unlock(&d->caps_lock); - - /* - * We need to trap 4-byte accesses to 0xcf8 (see admin_io_okay(), diff --git a/xsa492-4.21-07.patch b/xsa492-4.21-07.patch deleted file mode 100644 index e343773..0000000 --- a/xsa492-4.21-07.patch +++ /dev/null @@ -1,202 +0,0 @@ -From: Jan Beulich -Subject: domain: locking for irq_caps accesses - -In order to be able to pull at least the XEN_DOMCTL_{,un}bind_pt_irq -handling out of the domctl-locked region, a separate (per-domain) lock is -needed to synchronize in particular with XEN_DOMCTL_{irq,gsi}_permission. - -Locking is added only as far as domctl-s are affected. Uses presently -outside of the domctl lock may want dealing with subsequently (perhaps -limited to non-__init code). - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Reviewed-by: Julien Grall - ---- a/xen/arch/arm/domctl.c -+++ b/xen/arch/arm/domctl.c -@@ -76,6 +76,7 @@ long arch_do_domctl(struct xen_domctl *d - case XEN_DOMCTL_bind_pt_irq: - { - int rc; -+ struct domain *currd = current->domain; - struct xen_domctl_bind_pt_irq *bind = &domctl->u.bind_pt_irq; - uint32_t irq = bind->u.spi.spi; - uint32_t virq = bind->machine_irq; -@@ -107,21 +108,26 @@ long arch_do_domctl(struct xen_domctl *d - if ( rc ) - return rc; - -- if ( !irq_access_permitted(current->domain, irq) ) -- return -EPERM; -+ read_lock(&currd->caps_lock); - -- if ( !vgic_reserve_virq(d, virq) ) -- return -EBUSY; -- -- rc = route_irq_to_guest(d, virq, irq, "routed IRQ"); -- if ( rc ) -- vgic_free_virq(d, virq); -+ if ( !irq_access_permitted(currd, irq) ) -+ rc = -EPERM; -+ else if ( !vgic_reserve_virq(d, virq) ) -+ rc = -EBUSY; -+ else -+ { -+ rc = route_irq_to_guest(d, virq, irq, "routed IRQ"); -+ if ( rc ) -+ vgic_free_virq(d, virq); -+ } - -+ read_unlock(&currd->caps_lock); - return rc; - } - case XEN_DOMCTL_unbind_pt_irq: - { - int rc; -+ struct domain *currd = current->domain; - struct xen_domctl_bind_pt_irq *bind = &domctl->u.bind_pt_irq; - uint32_t irq = bind->u.spi.spi; - uint32_t virq = bind->machine_irq; -@@ -138,16 +144,15 @@ long arch_do_domctl(struct xen_domctl *d - if ( rc ) - return rc; - -- if ( !irq_access_permitted(current->domain, irq) ) -- return -EPERM; -- -- rc = release_guest_irq(d, virq); -- if ( rc ) -- return rc; -+ read_lock(&currd->caps_lock); - -- vgic_free_virq(d, virq); -+ if ( !irq_access_permitted(currd, irq) ) -+ rc = -EPERM; -+ else if ( !(rc = release_guest_irq(d, virq)) ) -+ vgic_free_virq(d, virq); - -- return 0; -+ read_unlock(&currd->caps_lock); -+ return rc; - } - - case XEN_DOMCTL_vuart_op: ---- a/xen/arch/x86/domctl.c -+++ b/xen/arch/x86/domctl.c -@@ -267,16 +267,17 @@ long arch_do_domctl( - break; - } - -- ret = -EPERM; -+ iocaps_double_lock(d, true); -+ - if ( !irq_access_permitted(currd, irq) || - xsm_irq_permission(XSM_HOOK, d, irq, flags) ) -- break; -- -- if ( flags ) -+ ret = -EPERM; -+ else if ( flags ) - ret = irq_permit_access(d, irq); - else - ret = irq_deny_access(d, irq); - -+ iocaps_double_unlock(d, true); - break; - } - -@@ -579,20 +580,27 @@ long arch_do_domctl( - break; - - irq = domain_pirq_to_irq(d, bind->machine_irq); -- ret = -EPERM; -- if ( irq <= 0 || !irq_access_permitted(currd, irq) ) -- break; -+ if ( irq <= 0 ) -+ ret = -EPERM; - -- ret = -ESRCH; -- if ( is_iommu_enabled(d) ) -+ read_lock(&currd->caps_lock); -+ -+ if ( !irq_access_permitted(currd, irq) ) -+ ret = -EPERM; -+ else if ( is_iommu_enabled(d) ) - { - pcidevs_lock(); - ret = pt_irq_create_bind(d, bind); - pcidevs_unlock(); -+ -+ if ( ret < 0 ) -+ printk(XENLOG_G_ERR "pt_irq_create_bind failed (%ld) for %pd\n", -+ ret, d); - } -- if ( ret < 0 ) -- printk(XENLOG_G_ERR "pt_irq_create_bind failed (%ld) for dom%d\n", -- ret, d->domain_id); -+ else -+ ret = -ESRCH; -+ -+ read_unlock(&currd->caps_lock); - break; - } - -@@ -605,23 +613,26 @@ long arch_do_domctl( - if ( !is_hvm_domain(d) ) - break; - -- ret = -EPERM; -- if ( irq <= 0 || !irq_access_permitted(currd, irq) ) -- break; -- - ret = xsm_unbind_pt_irq(XSM_HOOK, d, bind); - if ( ret ) - break; - -- if ( is_iommu_enabled(d) ) -+ read_lock(&currd->caps_lock); -+ -+ if ( !irq_access_permitted(currd, irq) ) -+ ret = -EPERM; -+ else if ( is_iommu_enabled(d) ) - { - pcidevs_lock(); - ret = pt_irq_destroy_bind(d, bind); - pcidevs_unlock(); -+ -+ if ( ret < 0 ) -+ printk(XENLOG_G_ERR "pt_irq_destroy_bind failed (%ld) for %pd\n", -+ ret, d); - } -- if ( ret < 0 ) -- printk(XENLOG_G_ERR "pt_irq_destroy_bind failed (%ld) for dom%d\n", -- ret, d->domain_id); -+ -+ read_unlock(&currd->caps_lock); - break; - } - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -695,6 +695,9 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - ret = -EINVAL; - break; - } -+ -+ iocaps_double_lock(d, true); -+ - irq = pirq_access_permitted(current->domain, pirq); - if ( !irq || xsm_irq_permission(XSM_HOOK, d, irq, allow) ) - ret = -EPERM; -@@ -702,6 +705,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - ret = irq_permit_access(d, irq); - else - ret = irq_deny_access(d, irq); -+ -+ iocaps_double_unlock(d, true); - break; - } - #endif diff --git a/xsa492-4.21-08.patch b/xsa492-4.21-08.patch deleted file mode 100644 index 4aefbf4..0000000 --- a/xsa492-4.21-08.patch +++ /dev/null @@ -1,85 +0,0 @@ -From: Jan Beulich -Subject: XSM/Flask: split the .iomem_mapping() hook - -It's used twice in entirely different situations. The use in do_domctl() -wants to become an ordinary XSM_DM_PRIV invocation, while the one in vPCI -code need to remain XSM_HOOK (it may plausibly become XSM_TARGET). For -Flask, the same backing function will continue to be used for the time -being. - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith - ---- a/xen/drivers/vpci/header.c -+++ b/xen/drivers/vpci/header.c -@@ -67,7 +67,7 @@ static int cf_check map_range( - return -EPERM; - } - -- rc = xsm_iomem_mapping(XSM_HOOK, map->d, map_mfn, m_end, map->map); -+ rc = xsm_iomem_mapping_vpci(XSM_HOOK, map->d, map_mfn, m_end, map->map); - if ( rc ) - { - printk(XENLOG_G_WARNING ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -580,6 +580,13 @@ static XSM_INLINE int cf_check xsm_iomem - return xsm_default_action(action, current->domain, d); - } - -+static XSM_INLINE int cf_check xsm_iomem_mapping_vpci( -+ XSM_DEFAULT_ARG struct domain *d, uint64_t s, uint64_t e, uint8_t allow) -+{ -+ XSM_ASSERT_ACTION(XSM_HOOK); -+ return xsm_default_action(action, current->domain, d); -+} -+ - static XSM_INLINE int cf_check xsm_pci_config_permission( - XSM_DEFAULT_ARG struct domain *d, uint32_t machine_bdf, uint16_t start, - uint16_t end, uint8_t access) ---- a/xen/include/xsm/xsm.h -+++ b/xen/include/xsm/xsm.h -@@ -118,6 +118,8 @@ struct xsm_ops { - uint8_t allow); - int (*iomem_mapping)(struct domain *d, uint64_t s, uint64_t e, - uint8_t allow); -+ int (*iomem_mapping_vpci)(struct domain *d, uint64_t s, uint64_t e, -+ uint8_t allow); - int (*pci_config_permission)(struct domain *d, uint32_t machine_bdf, - uint16_t start, uint16_t end, uint8_t access); - -@@ -523,6 +525,12 @@ static inline int xsm_iomem_mapping( - return alternative_call(xsm_ops.iomem_mapping, d, s, e, allow); - } - -+static inline int xsm_iomem_mapping_vpci( -+ xsm_default_t def, struct domain *d, uint64_t s, uint64_t e, uint8_t allow) -+{ -+ return alternative_call(xsm_ops.iomem_mapping_vpci, d, s, e, allow); -+} -+ - static inline int xsm_pci_config_permission( - xsm_default_t def, struct domain *d, uint32_t machine_bdf, uint16_t start, - uint16_t end, uint8_t access) ---- a/xen/xsm/dummy.c -+++ b/xen/xsm/dummy.c -@@ -76,6 +76,7 @@ static const struct xsm_ops __initconst_ - .irq_permission = xsm_irq_permission, - .iomem_permission = xsm_iomem_permission, - .iomem_mapping = xsm_iomem_mapping, -+ .iomem_mapping_vpci = xsm_iomem_mapping_vpci, - .pci_config_permission = xsm_pci_config_permission, - .get_vnumainfo = xsm_get_vnumainfo, - ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -1950,6 +1950,7 @@ static const struct xsm_ops __initconst_ - .irq_permission = flask_irq_permission, - .iomem_permission = flask_iomem_permission, - .iomem_mapping = flask_iomem_mapping, -+ .iomem_mapping_vpci = flask_iomem_mapping, - .pci_config_permission = flask_pci_config_permission, - - .resource_plug_core = flask_resource_plug_core, diff --git a/xsa492-4.21-09.patch b/xsa492-4.21-09.patch deleted file mode 100644 index 96e9403..0000000 --- a/xsa492-4.21-09.patch +++ /dev/null @@ -1,194 +0,0 @@ -From: Jan Beulich -Subject: domctl: handle XEN_DOMCTL_memory_mapping without acquiring domctl lock - -With dedicated locking added, the domctl lock isn't required here anymore. -Move the re-purposed dedicated XSM check as early as possible. - -Minimal "modernization": Switch "add" to bool and use %pd in log messages. - -This is part of XSA-492. - -Fixes: fda49f9b3fbb ("Add build option to allow more hypercalls from stubdoms") -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith -Reviewed-by: Roger Pau Monné - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -376,6 +376,66 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - copyback = true; - goto domctl_out_unlock_domonly; - -+ case XEN_DOMCTL_memory_mapping: -+ { -+ unsigned long gfn = op->u.memory_mapping.first_gfn; -+ unsigned long mfn = op->u.memory_mapping.first_mfn; -+ unsigned long nr_mfns = op->u.memory_mapping.nr_mfns; -+ unsigned long mfn_end = mfn + nr_mfns - 1; -+ bool add = op->u.memory_mapping.add_mapping; -+ -+ ret = -EINVAL; -+ if ( mfn_end < mfn || /* Wrap? */ -+ ((mfn | mfn_end) >> (paddr_bits - PAGE_SHIFT)) || -+ (gfn + nr_mfns - 1) < gfn ) /* Wrap? */ -+ goto domctl_out_unlock_domonly; -+ -+ ret = xsm_iomem_mapping(XSM_DM_PRIV, d, mfn, mfn_end, add); -+ if ( ret || !paging_mode_translate(d) ) -+ goto domctl_out_unlock_domonly; -+ -+#ifndef CONFIG_X86 /* XXX ARM!? */ -+ ret = -E2BIG; -+ /* Must break hypercall up as this could take a while. */ -+ if ( nr_mfns > 64 ) -+ goto domctl_out_unlock_domonly; -+#endif -+ -+ iocaps_double_lock(d, false); -+ -+ ret = -EPERM; -+ if ( !iomem_access_permitted(current->domain, mfn, mfn_end) || -+ !iomem_access_permitted(d, mfn, mfn_end) ) -+ /* Nothing. */; -+ else if ( add ) -+ { -+ printk(XENLOG_G_DEBUG -+ "memory_map:add: %pd gfn=%lx mfn=%lx nr=%lx\n", -+ d, gfn, mfn, nr_mfns); -+ -+ ret = map_mmio_regions(d, _gfn(gfn), nr_mfns, _mfn(mfn)); -+ if ( ret < 0 ) -+ printk(XENLOG_G_WARNING -+ "memory_map:fail: %pd gfn=%lx mfn=%lx nr=%lx ret:%ld\n", -+ d, gfn, mfn, nr_mfns, ret); -+ } -+ else -+ { -+ printk(XENLOG_G_DEBUG -+ "memory_map:remove: %pd gfn=%lx mfn=%lx nr=%lx\n", -+ d, gfn, mfn, nr_mfns); -+ -+ ret = unmap_mmio_regions(d, _gfn(gfn), nr_mfns, _mfn(mfn)); -+ if ( ret < 0 && is_hardware_domain(current->domain) ) -+ printk(XENLOG_ERR -+ "memory_map: error %ld removing %pd access to [%lx,%lx]\n", -+ ret, d, mfn, mfn_end); -+ } -+ -+ iocaps_double_unlock(d, false); -+ goto domctl_out_unlock_domonly; -+ } -+ - default: - /* Everything else handled further down. */ - break; -@@ -736,64 +796,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - break; - } - -- case XEN_DOMCTL_memory_mapping: -- { -- unsigned long gfn = op->u.memory_mapping.first_gfn; -- unsigned long mfn = op->u.memory_mapping.first_mfn; -- unsigned long nr_mfns = op->u.memory_mapping.nr_mfns; -- unsigned long mfn_end = mfn + nr_mfns - 1; -- int add = op->u.memory_mapping.add_mapping; -- -- ret = -EINVAL; -- if ( mfn_end < mfn || /* wrap? */ -- ((mfn | mfn_end) >> (paddr_bits - PAGE_SHIFT)) || -- (gfn + nr_mfns - 1) < gfn ) /* wrap? */ -- break; -- --#ifndef CONFIG_X86 /* XXX ARM!? */ -- ret = -E2BIG; -- /* Must break hypercall up as this could take a while. */ -- if ( nr_mfns > 64 ) -- break; --#endif -- -- iocaps_double_lock(d, false); -- -- ret = -EPERM; -- if ( !iomem_access_permitted(current->domain, mfn, mfn_end) || -- !iomem_access_permitted(d, mfn, mfn_end) || -- (ret = xsm_iomem_mapping(XSM_HOOK, d, mfn, mfn_end, add)) || -- !paging_mode_translate(d) ) -- /* Nothing. */; -- else if ( add ) -- { -- printk(XENLOG_G_DEBUG -- "memory_map:add: dom%d gfn=%lx mfn=%lx nr=%lx\n", -- d->domain_id, gfn, mfn, nr_mfns); -- -- ret = map_mmio_regions(d, _gfn(gfn), nr_mfns, _mfn(mfn)); -- if ( ret < 0 ) -- printk(XENLOG_G_WARNING -- "memory_map:fail: dom%d gfn=%lx mfn=%lx nr=%lx ret:%ld\n", -- d->domain_id, gfn, mfn, nr_mfns, ret); -- } -- else -- { -- printk(XENLOG_G_DEBUG -- "memory_map:remove: dom%d gfn=%lx mfn=%lx nr=%lx\n", -- d->domain_id, gfn, mfn, nr_mfns); -- -- ret = unmap_mmio_regions(d, _gfn(gfn), nr_mfns, _mfn(mfn)); -- if ( ret < 0 && is_hardware_domain(current->domain) ) -- printk(XENLOG_ERR -- "memory_map: error %ld removing dom%d access to [%lx,%lx]\n", -- ret, d->domain_id, mfn, mfn_end); -- } -- -- iocaps_double_unlock(d, false); -- break; -- } -- - case XEN_DOMCTL_settimeoffset: - domain_set_time_offset(d, op->u.settimeoffset.time_offset_seconds); - break; ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -168,13 +168,13 @@ static XSM_INLINE int cf_check xsm_domct - switch ( cmd ) - { - case XEN_DOMCTL_ioport_mapping: -- case XEN_DOMCTL_memory_mapping: - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_unbind_pt_irq: - return xsm_default_action(XSM_DM_PRIV, current->domain, d); - - case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: -+ case XEN_DOMCTL_memory_mapping: - ASSERT_UNREACHABLE(); - return -EILSEQ; - -@@ -576,7 +576,7 @@ static XSM_INLINE int cf_check xsm_iomem - static XSM_INLINE int cf_check xsm_iomem_mapping( - XSM_DEFAULT_ARG struct domain *d, uint64_t s, uint64_t e, uint8_t allow) - { -- XSM_ASSERT_ACTION(XSM_HOOK); -+ XSM_ASSERT_ACTION(XSM_DM_PRIV); - return xsm_default_action(action, current->domain, d); - } - ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -685,6 +685,7 @@ static int cf_check flask_domctl(struct - /* These have individual XSM hooks and don't make it here. */ - case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: -+ case XEN_DOMCTL_memory_mapping: - ASSERT_UNREACHABLE(); - return -EILSEQ; - -@@ -692,7 +693,6 @@ static int cf_check flask_domctl(struct - case XEN_DOMCTL_scheduler_op: - case XEN_DOMCTL_irq_permission: - case XEN_DOMCTL_iomem_permission: -- case XEN_DOMCTL_memory_mapping: - case XEN_DOMCTL_set_target: - case XEN_DOMCTL_vm_event_op: - diff --git a/xsa492-4.21-10.patch b/xsa492-4.21-10.patch deleted file mode 100644 index 6406a19..0000000 --- a/xsa492-4.21-10.patch +++ /dev/null @@ -1,97 +0,0 @@ -From: Jan Beulich -Subject: domctl: handle XEN_DOMCTL_ioport_mapping without acquiring domctl lock - -With dedicated locking added, the domctl lock isn't required here anymore. -As the handling is in arch-specific code (x86 only), almost no code is -being moved, but a 2nd (extensible to other sub-ops) invocation of -arch_do_domctl() is being added. Move just the re-purposed dedicated XSM -check as early as possible. - -In flask_domctl() don't put #ifdef around the moved case label. - -This is part of XSA-492. - -Fixes: fda49f9b3fbb ("Add build option to allow more hypercalls from stubdoms") -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Acked-by: Daniel P. Smith - ---- a/xen/arch/x86/domctl.c -+++ b/xen/arch/x86/domctl.c -@@ -663,12 +663,15 @@ long arch_do_domctl( - break; - } - -+ ret = xsm_ioport_mapping(XSM_DM_PRIV, d, fmp, fmp + np - 1, add); -+ if ( ret ) -+ break; -+ - hvm = &d->arch.hvm; - iocaps_double_lock(d, true); - -- if ( !ioports_access_permitted(currd, fmp, fmp + np - 1) || -- (ret = xsm_ioport_mapping(XSM_HOOK, d, fmp, fmp + np - 1, add)) ) -- ret = ret ?: -EPERM; -+ if ( !ioports_access_permitted(currd, fmp, fmp + np - 1) ) -+ ret = -EPERM; - else if ( add ) - { - printk(XENLOG_G_INFO ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -436,6 +436,10 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - goto domctl_out_unlock_domonly; - } - -+ case XEN_DOMCTL_ioport_mapping: -+ ret = arch_do_domctl(op, d, u_domctl); -+ goto domctl_out_unlock_domonly; -+ - default: - /* Everything else handled further down. */ - break; ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -167,13 +167,13 @@ static XSM_INLINE int cf_check xsm_domct - XSM_ASSERT_ACTION(XSM_OTHER); - switch ( cmd ) - { -- case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_unbind_pt_irq: - return xsm_default_action(XSM_DM_PRIV, current->domain, d); - - case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: -+ case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_memory_mapping: - ASSERT_UNREACHABLE(); - return -EILSEQ; -@@ -772,7 +772,7 @@ static XSM_INLINE int cf_check xsm_iopor - static XSM_INLINE int cf_check xsm_ioport_mapping( - XSM_DEFAULT_ARG struct domain *d, uint32_t s, uint32_t e, uint8_t allow) - { -- XSM_ASSERT_ACTION(XSM_HOOK); -+ XSM_ASSERT_ACTION(XSM_DM_PRIV); - return xsm_default_action(action, current->domain, d); - } - ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -685,6 +685,7 @@ static int cf_check flask_domctl(struct - /* These have individual XSM hooks and don't make it here. */ - case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: -+ case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_memory_mapping: - ASSERT_UNREACHABLE(); - return -EILSEQ; -@@ -703,7 +704,6 @@ static int cf_check flask_domctl(struct - /* These have individual XSM hooks (arch/x86/domctl.c) */ - case XEN_DOMCTL_shadow_op: - case XEN_DOMCTL_ioport_permission: -- case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_gsi_permission: - #endif - #ifdef CONFIG_HAS_PASSTHROUGH diff --git a/xsa492-4.21-11.patch b/xsa492-4.21-11.patch deleted file mode 100644 index 647fd5a..0000000 --- a/xsa492-4.21-11.patch +++ /dev/null @@ -1,128 +0,0 @@ -From: Jan Beulich -Subject: domctl: handle XEN_DOMCTL_{,un}bind_pt_irq without acquiring domctl lock - -With dedicated locking added, the domctl lock isn't required here anymore. -(It also already isn't used when pt_irq_{create,destroy}_bind() are -invoked for PVH Dom0.) As the handling is in arch-specific code, no code -is being moved, but the 2nd (extensible to other sub-ops like the ones -here) invocation of arch_do_domctl() is being re-used. - -This is part of XSA-492. - -Fixes: fda49f9b3fbb ("Add build option to allow more hypercalls from stubdoms") -Reported-by: Andrew Cooper -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Acked-by: Daniel P. Smith -Acked-by: Julien Grall - ---- a/xen/arch/arm/domctl.c -+++ b/xen/arch/arm/domctl.c -@@ -104,7 +104,7 @@ long arch_do_domctl(struct xen_domctl *d - if ( rc ) - return rc; - -- rc = xsm_bind_pt_irq(XSM_HOOK, d, bind); -+ rc = xsm_bind_pt_irq(XSM_DM_PRIV, d, bind); - if ( rc ) - return rc; - -@@ -140,7 +140,7 @@ long arch_do_domctl(struct xen_domctl *d - if ( irq != virq ) - return -EINVAL; - -- rc = xsm_unbind_pt_irq(XSM_HOOK, d, bind); -+ rc = xsm_unbind_pt_irq(XSM_DM_PRIV, d, bind); - if ( rc ) - return rc; - ---- a/xen/arch/x86/domctl.c -+++ b/xen/arch/x86/domctl.c -@@ -575,7 +575,7 @@ long arch_do_domctl( - if ( !is_hvm_domain(d) ) - break; - -- ret = xsm_bind_pt_irq(XSM_HOOK, d, bind); -+ ret = xsm_bind_pt_irq(XSM_DM_PRIV, d, bind); - if ( ret ) - break; - -@@ -613,7 +613,7 @@ long arch_do_domctl( - if ( !is_hvm_domain(d) ) - break; - -- ret = xsm_unbind_pt_irq(XSM_HOOK, d, bind); -+ ret = xsm_unbind_pt_irq(XSM_DM_PRIV, d, bind); - if ( ret ) - break; - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -437,6 +437,8 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - } - - case XEN_DOMCTL_ioport_mapping: -+ case XEN_DOMCTL_bind_pt_irq: -+ case XEN_DOMCTL_unbind_pt_irq: - ret = arch_do_domctl(op, d, u_domctl); - goto domctl_out_unlock_domonly; - ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -168,13 +168,11 @@ static XSM_INLINE int cf_check xsm_domct - switch ( cmd ) - { - case XEN_DOMCTL_bind_pt_irq: -- case XEN_DOMCTL_unbind_pt_irq: -- return xsm_default_action(XSM_DM_PRIV, current->domain, d); -- - case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: - case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_memory_mapping: -+ case XEN_DOMCTL_unbind_pt_irq: - ASSERT_UNREACHABLE(); - return -EILSEQ; - -@@ -541,14 +539,14 @@ static XSM_INLINE int cf_check xsm_unmap - static XSM_INLINE int cf_check xsm_bind_pt_irq( - XSM_DEFAULT_ARG struct domain *d, struct xen_domctl_bind_pt_irq *bind) - { -- XSM_ASSERT_ACTION(XSM_HOOK); -+ XSM_ASSERT_ACTION(XSM_DM_PRIV); - return xsm_default_action(action, current->domain, d); - } - - static XSM_INLINE int cf_check xsm_unbind_pt_irq( - XSM_DEFAULT_ARG struct domain *d, struct xen_domctl_bind_pt_irq *bind) - { -- XSM_ASSERT_ACTION(XSM_HOOK); -+ XSM_ASSERT_ACTION(XSM_DM_PRIV); - return xsm_default_action(action, current->domain, d); - } - ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -683,10 +683,12 @@ static int cf_check flask_domctl(struct - return avc_current_has_perm(ssidref, SECCLASS_DOMAIN, DOMAIN__CREATE, NULL); - - /* These have individual XSM hooks and don't make it here. */ -+ case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: - case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_memory_mapping: -+ case XEN_DOMCTL_unbind_pt_irq: - ASSERT_UNREACHABLE(); - return -EILSEQ; - -@@ -697,9 +699,6 @@ static int cf_check flask_domctl(struct - case XEN_DOMCTL_set_target: - case XEN_DOMCTL_vm_event_op: - -- /* These have individual XSM hooks (arch/../domctl.c) */ -- case XEN_DOMCTL_bind_pt_irq: -- case XEN_DOMCTL_unbind_pt_irq: - #ifdef CONFIG_X86 - /* These have individual XSM hooks (arch/x86/domctl.c) */ - case XEN_DOMCTL_shadow_op: diff --git a/xsa492-4.21-12.patch b/xsa492-4.21-12.patch deleted file mode 100644 index c19d1e1..0000000 --- a/xsa492-4.21-12.patch +++ /dev/null @@ -1,172 +0,0 @@ -From: Jan Beulich -Subject: domctl: handle XEN_DOMCTL_io{mem,port}_permission without acquiring domctl lock - -With dedicated locking added, the domctl lock isn't required here anymore. -As the I/O port handling is in arch-specific code (x86 only), no code is -being moved, but the 2nd invocation of arch_do_domctl() is re-used. Move -the re-purposed dedicated XSM checks as early as possible. - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné -Acked-by: Daniel P. Smith - ---- a/xen/arch/x86/domctl.c -+++ b/xen/arch/x86/domctl.c -@@ -233,12 +233,17 @@ long arch_do_domctl( - unsigned int np = domctl->u.ioport_permission.nr_ports; - int allow = domctl->u.ioport_permission.allow_access; - -+ ret = -EINVAL; -+ if ( (fp + np) <= fp || (fp + np) > MAX_IOPORTS ) -+ break; -+ -+ ret = xsm_ioport_permission(XSM_PRIV, d, fp, fp + np - 1, allow); -+ if ( ret ) -+ break; -+ - iocaps_double_lock(d, true); - -- if ( (fp + np) <= fp || (fp + np) > MAX_IOPORTS ) -- ret = -EINVAL; -- else if ( !ioports_access_permitted(currd, fp, fp + np - 1) || -- xsm_ioport_permission(XSM_HOOK, d, fp, fp + np - 1, allow) ) -+ if ( !ioports_access_permitted(currd, fp, fp + np - 1) ) - ret = -EPERM; - else if ( allow ) - ret = ioports_permit_access(d, fp, fp + np - 1); ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -376,6 +376,34 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - copyback = true; - goto domctl_out_unlock_domonly; - -+ case XEN_DOMCTL_iomem_permission: -+ { -+ unsigned long mfn = op->u.iomem_permission.first_mfn; -+ unsigned long nr_mfns = op->u.iomem_permission.nr_mfns; -+ bool allow = op->u.iomem_permission.allow_access; -+ -+ ret = -EINVAL; -+ if ( (mfn + nr_mfns - 1) < mfn ) /* Wrap? */ -+ goto domctl_out_unlock_domonly; -+ -+ ret = xsm_iomem_permission(XSM_PRIV, d, mfn, mfn + nr_mfns - 1, allow); -+ if ( ret ) -+ goto domctl_out_unlock_domonly; -+ -+ iocaps_double_lock(d, true); -+ -+ if ( !iomem_access_permitted(current->domain, -+ mfn, mfn + nr_mfns - 1) ) -+ ret = -EPERM; -+ else if ( allow ) -+ ret = iomem_permit_access(d, mfn, mfn + nr_mfns - 1); -+ else -+ ret = iomem_deny_access(d, mfn, mfn + nr_mfns - 1); -+ -+ iocaps_double_unlock(d, true); -+ goto domctl_out_unlock_domonly; -+ } -+ - case XEN_DOMCTL_memory_mapping: - { - unsigned long gfn = op->u.memory_mapping.first_gfn; -@@ -436,6 +464,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - goto domctl_out_unlock_domonly; - } - -+ case XEN_DOMCTL_ioport_permission: - case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_unbind_pt_irq: -@@ -777,31 +806,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - } - #endif - -- case XEN_DOMCTL_iomem_permission: -- { -- unsigned long mfn = op->u.iomem_permission.first_mfn; -- unsigned long nr_mfns = op->u.iomem_permission.nr_mfns; -- int allow = op->u.iomem_permission.allow_access; -- -- ret = -EINVAL; -- if ( (mfn + nr_mfns - 1) < mfn ) /* wrap? */ -- break; -- -- iocaps_double_lock(d, true); -- -- if ( !iomem_access_permitted(current->domain, -- mfn, mfn + nr_mfns - 1) || -- xsm_iomem_permission(XSM_HOOK, d, mfn, mfn + nr_mfns - 1, allow) ) -- ret = -EPERM; -- else if ( allow ) -- ret = iomem_permit_access(d, mfn, mfn + nr_mfns - 1); -- else -- ret = iomem_deny_access(d, mfn, mfn + nr_mfns - 1); -- -- iocaps_double_unlock(d, true); -- break; -- } -- - case XEN_DOMCTL_settimeoffset: - domain_set_time_offset(d, op->u.settimeoffset.time_offset_seconds); - break; ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -170,7 +170,9 @@ static XSM_INLINE int cf_check xsm_domct - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: -+ case XEN_DOMCTL_iomem_permission: - case XEN_DOMCTL_ioport_mapping: -+ case XEN_DOMCTL_ioport_permission: - case XEN_DOMCTL_memory_mapping: - case XEN_DOMCTL_unbind_pt_irq: - ASSERT_UNREACHABLE(); -@@ -567,7 +569,7 @@ static XSM_INLINE int cf_check xsm_irq_p - static XSM_INLINE int cf_check xsm_iomem_permission( - XSM_DEFAULT_ARG struct domain *d, uint64_t s, uint64_t e, uint8_t allow) - { -- XSM_ASSERT_ACTION(XSM_HOOK); -+ XSM_ASSERT_ACTION(XSM_PRIV); - return xsm_default_action(action, current->domain, d); - } - -@@ -763,7 +765,7 @@ static XSM_INLINE int cf_check xsm_priv_ - static XSM_INLINE int cf_check xsm_ioport_permission( - XSM_DEFAULT_ARG struct domain *d, uint32_t s, uint32_t e, uint8_t allow) - { -- XSM_ASSERT_ACTION(XSM_HOOK); -+ XSM_ASSERT_ACTION(XSM_PRIV); - return xsm_default_action(action, current->domain, d); - } - ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -686,7 +686,9 @@ static int cf_check flask_domctl(struct - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: -+ case XEN_DOMCTL_iomem_permission: - case XEN_DOMCTL_ioport_mapping: -+ case XEN_DOMCTL_ioport_permission: - case XEN_DOMCTL_memory_mapping: - case XEN_DOMCTL_unbind_pt_irq: - ASSERT_UNREACHABLE(); -@@ -695,14 +697,12 @@ static int cf_check flask_domctl(struct - /* These have individual XSM hooks (common/domctl.c) */ - case XEN_DOMCTL_scheduler_op: - case XEN_DOMCTL_irq_permission: -- case XEN_DOMCTL_iomem_permission: - case XEN_DOMCTL_set_target: - case XEN_DOMCTL_vm_event_op: - - #ifdef CONFIG_X86 - /* These have individual XSM hooks (arch/x86/domctl.c) */ - case XEN_DOMCTL_shadow_op: -- case XEN_DOMCTL_ioport_permission: - case XEN_DOMCTL_gsi_permission: - #endif - #ifdef CONFIG_HAS_PASSTHROUGH diff --git a/xsa492-4.21-13.patch b/xsa492-4.21-13.patch deleted file mode 100644 index 91ce1ae..0000000 --- a/xsa492-4.21-13.patch +++ /dev/null @@ -1,163 +0,0 @@ -From: Jan Beulich -Subject: domctl: handle XEN_DOMCTL_{irq,gsi}_permission without acquiring domctl lock - -With dedicated locking added, the domctl lock isn't required here anymore. -As the GSI handling is in arch-specific code (x86 only), no code is being -moved there; the 2nd invocation of arch_do_domctl() is re-used. Move the -re-purposed (XSM_HOOK -> XSM_PRIV, as xsm_domctl() is now bypassed) -dedicated XSM checks as early as possible. - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/domctl.c -+++ b/xen/arch/x86/domctl.c -@@ -272,10 +272,13 @@ long arch_do_domctl( - break; - } - -+ ret = xsm_irq_permission(XSM_PRIV, d, irq, flags); -+ if ( ret ) -+ break; -+ - iocaps_double_lock(d, true); - -- if ( !irq_access_permitted(currd, irq) || -- xsm_irq_permission(XSM_HOOK, d, irq, flags) ) -+ if ( !irq_access_permitted(currd, irq) ) - ret = -EPERM; - else if ( flags ) - ret = irq_permit_access(d, irq); ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -464,8 +464,41 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - goto domctl_out_unlock_domonly; - } - -+#ifdef CONFIG_HAS_PIRQ -+ case XEN_DOMCTL_irq_permission: -+ { -+ unsigned int pirq = op->u.irq_permission.pirq, irq; -+ bool allow = op->u.irq_permission.allow_access; -+ -+ ret = -EINVAL; -+ if ( pirq >= current->domain->nr_pirqs ) -+ goto domctl_out_unlock_domonly; -+ -+ irq = domain_pirq_to_irq(current->domain, pirq); -+ -+ ret = -EPERM; -+ if ( irq ) -+ ret = xsm_irq_permission(XSM_PRIV, d, irq, allow); -+ if ( ret ) -+ goto domctl_out_unlock_domonly; -+ -+ iocaps_double_lock(d, true); -+ -+ if ( !irq_access_permitted(current->domain, irq) ) -+ ret = -EPERM; -+ else if ( allow ) -+ ret = irq_permit_access(d, irq); -+ else -+ ret = irq_deny_access(d, irq); -+ -+ iocaps_double_unlock(d, true); -+ goto domctl_out_unlock_domonly; -+ } -+#endif -+ - case XEN_DOMCTL_ioport_permission: - case XEN_DOMCTL_ioport_mapping: -+ case XEN_DOMCTL_gsi_permission: - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_unbind_pt_irq: - ret = arch_do_domctl(op, d, u_domctl); -@@ -779,33 +812,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - } - break; - --#ifdef CONFIG_HAS_PIRQ -- case XEN_DOMCTL_irq_permission: -- { -- unsigned int pirq = op->u.irq_permission.pirq, irq; -- int allow = op->u.irq_permission.allow_access; -- -- if ( pirq >= current->domain->nr_pirqs ) -- { -- ret = -EINVAL; -- break; -- } -- -- iocaps_double_lock(d, true); -- -- irq = pirq_access_permitted(current->domain, pirq); -- if ( !irq || xsm_irq_permission(XSM_HOOK, d, irq, allow) ) -- ret = -EPERM; -- else if ( allow ) -- ret = irq_permit_access(d, irq); -- else -- ret = irq_deny_access(d, irq); -- -- iocaps_double_unlock(d, true); -- break; -- } --#endif -- - case XEN_DOMCTL_settimeoffset: - domain_set_time_offset(d, op->u.settimeoffset.time_offset_seconds); - break; ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -170,9 +170,11 @@ static XSM_INLINE int cf_check xsm_domct - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: -+ case XEN_DOMCTL_gsi_permission: - case XEN_DOMCTL_iomem_permission: - case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_ioport_permission: -+ case XEN_DOMCTL_irq_permission: - case XEN_DOMCTL_memory_mapping: - case XEN_DOMCTL_unbind_pt_irq: - ASSERT_UNREACHABLE(); -@@ -562,7 +564,7 @@ static XSM_INLINE int cf_check xsm_unmap - static XSM_INLINE int cf_check xsm_irq_permission( - XSM_DEFAULT_ARG struct domain *d, int pirq, uint8_t allow) - { -- XSM_ASSERT_ACTION(XSM_HOOK); -+ XSM_ASSERT_ACTION(XSM_PRIV); - return xsm_default_action(action, current->domain, d); - } - ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -686,9 +686,11 @@ static int cf_check flask_domctl(struct - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_getdomaininfo: - case XEN_DOMCTL_get_domain_state: -+ case XEN_DOMCTL_gsi_permission: - case XEN_DOMCTL_iomem_permission: - case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_ioport_permission: -+ case XEN_DOMCTL_irq_permission: - case XEN_DOMCTL_memory_mapping: - case XEN_DOMCTL_unbind_pt_irq: - ASSERT_UNREACHABLE(); -@@ -696,14 +698,12 @@ static int cf_check flask_domctl(struct - - /* These have individual XSM hooks (common/domctl.c) */ - case XEN_DOMCTL_scheduler_op: -- case XEN_DOMCTL_irq_permission: - case XEN_DOMCTL_set_target: - case XEN_DOMCTL_vm_event_op: - - #ifdef CONFIG_X86 - /* These have individual XSM hooks (arch/x86/domctl.c) */ - case XEN_DOMCTL_shadow_op: -- case XEN_DOMCTL_gsi_permission: - #endif - #ifdef CONFIG_HAS_PASSTHROUGH - /* diff --git a/xsa492-4.21-14.patch b/xsa492-4.21-14.patch deleted file mode 100644 index 2b13377..0000000 --- a/xsa492-4.21-14.patch +++ /dev/null @@ -1,179 +0,0 @@ -From: Jan Beulich -Subject: domctl/XSM: drop vm_event_control hook - -Integrate the checking with xsm_domctl(). Care needs to be taken with the -GET_VERSION sub-op, which may be invoked with DOMID_INVALID, and which has -been (and continues to be) bypassing XSM checking. - -Since the latter two parameters were unused, monitor_domctl() invoking the -hook was actually redundant with the earlier xsm_domctl() (as can be seen -nicely from the hunks changing xsm/flask/hooks.c). - -As a positive side effect, permissions are then checked at the same early -point with and without Flask. - -While folding XEN_DOMCTL_monitor_op and XEN_DOMCTL_vm_event_op in -flask_domctl(), also fold in XEN_DOMCTL_set_access_required. - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith -Reviewed-by: Roger Pau Monné - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -496,6 +496,23 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - } - #endif - -+ case XEN_DOMCTL_vm_event_op: -+ if ( op->u.vm_event_op.op == XEN_VM_EVENT_GET_VERSION ) -+ { -+ /* No XSM check (and potentially d == NULL) here. */ -+ ret = vm_event_domctl(d, &op->u.vm_event_op); -+ if ( !ret ) -+ copyback = true; -+ goto domctl_out_unlock_domonly; -+ } -+ if ( !d ) -+ { -+ ret = -ESRCH; -+ goto domctl_out_unlock_domonly; -+ } -+ /* Other sub-ops handled further down. */ -+ break; -+ - case XEN_DOMCTL_ioport_permission: - case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_gsi_permission: ---- a/xen/common/monitor.c -+++ b/xen/common/monitor.c -@@ -30,16 +30,11 @@ - - int monitor_domctl(struct domain *d, struct xen_domctl_monitor_op *mop) - { -- int rc; - bool requested_status = false; - - if ( unlikely(current->domain == d) ) /* no domain_pause() */ - return -EPERM; - -- rc = xsm_vm_event_control(XSM_PRIV, d, mop->op, mop->event); -- if ( unlikely(rc) ) -- return rc; -- - switch ( mop->op ) - { - case XEN_DOMCTL_MONITOR_OP_ENABLE: ---- a/xen/common/vm_event.c -+++ b/xen/common/vm_event.c -@@ -603,11 +603,10 @@ int vm_event_domctl(struct domain *d, st - - /* All other subops need to target a real domain. */ - if ( unlikely(d == NULL) ) -- return -ESRCH; -- -- rc = xsm_vm_event_control(XSM_PRIV, d, vec->mode, vec->op); -- if ( rc ) -- return rc; -+ { -+ ASSERT_UNREACHABLE(); -+ return -EILSEQ; -+ } - - if ( unlikely(d == current->domain) ) /* no domain_pause() */ - { ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -652,13 +652,6 @@ static XSM_INLINE int cf_check xsm_hvm_a - } - } - --static XSM_INLINE int cf_check xsm_vm_event_control( -- XSM_DEFAULT_ARG struct domain *d, int mode, int op) --{ -- XSM_ASSERT_ACTION(XSM_PRIV); -- return xsm_default_action(action, current->domain, d); --} -- - #ifdef CONFIG_VM_EVENT - static XSM_INLINE int cf_check xsm_mem_access(XSM_DEFAULT_ARG struct domain *d) - { ---- a/xen/include/xsm/xsm.h -+++ b/xen/include/xsm/xsm.h -@@ -157,8 +157,6 @@ struct xsm_ops { - int (*hvm_altp2mhvm_op)(struct domain *d, uint64_t mode, uint32_t op); - int (*get_vnumainfo)(struct domain *d); - -- int (*vm_event_control)(struct domain *d, int mode, int op); -- - #ifdef CONFIG_VM_EVENT - int (*mem_access)(struct domain *d); - #endif -@@ -657,12 +655,6 @@ static inline int xsm_get_vnumainfo(xsm_ - return alternative_call(xsm_ops.get_vnumainfo, d); - } - --static inline int xsm_vm_event_control( -- xsm_default_t def, struct domain *d, int mode, int op) --{ -- return alternative_call(xsm_ops.vm_event_control, d, mode, op); --} -- - #ifdef CONFIG_VM_EVENT - static inline int xsm_mem_access(xsm_default_t def, struct domain *d) - { ---- a/xen/xsm/dummy.c -+++ b/xen/xsm/dummy.c -@@ -116,8 +116,6 @@ static const struct xsm_ops __initconst_ - .remove_from_physmap = xsm_remove_from_physmap, - .map_gmfn_foreign = xsm_map_gmfn_foreign, - -- .vm_event_control = xsm_vm_event_control, -- - #ifdef CONFIG_VM_EVENT - .mem_access = xsm_mem_access, - #endif ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -699,7 +699,6 @@ static int cf_check flask_domctl(struct - /* These have individual XSM hooks (common/domctl.c) */ - case XEN_DOMCTL_scheduler_op: - case XEN_DOMCTL_set_target: -- case XEN_DOMCTL_vm_event_op: - - #ifdef CONFIG_X86 - /* These have individual XSM hooks (arch/x86/domctl.c) */ -@@ -793,9 +792,8 @@ static int cf_check flask_domctl(struct - return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__TRIGGER); - - case XEN_DOMCTL_set_access_required: -- return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__VM_EVENT); -- - case XEN_DOMCTL_monitor_op: -+ case XEN_DOMCTL_vm_event_op: - return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__VM_EVENT); - - case XEN_DOMCTL_debug_op: -@@ -1368,11 +1366,6 @@ static int cf_check flask_hvm_altp2mhvm_ - return current_has_perm(d, SECCLASS_HVM, HVM__ALTP2MHVM_OP); - } - --static int cf_check flask_vm_event_control(struct domain *d, int mode, int op) --{ -- return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__VM_EVENT); --} -- - #ifdef CONFIG_VM_EVENT - static int cf_check flask_mem_access(struct domain *d) - { -@@ -1971,8 +1964,6 @@ static const struct xsm_ops __initconst_ - .do_xsm_op = do_flask_op, - .get_vnumainfo = flask_get_vnumainfo, - -- .vm_event_control = flask_vm_event_control, -- - #ifdef CONFIG_VM_EVENT - .mem_access = flask_mem_access, - #endif diff --git a/xsa492-4.21-15.patch b/xsa492-4.21-15.patch deleted file mode 100644 index ac87f3b..0000000 --- a/xsa492-4.21-15.patch +++ /dev/null @@ -1,108 +0,0 @@ -From: Jan Beulich -Subject: domctl/XSM: pass full struct xen_domctl to xsm_domctl() - -Subsequently some sub-ops will want to inspect their sub-sub-ops. Plus -this way we don't need to pass SSIDref separately anymore for -domain_create. - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith - ---- a/xen/arch/x86/mm/paging.c -+++ b/xen/arch/x86/mm/paging.c -@@ -735,7 +735,7 @@ long do_paging_domctl_cont( - if ( d == NULL ) - return -ESRCH; - -- ret = xsm_domctl(XSM_OTHER, d, op.cmd, 0 /* SSIDref not applicable */); -+ ret = xsm_domctl(XSM_OTHER, d, &op); - if ( !ret ) - { - if ( domctl_lock_acquire() ) ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -526,9 +526,7 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - break; - } - -- ret = xsm_domctl(XSM_OTHER, d, op->cmd, -- /* SSIDRef only applicable for cmd == createdomain */ -- op->u.createdomain.ssidref); -+ ret = xsm_domctl(XSM_OTHER, d, op); - if ( ret ) - goto domctl_out_unlock_domonly; - ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -162,10 +162,10 @@ static XSM_INLINE int cf_check xsm_set_t - } - - static XSM_INLINE int cf_check xsm_domctl( -- XSM_DEFAULT_ARG struct domain *d, unsigned int cmd, uint32_t ssidref) -+ XSM_DEFAULT_ARG struct domain *d, struct xen_domctl *op) - { - XSM_ASSERT_ACTION(XSM_OTHER); -- switch ( cmd ) -+ switch ( op->cmd ) - { - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_getdomaininfo: ---- a/xen/include/xsm/xsm.h -+++ b/xen/include/xsm/xsm.h -@@ -61,7 +61,7 @@ struct xsm_ops { - int (*sysctl_scheduler_op)(int op); - #endif - int (*set_target)(struct domain *d, struct domain *e); -- int (*domctl)(struct domain *d, unsigned int cmd, uint32_t ssidref); -+ int (*domctl)(struct domain *d, struct xen_domctl *op); - int (*sysctl)(int cmd); - int (*readconsole)(uint32_t clear); - -@@ -260,9 +260,9 @@ static inline int xsm_set_target( - } - - static inline int xsm_domctl(xsm_default_t def, struct domain *d, -- unsigned int cmd, uint32_t ssidref) -+ struct xen_domctl *op) - { -- return alternative_call(xsm_ops.domctl, d, cmd, ssidref); -+ return alternative_call(xsm_ops.domctl, d, op); - } - - static inline int xsm_sysctl(xsm_default_t def, int cmd) ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -667,10 +667,9 @@ static int cf_check flask_set_target(str - return rc; - } - --static int cf_check flask_domctl(struct domain *d, unsigned int cmd, -- uint32_t ssidref) -+static int cf_check flask_domctl(struct domain *d, struct xen_domctl *op) - { -- switch ( cmd ) -+ switch ( op->cmd ) - { - case XEN_DOMCTL_createdomain: - /* -@@ -680,7 +679,8 @@ static int cf_check flask_domctl(struct - * Note that d is NULL because we haven't even allocated memory for it - * this early in XEN_DOMCTL_createdomain. - */ -- return avc_current_has_perm(ssidref, SECCLASS_DOMAIN, DOMAIN__CREATE, NULL); -+ return avc_current_has_perm(op->u.createdomain.ssidref, SECCLASS_DOMAIN, -+ DOMAIN__CREATE, NULL); - - /* These have individual XSM hooks and don't make it here. */ - case XEN_DOMCTL_bind_pt_irq: -@@ -855,7 +855,7 @@ static int cf_check flask_domctl(struct - return current_has_perm(d, SECCLASS_DOMAIN2, DOMAIN2__SET_LLC_COLORS); - - default: -- return avc_unknown_permission("domctl", cmd); -+ return avc_unknown_permission("domctl", op->cmd); - } - } - diff --git a/xsa492-4.21-16.patch b/xsa492-4.21-16.patch deleted file mode 100644 index 2cb8619..0000000 --- a/xsa492-4.21-16.patch +++ /dev/null @@ -1,112 +0,0 @@ -From: Jan Beulich -Subject: domctl/XSM: drop scheduler_op hook - -Integrate the checking with xsm_domctl(), now that it has the full op -struct passed. As a positive side effect, permissions are then checked at -the same early point with and without Flask. - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith -Reviewed-by: Juergen Gross - ---- a/xen/common/sched/core.c -+++ b/xen/common/sched/core.c -@@ -2074,10 +2074,6 @@ long sched_adjust(struct domain *d, stru - { - long ret; - -- ret = xsm_domctl_scheduler_op(XSM_HOOK, d, op->cmd); -- if ( ret ) -- return ret; -- - if ( op->sched_id != dom_scheduler(d)->sched_id ) - return -EINVAL; - ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -141,13 +141,6 @@ static XSM_INLINE int cf_check xsm_getdo - return xsm_default_action(action, current->domain, d); - } - --static XSM_INLINE int cf_check xsm_domctl_scheduler_op( -- XSM_DEFAULT_ARG struct domain *d, int cmd) --{ -- XSM_ASSERT_ACTION(XSM_HOOK); -- return xsm_default_action(action, current->domain, d); --} -- - static XSM_INLINE int cf_check xsm_sysctl_scheduler_op(XSM_DEFAULT_ARG int cmd) - { - XSM_ASSERT_ACTION(XSM_HOOK); ---- a/xen/include/xsm/xsm.h -+++ b/xen/include/xsm/xsm.h -@@ -56,7 +56,6 @@ struct xsm_ops { - struct xen_domctl_getdomaininfo *info); - int (*domain_create)(struct domain *d, uint32_t ssidref); - int (*getdomaininfo)(struct domain *d); -- int (*domctl_scheduler_op)(struct domain *d, int op); - #ifdef CONFIG_SYSCTL - int (*sysctl_scheduler_op)(int op); - #endif -@@ -240,12 +239,6 @@ static inline int xsm_get_domain_state(x - return alternative_call(xsm_ops.get_domain_state, d); - } - --static inline int xsm_domctl_scheduler_op( -- xsm_default_t def, struct domain *d, int cmd) --{ -- return alternative_call(xsm_ops.domctl_scheduler_op, d, cmd); --} -- - #ifdef CONFIG_SYSCTL - static inline int xsm_sysctl_scheduler_op(xsm_default_t def, int cmd) - { ---- a/xen/xsm/dummy.c -+++ b/xen/xsm/dummy.c -@@ -18,7 +18,6 @@ static const struct xsm_ops __initconst_ - .security_domaininfo = xsm_security_domaininfo, - .domain_create = xsm_domain_create, - .getdomaininfo = xsm_getdomaininfo, -- .domctl_scheduler_op = xsm_domctl_scheduler_op, - #ifdef CONFIG_SYSCTL - .sysctl_scheduler_op = xsm_sysctl_scheduler_op, - #endif ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -609,7 +609,7 @@ static int cf_check flask_getdomaininfo( - return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETDOMAININFO); - } - --static int cf_check flask_domctl_scheduler_op(struct domain *d, int op) -+static int flask_domctl_scheduler_op(struct domain *d, int op) - { - switch ( op ) - { -@@ -697,7 +697,6 @@ static int cf_check flask_domctl(struct - return -EILSEQ; - - /* These have individual XSM hooks (common/domctl.c) */ -- case XEN_DOMCTL_scheduler_op: - case XEN_DOMCTL_set_target: - - #ifdef CONFIG_X86 -@@ -745,6 +744,9 @@ static int cf_check flask_domctl(struct - case XEN_DOMCTL_setdomainhandle: - return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__SETDOMAINHANDLE); - -+ case XEN_DOMCTL_scheduler_op: -+ return flask_domctl_scheduler_op(d, op->u.scheduler_op.cmd); -+ - case XEN_DOMCTL_set_ext_vcpucontext: - case XEN_DOMCTL_set_vcpu_msrs: - case XEN_DOMCTL_setvcpucontext: -@@ -1884,7 +1886,6 @@ static const struct xsm_ops __initconst_ - .security_domaininfo = flask_security_domaininfo, - .domain_create = flask_domain_create, - .getdomaininfo = flask_getdomaininfo, -- .domctl_scheduler_op = flask_domctl_scheduler_op, - #ifdef CONFIG_SYSCTL - .sysctl_scheduler_op = flask_sysctl_scheduler_op, - #endif diff --git a/xsa492-4.21-17.patch b/xsa492-4.21-17.patch deleted file mode 100644 index 99542df..0000000 --- a/xsa492-4.21-17.patch +++ /dev/null @@ -1,124 +0,0 @@ -From: Jan Beulich -Subject: domctl/XSM: drop shadow_control_op hook - -Integrate the checking with xsm_domctl(), now that it has the full op -struct passed. As a positive side effect, permissions are then checked at -the same early point with and without Flask. - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith - ---- a/xen/arch/x86/mm/paging.c -+++ b/xen/arch/x86/mm/paging.c -@@ -677,10 +677,6 @@ int paging_domctl(struct domain *d, stru - return -EBUSY; - } - -- rc = xsm_shadow_control(XSM_HOOK, d, sc->op); -- if ( rc ) -- return rc; -- - /* Code to handle log-dirty. Note that some log dirty operations - * piggy-back on shadow operations. For example, when - * XEN_DOMCTL_SHADOW_OP_OFF is called, it first checks whether log dirty ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -682,13 +682,6 @@ static XSM_INLINE int cf_check xsm_do_mc - return xsm_default_action(action, current->domain, NULL); - } - --static XSM_INLINE int cf_check xsm_shadow_control( -- XSM_DEFAULT_ARG struct domain *d, uint32_t op) --{ -- XSM_ASSERT_ACTION(XSM_HOOK); -- return xsm_default_action(action, current->domain, d); --} -- - static XSM_INLINE int cf_check xsm_mem_sharing_op( - XSM_DEFAULT_ARG struct domain *d, struct domain *cd, int op) - { ---- a/xen/include/xsm/xsm.h -+++ b/xen/include/xsm/xsm.h -@@ -172,7 +172,6 @@ struct xsm_ops { - - #ifdef CONFIG_X86 - int (*do_mca)(void); -- int (*shadow_control)(struct domain *d, uint32_t op); - int (*mem_sharing_op)(struct domain *d, struct domain *cd, int op); - int (*apic)(struct domain *d, int cmd); - int (*machine_memory_map)(void); -@@ -680,12 +679,6 @@ static inline int xsm_do_mca(xsm_default - return alternative_call(xsm_ops.do_mca); - } - --static inline int xsm_shadow_control( -- xsm_default_t def, struct domain *d, uint32_t op) --{ -- return alternative_call(xsm_ops.shadow_control, d, op); --} -- - static inline int xsm_mem_sharing_op( - xsm_default_t def, struct domain *d, struct domain *cd, int op) - { ---- a/xen/xsm/dummy.c -+++ b/xen/xsm/dummy.c -@@ -130,7 +130,6 @@ static const struct xsm_ops __initconst_ - .platform_op = xsm_platform_op, - #ifdef CONFIG_X86 - .do_mca = xsm_do_mca, -- .shadow_control = xsm_shadow_control, - .mem_sharing_op = xsm_mem_sharing_op, - .apic = xsm_apic, - .machine_memory_map = xsm_machine_memory_map, ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -40,6 +40,7 @@ - - #ifdef CONFIG_X86 - #include -+static int flask_shadow_control(struct domain *d, unsigned int op); - #else - #define pv_shim false - #endif -@@ -699,10 +700,6 @@ static int cf_check flask_domctl(struct - /* These have individual XSM hooks (common/domctl.c) */ - case XEN_DOMCTL_set_target: - --#ifdef CONFIG_X86 -- /* These have individual XSM hooks (arch/x86/domctl.c) */ -- case XEN_DOMCTL_shadow_op: --#endif - #ifdef CONFIG_HAS_PASSTHROUGH - /* - * These have individual XSM hooks -@@ -787,6 +784,11 @@ static int cf_check flask_domctl(struct - case XEN_DOMCTL_get_address_size: - return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__GETADDRSIZE); - -+#ifdef CONFIG_X86 -+ case XEN_DOMCTL_shadow_op: -+ return flask_shadow_control(d, op->u.shadow_op.op); -+#endif -+ - case XEN_DOMCTL_mem_sharing_op: - return current_has_perm(d, SECCLASS_HVM, HVM__MEM_SHARING); - -@@ -1603,7 +1605,7 @@ static int cf_check flask_do_mca(void) - return domain_has_xen(current->domain, XEN__MCA_OP); - } - --static int cf_check flask_shadow_control(struct domain *d, uint32_t op) -+static int flask_shadow_control(struct domain *d, unsigned int op) - { - uint32_t perm; - -@@ -1999,7 +2001,6 @@ static const struct xsm_ops __initconst_ - .platform_op = flask_platform_op, - #ifdef CONFIG_X86 - .do_mca = flask_do_mca, -- .shadow_control = flask_shadow_control, - .mem_sharing_op = flask_mem_sharing_op, - .apic = flask_apic, - .machine_memory_map = flask_machine_memory_map, diff --git a/xsa492-4.21-18.patch b/xsa492-4.21-18.patch deleted file mode 100644 index 1d82124..0000000 --- a/xsa492-4.21-18.patch +++ /dev/null @@ -1,94 +0,0 @@ -From: Jan Beulich -Subject: domctl: handle XEN_DOMCTL_get_device_group without acquiring domctl lock - -iommu_get_device_group() uses its own locking. Thus, with caller side -locking irrelevant, it can as well be called with the domctl lock not -held. - -Move the handling not only ahead of acquiring the lock, but also ahead -of the XSM check, leveraging that the sub-op has its own hook. - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith -Reviewed-by: Roger Pau Monné - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -513,6 +513,10 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - /* Other sub-ops handled further down. */ - break; - -+ case XEN_DOMCTL_get_device_group: -+ ret = iommu_do_domctl(op, d, u_domctl); -+ goto domctl_out_unlock_domonly; -+ - case XEN_DOMCTL_ioport_permission: - case XEN_DOMCTL_ioport_mapping: - case XEN_DOMCTL_gsi_permission: -@@ -918,7 +922,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - case XEN_DOMCTL_assign_device: - case XEN_DOMCTL_test_assign_device: - case XEN_DOMCTL_deassign_device: -- case XEN_DOMCTL_get_device_group: - ret = iommu_do_domctl(op, d, u_domctl); - break; - ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1620,7 +1620,7 @@ static int iommu_get_device_group( - if ( (pdev->seg != seg) || ((b == bus) && (df == devfn)) ) - continue; - -- if ( xsm_get_device_group(XSM_HOOK, (seg << 16) | (b << 8) | df) ) -+ if ( xsm_get_device_group(XSM_PRIV, (seg << 16) | (b << 8) | df) ) - continue; - - sdev_id = iommu_call(ops, get_device_group_id, seg, b, df); -@@ -1690,7 +1690,7 @@ int iommu_do_pci_domctl( - u32 max_sdevs; - XEN_GUEST_HANDLE_64(uint32) sdevs; - -- ret = xsm_get_device_group(XSM_HOOK, domctl->u.get_device_group.machine_sbdf); -+ ret = xsm_get_device_group(XSM_PRIV, domctl->u.get_device_group.machine_sbdf); - if ( ret ) - break; - ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -162,6 +162,7 @@ static XSM_INLINE int cf_check xsm_domct - { - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_getdomaininfo: -+ case XEN_DOMCTL_get_device_group: - case XEN_DOMCTL_get_domain_state: - case XEN_DOMCTL_gsi_permission: - case XEN_DOMCTL_iomem_permission: -@@ -401,7 +402,7 @@ static XSM_INLINE int cf_check xsm_get_v - static XSM_INLINE int cf_check xsm_get_device_group( - XSM_DEFAULT_ARG uint32_t machine_bdf) - { -- XSM_ASSERT_ACTION(XSM_HOOK); -+ XSM_ASSERT_ACTION(XSM_PRIV); - return xsm_default_action(action, current->domain, NULL); - } - ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -686,6 +686,7 @@ static int cf_check flask_domctl(struct - /* These have individual XSM hooks and don't make it here. */ - case XEN_DOMCTL_bind_pt_irq: - case XEN_DOMCTL_getdomaininfo: -+ case XEN_DOMCTL_get_device_group: - case XEN_DOMCTL_get_domain_state: - case XEN_DOMCTL_gsi_permission: - case XEN_DOMCTL_iomem_permission: -@@ -705,7 +706,6 @@ static int cf_check flask_domctl(struct - * These have individual XSM hooks - * (drivers/passthrough/{pci,device_tree.c) - */ -- case XEN_DOMCTL_get_device_group: - case XEN_DOMCTL_test_assign_device: - case XEN_DOMCTL_assign_device: - case XEN_DOMCTL_deassign_device: diff --git a/xsa492-4.21-19.patch b/xsa492-4.21-19.patch deleted file mode 100644 index 54a1117..0000000 --- a/xsa492-4.21-19.patch +++ /dev/null @@ -1,378 +0,0 @@ -From: Jan Beulich -Subject: domctl/XSM: drop {,de}assign_{,dt}device hooks - -Integrate the checking with xsm_domctl(). As a positive side effect, -permissions are then checked at the same early point with and without -Flask. As the DT device path needs fetching earlier (but must not be -double fetched), cache it in a private field of the public interface -struct. - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith -Reviewed-by: Roger Pau Monné - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -325,6 +325,10 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - case XEN_DOMCTL_deassign_device: - if ( op->domain == DOMID_IO ) - { -+#ifdef CONFIG_HAS_DEVICE_TREE_DISCOVERY -+ if ( op->u.assign_device.dev == XEN_DOMCTL_DEV_DT ) -+ op->u.assign_device.u.dt.dev = NULL; -+#endif - d = dom_io; - break; - } -@@ -332,6 +336,11 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - return -ESRCH; - fallthrough; - case XEN_DOMCTL_test_assign_device: -+#ifdef CONFIG_HAS_DEVICE_TREE_DISCOVERY -+ if ( op->u.assign_device.dev == XEN_DOMCTL_DEV_DT ) -+ op->u.assign_device.u.dt.dev = NULL; -+ fallthrough; -+#endif - case XEN_DOMCTL_vm_event_op: - if ( op->domain == DOMID_INVALID ) - { ---- a/xen/drivers/passthrough/device_tree.c -+++ b/xen/drivers/passthrough/device_tree.c -@@ -340,15 +340,15 @@ int iommu_do_dt_domctl(struct xen_domctl - if ( (d && d->is_dying) || domctl->u.assign_device.flags ) - break; - -- ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path, -- domctl->u.assign_device.u.dt.size, -- &dev); -- if ( ret ) -- break; -- -- ret = xsm_assign_dtdevice(XSM_HOOK, d, dt_node_full_name(dev)); -- if ( ret ) -- break; -+ dev = domctl->u.assign_device.u.dt.dev; -+ if ( !dev ) -+ { -+ ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path, -+ domctl->u.assign_device.u.dt.size, -+ &dev); -+ if ( ret ) -+ break; -+ } - - if ( domctl->cmd == XEN_DOMCTL_test_assign_device ) - { -@@ -396,15 +396,15 @@ int iommu_do_dt_domctl(struct xen_domctl - if ( domctl->u.assign_device.flags ) - break; - -- ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path, -- domctl->u.assign_device.u.dt.size, -- &dev); -- if ( ret ) -- break; -- -- ret = xsm_deassign_dtdevice(XSM_HOOK, d, dt_node_full_name(dev)); -- if ( ret ) -- break; -+ dev = domctl->u.assign_device.u.dt.dev; -+ if ( !dev ) -+ { -+ ret = dt_find_node_by_gpath(domctl->u.assign_device.u.dt.path, -+ domctl->u.assign_device.u.dt.size, -+ &dev); -+ if ( ret ) -+ break; -+ } - - if ( d == dom_io ) - { ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -1740,10 +1740,6 @@ int iommu_do_pci_domctl( - - machine_sbdf = domctl->u.assign_device.u.pci.machine_sbdf; - -- ret = xsm_assign_device(XSM_HOOK, d, machine_sbdf); -- if ( ret ) -- break; -- - seg = machine_sbdf >> 16; - bus = PCI_BUS(machine_sbdf); - devfn = PCI_DEVFN(machine_sbdf); -@@ -1785,10 +1781,6 @@ int iommu_do_pci_domctl( - - machine_sbdf = domctl->u.assign_device.u.pci.machine_sbdf; - -- ret = xsm_deassign_device(XSM_HOOK, d, machine_sbdf); -- if ( ret ) -- break; -- - seg = machine_sbdf >> 16; - bus = PCI_BUS(machine_sbdf); - devfn = PCI_DEVFN(machine_sbdf); ---- a/xen/include/public/domctl.h -+++ b/xen/include/public/domctl.h -@@ -575,7 +575,10 @@ struct xen_domctl_assign_device { - } pci; - struct { - uint32_t size; /* Length of the path */ -- XEN_GUEST_HANDLE_64(char) path; /* path to the device tree node */ -+ XEN_GUEST_HANDLE_64(char) path; /* Path to the device tree node */ -+#ifdef __XEN__ -+ struct dt_device_node *dev; /* Resolved device node of the above */ -+#endif - } dt; - } u; - }; ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -405,40 +405,8 @@ static XSM_INLINE int cf_check xsm_get_d - XSM_ASSERT_ACTION(XSM_PRIV); - return xsm_default_action(action, current->domain, NULL); - } -- --static XSM_INLINE int cf_check xsm_assign_device( -- XSM_DEFAULT_ARG struct domain *d, uint32_t machine_bdf) --{ -- XSM_ASSERT_ACTION(XSM_HOOK); -- return xsm_default_action(action, current->domain, d); --} -- --static XSM_INLINE int cf_check xsm_deassign_device( -- XSM_DEFAULT_ARG struct domain *d, uint32_t machine_bdf) --{ -- XSM_ASSERT_ACTION(XSM_HOOK); -- return xsm_default_action(action, current->domain, d); --} -- - #endif /* HAS_PASSTHROUGH && HAS_PCI */ - --#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY) --static XSM_INLINE int cf_check xsm_assign_dtdevice( -- XSM_DEFAULT_ARG struct domain *d, const char *dtpath) --{ -- XSM_ASSERT_ACTION(XSM_HOOK); -- return xsm_default_action(action, current->domain, d); --} -- --static XSM_INLINE int cf_check xsm_deassign_dtdevice( -- XSM_DEFAULT_ARG struct domain *d, const char *dtpath) --{ -- XSM_ASSERT_ACTION(XSM_HOOK); -- return xsm_default_action(action, current->domain, d); --} -- --#endif /* HAS_PASSTHROUGH && HAS_DEVICE_TREE_DISCOVERY */ -- - static XSM_INLINE int cf_check xsm_resource_plug_core(XSM_DEFAULT_VOID) - { - XSM_ASSERT_ACTION(XSM_HOOK); ---- a/xen/include/xsm/xsm.h -+++ b/xen/include/xsm/xsm.h -@@ -124,13 +124,6 @@ struct xsm_ops { - - #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI) - int (*get_device_group)(uint32_t machine_bdf); -- int (*assign_device)(struct domain *d, uint32_t machine_bdf); -- int (*deassign_device)(struct domain *d, uint32_t machine_bdf); --#endif -- --#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY) -- int (*assign_dtdevice)(struct domain *d, const char *dtpath); -- int (*deassign_dtdevice)(struct domain *d, const char *dtpath); - #endif - - int (*resource_plug_core)(void); -@@ -533,35 +526,8 @@ static inline int xsm_get_device_group(x - { - return alternative_call(xsm_ops.get_device_group, machine_bdf); - } -- --static inline int xsm_assign_device( -- xsm_default_t def, struct domain *d, uint32_t machine_bdf) --{ -- return alternative_call(xsm_ops.assign_device, d, machine_bdf); --} -- --static inline int xsm_deassign_device( -- xsm_default_t def, struct domain *d, uint32_t machine_bdf) --{ -- return alternative_call(xsm_ops.deassign_device, d, machine_bdf); --} - #endif /* HAS_PASSTHROUGH && HAS_PCI) */ - --#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY) --static inline int xsm_assign_dtdevice( -- xsm_default_t def, struct domain *d, const char *dtpath) --{ -- return alternative_call(xsm_ops.assign_dtdevice, d, dtpath); --} -- --static inline int xsm_deassign_dtdevice( -- xsm_default_t def, struct domain *d, const char *dtpath) --{ -- return alternative_call(xsm_ops.deassign_dtdevice, d, dtpath); --} -- --#endif /* HAS_PASSTHROUGH && HAS_DEVICE_TREE_DISCOVERY */ -- - static inline int xsm_resource_plug_pci(xsm_default_t def, uint32_t machine_bdf) - { - return alternative_call(xsm_ops.resource_plug_pci, machine_bdf); ---- a/xen/xsm/dummy.c -+++ b/xen/xsm/dummy.c -@@ -81,13 +81,6 @@ static const struct xsm_ops __initconst_ - - #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI) - .get_device_group = xsm_get_device_group, -- .assign_device = xsm_assign_device, -- .deassign_device = xsm_deassign_device, --#endif -- --#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY) -- .assign_dtdevice = xsm_assign_dtdevice, -- .deassign_dtdevice = xsm_deassign_dtdevice, - #endif - - .resource_plug_core = xsm_resource_plug_core, ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -45,6 +45,17 @@ static int flask_shadow_control(struct d - #define pv_shim false - #endif - -+#ifdef CONFIG_HAS_PASSTHROUGH -+#ifdef CONFIG_HAS_PCI -+static int flask_assign_device(struct domain *d, unsigned int machine_bdf); -+static int flask_deassign_device(struct domain *d, unsigned int machine_bdf); -+#endif -+#ifdef CONFIG_HAS_DEVICE_TREE_DISCOVERY -+static int flask_assign_dtdevice(struct domain *d, const char *dtpath); -+static int flask_deassign_dtdevice(struct domain *d, const char *dtpath); -+#endif -+#endif /* CONFIG_HAS_PASSTHROUGH */ -+ - static uint32_t domain_sid(const struct domain *dom) - { - struct domain_security_struct *dsec = dom->ssid; -@@ -700,16 +711,6 @@ static int cf_check flask_domctl(struct - - /* These have individual XSM hooks (common/domctl.c) */ - case XEN_DOMCTL_set_target: -- --#ifdef CONFIG_HAS_PASSTHROUGH -- /* -- * These have individual XSM hooks -- * (drivers/passthrough/{pci,device_tree.c) -- */ -- case XEN_DOMCTL_test_assign_device: -- case XEN_DOMCTL_assign_device: -- case XEN_DOMCTL_deassign_device: --#endif - return 0; - - case XEN_DOMCTL_destroydomain: -@@ -789,6 +790,49 @@ static int cf_check flask_domctl(struct - return flask_shadow_control(d, op->u.shadow_op.op); - #endif - -+#ifdef CONFIG_HAS_PASSTHROUGH -+ -+ case XEN_DOMCTL_test_assign_device: -+ case XEN_DOMCTL_assign_device: -+ case XEN_DOMCTL_deassign_device: -+ switch ( op->u.assign_device.dev ) -+ { -+#ifdef CONFIG_HAS_PCI -+ case XEN_DOMCTL_DEV_PCI: -+ return op->cmd != XEN_DOMCTL_deassign_device -+ ? flask_assign_device( -+ d, op->u.assign_device.u.pci.machine_sbdf) -+ : flask_deassign_device( -+ d, op->u.assign_device.u.pci.machine_sbdf); -+#endif -+ -+#ifdef CONFIG_HAS_DEVICE_TREE_DISCOVERY -+ case XEN_DOMCTL_DEV_DT: -+ { -+ struct dt_device_node *dev; -+ int ret = dt_find_node_by_gpath(op->u.assign_device.u.dt.path, -+ op->u.assign_device.u.dt.size, -+ &dev); -+ -+ if ( ret ) -+ return ret; -+ -+ op->u.assign_device.u.dt.dev = dev; -+ -+ return op->cmd != XEN_DOMCTL_deassign_device -+ ? flask_assign_dtdevice(d, dt_node_full_name(dev)) -+ : flask_deassign_dtdevice(d, dt_node_full_name(dev)); -+ } -+#endif -+ -+ default: -+ /* Unknown type. */ -+ break; -+ } -+ return avc_unknown_permission("assign_device", op->cmd); -+ -+#endif /* CONFIG_HAS_PASSTHROUGH */ -+ - case XEN_DOMCTL_mem_sharing_op: - return current_has_perm(d, SECCLASS_HVM, HVM__MEM_SHARING); - -@@ -1416,7 +1460,7 @@ static int flask_test_assign_device(uint - return avc_current_has_perm(rsid, SECCLASS_RESOURCE, RESOURCE__STAT_DEVICE, NULL); - } - --static int cf_check flask_assign_device(struct domain *d, uint32_t machine_bdf) -+static int flask_assign_device(struct domain *d, uint32_t machine_bdf) - { - uint32_t dsid, rsid; - int rc = -EPERM; -@@ -1446,7 +1490,7 @@ static int cf_check flask_assign_device( - return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, dperm, &ad); - } - --static int cf_check flask_deassign_device( -+static int flask_deassign_device( - struct domain *d, uint32_t machine_bdf) - { - uint32_t rsid; -@@ -1478,7 +1522,7 @@ static int flask_test_assign_dtdevice(co - NULL); - } - --static int cf_check flask_assign_dtdevice(struct domain *d, const char *dtpath) -+static int flask_assign_dtdevice(struct domain *d, const char *dtpath) - { - uint32_t dsid, rsid; - int rc = -EPERM; -@@ -1508,7 +1552,7 @@ static int cf_check flask_assign_dtdevic - return avc_has_perm(dsid, rsid, SECCLASS_RESOURCE, dperm, &ad); - } - --static int cf_check flask_deassign_dtdevice( -+static int flask_deassign_dtdevice( - struct domain *d, const char *dtpath) - { - uint32_t rsid; -@@ -1989,13 +2033,6 @@ static const struct xsm_ops __initconst_ - - #if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_PCI) - .get_device_group = flask_get_device_group, -- .assign_device = flask_assign_device, -- .deassign_device = flask_deassign_device, --#endif -- --#if defined(CONFIG_HAS_PASSTHROUGH) && defined(CONFIG_HAS_DEVICE_TREE_DISCOVERY) -- .assign_dtdevice = flask_assign_dtdevice, -- .deassign_dtdevice = flask_deassign_dtdevice, - #endif - - .platform_op = flask_platform_op, diff --git a/xsa492-4.21-20.patch b/xsa492-4.21-20.patch deleted file mode 100644 index bfd10a9..0000000 --- a/xsa492-4.21-20.patch +++ /dev/null @@ -1,123 +0,0 @@ -From: Jan Beulich -Subject: domctl: handle XEN_DOMCTL_set_target without acquiring domctl lock - -The only locking required here is that between checking d->target and -setting it. To avoid the need for an explicit lock, use cmpxchgptr() to -update d->target. - -Move the handling not only ahead of acquiring the lock, but also ahead -of the XSM check, leveraging that the sub-op has its own hook. - -This is part of XSA-492. - -Signed-off-by: Jan Beulich -Acked-by: Daniel P. Smith -Reviewed-by: Roger Pau Monné - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -505,6 +505,30 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - } - #endif - -+ case XEN_DOMCTL_set_target: -+ { -+ struct domain *e = get_domain_by_id(op->u.set_target.target); -+ -+ ret = -ESRCH; -+ if ( !e ) -+ goto domctl_out_unlock_domonly; -+ -+ if ( d == e ) -+ ret = -EINVAL; -+ else if ( !is_hvm_domain(e) ) -+ ret = -EOPNOTSUPP; -+ else -+ ret = xsm_set_target(XSM_PRIV, d, e); -+ -+ /* Hold reference on @e until we destroy @d. */ -+ if ( !ret && cmpxchgptr(&d->target, NULL, e) ) -+ ret = -EINVAL; -+ -+ if ( ret ) -+ put_domain(e); -+ goto domctl_out_unlock_domonly; -+ } -+ - case XEN_DOMCTL_vm_event_op: - if ( op->u.vm_event_op.op == XEN_VM_EVENT_GET_VERSION ) - { -@@ -844,36 +868,6 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - domain_set_time_offset(d, op->u.settimeoffset.time_offset_seconds); - break; - -- case XEN_DOMCTL_set_target: -- { -- struct domain *e; -- -- ret = -ESRCH; -- e = get_domain_by_id(op->u.set_target.target); -- if ( e == NULL ) -- break; -- -- ret = -EINVAL; -- if ( (d == e) || (d->target != NULL) ) -- { -- put_domain(e); -- break; -- } -- -- ret = -EOPNOTSUPP; -- if ( is_hvm_domain(e) ) -- ret = xsm_set_target(XSM_HOOK, d, e); -- if ( ret ) -- { -- put_domain(e); -- break; -- } -- -- /* Hold reference on @e until we destroy @d. */ -- d->target = e; -- break; -- } -- - case XEN_DOMCTL_subscribe: - d->suspend_evtchn = op->u.subscribe.port; - break; ---- a/xen/include/xsm/dummy.h -+++ b/xen/include/xsm/dummy.h -@@ -150,7 +150,7 @@ static XSM_INLINE int cf_check xsm_sysct - static XSM_INLINE int cf_check xsm_set_target( - XSM_DEFAULT_ARG struct domain *d, struct domain *e) - { -- XSM_ASSERT_ACTION(XSM_HOOK); -+ XSM_ASSERT_ACTION(XSM_PRIV); - return xsm_default_action(action, current->domain, NULL); - } - -@@ -170,6 +170,7 @@ static XSM_INLINE int cf_check xsm_domct - case XEN_DOMCTL_ioport_permission: - case XEN_DOMCTL_irq_permission: - case XEN_DOMCTL_memory_mapping: -+ case XEN_DOMCTL_set_target: - case XEN_DOMCTL_unbind_pt_irq: - ASSERT_UNREACHABLE(); - return -EILSEQ; ---- a/xen/xsm/flask/hooks.c -+++ b/xen/xsm/flask/hooks.c -@@ -705,14 +705,11 @@ static int cf_check flask_domctl(struct - case XEN_DOMCTL_ioport_permission: - case XEN_DOMCTL_irq_permission: - case XEN_DOMCTL_memory_mapping: -+ case XEN_DOMCTL_set_target: - case XEN_DOMCTL_unbind_pt_irq: - ASSERT_UNREACHABLE(); - return -EILSEQ; - -- /* These have individual XSM hooks (common/domctl.c) */ -- case XEN_DOMCTL_set_target: -- return 0; -- - case XEN_DOMCTL_destroydomain: - return current_has_perm(d, SECCLASS_DOMAIN, DOMAIN__DESTROY); - diff --git a/xsa493-4.21-01.patch b/xsa493-4.21-01.patch deleted file mode 100644 index c06b9a5..0000000 --- a/xsa493-4.21-01.patch +++ /dev/null @@ -1,311 +0,0 @@ -From 2e21b5301765de353c06081eee953255bf327176 Mon Sep 17 00:00:00 2001 -From: Michal Orzel -Date: Tue, 14 Apr 2026 10:11:24 +0200 -Subject: xen/arm64: flushtlb: Optimize ARM64_WORKAROUND_REPEAT_TLBI - -The ARM64_WORKAROUND_REPEAT_TLBI workaround is used to mitigate several -errata where broadcast TLBI;DSB sequences don't provide all the -architecturally required synchronization. The workaround performs more -work than necessary, and can have significant overhead. This patch -optimizes the workaround, as explained below. - -1. All relevant errata only affect the ordering and/or completion of - memory accesses which have been translated by an invalidated TLB - entry. The actual invalidation of TLB entries is unaffected. - -2. The existing workaround is applied to both broadcast and local TLB - invalidation, whereas for all relevant errata it is only necessary to - apply a workaround for broadcast invalidation. - -3. The existing workaround replaces every TLBI with a TLBI;DSB;TLBI - sequence, whereas for all relevant errata it is only necessary to - execute a single additional TLBI;DSB sequence after any number of - TLBIs are completed by a DSB. - - For example, for a sequence of batched TLBIs: - - TLBI [, ] - TLBI [, ] - TLBI [, ] - DSB ISH - - ... the existing workaround will expand this to: - - TLBI [, ] - DSB ISH // additional - TLBI [, ] // additional - TLBI [, ] - DSB ISH // additional - TLBI [, ] // additional - TLBI [, ] - DSB ISH // additional - TLBI [, ] // additional - DSB ISH - - ... whereas it is sufficient to have: - - TLBI [, ] - TLBI [, ] - TLBI [, ] - DSB ISH - TLBI [, ] // additional - DSB ISH // additional - - Using a single additional TLBI and DSB at the end of the sequence can - have significantly lower overhead as each DSB which completes a TLBI - must synchronize with other PEs in the system, with potential - performance effects both locally and system-wide. - -4. The existing workaround repeats each specific TLBI operation, whereas - for all relevant errata it is sufficient for the additional TLBI to - use *any* operation which will be broadcast, regardless of which - translation regime or stage of translation the operation applies to. - - For example, for a single TLBI: - - TLBI ALLE2IS - DSB ISH - - ... the existing workaround will expand this to: - - TLBI ALLE2IS - DSB ISH - TLBI ALLE2IS // additional - DSB ISH // additional - - ... whereas it is sufficient to have: - - TLBI ALLE2IS - DSB ISH - TLBI VALE1IS, XZR // additional - DSB ISH // additional - - As the additional TLBI doesn't have to match a specific earlier TLBI, - the additional TLBI can be implemented in separate code, with no - memory of the earlier TLBIs. The additional TLBI can also use a - cheaper TLBI operation. - -5. The existing workaround is applied to both Stage-1 and Stage-2 TLB - invalidation, whereas for all relevant errata it is only necessary to - apply a workaround for Stage-1 invalidation. - - Architecturally, TLBI operations which invalidate only Stage-2 - information (e.g. IPAS2E1IS) are not required to invalidate TLB - entries which combine information from Stage-1 and Stage-2 - translation table entries, and consequently may not complete memory - accesses translated by those combined entries. In these cases, - completion of memory accesses is only guaranteed after subsequent - invalidation of Stage-1 information (e.g. VMALLE1IS). - -Rework the workaround logic as follows: - - add TLB_HELPER_LOCAL() to be used for local TLB ops without a - workaround, - - modify TLB_HELPER() workaround to use tlbi vale2is, xzr as a second - TLBI, - - drop TLB_HELPER_VA(). It's used only by __flush_xen_tlb_one_local - which is local and does not need workaround and by - __flush_xen_tlb_one. In the latter case, since it's used in a loop, - we don't need a workaround in the middle. Add __tlb_repeat_sync with - a workaround to be used at the end after DSB and before final ISB, - - TLBI VALE2IS passing XZR is used as an additional TLBI. While there is - an identity mapping there, it's used very rarely. The performance - impact is therefore negligible. If things change in the future, we - can revisit the decision. - -Signed-off-by: Michal Orzel -Reviewed-by: Luca Fancellu -Reviewed-by: Julien Grall -(cherry picked from commit 7c502d7591519135765b8041cbd1c70e56e5a0b9) - -diff --git a/xen/arch/arm/include/asm/arm32/flushtlb.h b/xen/arch/arm/include/asm/arm32/flushtlb.h -index 61c25a318998..5483be08fbbe 100644 ---- a/xen/arch/arm/include/asm/arm32/flushtlb.h -+++ b/xen/arch/arm/include/asm/arm32/flushtlb.h -@@ -57,6 +57,9 @@ static inline void __flush_xen_tlb_one(vaddr_t va) - asm volatile(STORE_CP32(0, TLBIMVAHIS) : : "r" (va) : "memory"); - } - -+/* Only for ARM64_WORKAROUND_REPEAT_TLBI */ -+static inline void __tlb_repeat_sync(void) {} -+ - #endif /* __ASM_ARM_ARM32_FLUSHTLB_H__ */ - /* - * Local variables: -diff --git a/xen/arch/arm/include/asm/arm64/flushtlb.h b/xen/arch/arm/include/asm/arm64/flushtlb.h -index 3b99c11b50d1..1606b26bf28a 100644 ---- a/xen/arch/arm/include/asm/arm64/flushtlb.h -+++ b/xen/arch/arm/include/asm/arm64/flushtlb.h -@@ -12,9 +12,14 @@ - * ARM64_WORKAROUND_REPEAT_TLBI: - * Modification of the translation table for a virtual address might lead to - * read-after-read ordering violation. -- * The workaround repeats TLBI+DSB ISH operation for all the TLB flush -- * operations. While this is strictly not necessary, we don't want to -- * take any risk. -+ * The workaround repeats TLBI+DSB ISH operation for broadcast TLB flush -+ * operations. The workaround is not needed for local operations. -+ * -+ * It is sufficient for the additional TLBI to use *any* operation which will -+ * be broadcast, regardless of which translation regime or stage of translation -+ * the operation applies to. TLBI VALE2IS is used passing XZR. While there is -+ * an identity mapping there, it's only used during suspend/resume, CPU on/off, -+ * so the impact (performance if any) is negligible. - * - * For Xen page-tables the ISB will discard any instructions fetched - * from the old mappings. -@@ -26,69 +31,90 @@ - * Note that for local TLB flush, using non-shareable (nsh) is sufficient - * (see D5-4929 in ARM DDI 0487H.a). Although, the memory barrier in - * for the workaround is left as inner-shareable to match with Linux -- * v6.1-rc8. -+ * v6.19. - */ --#define TLB_HELPER(name, tlbop, sh) \ -+#define TLB_HELPER_LOCAL(name, tlbop) \ - static inline void name(void) \ - { \ - asm_inline volatile ( \ -- "dsb " # sh "st;" \ -+ "dsb nshst;" \ - "tlbi " # tlbop ";" \ -- ALTERNATIVE( \ -- "nop; nop;", \ -- "dsb ish;" \ -- "tlbi " # tlbop ";", \ -- ARM64_WORKAROUND_REPEAT_TLBI, \ -- CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) \ -- "dsb " # sh ";" \ -+ "dsb nsh;" \ - "isb;" \ - : : : "memory"); \ - } - --/* -- * FLush TLB by VA. This will likely be used in a loop, so the caller -- * is responsible to use the appropriate memory barriers before/after -- * the sequence. -- * -- * See above about the ARM64_WORKAROUND_REPEAT_TLBI sequence. -- */ --#define TLB_HELPER_VA(name, tlbop) \ --static inline void name(vaddr_t va) \ --{ \ -- asm_inline volatile ( \ -- "tlbi " # tlbop ", %0;" \ -- ALTERNATIVE( \ -- "nop; nop;", \ -- "dsb ish;" \ -- "tlbi " # tlbop ", %0;", \ -- ARM64_WORKAROUND_REPEAT_TLBI, \ -- CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) \ -- : : "r" (va >> PAGE_SHIFT) : "memory"); \ -+#define TLB_HELPER(name, tlbop) \ -+static inline void name(void) \ -+{ \ -+ asm_inline volatile ( \ -+ "dsb ishst;" \ -+ "tlbi " # tlbop ";" \ -+ ALTERNATIVE( \ -+ "nop; nop;", \ -+ "dsb ish;" \ -+ "tlbi vale2is, xzr;", \ -+ ARM64_WORKAROUND_REPEAT_TLBI, \ -+ CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) \ -+ "dsb ish;" \ -+ "isb;" \ -+ : : : "memory"); \ - } - - /* Flush local TLBs, current VMID only. */ --TLB_HELPER(flush_guest_tlb_local, vmalls12e1, nsh) -+TLB_HELPER_LOCAL(flush_guest_tlb_local, vmalls12e1) - - /* Flush innershareable TLBs, current VMID only */ --TLB_HELPER(flush_guest_tlb, vmalls12e1is, ish) -+TLB_HELPER(flush_guest_tlb, vmalls12e1is) - - /* Flush local TLBs, all VMIDs, non-hypervisor mode */ --TLB_HELPER(flush_all_guests_tlb_local, alle1, nsh) -+TLB_HELPER_LOCAL(flush_all_guests_tlb_local, alle1) - - /* Flush innershareable TLBs, all VMIDs, non-hypervisor mode */ --TLB_HELPER(flush_all_guests_tlb, alle1is, ish) -+TLB_HELPER(flush_all_guests_tlb, alle1is) - - /* Flush all hypervisor mappings from the TLB of the local processor. */ --TLB_HELPER(flush_xen_tlb_local, alle2, nsh) -+TLB_HELPER_LOCAL(flush_xen_tlb_local, alle2) -+ -+#undef TLB_HELPER_LOCAL -+#undef TLB_HELPER -+ -+/* -+ * FLush TLB by VA. This will likely be used in a loop, so the caller -+ * is responsible to use the appropriate memory barriers before/after -+ * the sequence. -+ */ - - /* Flush TLB of local processor for address va. */ --TLB_HELPER_VA(__flush_xen_tlb_one_local, vae2) -+static inline void __flush_xen_tlb_one_local(vaddr_t va) -+{ -+ asm_inline volatile ( -+ "tlbi vae2, %0" : : "r" (va >> PAGE_SHIFT) : "memory"); -+} - - /* Flush TLB of all processors in the inner-shareable domain for address va. */ --TLB_HELPER_VA(__flush_xen_tlb_one, vae2is) -+static inline void __flush_xen_tlb_one(vaddr_t va) -+{ -+ asm_inline volatile ( -+ "tlbi vae2is, %0" : : "r" (va >> PAGE_SHIFT) : "memory"); -+} - --#undef TLB_HELPER --#undef TLB_HELPER_VA -+/* -+ * ARM64_WORKAROUND_REPEAT_TLBI: -+ * For all relevant erratas it is only necessary to execute a single -+ * additional TLBI;DSB sequence after any number of TLBIs are completed by DSB. -+ */ -+static inline void __tlb_repeat_sync(void) -+{ -+ asm_inline volatile ( -+ ALTERNATIVE( -+ "nop; nop;", -+ "tlbi vale2is, xzr;" -+ "dsb ish;", -+ ARM64_WORKAROUND_REPEAT_TLBI, -+ CONFIG_ARM64_WORKAROUND_REPEAT_TLBI) -+ : : : "memory"); -+} - - #endif /* __ASM_ARM_ARM64_FLUSHTLB_H__ */ - /* -diff --git a/xen/arch/arm/include/asm/flushtlb.h b/xen/arch/arm/include/asm/flushtlb.h -index e45fb6d97b02..c292c3c00d29 100644 ---- a/xen/arch/arm/include/asm/flushtlb.h -+++ b/xen/arch/arm/include/asm/flushtlb.h -@@ -65,6 +65,7 @@ static inline void flush_xen_tlb_range_va(vaddr_t va, - va += PAGE_SIZE; - } - dsb(ish); /* Ensure the TLB invalidation has completed */ -+ __tlb_repeat_sync(); - isb(); - } - -diff --git a/xen/arch/arm/include/asm/mmu/layout.h b/xen/arch/arm/include/asm/mmu/layout.h -index 19c0ec63a59a..feafc14ebfda 100644 ---- a/xen/arch/arm/include/asm/mmu/layout.h -+++ b/xen/arch/arm/include/asm/mmu/layout.h -@@ -23,6 +23,10 @@ - * - * Reserved to identity map Xen - * -+ * Note: As part of ARM64_WORKAROUND_REPEAT_TLBI, VA 0 is used for an extra -+ * TLBI operation given its rare use (only identity mapping) and thus -+ * negligible performance impact. -+ * - * 0x00000a0000000000 - 0x00000a7fffffffff (512GB, L0 slot [20]) - * (Relative offsets) - * 0 - 2M Unmapped diff --git a/xsa493-4.21-02.patch b/xsa493-4.21-02.patch deleted file mode 100644 index f80119c..0000000 --- a/xsa493-4.21-02.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 7e70b87512c966248b1e8453d9ac54c643c06f44 Mon Sep 17 00:00:00 2001 -From: Michal Orzel -Date: Fri, 22 May 2026 09:35:55 +0200 -Subject: xen/arm: Sync missing definitions for Arm CPUs with Linux - -Synchronize with Linux kernel 7.0 definitions for the following CPUs: - - Cortex-A76AE, - - Cortex-A78AE, - - Cortex-X1C, - - Cortex-X3, - - Neoverse-V2, - - Cortex-X4, - - Neoverse-V3AE, - - Neoverse-V3, - - Cortex-X925. - -These will be used for errata detection in subsequent patches. - -Signed-off-by: Michal Orzel -Reviewed-by: Julien Grall - -diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h -index ec23fd098b63..907778683b08 100644 ---- a/xen/arch/arm/include/asm/processor.h -+++ b/xen/arch/arm/include/asm/processor.h -@@ -89,13 +89,22 @@ - #define ARM_CPU_PART_CORTEX_A76 0xD0B - #define ARM_CPU_PART_NEOVERSE_N1 0xD0C - #define ARM_CPU_PART_CORTEX_A77 0xD0D -+#define ARM_CPU_PART_CORTEX_A76AE 0xD0E - #define ARM_CPU_PART_NEOVERSE_V1 0xD40 - #define ARM_CPU_PART_CORTEX_A78 0xD41 -+#define ARM_CPU_PART_CORTEX_A78AE 0xD42 - #define ARM_CPU_PART_CORTEX_X1 0xD44 - #define ARM_CPU_PART_CORTEX_A710 0xD47 - #define ARM_CPU_PART_CORTEX_X2 0xD48 - #define ARM_CPU_PART_NEOVERSE_N2 0xD49 - #define ARM_CPU_PART_CORTEX_A78C 0xD4B -+#define ARM_CPU_PART_CORTEX_X1C 0xD4C -+#define ARM_CPU_PART_CORTEX_X3 0xD4E -+#define ARM_CPU_PART_NEOVERSE_V2 0xD4F -+#define ARM_CPU_PART_CORTEX_X4 0xD82 -+#define ARM_CPU_PART_NEOVERSE_V3AE 0xD83 -+#define ARM_CPU_PART_NEOVERSE_V3 0xD84 -+#define ARM_CPU_PART_CORTEX_X925 0xD85 - - #define MIDR_CORTEX_A12 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A12) - #define MIDR_CORTEX_A17 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A17) -@@ -110,13 +119,22 @@ - #define MIDR_CORTEX_A76 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76) - #define MIDR_NEOVERSE_N1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N1) - #define MIDR_CORTEX_A77 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A77) -+#define MIDR_CORTEX_A76AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A76AE) - #define MIDR_NEOVERSE_V1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V1) - #define MIDR_CORTEX_A78 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78) -+#define MIDR_CORTEX_A78AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78AE) - #define MIDR_CORTEX_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1) - #define MIDR_CORTEX_A710 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A710) - #define MIDR_CORTEX_X2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X2) - #define MIDR_NEOVERSE_N2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N2) - #define MIDR_CORTEX_A78C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78C) -+#define MIDR_CORTEX_X1C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1C) -+#define MIDR_CORTEX_X3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X3) -+#define MIDR_NEOVERSE_V2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V2) -+#define MIDR_CORTEX_X4 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X4) -+#define MIDR_NEOVERSE_V3AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3AE) -+#define MIDR_NEOVERSE_V3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3) -+#define MIDR_CORTEX_X925 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X925) - - /* MPIDR Multiprocessor Affinity Register */ - #define _MPIDR_UP (30) diff --git a/xsa493-4.21-03.patch b/xsa493-4.21-03.patch deleted file mode 100644 index 86bae68..0000000 --- a/xsa493-4.21-03.patch +++ /dev/null @@ -1,37 +0,0 @@ -From c0f7b40fdbb986b3cf470ed51f3878261e33f9cb Mon Sep 17 00:00:00 2001 -From: Michal Orzel -Date: Fri, 22 May 2026 09:35:56 +0200 -Subject: xen/arm: Add C1-Ultra definitions - -Add processor definitions for C1-Ultra. These will be used for errata -detection in subsequent patches. - -These values can be found in the C1-Ultra TRM: - - https://developer.arm.com/documentation/108014/0100/ - -... in section A.5.1 ("MIDR_EL1, Main ID Register"). - -Signed-off-by: Michal Orzel -Reviewed-by: Julien Grall - -diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h -index 907778683b08..72745cca62bc 100644 ---- a/xen/arch/arm/include/asm/processor.h -+++ b/xen/arch/arm/include/asm/processor.h -@@ -105,6 +105,7 @@ - #define ARM_CPU_PART_NEOVERSE_V3AE 0xD83 - #define ARM_CPU_PART_NEOVERSE_V3 0xD84 - #define ARM_CPU_PART_CORTEX_X925 0xD85 -+#define ARM_CPU_PART_C1_ULTRA 0xD8C - - #define MIDR_CORTEX_A12 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A12) - #define MIDR_CORTEX_A17 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A17) -@@ -135,6 +136,7 @@ - #define MIDR_NEOVERSE_V3AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3AE) - #define MIDR_NEOVERSE_V3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3) - #define MIDR_CORTEX_X925 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X925) -+#define MIDR_C1_ULTRA MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_ULTRA) - - /* MPIDR Multiprocessor Affinity Register */ - #define _MPIDR_UP (30) diff --git a/xsa493-4.21-04.patch b/xsa493-4.21-04.patch deleted file mode 100644 index b59ee74..0000000 --- a/xsa493-4.21-04.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 6af67aeca418bffb807424eb3415fab59e581733 Mon Sep 17 00:00:00 2001 -From: Michal Orzel -Date: Fri, 22 May 2026 09:35:57 +0200 -Subject: xen/arm: Add C1-Premium definitions - -Add processor definitions for C1-Premium. These will be used for errata -detection in subsequent patches. - -These values can be found in the C1-Premium TRM: - - https://developer.arm.com/documentation/109416/0100/ - -... in section A.5.1 ("MIDR_EL1, Main ID Register"). - -Signed-off-by: Michal Orzel -Reviewed-by: Julien Grall - -diff --git a/xen/arch/arm/include/asm/processor.h b/xen/arch/arm/include/asm/processor.h -index 72745cca62bc..25c5762c6706 100644 ---- a/xen/arch/arm/include/asm/processor.h -+++ b/xen/arch/arm/include/asm/processor.h -@@ -106,6 +106,7 @@ - #define ARM_CPU_PART_NEOVERSE_V3 0xD84 - #define ARM_CPU_PART_CORTEX_X925 0xD85 - #define ARM_CPU_PART_C1_ULTRA 0xD8C -+#define ARM_CPU_PART_C1_PREMIUM 0xD90 - - #define MIDR_CORTEX_A12 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A12) - #define MIDR_CORTEX_A17 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A17) -@@ -137,6 +138,7 @@ - #define MIDR_NEOVERSE_V3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_V3) - #define MIDR_CORTEX_X925 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X925) - #define MIDR_C1_ULTRA MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_ULTRA) -+#define MIDR_C1_PREMIUM MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_PREMIUM) - - /* MPIDR Multiprocessor Affinity Register */ - #define _MPIDR_UP (30) diff --git a/xsa494-4.21.patch b/xsa494-4.21.patch deleted file mode 100644 index d52a0f1..0000000 --- a/xsa494-4.21.patch +++ /dev/null @@ -1,404 +0,0 @@ -From 579016a359741044c9076bf0884e1dbab00ab080 Mon Sep 17 00:00:00 2001 -From: Roger Pau Monne -Date: Mon, 16 Mar 2026 11:03:22 +0100 -Subject: [PATCH] x86/mm: accurately track which vCPU page-tables are loaded -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Neither current nor curr_vcpu per-CPU fields accurately track which -page-tables are loaded. There are corner cases when dealing with shadow -paging failures that switch to the idle vCPU page-tables without changing -current or curr_vcpu per-CPU fields. - -Introduce a new per-CPU field that attempts to track which vCPU page-tables -are loaded. Update such tracking when cr3 is changed, and do so in a -region with interrupts disabled, as to avoid handling interrupts with a -mismatch between the vCPU tracking field and the loaded page-tables. - -As a result of this newly more accurate tracking the mapcache override -functionality can be removed: the dom0 PV builder was the only user of it, -and it's updated here to properly signal which vCPU page-tables are loaded -in the calls to switch_cr3_cr4(). - -Note the EFI page-tables have the Xen owned L4 slots copied from the idle -page-tables, so for the effects of the mapcache the EFI page-tables could -use the idle mapcache if it had one. Pass the idle vCPU in the -switch_cr3_cr4() call that switches to the runtime EFI page-tables. - -There are known issues with the use of mapcache in NMI context.  This patch -does not alter the behaviour. - -This is CVE-2026-42488 / XSA-494. - -Fixes: fb0ff49fe9f7 ("x86/shadow: defer releasing of PV's top-level shadow reference") -Signed-off-by: Roger Pau Monné -Acked-by: Andrew Cooper ---- - xen/arch/x86/domain_page.c | 48 ++++++++++++---------------- - xen/arch/x86/flushtlb.c | 5 ++- - xen/arch/x86/include/asm/domain.h | 1 - - xen/arch/x86/include/asm/flushtlb.h | 2 +- - xen/arch/x86/include/asm/processor.h | 3 ++ - xen/arch/x86/mm.c | 4 +-- - xen/arch/x86/pv/dom0_build.c | 12 +++---- - xen/arch/x86/pv/domain.c | 13 ++++++-- - xen/arch/x86/smpboot.c | 1 + - xen/common/efi/common-stub.c | 5 --- - xen/common/efi/runtime.c | 21 +++++------- - xen/include/xen/efi.h | 1 - - 12 files changed, 54 insertions(+), 62 deletions(-) - -diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c -index eac5e3304fb8..72c00194f315 100644 ---- a/xen/arch/x86/domain_page.c -+++ b/xen/arch/x86/domain_page.c -@@ -18,48 +18,40 @@ - #include - #include - --static DEFINE_PER_CPU(struct vcpu *, override); -- - static inline struct vcpu *mapcache_current_vcpu(void) - { -- /* In the common case we use the mapcache of the running VCPU. */ -- struct vcpu *v = this_cpu(override) ?: current; -- -- /* -- * When current isn't properly set up yet, this is equivalent to -- * running in an idle vCPU (callers must check for NULL). -- */ -- if ( !v ) -- return NULL; -+ struct vcpu *v = this_cpu(pgtable_vcpu); -+ struct vcpu *curr = current; - - /* -- * When using efi runtime page tables, we have the equivalent of the idle -- * domain's page tables but current may point at another domain's VCPU. -- * Return NULL as though current is not properly set up yet. -+ * During early boot pgtable_vcpu is not set, callers must handle NULL. -+ * Non-PV domains don't have a mapcache, the directmap covers all physical -+ * address space. - */ -- if ( efi_rs_using_pgtables() ) -+ if ( !v || !is_pv_vcpu(v) ) - return NULL; - - /* -- * If guest_table is NULL, and we are running a paravirtualised guest, -- * then it means we are running on the idle domain's page table and must -- * therefore use its mapcache. -+ * If we are in a lazy context-switch state from a PV vCPU do a full switch -+ * to the idle vCPU now, otherwise an incoming FLUSH_VCPU_STATE IPI would -+ * change the page tables under our feet an invalidate any in-use mapcache -+ * entries. - */ -- if ( unlikely(pagetable_is_null(v->arch.guest_table)) && is_pv_vcpu(v) ) -+ if ( unlikely(this_cpu(curr_vcpu) != curr) ) - { -- /* If we really are idling, perform lazy context switch now. */ -- if ( (v = idle_vcpu[smp_processor_id()]) == current ) -- sync_local_execstate(); -+ ASSERT(curr == idle_vcpu[smp_processor_id()]); -+ sync_local_execstate(); - /* We must now be running on the idle page table. */ - ASSERT(cr3_pa(read_cr3()) == __pa(idle_pg_table)); - } - -- return v; --} -- --void __init mapcache_override_current(struct vcpu *v) --{ -- this_cpu(override) = v; -+ /* -+ * At this point we can guarantee Xen is not in lazy context switch: either -+ * the code above will have synced the state, or an incoming -+ * FLUSH_VCPU_STATE IPI has done so behind our back. Use ACCESS_ONCE to -+ * ensure the compiler never returns the locally cached pgtable_vcpu value. -+ */ -+ return ACCESS_ONCE(this_cpu(pgtable_vcpu)); - } - - #define mapcache_l2_entry(e) ((e) >> PAGETABLE_ORDER) -diff --git a/xen/arch/x86/flushtlb.c b/xen/arch/x86/flushtlb.c -index 09e676c151fa..928bca66b433 100644 ---- a/xen/arch/x86/flushtlb.c -+++ b/xen/arch/x86/flushtlb.c -@@ -111,7 +111,9 @@ static void do_tlb_flush(void) - local_irq_restore(flags); - } - --void switch_cr3_cr4(unsigned long cr3, unsigned long cr4) -+DEFINE_PER_CPU(struct vcpu *, pgtable_vcpu); -+ -+void switch_cr3_cr4(struct vcpu *v, unsigned long cr3, unsigned long cr4) - { - unsigned long flags, old_cr4; - u32 t = 0; -@@ -155,6 +157,7 @@ void switch_cr3_cr4(unsigned long cr3, unsigned long cr4) - if ( (old_cr4 & X86_CR4_PCIDE) > (cr4 & X86_CR4_PCIDE) ) - cr3 |= X86_CR3_NOFLUSH; - write_cr3(cr3); -+ this_cpu(pgtable_vcpu) = v; - - if ( old_cr4 != cr4 ) - write_cr4(cr4); -diff --git a/xen/arch/x86/include/asm/domain.h b/xen/arch/x86/include/asm/domain.h -index 828f42c3e448..10d2b9fe2546 100644 ---- a/xen/arch/x86/include/asm/domain.h -+++ b/xen/arch/x86/include/asm/domain.h -@@ -75,7 +75,6 @@ struct mapcache_domain { - - int mapcache_domain_init(struct domain *d); - int mapcache_vcpu_init(struct vcpu *v); --void mapcache_override_current(struct vcpu *v); - - /* x86/64: toggle guest between kernel and user modes. */ - void toggle_guest_mode(struct vcpu *v); -diff --git a/xen/arch/x86/include/asm/flushtlb.h b/xen/arch/x86/include/asm/flushtlb.h -index 7bcbca2b7f31..345677eb72ae 100644 ---- a/xen/arch/x86/include/asm/flushtlb.h -+++ b/xen/arch/x86/include/asm/flushtlb.h -@@ -104,7 +104,7 @@ static inline void invlpg(const void *p) - } - - /* Write pagetable base and implicitly tick the tlbflush clock. */ --void switch_cr3_cr4(unsigned long cr3, unsigned long cr4); -+void switch_cr3_cr4(struct vcpu *v, unsigned long cr3, unsigned long cr4); - - /* flush_* flag fields: */ - /* -diff --git a/xen/arch/x86/include/asm/processor.h b/xen/arch/x86/include/asm/processor.h -index 2e087c625770..d2cacdfedb74 100644 ---- a/xen/arch/x86/include/asm/processor.h -+++ b/xen/arch/x86/include/asm/processor.h -@@ -328,6 +328,9 @@ DECLARE_PER_CPU(struct tss_page, tss_page); - - DECLARE_PER_CPU(root_pgentry_t *, root_pgt); - -+/* vCPU of the currently loaded page-tables. */ -+DECLARE_PER_CPU(struct vcpu *, pgtable_vcpu); -+ - extern void write_ptbase(struct vcpu *v); - - /* PAUSE (encoding: REP NOP) is a good thing to insert into busy-wait loops. */ -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 2b23bf2e7a75..d02c9862d387 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -535,7 +535,7 @@ void write_ptbase(struct vcpu *v) - cpu_info->pv_cr3 = __pa(this_cpu(root_pgt)); - if ( new_cr4 & X86_CR4_PCIDE ) - cpu_info->pv_cr3 |= get_pcid_bits(v, true); -- switch_cr3_cr4(v->arch.cr3, new_cr4); -+ switch_cr3_cr4(v, v->arch.cr3, new_cr4); - } - else - { -@@ -543,7 +543,7 @@ void write_ptbase(struct vcpu *v) - cpu_info->use_pv_cr3 = false; - cpu_info->xen_cr3 = 0; - /* switch_cr3_cr4() serializes. */ -- switch_cr3_cr4(v->arch.cr3, new_cr4); -+ switch_cr3_cr4(v, v->arch.cr3, new_cr4); - cpu_info->pv_cr3 = 0; - } - } -diff --git a/xen/arch/x86/pv/dom0_build.c b/xen/arch/x86/pv/dom0_build.c -index 37729091dfaa..42bc530c0f0d 100644 ---- a/xen/arch/x86/pv/dom0_build.c -+++ b/xen/arch/x86/pv/dom0_build.c -@@ -828,8 +828,7 @@ static int __init dom0_construct(const struct boot_domain *bd) - update_cr3(v); - - /* We run on dom0's page tables for the final part of the build process. */ -- switch_cr3_cr4(cr3_pa(v->arch.cr3), read_cr4()); -- mapcache_override_current(v); -+ switch_cr3_cr4(v, cr3_pa(v->arch.cr3), read_cr4()); - - /* Copy the OS image and free temporary buffer. */ - elf.dest_base = (void*)vkern_start; -@@ -838,8 +837,7 @@ static int __init dom0_construct(const struct boot_domain *bd) - rc = elf_load_binary(&elf); - if ( rc < 0 ) - { -- mapcache_override_current(NULL); -- switch_cr3_cr4(current->arch.cr3, read_cr4()); -+ switch_cr3_cr4(current, current->arch.cr3, read_cr4()); - printk("Failed to load the kernel binary\n"); - goto out; - } -@@ -850,8 +848,7 @@ static int __init dom0_construct(const struct boot_domain *bd) - if ( (parms.virt_hypercall < v_start) || - (parms.virt_hypercall >= v_end) ) - { -- mapcache_override_current(NULL); -- switch_cr3_cr4(current->arch.cr3, read_cr4()); -+ switch_cr3_cr4(current, current->arch.cr3, read_cr4()); - printk("Invalid HYPERCALL_PAGE field in ELF notes.\n"); - return -EINVAL; - } -@@ -992,8 +989,7 @@ static int __init dom0_construct(const struct boot_domain *bd) - #endif - - /* Return to idle domain's page tables. */ -- mapcache_override_current(NULL); -- switch_cr3_cr4(current->arch.cr3, read_cr4()); -+ switch_cr3_cr4(current, current->arch.cr3, read_cr4()); - - update_domain_wallclock_time(d); - -diff --git a/xen/arch/x86/pv/domain.c b/xen/arch/x86/pv/domain.c -index ef4f442e7332..d9e52f5f88f3 100644 ---- a/xen/arch/x86/pv/domain.c -+++ b/xen/arch/x86/pv/domain.c -@@ -451,6 +451,8 @@ static void _toggle_guest_pt(struct vcpu *v) - pagetable_t old_shadow; - unsigned long cr3; - -+ ASSERT(local_irq_is_enabled()); -+ - v->arch.flags ^= TF_kernel_mode; - guest_update = v->arch.flags & TF_kernel_mode; - old_shadow = update_cr3(v); -@@ -473,15 +475,22 @@ static void _toggle_guest_pt(struct vcpu *v) - { - cr3 &= ~X86_CR3_NOFLUSH; - -+ local_irq_disable(); - if ( unlikely(mfn_eq(pagetable_get_mfn(old_shadow), - maddr_to_mfn(cr3))) ) - { -- cr3 = idle_vcpu[v->processor]->arch.cr3; - /* Also suppress runstate/time area updates below. */ - guest_update = false; -+ -+ cr3 = idle_vcpu[v->processor]->arch.cr3; -+ this_cpu(pgtable_vcpu) = idle_vcpu[v->processor]; - } -+ -+ write_cr3(cr3); -+ local_irq_enable(); - } -- write_cr3(cr3); -+ else -+ write_cr3(cr3); - - if ( !pagetable_is_null(old_shadow) ) - shadow_put_top_level(v->domain, old_shadow); -diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c -index 27628800a821..b37feab3bef4 100644 ---- a/xen/arch/x86/smpboot.c -+++ b/xen/arch/x86/smpboot.c -@@ -1063,6 +1063,7 @@ static int cpu_smpboot_alloc(unsigned int cpu) - - info->current_vcpu = idle_vcpu[cpu]; /* set_current() */ - per_cpu(curr_vcpu, cpu) = idle_vcpu[cpu]; -+ per_cpu(pgtable_vcpu, cpu) = idle_vcpu[cpu]; - - gdt = per_cpu(gdt, cpu) ?: alloc_xenheap_pages(0, memflags); - if ( gdt == NULL ) -diff --git a/xen/common/efi/common-stub.c b/xen/common/efi/common-stub.c -index 77f138a6c574..7b12005bea3f 100644 ---- a/xen/common/efi/common-stub.c -+++ b/xen/common/efi/common-stub.c -@@ -7,11 +7,6 @@ bool efi_enabled(unsigned int feature) - return false; - } - --bool efi_rs_using_pgtables(void) --{ -- return false; --} -- - unsigned long efi_get_time(void) - { - BUG(); -diff --git a/xen/common/efi/runtime.c b/xen/common/efi/runtime.c -index 30d649ca5c1b..feb09acf754c 100644 ---- a/xen/common/efi/runtime.c -+++ b/xen/common/efi/runtime.c -@@ -49,7 +49,6 @@ const CHAR16 *__read_mostly efi_fw_vendor; - const EFI_RUNTIME_SERVICES *__read_mostly efi_rs; - #ifndef CONFIG_ARM /* TODO - disabled until implemented on ARM */ - static DEFINE_SPINLOCK(efi_rs_lock); --static unsigned int efi_rs_on_cpu = NR_CPUS; - #endif - - UINTN __read_mostly efi_memmap_size; -@@ -92,6 +91,11 @@ struct efi_rs_state efi_rs_enter(void) - if ( mfn_eq(efi_l4_mfn, INVALID_MFN) ) - return state; - -+ /* -+ * If in lazy idle context switch state sync now to avoid an incoming -+ * FLUSH_VCPU_STATE IPI changing the loaded page-tables. -+ */ -+ sync_local_execstate(); - state.cr3 = read_cr3(); - save_fpu_enable(); - asm volatile ( "fnclex; fldcw %0" :: "m" (fcw) ); -@@ -99,8 +103,6 @@ struct efi_rs_state efi_rs_enter(void) - - spin_lock(&efi_rs_lock); - -- efi_rs_on_cpu = smp_processor_id(); -- - /* prevent fixup_page_fault() from doing anything */ - irq_enter(); - -@@ -115,7 +117,8 @@ struct efi_rs_state efi_rs_enter(void) - lgdt(&gdt_desc); - } - -- switch_cr3_cr4(mfn_to_maddr(efi_l4_mfn), read_cr4()); -+ switch_cr3_cr4(idle_vcpu[smp_processor_id()], mfn_to_maddr(efi_l4_mfn), -+ read_cr4()); - - /* - * At the time of writing (2022), no UEFI firwmare is CET-IBT compatible. -@@ -143,7 +146,7 @@ void efi_rs_leave(struct efi_rs_state *state) - if ( state->msr_s_cet ) - wrmsrl(MSR_S_CET, state->msr_s_cet); - -- switch_cr3_cr4(state->cr3, read_cr4()); -+ switch_cr3_cr4(curr, state->cr3, read_cr4()); - if ( is_pv_vcpu(curr) && !is_idle_vcpu(curr) ) - { - struct desc_ptr gdt_desc = { -@@ -154,18 +157,10 @@ void efi_rs_leave(struct efi_rs_state *state) - lgdt(&gdt_desc); - } - irq_exit(); -- efi_rs_on_cpu = NR_CPUS; - spin_unlock(&efi_rs_lock); - vcpu_restore_fpu_nonlazy(curr, true); - } - --bool efi_rs_using_pgtables(void) --{ -- return !mfn_eq(efi_l4_mfn, INVALID_MFN) && -- (smp_processor_id() == efi_rs_on_cpu) && -- (read_cr3() == mfn_to_maddr(efi_l4_mfn)); --} -- - unsigned long efi_get_time(void) - { - EFI_TIME time; -diff --git a/xen/include/xen/efi.h b/xen/include/xen/efi.h -index 723cb8085270..9953197ee553 100644 ---- a/xen/include/xen/efi.h -+++ b/xen/include/xen/efi.h -@@ -40,7 +40,6 @@ extern bool efi_secure_boot; - - void efi_init_memory(void); - bool efi_boot_mem_unused(unsigned long *start, unsigned long *end); --bool efi_rs_using_pgtables(void); - unsigned long efi_get_time(void); - void efi_halt_system(void); - void efi_reset_system(bool warm); --- -2.53.0 - diff --git a/zstd-dom0.patch b/zstd-dom0.patch new file mode 100644 index 0000000..57b7f76 --- /dev/null +++ b/zstd-dom0.patch @@ -0,0 +1,9214 @@ +diff --git a/xen/common/Makefile b/xen/common/Makefile +index d109f279a4..5ba09f04ac 100644 +--- a/xen/common/Makefile ++++ b/xen/common/Makefile +@@ -59,7 +59,7 @@ obj-bin-y += warning.init.o + obj-$(CONFIG_XENOPROF) += xenoprof.o + obj-y += xmalloc_tlsf.o + +-obj-bin-$(CONFIG_X86) += $(foreach n,decompress bunzip2 unxz unlzma lzo unlzo unlz4 earlycpio,$(n).init.o) ++obj-bin-$(CONFIG_X86) += $(foreach n,decompress bunzip2 unxz unlzma lzo unlzo unlz4 unzstd earlycpio,$(n).init.o) + + obj-$(CONFIG_COMPAT) += $(addprefix compat/,domain.o kernel.o memory.o multicall.o xlat.o) + +diff --git a/xen/common/decompress.c b/xen/common/decompress.c +index 9d6e0c4ab0..0da27b0ab6 100644 +--- a/xen/common/decompress.c ++++ b/xen/common/decompress.c +@@ -31,5 +31,8 @@ int __init decompress(void *inbuf, unsigned int len, void *outbuf) + if ( len >= 2 && !memcmp(inbuf, "\x02\x21", 2) ) + return unlz4(inbuf, len, NULL, NULL, outbuf, NULL, error); + ++ if ( len >= 4 && !memcmp(inbuf, "\050\265\057\375", 4) ) ++ return unzstd(inbuf, len, NULL, NULL, outbuf, NULL, error); ++ + return 1; + } +diff --git a/xen/common/unzstd.c b/xen/common/unzstd.c +new file mode 100644 +index 0000000000..a2c382fddc +--- /dev/null ++++ b/xen/common/unzstd.c +@@ -0,0 +1,332 @@ ++/* ++ * Important notes about in-place decompression ++ * ++ * At least on x86, the kernel is decompressed in place: the compressed data ++ * is placed to the end of the output buffer, and the decompressor overwrites ++ * most of the compressed data. There must be enough safety margin to ++ * guarantee that the write position is always behind the read position. ++ * ++ * The safety margin for ZSTD with a 128 KB block size is calculated below. ++ * Note that the margin with ZSTD is bigger than with GZIP or XZ! ++ * ++ * The worst case for in-place decompression is that the beginning of ++ * the file is compressed extremely well, and the rest of the file is ++ * uncompressible. Thus, we must look for worst-case expansion when the ++ * compressor is encoding uncompressible data. ++ * ++ * The structure of the .zst file in case of a compresed kernel is as follows. ++ * Maximum sizes (as bytes) of the fields are in parenthesis. ++ * ++ * Frame Header: (18) ++ * Blocks: (N) ++ * Checksum: (4) ++ * ++ * The frame header and checksum overhead is at most 22 bytes. ++ * ++ * ZSTD stores the data in blocks. Each block has a header whose size is ++ * a 3 bytes. After the block header, there is up to 128 KB of payload. ++ * The maximum uncompressed size of the payload is 128 KB. The minimum ++ * uncompressed size of the payload is never less than the payload size ++ * (excluding the block header). ++ * ++ * The assumption, that the uncompressed size of the payload is never ++ * smaller than the payload itself, is valid only when talking about ++ * the payload as a whole. It is possible that the payload has parts where ++ * the decompressor consumes more input than it produces output. Calculating ++ * the worst case for this would be tricky. Instead of trying to do that, ++ * let's simply make sure that the decompressor never overwrites any bytes ++ * of the payload which it is currently reading. ++ * ++ * Now we have enough information to calculate the safety margin. We need ++ * - 22 bytes for the .zst file format headers; ++ * - 3 bytes per every 128 KiB of uncompressed size (one block header per ++ * block); and ++ * - 128 KiB (biggest possible zstd block size) to make sure that the ++ * decompressor never overwrites anything from the block it is currently ++ * reading. ++ * ++ * We get the following formula: ++ * ++ * safety_margin = 22 + uncompressed_size * 3 / 131072 + 131072 ++ * <= 22 + (uncompressed_size >> 15) + 131072 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * Preboot environments #include "path/to/decompress_unzstd.c". ++ * All of the source files we depend on must be #included. ++ * zstd's only source dependeny is xxhash, which has no source ++ * dependencies. ++ * ++ * When UNZSTD_PREBOOT is defined we declare __decompress(), which is ++ * used for kernel decompression, instead of unzstd(). ++ * ++ * Define __DISABLE_EXPORTS in preboot environments to prevent symbols ++ * from xxhash and zstd from being exported by the EXPORT_SYMBOL macro. ++ */ ++ ++#include "decompress.h" ++#include "xxhash.c" ++#include "zstd/entropy_common.c" ++#include "zstd/fse_decompress.c" ++#include "zstd/huf_decompress.c" ++#include "zstd/zstd_common.c" ++#include "zstd/decompress.c" ++ ++#include ++ ++/* 128MB is the maximum window size supported by zstd. */ ++#define ZSTD_WINDOWSIZE_MAX (1 << ZSTD_WINDOWLOG_MAX) ++/* ++ * Size of the input and output buffers in multi-call mode. ++ * Pick a larger size because it isn't used during kernel decompression, ++ * since that is single pass, and we have to allocate a large buffer for ++ * zstd's window anyway. The larger size speeds up initramfs decompression. ++ */ ++#define ZSTD_IOBUF_SIZE (1 << 17) ++ ++static int INIT handle_zstd_error(size_t ret, void (*error)(const char *x)) ++{ ++ const int err = ZSTD_getErrorCode(ret); ++ ++ if (!ZSTD_isError(ret)) ++ return 0; ++ ++ switch (err) { ++ case ZSTD_error_memory_allocation: ++ error("ZSTD decompressor ran out of memory"); ++ break; ++ case ZSTD_error_prefix_unknown: ++ error("Input is not in the ZSTD format (wrong magic bytes)"); ++ break; ++ case ZSTD_error_dstSize_tooSmall: ++ case ZSTD_error_corruption_detected: ++ case ZSTD_error_checksum_wrong: ++ error("ZSTD-compressed data is corrupt"); ++ break; ++ default: ++ error("ZSTD-compressed data is probably corrupt"); ++ break; ++ } ++ return -1; ++} ++ ++/* ++ * Handle the case where we have the entire input and output in one segment. ++ * We can allocate less memory (no circular buffer for the sliding window), ++ * and avoid some memcpy() calls. ++ */ ++static int INIT decompress_single(const u8 *in_buf, unsigned int in_len, u8 *out_buf, ++ long out_len, unsigned int *in_pos, ++ void (*error)(const char *x)) ++{ ++ const size_t wksp_size = ZSTD_DCtxWorkspaceBound(); ++ void *wksp = large_malloc(wksp_size); ++ ZSTD_DCtx *dctx = ZSTD_initDCtx(wksp, wksp_size); ++ int err; ++ size_t ret; ++ ++ if (dctx == NULL) { ++ error("Out of memory while allocating ZSTD_DCtx"); ++ err = -1; ++ goto out; ++ } ++ /* ++ * Find out how large the frame actually is, there may be junk at ++ * the end of the frame that ZSTD_decompressDCtx() can't handle. ++ */ ++ ret = ZSTD_findFrameCompressedSize(in_buf, in_len); ++ err = handle_zstd_error(ret, error); ++ if (err) ++ goto out; ++ in_len = (long)ret; ++ ++ ret = ZSTD_decompressDCtx(dctx, out_buf, out_len, in_buf, in_len); ++ err = handle_zstd_error(ret, error); ++ if (err) ++ goto out; ++ ++ if (in_pos != NULL) ++ *in_pos = in_len; ++ ++ err = 0; ++out: ++ if (wksp != NULL) ++ large_free(wksp); ++ return err; ++} ++ ++static int INIT __unzstd(unsigned char *in_buf, unsigned int in_len, ++ int (*fill)(void*, unsigned int), ++ int (*flush)(void*, unsigned int), ++ unsigned char *out_buf, long out_len, ++ unsigned int *in_pos, ++ void (*error)(const char *x)) ++{ ++ ZSTD_inBuffer in; ++ ZSTD_outBuffer out; ++ ZSTD_frameParams params; ++ void *in_allocated = NULL; ++ void *out_allocated = NULL; ++ void *wksp = NULL; ++ size_t wksp_size; ++ ZSTD_DStream *dstream; ++ int err; ++ size_t ret; ++ ++ if (out_len == 0) ++ out_len = INT_MAX; /* no limit */ ++ ++ if (fill == NULL && flush == NULL) ++ /* ++ * We can decompress faster and with less memory when we have a ++ * single chunk. ++ */ ++ return decompress_single(in_buf, in_len, out_buf, out_len, ++ in_pos, error); ++ ++ /* ++ * If in_buf is not provided, we must be using fill(), so allocate ++ * a large enough buffer. If it is provided, it must be at least ++ * ZSTD_IOBUF_SIZE large. ++ */ ++ if (in_buf == NULL) { ++ in_allocated = large_malloc(ZSTD_IOBUF_SIZE); ++ if (in_allocated == NULL) { ++ error("Out of memory while allocating input buffer"); ++ err = -1; ++ goto out; ++ } ++ in_buf = in_allocated; ++ in_len = 0; ++ } ++ /* Read the first chunk, since we need to decode the frame header. */ ++ if (fill != NULL) ++ in_len = fill(in_buf, ZSTD_IOBUF_SIZE); ++ if (in_len < 0) { ++ error("ZSTD-compressed data is truncated"); ++ err = -1; ++ goto out; ++ } ++ /* Set the first non-empty input buffer. */ ++ in.src = in_buf; ++ in.pos = 0; ++ in.size = in_len; ++ /* Allocate the output buffer if we are using flush(). */ ++ if (flush != NULL) { ++ out_allocated = large_malloc(ZSTD_IOBUF_SIZE); ++ if (out_allocated == NULL) { ++ error("Out of memory while allocating output buffer"); ++ err = -1; ++ goto out; ++ } ++ out_buf = out_allocated; ++ out_len = ZSTD_IOBUF_SIZE; ++ } ++ /* Set the output buffer. */ ++ out.dst = out_buf; ++ out.pos = 0; ++ out.size = out_len; ++ ++ /* ++ * We need to know the window size to allocate the ZSTD_DStream. ++ * Since we are streaming, we need to allocate a buffer for the sliding ++ * window. The window size varies from 1 KB to ZSTD_WINDOWSIZE_MAX ++ * (8 MB), so it is important to use the actual value so as not to ++ * waste memory when it is smaller. ++ */ ++ ret = ZSTD_getFrameParams(¶ms, in.src, in.size); ++ err = handle_zstd_error(ret, error); ++ if (err) ++ goto out; ++ if (ret != 0) { ++ error("ZSTD-compressed data has an incomplete frame header"); ++ err = -1; ++ goto out; ++ } ++ if (params.windowSize > ZSTD_WINDOWSIZE_MAX) { ++ error("ZSTD-compressed data has too large a window size"); ++ err = -1; ++ goto out; ++ } ++ ++ /* ++ * Allocate the ZSTD_DStream now that we know how much memory is ++ * required. ++ */ ++ wksp_size = ZSTD_DStreamWorkspaceBound(params.windowSize); ++ wksp = large_malloc(wksp_size); ++ dstream = ZSTD_initDStream(params.windowSize, wksp, wksp_size); ++ if (dstream == NULL) { ++ error("Out of memory while allocating ZSTD_DStream"); ++ err = -1; ++ goto out; ++ } ++ ++ /* ++ * Decompression loop: ++ * Read more data if necessary (error if no more data can be read). ++ * Call the decompression function, which returns 0 when finished. ++ * Flush any data produced if using flush(). ++ */ ++ if (in_pos != NULL) ++ *in_pos = 0; ++ do { ++ /* ++ * If we need to reload data, either we have fill() and can ++ * try to get more data, or we don't and the input is truncated. ++ */ ++ if (in.pos == in.size) { ++ if (in_pos != NULL) ++ *in_pos += in.pos; ++ in_len = fill ? fill(in_buf, ZSTD_IOBUF_SIZE) : -1; ++ if (in_len < 0) { ++ error("ZSTD-compressed data is truncated"); ++ err = -1; ++ goto out; ++ } ++ in.pos = 0; ++ in.size = in_len; ++ } ++ /* Returns zero when the frame is complete. */ ++ ret = ZSTD_decompressStream(dstream, &out, &in); ++ err = handle_zstd_error(ret, error); ++ if (err) ++ goto out; ++ /* Flush all of the data produced if using flush(). */ ++ if (flush != NULL && out.pos > 0) { ++ if (out.pos != flush(out.dst, out.pos)) { ++ error("Failed to flush()"); ++ err = -1; ++ goto out; ++ } ++ out.pos = 0; ++ } ++ } while (ret != 0); ++ ++ if (in_pos != NULL) ++ *in_pos += in.pos; ++ ++ err = 0; ++out: ++ if (in_allocated != NULL) ++ large_free(in_allocated); ++ if (out_allocated != NULL) ++ large_free(out_allocated); ++ if (wksp != NULL) ++ large_free(wksp); ++ return err; ++} ++ ++STATIC int INIT unzstd(unsigned char *buf, unsigned int len, ++ int (*fill)(void*, unsigned int), ++ int (*flush)(void*, unsigned int), ++ unsigned char *out_buf, ++ unsigned int *pos, ++ void (*error)(const char *x)) ++{ ++ return __unzstd(buf, len, fill, flush, out_buf, 0, pos, error); ++} +diff --git a/xen/common/xxhash.c b/xen/common/xxhash.c +new file mode 100644 +index 0000000000..3ab3e01859 +--- /dev/null ++++ b/xen/common/xxhash.c +@@ -0,0 +1,484 @@ ++/* ++ * xxHash - Extremely Fast Hash algorithm ++ * Copyright (C) 2012-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at: ++ * - xxHash homepage: https://cyan4973.github.io/xxHash/ ++ * - xxHash source repository: https://github.com/Cyan4973/xxHash ++ */ ++ ++#include ++#include ++#include ++#include "zstd/private.h" ++ ++/*-************************************* ++ * Macros ++ **************************************/ ++#define xxh_rotl32(x, r) ((x << r) | (x >> (32 - r))) ++#define xxh_rotl64(x, r) ((x << r) | (x >> (64 - r))) ++ ++#ifdef __LITTLE_ENDIAN ++# define XXH_CPU_LITTLE_ENDIAN 1 ++#else ++# define XXH_CPU_LITTLE_ENDIAN 0 ++#endif ++ ++/*-************************************* ++ * Constants ++ **************************************/ ++static const uint32_t PRIME32_1 = 2654435761U; ++static const uint32_t PRIME32_2 = 2246822519U; ++static const uint32_t PRIME32_3 = 3266489917U; ++static const uint32_t PRIME32_4 = 668265263U; ++static const uint32_t PRIME32_5 = 374761393U; ++ ++static const uint64_t PRIME64_1 = 11400714785074694791ULL; ++static const uint64_t PRIME64_2 = 14029467366897019727ULL; ++static const uint64_t PRIME64_3 = 1609587929392839161ULL; ++static const uint64_t PRIME64_4 = 9650029242287828579ULL; ++static const uint64_t PRIME64_5 = 2870177450012600261ULL; ++ ++/*-************************** ++ * Utils ++ ***************************/ ++void INIT xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src) ++{ ++ memcpy(dst, src, sizeof(*dst)); ++} ++ ++void INIT xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src) ++{ ++ memcpy(dst, src, sizeof(*dst)); ++} ++ ++/*-*************************** ++ * Simple Hash Functions ++ ****************************/ ++static uint32_t INIT xxh32_round(uint32_t seed, const uint32_t input) ++{ ++ seed += input * PRIME32_2; ++ seed = xxh_rotl32(seed, 13); ++ seed *= PRIME32_1; ++ return seed; ++} ++ ++uint32_t INIT xxh32(const void *input, const size_t len, const uint32_t seed) ++{ ++ const uint8_t *p = (const uint8_t *)input; ++ const uint8_t *b_end = p + len; ++ uint32_t h32; ++ ++ if (len >= 16) { ++ const uint8_t *const limit = b_end - 16; ++ uint32_t v1 = seed + PRIME32_1 + PRIME32_2; ++ uint32_t v2 = seed + PRIME32_2; ++ uint32_t v3 = seed + 0; ++ uint32_t v4 = seed - PRIME32_1; ++ ++ do { ++ v1 = xxh32_round(v1, get_unaligned_le32(p)); ++ p += 4; ++ v2 = xxh32_round(v2, get_unaligned_le32(p)); ++ p += 4; ++ v3 = xxh32_round(v3, get_unaligned_le32(p)); ++ p += 4; ++ v4 = xxh32_round(v4, get_unaligned_le32(p)); ++ p += 4; ++ } while (p <= limit); ++ ++ h32 = xxh_rotl32(v1, 1) + xxh_rotl32(v2, 7) + ++ xxh_rotl32(v3, 12) + xxh_rotl32(v4, 18); ++ } else { ++ h32 = seed + PRIME32_5; ++ } ++ ++ h32 += (uint32_t)len; ++ ++ while (p + 4 <= b_end) { ++ h32 += get_unaligned_le32(p) * PRIME32_3; ++ h32 = xxh_rotl32(h32, 17) * PRIME32_4; ++ p += 4; ++ } ++ ++ while (p < b_end) { ++ h32 += (*p) * PRIME32_5; ++ h32 = xxh_rotl32(h32, 11) * PRIME32_1; ++ p++; ++ } ++ ++ h32 ^= h32 >> 15; ++ h32 *= PRIME32_2; ++ h32 ^= h32 >> 13; ++ h32 *= PRIME32_3; ++ h32 ^= h32 >> 16; ++ ++ return h32; ++} ++ ++static uint64_t INIT xxh64_round(uint64_t acc, const uint64_t input) ++{ ++ acc += input * PRIME64_2; ++ acc = xxh_rotl64(acc, 31); ++ acc *= PRIME64_1; ++ return acc; ++} ++ ++static uint64_t INIT xxh64_merge_round(uint64_t acc, uint64_t val) ++{ ++ val = xxh64_round(0, val); ++ acc ^= val; ++ acc = acc * PRIME64_1 + PRIME64_4; ++ return acc; ++} ++ ++uint64_t INIT xxh64(const void *input, const size_t len, const uint64_t seed) ++{ ++ const uint8_t *p = (const uint8_t *)input; ++ const uint8_t *const b_end = p + len; ++ uint64_t h64; ++ ++ if (len >= 32) { ++ const uint8_t *const limit = b_end - 32; ++ uint64_t v1 = seed + PRIME64_1 + PRIME64_2; ++ uint64_t v2 = seed + PRIME64_2; ++ uint64_t v3 = seed + 0; ++ uint64_t v4 = seed - PRIME64_1; ++ ++ do { ++ v1 = xxh64_round(v1, get_unaligned_le64(p)); ++ p += 8; ++ v2 = xxh64_round(v2, get_unaligned_le64(p)); ++ p += 8; ++ v3 = xxh64_round(v3, get_unaligned_le64(p)); ++ p += 8; ++ v4 = xxh64_round(v4, get_unaligned_le64(p)); ++ p += 8; ++ } while (p <= limit); ++ ++ h64 = xxh_rotl64(v1, 1) + xxh_rotl64(v2, 7) + ++ xxh_rotl64(v3, 12) + xxh_rotl64(v4, 18); ++ h64 = xxh64_merge_round(h64, v1); ++ h64 = xxh64_merge_round(h64, v2); ++ h64 = xxh64_merge_round(h64, v3); ++ h64 = xxh64_merge_round(h64, v4); ++ ++ } else { ++ h64 = seed + PRIME64_5; ++ } ++ ++ h64 += (uint64_t)len; ++ ++ while (p + 8 <= b_end) { ++ const uint64_t k1 = xxh64_round(0, get_unaligned_le64(p)); ++ ++ h64 ^= k1; ++ h64 = xxh_rotl64(h64, 27) * PRIME64_1 + PRIME64_4; ++ p += 8; ++ } ++ ++ if (p + 4 <= b_end) { ++ h64 ^= (uint64_t)(get_unaligned_le32(p)) * PRIME64_1; ++ h64 = xxh_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; ++ p += 4; ++ } ++ ++ while (p < b_end) { ++ h64 ^= (*p) * PRIME64_5; ++ h64 = xxh_rotl64(h64, 11) * PRIME64_1; ++ p++; ++ } ++ ++ h64 ^= h64 >> 33; ++ h64 *= PRIME64_2; ++ h64 ^= h64 >> 29; ++ h64 *= PRIME64_3; ++ h64 ^= h64 >> 32; ++ ++ return h64; ++} ++ ++/*-************************************************** ++ * Advanced Hash Functions ++ ***************************************************/ ++void INIT xxh32_reset(struct xxh32_state *statePtr, const uint32_t seed) ++{ ++ /* use a local state for memcpy() to avoid strict-aliasing warnings */ ++ struct xxh32_state state; ++ ++ memset(&state, 0, sizeof(state)); ++ state.v1 = seed + PRIME32_1 + PRIME32_2; ++ state.v2 = seed + PRIME32_2; ++ state.v3 = seed + 0; ++ state.v4 = seed - PRIME32_1; ++ memcpy(statePtr, &state, sizeof(state)); ++} ++ ++void INIT xxh64_reset(struct xxh64_state *statePtr, const uint64_t seed) ++{ ++ /* use a local state for memcpy() to avoid strict-aliasing warnings */ ++ struct xxh64_state state; ++ ++ memset(&state, 0, sizeof(state)); ++ state.v1 = seed + PRIME64_1 + PRIME64_2; ++ state.v2 = seed + PRIME64_2; ++ state.v3 = seed + 0; ++ state.v4 = seed - PRIME64_1; ++ memcpy(statePtr, &state, sizeof(state)); ++} ++ ++int INIT xxh32_update(struct xxh32_state *state, const void *input, const size_t len) ++{ ++ const uint8_t *p = (const uint8_t *)input; ++ const uint8_t *const b_end = p + len; ++ ++ if (input == NULL) ++ return -EINVAL; ++ ++ state->total_len_32 += (uint32_t)len; ++ state->large_len |= (len >= 16) | (state->total_len_32 >= 16); ++ ++ if (state->memsize + len < 16) { /* fill in tmp buffer */ ++ memcpy((uint8_t *)(state->mem32) + state->memsize, input, len); ++ state->memsize += (uint32_t)len; ++ return 0; ++ } ++ ++ if (state->memsize) { /* some data left from previous update */ ++ const uint32_t *p32 = state->mem32; ++ ++ memcpy((uint8_t *)(state->mem32) + state->memsize, input, ++ 16 - state->memsize); ++ ++ state->v1 = xxh32_round(state->v1, get_unaligned_le32(p32)); ++ p32++; ++ state->v2 = xxh32_round(state->v2, get_unaligned_le32(p32)); ++ p32++; ++ state->v3 = xxh32_round(state->v3, get_unaligned_le32(p32)); ++ p32++; ++ state->v4 = xxh32_round(state->v4, get_unaligned_le32(p32)); ++ p32++; ++ ++ p += 16-state->memsize; ++ state->memsize = 0; ++ } ++ ++ if (p <= b_end - 16) { ++ const uint8_t *const limit = b_end - 16; ++ uint32_t v1 = state->v1; ++ uint32_t v2 = state->v2; ++ uint32_t v3 = state->v3; ++ uint32_t v4 = state->v4; ++ ++ do { ++ v1 = xxh32_round(v1, get_unaligned_le32(p)); ++ p += 4; ++ v2 = xxh32_round(v2, get_unaligned_le32(p)); ++ p += 4; ++ v3 = xxh32_round(v3, get_unaligned_le32(p)); ++ p += 4; ++ v4 = xxh32_round(v4, get_unaligned_le32(p)); ++ p += 4; ++ } while (p <= limit); ++ ++ state->v1 = v1; ++ state->v2 = v2; ++ state->v3 = v3; ++ state->v4 = v4; ++ } ++ ++ if (p < b_end) { ++ memcpy(state->mem32, p, (size_t)(b_end-p)); ++ state->memsize = (uint32_t)(b_end-p); ++ } ++ ++ return 0; ++} ++ ++uint32_t INIT xxh32_digest(const struct xxh32_state *state) ++{ ++ const uint8_t *p = (const uint8_t *)state->mem32; ++ const uint8_t *const b_end = (const uint8_t *)(state->mem32) + ++ state->memsize; ++ uint32_t h32; ++ ++ if (state->large_len) { ++ h32 = xxh_rotl32(state->v1, 1) + xxh_rotl32(state->v2, 7) + ++ xxh_rotl32(state->v3, 12) + xxh_rotl32(state->v4, 18); ++ } else { ++ h32 = state->v3 /* == seed */ + PRIME32_5; ++ } ++ ++ h32 += state->total_len_32; ++ ++ while (p + 4 <= b_end) { ++ h32 += get_unaligned_le32(p) * PRIME32_3; ++ h32 = xxh_rotl32(h32, 17) * PRIME32_4; ++ p += 4; ++ } ++ ++ while (p < b_end) { ++ h32 += (*p) * PRIME32_5; ++ h32 = xxh_rotl32(h32, 11) * PRIME32_1; ++ p++; ++ } ++ ++ h32 ^= h32 >> 15; ++ h32 *= PRIME32_2; ++ h32 ^= h32 >> 13; ++ h32 *= PRIME32_3; ++ h32 ^= h32 >> 16; ++ ++ return h32; ++} ++ ++int INIT xxh64_update(struct xxh64_state *state, const void *input, const size_t len) ++{ ++ const uint8_t *p = (const uint8_t *)input; ++ const uint8_t *const b_end = p + len; ++ ++ if (input == NULL) ++ return -EINVAL; ++ ++ state->total_len += len; ++ ++ if (state->memsize + len < 32) { /* fill in tmp buffer */ ++ memcpy(((uint8_t *)state->mem64) + state->memsize, input, len); ++ state->memsize += (uint32_t)len; ++ return 0; ++ } ++ ++ if (state->memsize) { /* tmp buffer is full */ ++ uint64_t *p64 = state->mem64; ++ ++ memcpy(((uint8_t *)p64) + state->memsize, input, ++ 32 - state->memsize); ++ ++ state->v1 = xxh64_round(state->v1, get_unaligned_le64(p64)); ++ p64++; ++ state->v2 = xxh64_round(state->v2, get_unaligned_le64(p64)); ++ p64++; ++ state->v3 = xxh64_round(state->v3, get_unaligned_le64(p64)); ++ p64++; ++ state->v4 = xxh64_round(state->v4, get_unaligned_le64(p64)); ++ ++ p += 32 - state->memsize; ++ state->memsize = 0; ++ } ++ ++ if (p + 32 <= b_end) { ++ const uint8_t *const limit = b_end - 32; ++ uint64_t v1 = state->v1; ++ uint64_t v2 = state->v2; ++ uint64_t v3 = state->v3; ++ uint64_t v4 = state->v4; ++ ++ do { ++ v1 = xxh64_round(v1, get_unaligned_le64(p)); ++ p += 8; ++ v2 = xxh64_round(v2, get_unaligned_le64(p)); ++ p += 8; ++ v3 = xxh64_round(v3, get_unaligned_le64(p)); ++ p += 8; ++ v4 = xxh64_round(v4, get_unaligned_le64(p)); ++ p += 8; ++ } while (p <= limit); ++ ++ state->v1 = v1; ++ state->v2 = v2; ++ state->v3 = v3; ++ state->v4 = v4; ++ } ++ ++ if (p < b_end) { ++ memcpy(state->mem64, p, (size_t)(b_end-p)); ++ state->memsize = (uint32_t)(b_end - p); ++ } ++ ++ return 0; ++} ++ ++uint64_t INIT xxh64_digest(const struct xxh64_state *state) ++{ ++ const uint8_t *p = (const uint8_t *)state->mem64; ++ const uint8_t *const b_end = (const uint8_t *)state->mem64 + ++ state->memsize; ++ uint64_t h64; ++ ++ if (state->total_len >= 32) { ++ const uint64_t v1 = state->v1; ++ const uint64_t v2 = state->v2; ++ const uint64_t v3 = state->v3; ++ const uint64_t v4 = state->v4; ++ ++ h64 = xxh_rotl64(v1, 1) + xxh_rotl64(v2, 7) + ++ xxh_rotl64(v3, 12) + xxh_rotl64(v4, 18); ++ h64 = xxh64_merge_round(h64, v1); ++ h64 = xxh64_merge_round(h64, v2); ++ h64 = xxh64_merge_round(h64, v3); ++ h64 = xxh64_merge_round(h64, v4); ++ } else { ++ h64 = state->v3 + PRIME64_5; ++ } ++ ++ h64 += (uint64_t)state->total_len; ++ ++ while (p + 8 <= b_end) { ++ const uint64_t k1 = xxh64_round(0, get_unaligned_le64(p)); ++ ++ h64 ^= k1; ++ h64 = xxh_rotl64(h64, 27) * PRIME64_1 + PRIME64_4; ++ p += 8; ++ } ++ ++ if (p + 4 <= b_end) { ++ h64 ^= (uint64_t)(get_unaligned_le32(p)) * PRIME64_1; ++ h64 = xxh_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; ++ p += 4; ++ } ++ ++ while (p < b_end) { ++ h64 ^= (*p) * PRIME64_5; ++ h64 = xxh_rotl64(h64, 11) * PRIME64_1; ++ p++; ++ } ++ ++ h64 ^= h64 >> 33; ++ h64 *= PRIME64_2; ++ h64 ^= h64 >> 29; ++ h64 *= PRIME64_3; ++ h64 ^= h64 >> 32; ++ ++ return h64; ++} +diff --git a/xen/common/zstd/bitstream.h b/xen/common/zstd/bitstream.h +new file mode 100644 +index 0000000000..3a49784d5c +--- /dev/null ++++ b/xen/common/zstd/bitstream.h +@@ -0,0 +1,379 @@ ++/* ++ * bitstream ++ * Part of FSE library ++ * header file (to include) ++ * Copyright (C) 2013-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++#ifndef BITSTREAM_H_MODULE ++#define BITSTREAM_H_MODULE ++ ++/* ++* This API consists of small unitary functions, which must be inlined for best performance. ++* Since link-time-optimization is not available for all compilers, ++* these functions are defined into a .h to be included. ++*/ ++ ++/*-**************************************** ++* Dependencies ++******************************************/ ++#include "error_private.h" /* error codes and messages */ ++#include "mem.h" /* unaligned access routines */ ++ ++/*========================================= ++* Target specific ++=========================================*/ ++#define STREAM_ACCUMULATOR_MIN_32 25 ++#define STREAM_ACCUMULATOR_MIN_64 57 ++#define STREAM_ACCUMULATOR_MIN ((U32)(ZSTD_32bits() ? STREAM_ACCUMULATOR_MIN_32 : STREAM_ACCUMULATOR_MIN_64)) ++ ++/*-****************************************** ++* bitStream encoding API (write forward) ++********************************************/ ++/* bitStream can mix input from multiple sources. ++* A critical property of these streams is that they encode and decode in **reverse** direction. ++* So the first bit sequence you add will be the last to be read, like a LIFO stack. ++*/ ++typedef struct { ++ size_t bitContainer; ++ int bitPos; ++ char *startPtr; ++ char *ptr; ++ char *endPtr; ++} BIT_CStream_t; ++ ++ZSTD_STATIC size_t BIT_initCStream(BIT_CStream_t *bitC, void *dstBuffer, size_t dstCapacity); ++ZSTD_STATIC void BIT_addBits(BIT_CStream_t *bitC, size_t value, unsigned nbBits); ++ZSTD_STATIC void BIT_flushBits(BIT_CStream_t *bitC); ++ZSTD_STATIC size_t BIT_closeCStream(BIT_CStream_t *bitC); ++ ++/* Start with initCStream, providing the size of buffer to write into. ++* bitStream will never write outside of this buffer. ++* `dstCapacity` must be >= sizeof(bitD->bitContainer), otherwise @return will be an error code. ++* ++* bits are first added to a local register. ++* Local register is size_t, hence 64-bits on 64-bits systems, or 32-bits on 32-bits systems. ++* Writing data into memory is an explicit operation, performed by the flushBits function. ++* Hence keep track how many bits are potentially stored into local register to avoid register overflow. ++* After a flushBits, a maximum of 7 bits might still be stored into local register. ++* ++* Avoid storing elements of more than 24 bits if you want compatibility with 32-bits bitstream readers. ++* ++* Last operation is to close the bitStream. ++* The function returns the final size of CStream in bytes. ++* If data couldn't fit into `dstBuffer`, it will return a 0 ( == not storable) ++*/ ++ ++/*-******************************************** ++* bitStream decoding API (read backward) ++**********************************************/ ++typedef struct { ++ size_t bitContainer; ++ unsigned bitsConsumed; ++ const char *ptr; ++ const char *start; ++} BIT_DStream_t; ++ ++typedef enum { ++ BIT_DStream_unfinished = 0, ++ BIT_DStream_endOfBuffer = 1, ++ BIT_DStream_completed = 2, ++ BIT_DStream_overflow = 3 ++} BIT_DStream_status; /* result of BIT_reloadDStream() */ ++/* 1,2,4,8 would be better for bitmap combinations, but slows down performance a bit ... :( */ ++ ++ZSTD_STATIC size_t BIT_initDStream(BIT_DStream_t *bitD, const void *srcBuffer, size_t srcSize); ++ZSTD_STATIC size_t BIT_readBits(BIT_DStream_t *bitD, unsigned nbBits); ++ZSTD_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t *bitD); ++ZSTD_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t *bitD); ++ ++/* Start by invoking BIT_initDStream(). ++* A chunk of the bitStream is then stored into a local register. ++* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). ++* You can then retrieve bitFields stored into the local register, **in reverse order**. ++* Local register is explicitly reloaded from memory by the BIT_reloadDStream() method. ++* A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BIT_DStream_unfinished. ++* Otherwise, it can be less than that, so proceed accordingly. ++* Checking if DStream has reached its end can be performed with BIT_endOfDStream(). ++*/ ++ ++/*-**************************************** ++* unsafe API ++******************************************/ ++ZSTD_STATIC void BIT_addBitsFast(BIT_CStream_t *bitC, size_t value, unsigned nbBits); ++/* faster, but works only if value is "clean", meaning all high bits above nbBits are 0 */ ++ ++ZSTD_STATIC void BIT_flushBitsFast(BIT_CStream_t *bitC); ++/* unsafe version; does not check buffer overflow */ ++ ++ZSTD_STATIC size_t BIT_readBitsFast(BIT_DStream_t *bitD, unsigned nbBits); ++/* faster, but works only if nbBits >= 1 */ ++ ++/*-************************************************************** ++* Internal functions ++****************************************************************/ ++ZSTD_STATIC unsigned BIT_highbit32(register U32 val) { return 31 - __builtin_clz(val); } ++ ++/*===== Local Constants =====*/ ++static const unsigned BIT_mask[] = {0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, ++ 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF, ++ 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF}; /* up to 26 bits */ ++ ++/*-************************************************************** ++* bitStream encoding ++****************************************************************/ ++/*! BIT_initCStream() : ++ * `dstCapacity` must be > sizeof(void*) ++ * @return : 0 if success, ++ otherwise an error code (can be tested using ERR_isError() ) */ ++ZSTD_STATIC size_t BIT_initCStream(BIT_CStream_t *bitC, void *startPtr, size_t dstCapacity) ++{ ++ bitC->bitContainer = 0; ++ bitC->bitPos = 0; ++ bitC->startPtr = (char *)startPtr; ++ bitC->ptr = bitC->startPtr; ++ bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->ptr); ++ if (dstCapacity <= sizeof(bitC->ptr)) ++ return ERROR(dstSize_tooSmall); ++ return 0; ++} ++ ++/*! BIT_addBits() : ++ can add up to 26 bits into `bitC`. ++ Does not check for register overflow ! */ ++ZSTD_STATIC void BIT_addBits(BIT_CStream_t *bitC, size_t value, unsigned nbBits) ++{ ++ bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos; ++ bitC->bitPos += nbBits; ++} ++ ++/*! BIT_addBitsFast() : ++ * works only if `value` is _clean_, meaning all high bits above nbBits are 0 */ ++ZSTD_STATIC void BIT_addBitsFast(BIT_CStream_t *bitC, size_t value, unsigned nbBits) ++{ ++ bitC->bitContainer |= value << bitC->bitPos; ++ bitC->bitPos += nbBits; ++} ++ ++/*! BIT_flushBitsFast() : ++ * unsafe version; does not check buffer overflow */ ++ZSTD_STATIC void BIT_flushBitsFast(BIT_CStream_t *bitC) ++{ ++ size_t const nbBytes = bitC->bitPos >> 3; ++ ZSTD_writeLEST(bitC->ptr, bitC->bitContainer); ++ bitC->ptr += nbBytes; ++ bitC->bitPos &= 7; ++ bitC->bitContainer >>= nbBytes * 8; /* if bitPos >= sizeof(bitContainer)*8 --> undefined behavior */ ++} ++ ++/*! BIT_flushBits() : ++ * safe version; check for buffer overflow, and prevents it. ++ * note : does not signal buffer overflow. This will be revealed later on using BIT_closeCStream() */ ++ZSTD_STATIC void BIT_flushBits(BIT_CStream_t *bitC) ++{ ++ size_t const nbBytes = bitC->bitPos >> 3; ++ ZSTD_writeLEST(bitC->ptr, bitC->bitContainer); ++ bitC->ptr += nbBytes; ++ if (bitC->ptr > bitC->endPtr) ++ bitC->ptr = bitC->endPtr; ++ bitC->bitPos &= 7; ++ bitC->bitContainer >>= nbBytes * 8; /* if bitPos >= sizeof(bitContainer)*8 --> undefined behavior */ ++} ++ ++/*! BIT_closeCStream() : ++ * @return : size of CStream, in bytes, ++ or 0 if it could not fit into dstBuffer */ ++ZSTD_STATIC size_t BIT_closeCStream(BIT_CStream_t *bitC) ++{ ++ BIT_addBitsFast(bitC, 1, 1); /* endMark */ ++ BIT_flushBits(bitC); ++ ++ if (bitC->ptr >= bitC->endPtr) ++ return 0; /* doesn't fit within authorized budget : cancel */ ++ ++ return (bitC->ptr - bitC->startPtr) + (bitC->bitPos > 0); ++} ++ ++/*-******************************************************** ++* bitStream decoding ++**********************************************************/ ++/*! BIT_initDStream() : ++* Initialize a BIT_DStream_t. ++* `bitD` : a pointer to an already allocated BIT_DStream_t structure. ++* `srcSize` must be the *exact* size of the bitStream, in bytes. ++* @return : size of stream (== srcSize) or an errorCode if a problem is detected ++*/ ++ZSTD_STATIC size_t BIT_initDStream(BIT_DStream_t *bitD, const void *srcBuffer, size_t srcSize) ++{ ++ if (srcSize < 1) { ++ memset(bitD, 0, sizeof(*bitD)); ++ return ERROR(srcSize_wrong); ++ } ++ ++ if (srcSize >= sizeof(bitD->bitContainer)) { /* normal case */ ++ bitD->start = (const char *)srcBuffer; ++ bitD->ptr = (const char *)srcBuffer + srcSize - sizeof(bitD->bitContainer); ++ bitD->bitContainer = ZSTD_readLEST(bitD->ptr); ++ { ++ BYTE const lastByte = ((const BYTE *)srcBuffer)[srcSize - 1]; ++ bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */ ++ if (lastByte == 0) ++ return ERROR(GENERIC); /* endMark not present */ ++ } ++ } else { ++ bitD->start = (const char *)srcBuffer; ++ bitD->ptr = bitD->start; ++ bitD->bitContainer = *(const BYTE *)(bitD->start); ++ switch (srcSize) { ++ case 7: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[6]) << (sizeof(bitD->bitContainer) * 8 - 16); ++ /* fall through */ ++ case 6: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[5]) << (sizeof(bitD->bitContainer) * 8 - 24); ++ /* fall through */ ++ case 5: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[4]) << (sizeof(bitD->bitContainer) * 8 - 32); ++ /* fall through */ ++ case 4: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[3]) << 24; ++ /* fall through */ ++ case 3: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[2]) << 16; ++ /* fall through */ ++ case 2: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[1]) << 8; ++ default:; ++ } ++ { ++ BYTE const lastByte = ((const BYTE *)srcBuffer)[srcSize - 1]; ++ bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; ++ if (lastByte == 0) ++ return ERROR(GENERIC); /* endMark not present */ ++ } ++ bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize) * 8; ++ } ++ ++ return srcSize; ++} ++ ++ZSTD_STATIC size_t BIT_getUpperBits(size_t bitContainer, U32 const start) { return bitContainer >> start; } ++ ++ZSTD_STATIC size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const nbBits) { return (bitContainer >> start) & BIT_mask[nbBits]; } ++ ++ZSTD_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) { return bitContainer & BIT_mask[nbBits]; } ++ ++/*! BIT_lookBits() : ++ * Provides next n bits from local register. ++ * local register is not modified. ++ * On 32-bits, maxNbBits==24. ++ * On 64-bits, maxNbBits==56. ++ * @return : value extracted ++ */ ++ZSTD_STATIC size_t BIT_lookBits(const BIT_DStream_t *bitD, U32 nbBits) ++{ ++ U32 const bitMask = sizeof(bitD->bitContainer) * 8 - 1; ++ return ((bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> 1) >> ((bitMask - nbBits) & bitMask); ++} ++ ++/*! BIT_lookBitsFast() : ++* unsafe version; only works only if nbBits >= 1 */ ++ZSTD_STATIC size_t BIT_lookBitsFast(const BIT_DStream_t *bitD, U32 nbBits) ++{ ++ U32 const bitMask = sizeof(bitD->bitContainer) * 8 - 1; ++ return (bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> (((bitMask + 1) - nbBits) & bitMask); ++} ++ ++ZSTD_STATIC void BIT_skipBits(BIT_DStream_t *bitD, U32 nbBits) { bitD->bitsConsumed += nbBits; } ++ ++/*! BIT_readBits() : ++ * Read (consume) next n bits from local register and update. ++ * Pay attention to not read more than nbBits contained into local register. ++ * @return : extracted value. ++ */ ++ZSTD_STATIC size_t BIT_readBits(BIT_DStream_t *bitD, U32 nbBits) ++{ ++ size_t const value = BIT_lookBits(bitD, nbBits); ++ BIT_skipBits(bitD, nbBits); ++ return value; ++} ++ ++/*! BIT_readBitsFast() : ++* unsafe version; only works only if nbBits >= 1 */ ++ZSTD_STATIC size_t BIT_readBitsFast(BIT_DStream_t *bitD, U32 nbBits) ++{ ++ size_t const value = BIT_lookBitsFast(bitD, nbBits); ++ BIT_skipBits(bitD, nbBits); ++ return value; ++} ++ ++/*! BIT_reloadDStream() : ++* Refill `bitD` from buffer previously set in BIT_initDStream() . ++* This function is safe, it guarantees it will not read beyond src buffer. ++* @return : status of `BIT_DStream_t` internal register. ++ if status == BIT_DStream_unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */ ++ZSTD_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t *bitD) ++{ ++ if (bitD->bitsConsumed > (sizeof(bitD->bitContainer) * 8)) /* should not happen => corruption detected */ ++ return BIT_DStream_overflow; ++ ++ if (bitD->ptr >= bitD->start + sizeof(bitD->bitContainer)) { ++ bitD->ptr -= bitD->bitsConsumed >> 3; ++ bitD->bitsConsumed &= 7; ++ bitD->bitContainer = ZSTD_readLEST(bitD->ptr); ++ return BIT_DStream_unfinished; ++ } ++ if (bitD->ptr == bitD->start) { ++ if (bitD->bitsConsumed < sizeof(bitD->bitContainer) * 8) ++ return BIT_DStream_endOfBuffer; ++ return BIT_DStream_completed; ++ } ++ { ++ U32 nbBytes = bitD->bitsConsumed >> 3; ++ BIT_DStream_status result = BIT_DStream_unfinished; ++ if (bitD->ptr - nbBytes < bitD->start) { ++ nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */ ++ result = BIT_DStream_endOfBuffer; ++ } ++ bitD->ptr -= nbBytes; ++ bitD->bitsConsumed -= nbBytes * 8; ++ bitD->bitContainer = ZSTD_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD) */ ++ return result; ++ } ++} ++ ++/*! BIT_endOfDStream() : ++* @return Tells if DStream has exactly reached its end (all bits consumed). ++*/ ++ZSTD_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t *DStream) ++{ ++ return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer) * 8)); ++} ++ ++#endif /* BITSTREAM_H_MODULE */ +diff --git a/xen/common/zstd/decompress.c b/xen/common/zstd/decompress.c +new file mode 100644 +index 0000000000..8e627d881a +--- /dev/null ++++ b/xen/common/zstd/decompress.c +@@ -0,0 +1,2489 @@ ++/** ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++/* *************************************************************** ++* Tuning parameters ++*****************************************************************/ ++/*! ++* MAXWINDOWSIZE_DEFAULT : ++* maximum window size accepted by DStream, by default. ++* Frames requiring more memory will be rejected. ++*/ ++#ifndef ZSTD_MAXWINDOWSIZE_DEFAULT ++#define ZSTD_MAXWINDOWSIZE_DEFAULT ((1 << ZSTD_WINDOWLOG_MAX) + 1) /* defined within zstd.h */ ++#endif ++ ++/*-******************************************************* ++* Dependencies ++*********************************************************/ ++#include "fse.h" ++#include "huf.h" ++#include "mem.h" /* low level memory routines */ ++#include "zstd_internal.h" ++#include /* memcpy, memmove, memset */ ++ ++#define ZSTD_PREFETCH(ptr) __builtin_prefetch(ptr, 0, 0) ++ ++/*-************************************* ++* Macros ++***************************************/ ++#define ZSTD_isError ERR_isError /* for inlining */ ++#define FSE_isError ERR_isError ++#define HUF_isError ERR_isError ++ ++/*_******************************************************* ++* Memory operations ++**********************************************************/ ++static void INIT ZSTD_copy4(void *dst, const void *src) { memcpy(dst, src, 4); } ++ ++/*-************************************************************* ++* Context management ++***************************************************************/ ++typedef enum { ++ ZSTDds_getFrameHeaderSize, ++ ZSTDds_decodeFrameHeader, ++ ZSTDds_decodeBlockHeader, ++ ZSTDds_decompressBlock, ++ ZSTDds_decompressLastBlock, ++ ZSTDds_checkChecksum, ++ ZSTDds_decodeSkippableHeader, ++ ZSTDds_skipFrame ++} ZSTD_dStage; ++ ++typedef struct { ++ FSE_DTable LLTable[FSE_DTABLE_SIZE_U32(LLFSELog)]; ++ FSE_DTable OFTable[FSE_DTABLE_SIZE_U32(OffFSELog)]; ++ FSE_DTable MLTable[FSE_DTABLE_SIZE_U32(MLFSELog)]; ++ HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */ ++ U64 workspace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32 / 2]; ++ U32 rep[ZSTD_REP_NUM]; ++} ZSTD_entropyTables_t; ++ ++struct ZSTD_DCtx_s { ++ const FSE_DTable *LLTptr; ++ const FSE_DTable *MLTptr; ++ const FSE_DTable *OFTptr; ++ const HUF_DTable *HUFptr; ++ ZSTD_entropyTables_t entropy; ++ const void *previousDstEnd; /* detect continuity */ ++ const void *base; /* start of curr segment */ ++ const void *vBase; /* virtual start of previous segment if it was just before curr one */ ++ const void *dictEnd; /* end of previous segment */ ++ size_t expected; ++ ZSTD_frameParams fParams; ++ blockType_e bType; /* used in ZSTD_decompressContinue(), to transfer blockType between header decoding and block decoding stages */ ++ ZSTD_dStage stage; ++ U32 litEntropy; ++ U32 fseEntropy; ++ struct xxh64_state xxhState; ++ size_t headerSize; ++ U32 dictID; ++ const BYTE *litPtr; ++ ZSTD_customMem customMem; ++ size_t litSize; ++ size_t rleSize; ++ BYTE litBuffer[ZSTD_BLOCKSIZE_ABSOLUTEMAX + WILDCOPY_OVERLENGTH]; ++ BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; ++}; /* typedef'd to ZSTD_DCtx within "zstd.h" */ ++ ++size_t INIT ZSTD_DCtxWorkspaceBound(void) { return ZSTD_ALIGN(sizeof(ZSTD_stack)) + ZSTD_ALIGN(sizeof(ZSTD_DCtx)); } ++ ++size_t INIT ZSTD_decompressBegin(ZSTD_DCtx *dctx) ++{ ++ dctx->expected = ZSTD_frameHeaderSize_prefix; ++ dctx->stage = ZSTDds_getFrameHeaderSize; ++ dctx->previousDstEnd = NULL; ++ dctx->base = NULL; ++ dctx->vBase = NULL; ++ dctx->dictEnd = NULL; ++ dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ ++ dctx->litEntropy = dctx->fseEntropy = 0; ++ dctx->dictID = 0; ++ ZSTD_STATIC_ASSERT(sizeof(dctx->entropy.rep) == sizeof(repStartValue)); ++ memcpy(dctx->entropy.rep, repStartValue, sizeof(repStartValue)); /* initial repcodes */ ++ dctx->LLTptr = dctx->entropy.LLTable; ++ dctx->MLTptr = dctx->entropy.MLTable; ++ dctx->OFTptr = dctx->entropy.OFTable; ++ dctx->HUFptr = dctx->entropy.hufTable; ++ return 0; ++} ++ ++ZSTD_DCtx INIT *ZSTD_createDCtx_advanced(ZSTD_customMem customMem) ++{ ++ ZSTD_DCtx *dctx; ++ ++ if (!customMem.customAlloc || !customMem.customFree) ++ return NULL; ++ ++ dctx = (ZSTD_DCtx *)ZSTD_malloc(sizeof(ZSTD_DCtx), customMem); ++ if (!dctx) ++ return NULL; ++ memcpy(&dctx->customMem, &customMem, sizeof(customMem)); ++ ZSTD_decompressBegin(dctx); ++ return dctx; ++} ++ ++ZSTD_DCtx INIT *ZSTD_initDCtx(void *workspace, size_t workspaceSize) ++{ ++ ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); ++ return ZSTD_createDCtx_advanced(stackMem); ++} ++ ++size_t INIT ZSTD_freeDCtx(ZSTD_DCtx *dctx) ++{ ++ if (dctx == NULL) ++ return 0; /* support free on NULL */ ++ ZSTD_free(dctx, dctx->customMem); ++ return 0; /* reserved as a potential error code in the future */ ++} ++ ++void INIT ZSTD_copyDCtx(ZSTD_DCtx *dstDCtx, const ZSTD_DCtx *srcDCtx) ++{ ++ size_t const workSpaceSize = (ZSTD_BLOCKSIZE_ABSOLUTEMAX + WILDCOPY_OVERLENGTH) + ZSTD_frameHeaderSize_max; ++ memcpy(dstDCtx, srcDCtx, sizeof(ZSTD_DCtx) - workSpaceSize); /* no need to copy workspace */ ++} ++ ++static void INIT ZSTD_refDDict(ZSTD_DCtx *dstDCtx, const ZSTD_DDict *ddict); ++ ++/*-************************************************************* ++* Decompression section ++***************************************************************/ ++ ++/*! ZSTD_isFrame() : ++ * Tells if the content of `buffer` starts with a valid Frame Identifier. ++ * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0. ++ * Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled. ++ * Note 3 : Skippable Frame Identifiers are considered valid. */ ++unsigned INIT ZSTD_isFrame(const void *buffer, size_t size) ++{ ++ if (size < 4) ++ return 0; ++ { ++ U32 const magic = ZSTD_readLE32(buffer); ++ if (magic == ZSTD_MAGICNUMBER) ++ return 1; ++ if ((magic & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) ++ return 1; ++ } ++ return 0; ++} ++ ++/** ZSTD_frameHeaderSize() : ++* srcSize must be >= ZSTD_frameHeaderSize_prefix. ++* @return : size of the Frame Header */ ++static size_t INIT ZSTD_frameHeaderSize(const void *src, size_t srcSize) ++{ ++ if (srcSize < ZSTD_frameHeaderSize_prefix) ++ return ERROR(srcSize_wrong); ++ { ++ BYTE const fhd = ((const BYTE *)src)[4]; ++ U32 const dictID = fhd & 3; ++ U32 const singleSegment = (fhd >> 5) & 1; ++ U32 const fcsId = fhd >> 6; ++ return ZSTD_frameHeaderSize_prefix + !singleSegment + ZSTD_did_fieldSize[dictID] + ZSTD_fcs_fieldSize[fcsId] + (singleSegment && !fcsId); ++ } ++} ++ ++/** ZSTD_getFrameParams() : ++* decode Frame Header, or require larger `srcSize`. ++* @return : 0, `fparamsPtr` is correctly filled, ++* >0, `srcSize` is too small, result is expected `srcSize`, ++* or an error code, which can be tested using ZSTD_isError() */ ++size_t INIT ZSTD_getFrameParams(ZSTD_frameParams *fparamsPtr, const void *src, size_t srcSize) ++{ ++ const BYTE *ip = (const BYTE *)src; ++ ++ if (srcSize < ZSTD_frameHeaderSize_prefix) ++ return ZSTD_frameHeaderSize_prefix; ++ if (ZSTD_readLE32(src) != ZSTD_MAGICNUMBER) { ++ if ((ZSTD_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { ++ if (srcSize < ZSTD_skippableHeaderSize) ++ return ZSTD_skippableHeaderSize; /* magic number + skippable frame length */ ++ memset(fparamsPtr, 0, sizeof(*fparamsPtr)); ++ fparamsPtr->frameContentSize = ZSTD_readLE32((const char *)src + 4); ++ fparamsPtr->windowSize = 0; /* windowSize==0 means a frame is skippable */ ++ return 0; ++ } ++ return ERROR(prefix_unknown); ++ } ++ ++ /* ensure there is enough `srcSize` to fully read/decode frame header */ ++ { ++ size_t const fhsize = ZSTD_frameHeaderSize(src, srcSize); ++ if (srcSize < fhsize) ++ return fhsize; ++ } ++ ++ { ++ BYTE const fhdByte = ip[4]; ++ size_t pos = 5; ++ U32 const dictIDSizeCode = fhdByte & 3; ++ U32 const checksumFlag = (fhdByte >> 2) & 1; ++ U32 const singleSegment = (fhdByte >> 5) & 1; ++ U32 const fcsID = fhdByte >> 6; ++ U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX; ++ U32 windowSize = 0; ++ U32 dictID = 0; ++ U64 frameContentSize = 0; ++ if ((fhdByte & 0x08) != 0) ++ return ERROR(frameParameter_unsupported); /* reserved bits, which must be zero */ ++ if (!singleSegment) { ++ BYTE const wlByte = ip[pos++]; ++ U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN; ++ if (windowLog > ZSTD_WINDOWLOG_MAX) ++ return ERROR(frameParameter_windowTooLarge); /* avoids issue with 1 << windowLog */ ++ windowSize = (1U << windowLog); ++ windowSize += (windowSize >> 3) * (wlByte & 7); ++ } ++ ++ switch (dictIDSizeCode) { ++ default: /* impossible */ ++ case 0: break; ++ case 1: ++ dictID = ip[pos]; ++ pos++; ++ break; ++ case 2: ++ dictID = ZSTD_readLE16(ip + pos); ++ pos += 2; ++ break; ++ case 3: ++ dictID = ZSTD_readLE32(ip + pos); ++ pos += 4; ++ break; ++ } ++ switch (fcsID) { ++ default: /* impossible */ ++ case 0: ++ if (singleSegment) ++ frameContentSize = ip[pos]; ++ break; ++ case 1: frameContentSize = ZSTD_readLE16(ip + pos) + 256; break; ++ case 2: frameContentSize = ZSTD_readLE32(ip + pos); break; ++ case 3: frameContentSize = ZSTD_readLE64(ip + pos); break; ++ } ++ if (!windowSize) ++ windowSize = (U32)frameContentSize; ++ if (windowSize > windowSizeMax) ++ return ERROR(frameParameter_windowTooLarge); ++ fparamsPtr->frameContentSize = frameContentSize; ++ fparamsPtr->windowSize = windowSize; ++ fparamsPtr->dictID = dictID; ++ fparamsPtr->checksumFlag = checksumFlag; ++ } ++ return 0; ++} ++ ++/** ZSTD_getFrameContentSize() : ++* compatible with legacy mode ++* @return : decompressed size of the single frame pointed to be `src` if known, otherwise ++* - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined ++* - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */ ++unsigned long long INIT ZSTD_getFrameContentSize(const void *src, size_t srcSize) ++{ ++ { ++ ZSTD_frameParams fParams; ++ if (ZSTD_getFrameParams(&fParams, src, srcSize) != 0) ++ return ZSTD_CONTENTSIZE_ERROR; ++ if (fParams.windowSize == 0) { ++ /* Either skippable or empty frame, size == 0 either way */ ++ return 0; ++ } else if (fParams.frameContentSize != 0) { ++ return fParams.frameContentSize; ++ } else { ++ return ZSTD_CONTENTSIZE_UNKNOWN; ++ } ++ } ++} ++ ++/** ZSTD_findDecompressedSize() : ++ * compatible with legacy mode ++ * `srcSize` must be the exact length of some number of ZSTD compressed and/or ++ * skippable frames ++ * @return : decompressed size of the frames contained */ ++unsigned long long INIT ZSTD_findDecompressedSize(const void *src, size_t srcSize) ++{ ++ { ++ unsigned long long totalDstSize = 0; ++ while (srcSize >= ZSTD_frameHeaderSize_prefix) { ++ const U32 magicNumber = ZSTD_readLE32(src); ++ ++ if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { ++ size_t skippableSize; ++ if (srcSize < ZSTD_skippableHeaderSize) ++ return ERROR(srcSize_wrong); ++ skippableSize = ZSTD_readLE32((const BYTE *)src + 4) + ZSTD_skippableHeaderSize; ++ if (srcSize < skippableSize) { ++ return ZSTD_CONTENTSIZE_ERROR; ++ } ++ ++ src = (const BYTE *)src + skippableSize; ++ srcSize -= skippableSize; ++ continue; ++ } ++ ++ { ++ unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize); ++ if (ret >= ZSTD_CONTENTSIZE_ERROR) ++ return ret; ++ ++ /* check for overflow */ ++ if (totalDstSize + ret < totalDstSize) ++ return ZSTD_CONTENTSIZE_ERROR; ++ totalDstSize += ret; ++ } ++ { ++ size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize); ++ if (ZSTD_isError(frameSrcSize)) { ++ return ZSTD_CONTENTSIZE_ERROR; ++ } ++ ++ src = (const BYTE *)src + frameSrcSize; ++ srcSize -= frameSrcSize; ++ } ++ } ++ ++ if (srcSize) { ++ return ZSTD_CONTENTSIZE_ERROR; ++ } ++ ++ return totalDstSize; ++ } ++} ++ ++/** ZSTD_decodeFrameHeader() : ++* `headerSize` must be the size provided by ZSTD_frameHeaderSize(). ++* @return : 0 if success, or an error code, which can be tested using ZSTD_isError() */ ++static size_t INIT ZSTD_decodeFrameHeader(ZSTD_DCtx *dctx, const void *src, size_t headerSize) ++{ ++ size_t const result = ZSTD_getFrameParams(&(dctx->fParams), src, headerSize); ++ if (ZSTD_isError(result)) ++ return result; /* invalid header */ ++ if (result > 0) ++ return ERROR(srcSize_wrong); /* headerSize too small */ ++ if (dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID)) ++ return ERROR(dictionary_wrong); ++ if (dctx->fParams.checksumFlag) ++ xxh64_reset(&dctx->xxhState, 0); ++ return 0; ++} ++ ++typedef struct { ++ blockType_e blockType; ++ U32 lastBlock; ++ U32 origSize; ++} blockProperties_t; ++ ++/*! ZSTD_getcBlockSize() : ++* Provides the size of compressed block from block header `src` */ ++size_t INIT ZSTD_getcBlockSize(const void *src, size_t srcSize, blockProperties_t *bpPtr) ++{ ++ if (srcSize < ZSTD_blockHeaderSize) ++ return ERROR(srcSize_wrong); ++ { ++ U32 const cBlockHeader = ZSTD_readLE24(src); ++ U32 const cSize = cBlockHeader >> 3; ++ bpPtr->lastBlock = cBlockHeader & 1; ++ bpPtr->blockType = (blockType_e)((cBlockHeader >> 1) & 3); ++ bpPtr->origSize = cSize; /* only useful for RLE */ ++ if (bpPtr->blockType == bt_rle) ++ return 1; ++ if (bpPtr->blockType == bt_reserved) ++ return ERROR(corruption_detected); ++ return cSize; ++ } ++} ++ ++static size_t INIT ZSTD_copyRawBlock(void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ ++ if (srcSize > dstCapacity) ++ return ERROR(dstSize_tooSmall); ++ memcpy(dst, src, srcSize); ++ return srcSize; ++} ++ ++static size_t INIT ZSTD_setRleBlock(void *dst, size_t dstCapacity, const void *src, size_t srcSize, size_t regenSize) ++{ ++ if (srcSize != 1) ++ return ERROR(srcSize_wrong); ++ if (regenSize > dstCapacity) ++ return ERROR(dstSize_tooSmall); ++ memset(dst, *(const BYTE *)src, regenSize); ++ return regenSize; ++} ++ ++/*! ZSTD_decodeLiteralsBlock() : ++ @return : nb of bytes read from src (< srcSize ) */ ++size_t INIT ZSTD_decodeLiteralsBlock(ZSTD_DCtx *dctx, const void *src, size_t srcSize) /* note : srcSize < BLOCKSIZE */ ++{ ++ if (srcSize < MIN_CBLOCK_SIZE) ++ return ERROR(corruption_detected); ++ ++ { ++ const BYTE *const istart = (const BYTE *)src; ++ symbolEncodingType_e const litEncType = (symbolEncodingType_e)(istart[0] & 3); ++ ++ switch (litEncType) { ++ case set_repeat: ++ if (dctx->litEntropy == 0) ++ return ERROR(dictionary_corrupted); ++ /* fall through */ ++ case set_compressed: ++ if (srcSize < 5) ++ return ERROR(corruption_detected); /* srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3 */ ++ { ++ size_t lhSize, litSize, litCSize; ++ U32 singleStream = 0; ++ U32 const lhlCode = (istart[0] >> 2) & 3; ++ U32 const lhc = ZSTD_readLE32(istart); ++ switch (lhlCode) { ++ case 0: ++ case 1: ++ default: /* note : default is impossible, since lhlCode into [0..3] */ ++ /* 2 - 2 - 10 - 10 */ ++ singleStream = !lhlCode; ++ lhSize = 3; ++ litSize = (lhc >> 4) & 0x3FF; ++ litCSize = (lhc >> 14) & 0x3FF; ++ break; ++ case 2: ++ /* 2 - 2 - 14 - 14 */ ++ lhSize = 4; ++ litSize = (lhc >> 4) & 0x3FFF; ++ litCSize = lhc >> 18; ++ break; ++ case 3: ++ /* 2 - 2 - 18 - 18 */ ++ lhSize = 5; ++ litSize = (lhc >> 4) & 0x3FFFF; ++ litCSize = (lhc >> 22) + (istart[4] << 10); ++ break; ++ } ++ if (litSize > ZSTD_BLOCKSIZE_ABSOLUTEMAX) ++ return ERROR(corruption_detected); ++ if (litCSize + lhSize > srcSize) ++ return ERROR(corruption_detected); ++ ++ if (HUF_isError( ++ (litEncType == set_repeat) ++ ? (singleStream ? HUF_decompress1X_usingDTable(dctx->litBuffer, litSize, istart + lhSize, litCSize, dctx->HUFptr) ++ : HUF_decompress4X_usingDTable(dctx->litBuffer, litSize, istart + lhSize, litCSize, dctx->HUFptr)) ++ : (singleStream ++ ? HUF_decompress1X2_DCtx_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart + lhSize, litCSize, ++ dctx->entropy.workspace, sizeof(dctx->entropy.workspace)) ++ : HUF_decompress4X_hufOnly_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart + lhSize, litCSize, ++ dctx->entropy.workspace, sizeof(dctx->entropy.workspace))))) ++ return ERROR(corruption_detected); ++ ++ dctx->litPtr = dctx->litBuffer; ++ dctx->litSize = litSize; ++ dctx->litEntropy = 1; ++ if (litEncType == set_compressed) ++ dctx->HUFptr = dctx->entropy.hufTable; ++ memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH); ++ return litCSize + lhSize; ++ } ++ ++ case set_basic: { ++ size_t litSize, lhSize; ++ U32 const lhlCode = ((istart[0]) >> 2) & 3; ++ switch (lhlCode) { ++ case 0: ++ case 2: ++ default: /* note : default is impossible, since lhlCode into [0..3] */ ++ lhSize = 1; ++ litSize = istart[0] >> 3; ++ break; ++ case 1: ++ lhSize = 2; ++ litSize = ZSTD_readLE16(istart) >> 4; ++ break; ++ case 3: ++ lhSize = 3; ++ litSize = ZSTD_readLE24(istart) >> 4; ++ break; ++ } ++ ++ if (lhSize + litSize + WILDCOPY_OVERLENGTH > srcSize) { /* risk reading beyond src buffer with wildcopy */ ++ if (litSize + lhSize > srcSize) ++ return ERROR(corruption_detected); ++ memcpy(dctx->litBuffer, istart + lhSize, litSize); ++ dctx->litPtr = dctx->litBuffer; ++ dctx->litSize = litSize; ++ memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH); ++ return lhSize + litSize; ++ } ++ /* direct reference into compressed stream */ ++ dctx->litPtr = istart + lhSize; ++ dctx->litSize = litSize; ++ return lhSize + litSize; ++ } ++ ++ case set_rle: { ++ U32 const lhlCode = ((istart[0]) >> 2) & 3; ++ size_t litSize, lhSize; ++ switch (lhlCode) { ++ case 0: ++ case 2: ++ default: /* note : default is impossible, since lhlCode into [0..3] */ ++ lhSize = 1; ++ litSize = istart[0] >> 3; ++ break; ++ case 1: ++ lhSize = 2; ++ litSize = ZSTD_readLE16(istart) >> 4; ++ break; ++ case 3: ++ lhSize = 3; ++ litSize = ZSTD_readLE24(istart) >> 4; ++ if (srcSize < 4) ++ return ERROR(corruption_detected); /* srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4 */ ++ break; ++ } ++ if (litSize > ZSTD_BLOCKSIZE_ABSOLUTEMAX) ++ return ERROR(corruption_detected); ++ memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH); ++ dctx->litPtr = dctx->litBuffer; ++ dctx->litSize = litSize; ++ return lhSize + 1; ++ } ++ default: ++ return ERROR(corruption_detected); /* impossible */ ++ } ++ } ++} ++ ++typedef union { ++ FSE_decode_t realData; ++ U32 alignedBy4; ++} FSE_decode_t4; ++ ++static const FSE_decode_t4 LL_defaultDTable[(1 << LL_DEFAULTNORMLOG) + 1] = { ++ {{LL_DEFAULTNORMLOG, 1, 1}}, /* header : tableLog, fastMode, fastMode */ ++ {{0, 0, 4}}, /* 0 : base, symbol, bits */ ++ {{16, 0, 4}}, ++ {{32, 1, 5}}, ++ {{0, 3, 5}}, ++ {{0, 4, 5}}, ++ {{0, 6, 5}}, ++ {{0, 7, 5}}, ++ {{0, 9, 5}}, ++ {{0, 10, 5}}, ++ {{0, 12, 5}}, ++ {{0, 14, 6}}, ++ {{0, 16, 5}}, ++ {{0, 18, 5}}, ++ {{0, 19, 5}}, ++ {{0, 21, 5}}, ++ {{0, 22, 5}}, ++ {{0, 24, 5}}, ++ {{32, 25, 5}}, ++ {{0, 26, 5}}, ++ {{0, 27, 6}}, ++ {{0, 29, 6}}, ++ {{0, 31, 6}}, ++ {{32, 0, 4}}, ++ {{0, 1, 4}}, ++ {{0, 2, 5}}, ++ {{32, 4, 5}}, ++ {{0, 5, 5}}, ++ {{32, 7, 5}}, ++ {{0, 8, 5}}, ++ {{32, 10, 5}}, ++ {{0, 11, 5}}, ++ {{0, 13, 6}}, ++ {{32, 16, 5}}, ++ {{0, 17, 5}}, ++ {{32, 19, 5}}, ++ {{0, 20, 5}}, ++ {{32, 22, 5}}, ++ {{0, 23, 5}}, ++ {{0, 25, 4}}, ++ {{16, 25, 4}}, ++ {{32, 26, 5}}, ++ {{0, 28, 6}}, ++ {{0, 30, 6}}, ++ {{48, 0, 4}}, ++ {{16, 1, 4}}, ++ {{32, 2, 5}}, ++ {{32, 3, 5}}, ++ {{32, 5, 5}}, ++ {{32, 6, 5}}, ++ {{32, 8, 5}}, ++ {{32, 9, 5}}, ++ {{32, 11, 5}}, ++ {{32, 12, 5}}, ++ {{0, 15, 6}}, ++ {{32, 17, 5}}, ++ {{32, 18, 5}}, ++ {{32, 20, 5}}, ++ {{32, 21, 5}}, ++ {{32, 23, 5}}, ++ {{32, 24, 5}}, ++ {{0, 35, 6}}, ++ {{0, 34, 6}}, ++ {{0, 33, 6}}, ++ {{0, 32, 6}}, ++}; /* LL_defaultDTable */ ++ ++static const FSE_decode_t4 ML_defaultDTable[(1 << ML_DEFAULTNORMLOG) + 1] = { ++ {{ML_DEFAULTNORMLOG, 1, 1}}, /* header : tableLog, fastMode, fastMode */ ++ {{0, 0, 6}}, /* 0 : base, symbol, bits */ ++ {{0, 1, 4}}, ++ {{32, 2, 5}}, ++ {{0, 3, 5}}, ++ {{0, 5, 5}}, ++ {{0, 6, 5}}, ++ {{0, 8, 5}}, ++ {{0, 10, 6}}, ++ {{0, 13, 6}}, ++ {{0, 16, 6}}, ++ {{0, 19, 6}}, ++ {{0, 22, 6}}, ++ {{0, 25, 6}}, ++ {{0, 28, 6}}, ++ {{0, 31, 6}}, ++ {{0, 33, 6}}, ++ {{0, 35, 6}}, ++ {{0, 37, 6}}, ++ {{0, 39, 6}}, ++ {{0, 41, 6}}, ++ {{0, 43, 6}}, ++ {{0, 45, 6}}, ++ {{16, 1, 4}}, ++ {{0, 2, 4}}, ++ {{32, 3, 5}}, ++ {{0, 4, 5}}, ++ {{32, 6, 5}}, ++ {{0, 7, 5}}, ++ {{0, 9, 6}}, ++ {{0, 12, 6}}, ++ {{0, 15, 6}}, ++ {{0, 18, 6}}, ++ {{0, 21, 6}}, ++ {{0, 24, 6}}, ++ {{0, 27, 6}}, ++ {{0, 30, 6}}, ++ {{0, 32, 6}}, ++ {{0, 34, 6}}, ++ {{0, 36, 6}}, ++ {{0, 38, 6}}, ++ {{0, 40, 6}}, ++ {{0, 42, 6}}, ++ {{0, 44, 6}}, ++ {{32, 1, 4}}, ++ {{48, 1, 4}}, ++ {{16, 2, 4}}, ++ {{32, 4, 5}}, ++ {{32, 5, 5}}, ++ {{32, 7, 5}}, ++ {{32, 8, 5}}, ++ {{0, 11, 6}}, ++ {{0, 14, 6}}, ++ {{0, 17, 6}}, ++ {{0, 20, 6}}, ++ {{0, 23, 6}}, ++ {{0, 26, 6}}, ++ {{0, 29, 6}}, ++ {{0, 52, 6}}, ++ {{0, 51, 6}}, ++ {{0, 50, 6}}, ++ {{0, 49, 6}}, ++ {{0, 48, 6}}, ++ {{0, 47, 6}}, ++ {{0, 46, 6}}, ++}; /* ML_defaultDTable */ ++ ++static const FSE_decode_t4 OF_defaultDTable[(1 << OF_DEFAULTNORMLOG) + 1] = { ++ {{OF_DEFAULTNORMLOG, 1, 1}}, /* header : tableLog, fastMode, fastMode */ ++ {{0, 0, 5}}, /* 0 : base, symbol, bits */ ++ {{0, 6, 4}}, ++ {{0, 9, 5}}, ++ {{0, 15, 5}}, ++ {{0, 21, 5}}, ++ {{0, 3, 5}}, ++ {{0, 7, 4}}, ++ {{0, 12, 5}}, ++ {{0, 18, 5}}, ++ {{0, 23, 5}}, ++ {{0, 5, 5}}, ++ {{0, 8, 4}}, ++ {{0, 14, 5}}, ++ {{0, 20, 5}}, ++ {{0, 2, 5}}, ++ {{16, 7, 4}}, ++ {{0, 11, 5}}, ++ {{0, 17, 5}}, ++ {{0, 22, 5}}, ++ {{0, 4, 5}}, ++ {{16, 8, 4}}, ++ {{0, 13, 5}}, ++ {{0, 19, 5}}, ++ {{0, 1, 5}}, ++ {{16, 6, 4}}, ++ {{0, 10, 5}}, ++ {{0, 16, 5}}, ++ {{0, 28, 5}}, ++ {{0, 27, 5}}, ++ {{0, 26, 5}}, ++ {{0, 25, 5}}, ++ {{0, 24, 5}}, ++}; /* OF_defaultDTable */ ++ ++/*! ZSTD_buildSeqTable() : ++ @return : nb bytes read from src, ++ or an error code if it fails, testable with ZSTD_isError() ++*/ ++static size_t INIT ZSTD_buildSeqTable(FSE_DTable *DTableSpace, const FSE_DTable **DTablePtr, symbolEncodingType_e type, U32 max, U32 maxLog, const void *src, ++ size_t srcSize, const FSE_decode_t4 *defaultTable, U32 flagRepeatTable, void *workspace, size_t workspaceSize) ++{ ++ const void *const tmpPtr = defaultTable; /* bypass strict aliasing */ ++ switch (type) { ++ case set_rle: ++ if (!srcSize) ++ return ERROR(srcSize_wrong); ++ if ((*(const BYTE *)src) > max) ++ return ERROR(corruption_detected); ++ FSE_buildDTable_rle(DTableSpace, *(const BYTE *)src); ++ *DTablePtr = DTableSpace; ++ return 1; ++ case set_basic: *DTablePtr = (const FSE_DTable *)tmpPtr; return 0; ++ case set_repeat: ++ if (!flagRepeatTable) ++ return ERROR(corruption_detected); ++ return 0; ++ default: /* impossible */ ++ case set_compressed: { ++ U32 tableLog; ++ S16 *norm = (S16 *)workspace; ++ size_t const spaceUsed32 = ALIGN(sizeof(S16) * (MaxSeq + 1), sizeof(U32)) >> 2; ++ ++ if ((spaceUsed32 << 2) > workspaceSize) ++ return ERROR(GENERIC); ++ workspace = (U32 *)workspace + spaceUsed32; ++ workspaceSize -= (spaceUsed32 << 2); ++ { ++ size_t const headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize); ++ if (FSE_isError(headerSize)) ++ return ERROR(corruption_detected); ++ if (tableLog > maxLog) ++ return ERROR(corruption_detected); ++ FSE_buildDTable_wksp(DTableSpace, norm, max, tableLog, workspace, workspaceSize); ++ *DTablePtr = DTableSpace; ++ return headerSize; ++ } ++ } ++ } ++} ++ ++size_t INIT ZSTD_decodeSeqHeaders(ZSTD_DCtx *dctx, int *nbSeqPtr, const void *src, size_t srcSize) ++{ ++ const BYTE *const istart = (const BYTE *const)src; ++ const BYTE *const iend = istart + srcSize; ++ const BYTE *ip = istart; ++ ++ /* check */ ++ if (srcSize < MIN_SEQUENCES_SIZE) ++ return ERROR(srcSize_wrong); ++ ++ /* SeqHead */ ++ { ++ int nbSeq = *ip++; ++ if (!nbSeq) { ++ *nbSeqPtr = 0; ++ return 1; ++ } ++ if (nbSeq > 0x7F) { ++ if (nbSeq == 0xFF) { ++ if (ip + 2 > iend) ++ return ERROR(srcSize_wrong); ++ nbSeq = ZSTD_readLE16(ip) + LONGNBSEQ, ip += 2; ++ } else { ++ if (ip >= iend) ++ return ERROR(srcSize_wrong); ++ nbSeq = ((nbSeq - 0x80) << 8) + *ip++; ++ } ++ } ++ *nbSeqPtr = nbSeq; ++ } ++ ++ /* FSE table descriptors */ ++ if (ip + 4 > iend) ++ return ERROR(srcSize_wrong); /* minimum possible size */ ++ { ++ symbolEncodingType_e const LLtype = (symbolEncodingType_e)(*ip >> 6); ++ symbolEncodingType_e const OFtype = (symbolEncodingType_e)((*ip >> 4) & 3); ++ symbolEncodingType_e const MLtype = (symbolEncodingType_e)((*ip >> 2) & 3); ++ ip++; ++ ++ /* Build DTables */ ++ { ++ size_t const llhSize = ZSTD_buildSeqTable(dctx->entropy.LLTable, &dctx->LLTptr, LLtype, MaxLL, LLFSELog, ip, iend - ip, ++ LL_defaultDTable, dctx->fseEntropy, dctx->entropy.workspace, sizeof(dctx->entropy.workspace)); ++ if (ZSTD_isError(llhSize)) ++ return ERROR(corruption_detected); ++ ip += llhSize; ++ } ++ { ++ size_t const ofhSize = ZSTD_buildSeqTable(dctx->entropy.OFTable, &dctx->OFTptr, OFtype, MaxOff, OffFSELog, ip, iend - ip, ++ OF_defaultDTable, dctx->fseEntropy, dctx->entropy.workspace, sizeof(dctx->entropy.workspace)); ++ if (ZSTD_isError(ofhSize)) ++ return ERROR(corruption_detected); ++ ip += ofhSize; ++ } ++ { ++ size_t const mlhSize = ZSTD_buildSeqTable(dctx->entropy.MLTable, &dctx->MLTptr, MLtype, MaxML, MLFSELog, ip, iend - ip, ++ ML_defaultDTable, dctx->fseEntropy, dctx->entropy.workspace, sizeof(dctx->entropy.workspace)); ++ if (ZSTD_isError(mlhSize)) ++ return ERROR(corruption_detected); ++ ip += mlhSize; ++ } ++ } ++ ++ return ip - istart; ++} ++ ++typedef struct { ++ size_t litLength; ++ size_t matchLength; ++ size_t offset; ++ const BYTE *match; ++} seq_t; ++ ++typedef struct { ++ BIT_DStream_t DStream; ++ FSE_DState_t stateLL; ++ FSE_DState_t stateOffb; ++ FSE_DState_t stateML; ++ size_t prevOffset[ZSTD_REP_NUM]; ++ const BYTE *base; ++ size_t pos; ++ uPtrDiff gotoDict; ++} seqState_t; ++ ++FORCE_NOINLINE ++size_t INIT ZSTD_execSequenceLast7(BYTE *op, BYTE *const oend, seq_t sequence, const BYTE **litPtr, const BYTE *const litLimit, const BYTE *const base, ++ const BYTE *const vBase, const BYTE *const dictEnd) ++{ ++ BYTE *const oLitEnd = op + sequence.litLength; ++ size_t const sequenceLength = sequence.litLength + sequence.matchLength; ++ BYTE *const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ ++ BYTE *const oend_w = oend - WILDCOPY_OVERLENGTH; ++ const BYTE *const iLitEnd = *litPtr + sequence.litLength; ++ const BYTE *match = oLitEnd - sequence.offset; ++ ++ /* check */ ++ if (oMatchEnd > oend) ++ return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ ++ if (iLitEnd > litLimit) ++ return ERROR(corruption_detected); /* over-read beyond lit buffer */ ++ if (oLitEnd <= oend_w) ++ return ERROR(GENERIC); /* Precondition */ ++ ++ /* copy literals */ ++ if (op < oend_w) { ++ ZSTD_wildcopy(op, *litPtr, oend_w - op); ++ *litPtr += oend_w - op; ++ op = oend_w; ++ } ++ while (op < oLitEnd) ++ *op++ = *(*litPtr)++; ++ ++ /* copy Match */ ++ if (sequence.offset > (size_t)(oLitEnd - base)) { ++ /* offset beyond prefix */ ++ if (sequence.offset > (size_t)(oLitEnd - vBase)) ++ return ERROR(corruption_detected); ++ match = dictEnd - (base - match); ++ if (match + sequence.matchLength <= dictEnd) { ++ memmove(oLitEnd, match, sequence.matchLength); ++ return sequenceLength; ++ } ++ /* span extDict & currPrefixSegment */ ++ { ++ size_t const length1 = dictEnd - match; ++ memmove(oLitEnd, match, length1); ++ op = oLitEnd + length1; ++ sequence.matchLength -= length1; ++ match = base; ++ } ++ } ++ while (op < oMatchEnd) ++ *op++ = *match++; ++ return sequenceLength; ++} ++ ++static seq_t INIT ZSTD_decodeSequence(seqState_t *seqState) ++{ ++ seq_t seq; ++ ++ U32 const llCode = FSE_peekSymbol(&seqState->stateLL); ++ U32 const mlCode = FSE_peekSymbol(&seqState->stateML); ++ U32 const ofCode = FSE_peekSymbol(&seqState->stateOffb); /* <= maxOff, by table construction */ ++ ++ U32 const llBits = LL_bits[llCode]; ++ U32 const mlBits = ML_bits[mlCode]; ++ U32 const ofBits = ofCode; ++ U32 const totalBits = llBits + mlBits + ofBits; ++ ++ static const U32 LL_base[MaxLL + 1] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, ++ 20, 22, 24, 28, 32, 40, 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000}; ++ ++ static const U32 ML_base[MaxML + 1] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ++ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, ++ 43, 47, 51, 59, 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, 0x1003, 0x2003, 0x4003, 0x8003, 0x10003}; ++ ++ static const U32 OF_base[MaxOff + 1] = {0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, 0xFD, 0x1FD, ++ 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, ++ 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD}; ++ ++ /* sequence */ ++ { ++ size_t offset; ++ if (!ofCode) ++ offset = 0; ++ else { ++ offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ ++ if (ZSTD_32bits()) ++ BIT_reloadDStream(&seqState->DStream); ++ } ++ ++ if (ofCode <= 1) { ++ offset += (llCode == 0); ++ if (offset) { ++ size_t temp = (offset == 3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; ++ temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ ++ if (offset != 1) ++ seqState->prevOffset[2] = seqState->prevOffset[1]; ++ seqState->prevOffset[1] = seqState->prevOffset[0]; ++ seqState->prevOffset[0] = offset = temp; ++ } else { ++ offset = seqState->prevOffset[0]; ++ } ++ } else { ++ seqState->prevOffset[2] = seqState->prevOffset[1]; ++ seqState->prevOffset[1] = seqState->prevOffset[0]; ++ seqState->prevOffset[0] = offset; ++ } ++ seq.offset = offset; ++ } ++ ++ seq.matchLength = ML_base[mlCode] + ((mlCode > 31) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ ++ if (ZSTD_32bits() && (mlBits + llBits > 24)) ++ BIT_reloadDStream(&seqState->DStream); ++ ++ seq.litLength = LL_base[llCode] + ((llCode > 15) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ ++ if (ZSTD_32bits() || (totalBits > 64 - 7 - (LLFSELog + MLFSELog + OffFSELog))) ++ BIT_reloadDStream(&seqState->DStream); ++ ++ /* ANS state update */ ++ FSE_updateState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ ++ FSE_updateState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ ++ if (ZSTD_32bits()) ++ BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ ++ FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ ++ ++ seq.match = NULL; ++ ++ return seq; ++} ++ ++FORCE_INLINE ++size_t ZSTD_execSequence(BYTE *op, BYTE *const oend, seq_t sequence, const BYTE **litPtr, const BYTE *const litLimit, const BYTE *const base, ++ const BYTE *const vBase, const BYTE *const dictEnd) ++{ ++ BYTE *const oLitEnd = op + sequence.litLength; ++ size_t const sequenceLength = sequence.litLength + sequence.matchLength; ++ BYTE *const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ ++ BYTE *const oend_w = oend - WILDCOPY_OVERLENGTH; ++ const BYTE *const iLitEnd = *litPtr + sequence.litLength; ++ const BYTE *match = oLitEnd - sequence.offset; ++ ++ /* check */ ++ if (oMatchEnd > oend) ++ return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ ++ if (iLitEnd > litLimit) ++ return ERROR(corruption_detected); /* over-read beyond lit buffer */ ++ if (oLitEnd > oend_w) ++ return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, base, vBase, dictEnd); ++ ++ /* copy Literals */ ++ ZSTD_copy8(op, *litPtr); ++ if (sequence.litLength > 8) ++ ZSTD_wildcopy(op + 8, (*litPtr) + 8, ++ sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */ ++ op = oLitEnd; ++ *litPtr = iLitEnd; /* update for next sequence */ ++ ++ /* copy Match */ ++ if (sequence.offset > (size_t)(oLitEnd - base)) { ++ /* offset beyond prefix */ ++ if (sequence.offset > (size_t)(oLitEnd - vBase)) ++ return ERROR(corruption_detected); ++ match = dictEnd + (match - base); ++ if (match + sequence.matchLength <= dictEnd) { ++ memmove(oLitEnd, match, sequence.matchLength); ++ return sequenceLength; ++ } ++ /* span extDict & currPrefixSegment */ ++ { ++ size_t const length1 = dictEnd - match; ++ memmove(oLitEnd, match, length1); ++ op = oLitEnd + length1; ++ sequence.matchLength -= length1; ++ match = base; ++ if (op > oend_w || sequence.matchLength < MINMATCH) { ++ U32 i; ++ for (i = 0; i < sequence.matchLength; ++i) ++ op[i] = match[i]; ++ return sequenceLength; ++ } ++ } ++ } ++ /* Requirement: op <= oend_w && sequence.matchLength >= MINMATCH */ ++ ++ /* match within prefix */ ++ if (sequence.offset < 8) { ++ /* close range match, overlap */ ++ static const U32 dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4}; /* added */ ++ static const int dec64table[] = {8, 8, 8, 7, 8, 9, 10, 11}; /* subtracted */ ++ int const sub2 = dec64table[sequence.offset]; ++ op[0] = match[0]; ++ op[1] = match[1]; ++ op[2] = match[2]; ++ op[3] = match[3]; ++ match += dec32table[sequence.offset]; ++ ZSTD_copy4(op + 4, match); ++ match -= sub2; ++ } else { ++ ZSTD_copy8(op, match); ++ } ++ op += 8; ++ match += 8; ++ ++ if (oMatchEnd > oend - (16 - MINMATCH)) { ++ if (op < oend_w) { ++ ZSTD_wildcopy(op, match, oend_w - op); ++ match += oend_w - op; ++ op = oend_w; ++ } ++ while (op < oMatchEnd) ++ *op++ = *match++; ++ } else { ++ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength - 8); /* works even if matchLength < 8 */ ++ } ++ return sequenceLength; ++} ++ ++static size_t INIT ZSTD_decompressSequences(ZSTD_DCtx *dctx, void *dst, size_t maxDstSize, const void *seqStart, size_t seqSize) ++{ ++ const BYTE *ip = (const BYTE *)seqStart; ++ const BYTE *const iend = ip + seqSize; ++ BYTE *const ostart = (BYTE * const)dst; ++ BYTE *const oend = ostart + maxDstSize; ++ BYTE *op = ostart; ++ const BYTE *litPtr = dctx->litPtr; ++ const BYTE *const litEnd = litPtr + dctx->litSize; ++ const BYTE *const base = (const BYTE *)(dctx->base); ++ const BYTE *const vBase = (const BYTE *)(dctx->vBase); ++ const BYTE *const dictEnd = (const BYTE *)(dctx->dictEnd); ++ int nbSeq; ++ ++ /* Build Decoding Tables */ ++ { ++ size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); ++ if (ZSTD_isError(seqHSize)) ++ return seqHSize; ++ ip += seqHSize; ++ } ++ ++ /* Regen sequences */ ++ if (nbSeq) { ++ seqState_t seqState; ++ dctx->fseEntropy = 1; ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ seqState.prevOffset[i] = dctx->entropy.rep[i]; ++ } ++ CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend - ip), corruption_detected); ++ FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); ++ FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ++ FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); ++ ++ for (; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq;) { ++ nbSeq--; ++ { ++ seq_t const sequence = ZSTD_decodeSequence(&seqState); ++ size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, base, vBase, dictEnd); ++ if (ZSTD_isError(oneSeqSize)) ++ return oneSeqSize; ++ op += oneSeqSize; ++ } ++ } ++ ++ /* check if reached exact end */ ++ if (nbSeq) ++ return ERROR(corruption_detected); ++ /* save reps for next block */ ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); ++ } ++ } ++ ++ /* last literal segment */ ++ { ++ size_t const lastLLSize = litEnd - litPtr; ++ if (lastLLSize > (size_t)(oend - op)) ++ return ERROR(dstSize_tooSmall); ++ memcpy(op, litPtr, lastLLSize); ++ op += lastLLSize; ++ } ++ ++ return op - ostart; ++} ++ ++FORCE_INLINE seq_t INIT ZSTD_decodeSequenceLong_generic(seqState_t *seqState, int const longOffsets) ++{ ++ seq_t seq; ++ ++ U32 const llCode = FSE_peekSymbol(&seqState->stateLL); ++ U32 const mlCode = FSE_peekSymbol(&seqState->stateML); ++ U32 const ofCode = FSE_peekSymbol(&seqState->stateOffb); /* <= maxOff, by table construction */ ++ ++ U32 const llBits = LL_bits[llCode]; ++ U32 const mlBits = ML_bits[mlCode]; ++ U32 const ofBits = ofCode; ++ U32 const totalBits = llBits + mlBits + ofBits; ++ ++ static const U32 LL_base[MaxLL + 1] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, ++ 20, 22, 24, 28, 32, 40, 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000}; ++ ++ static const U32 ML_base[MaxML + 1] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ++ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, ++ 43, 47, 51, 59, 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, 0x1003, 0x2003, 0x4003, 0x8003, 0x10003}; ++ ++ static const U32 OF_base[MaxOff + 1] = {0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, 0xFD, 0x1FD, ++ 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, ++ 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD}; ++ ++ /* sequence */ ++ { ++ size_t offset; ++ if (!ofCode) ++ offset = 0; ++ else { ++ if (longOffsets) { ++ int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN); ++ offset = OF_base[ofCode] + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); ++ if (ZSTD_32bits() || extraBits) ++ BIT_reloadDStream(&seqState->DStream); ++ if (extraBits) ++ offset += BIT_readBitsFast(&seqState->DStream, extraBits); ++ } else { ++ offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ ++ if (ZSTD_32bits()) ++ BIT_reloadDStream(&seqState->DStream); ++ } ++ } ++ ++ if (ofCode <= 1) { ++ offset += (llCode == 0); ++ if (offset) { ++ size_t temp = (offset == 3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; ++ temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ ++ if (offset != 1) ++ seqState->prevOffset[2] = seqState->prevOffset[1]; ++ seqState->prevOffset[1] = seqState->prevOffset[0]; ++ seqState->prevOffset[0] = offset = temp; ++ } else { ++ offset = seqState->prevOffset[0]; ++ } ++ } else { ++ seqState->prevOffset[2] = seqState->prevOffset[1]; ++ seqState->prevOffset[1] = seqState->prevOffset[0]; ++ seqState->prevOffset[0] = offset; ++ } ++ seq.offset = offset; ++ } ++ ++ seq.matchLength = ML_base[mlCode] + ((mlCode > 31) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ ++ if (ZSTD_32bits() && (mlBits + llBits > 24)) ++ BIT_reloadDStream(&seqState->DStream); ++ ++ seq.litLength = LL_base[llCode] + ((llCode > 15) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ ++ if (ZSTD_32bits() || (totalBits > 64 - 7 - (LLFSELog + MLFSELog + OffFSELog))) ++ BIT_reloadDStream(&seqState->DStream); ++ ++ { ++ size_t const pos = seqState->pos + seq.litLength; ++ seq.match = seqState->base + pos - seq.offset; /* single memory segment */ ++ if (seq.offset > pos) ++ seq.match += seqState->gotoDict; /* separate memory segment */ ++ seqState->pos = pos + seq.matchLength; ++ } ++ ++ /* ANS state update */ ++ FSE_updateState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ ++ FSE_updateState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ ++ if (ZSTD_32bits()) ++ BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ ++ FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ ++ ++ return seq; ++} ++ ++static seq_t INIT ZSTD_decodeSequenceLong(seqState_t *seqState, unsigned const windowSize) ++{ ++ if (ZSTD_highbit32(windowSize) > STREAM_ACCUMULATOR_MIN) { ++ return ZSTD_decodeSequenceLong_generic(seqState, 1); ++ } else { ++ return ZSTD_decodeSequenceLong_generic(seqState, 0); ++ } ++} ++ ++FORCE_INLINE ++size_t ZSTD_execSequenceLong(BYTE *op, BYTE *const oend, seq_t sequence, const BYTE **litPtr, const BYTE *const litLimit, const BYTE *const base, ++ const BYTE *const vBase, const BYTE *const dictEnd) ++{ ++ BYTE *const oLitEnd = op + sequence.litLength; ++ size_t const sequenceLength = sequence.litLength + sequence.matchLength; ++ BYTE *const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ ++ BYTE *const oend_w = oend - WILDCOPY_OVERLENGTH; ++ const BYTE *const iLitEnd = *litPtr + sequence.litLength; ++ const BYTE *match = sequence.match; ++ ++ /* check */ ++ if (oMatchEnd > oend) ++ return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ ++ if (iLitEnd > litLimit) ++ return ERROR(corruption_detected); /* over-read beyond lit buffer */ ++ if (oLitEnd > oend_w) ++ return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, base, vBase, dictEnd); ++ ++ /* copy Literals */ ++ ZSTD_copy8(op, *litPtr); ++ if (sequence.litLength > 8) ++ ZSTD_wildcopy(op + 8, (*litPtr) + 8, ++ sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */ ++ op = oLitEnd; ++ *litPtr = iLitEnd; /* update for next sequence */ ++ ++ /* copy Match */ ++ if (sequence.offset > (size_t)(oLitEnd - base)) { ++ /* offset beyond prefix */ ++ if (sequence.offset > (size_t)(oLitEnd - vBase)) ++ return ERROR(corruption_detected); ++ if (match + sequence.matchLength <= dictEnd) { ++ memmove(oLitEnd, match, sequence.matchLength); ++ return sequenceLength; ++ } ++ /* span extDict & currPrefixSegment */ ++ { ++ size_t const length1 = dictEnd - match; ++ memmove(oLitEnd, match, length1); ++ op = oLitEnd + length1; ++ sequence.matchLength -= length1; ++ match = base; ++ if (op > oend_w || sequence.matchLength < MINMATCH) { ++ U32 i; ++ for (i = 0; i < sequence.matchLength; ++i) ++ op[i] = match[i]; ++ return sequenceLength; ++ } ++ } ++ } ++ /* Requirement: op <= oend_w && sequence.matchLength >= MINMATCH */ ++ ++ /* match within prefix */ ++ if (sequence.offset < 8) { ++ /* close range match, overlap */ ++ static const U32 dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4}; /* added */ ++ static const int dec64table[] = {8, 8, 8, 7, 8, 9, 10, 11}; /* subtracted */ ++ int const sub2 = dec64table[sequence.offset]; ++ op[0] = match[0]; ++ op[1] = match[1]; ++ op[2] = match[2]; ++ op[3] = match[3]; ++ match += dec32table[sequence.offset]; ++ ZSTD_copy4(op + 4, match); ++ match -= sub2; ++ } else { ++ ZSTD_copy8(op, match); ++ } ++ op += 8; ++ match += 8; ++ ++ if (oMatchEnd > oend - (16 - MINMATCH)) { ++ if (op < oend_w) { ++ ZSTD_wildcopy(op, match, oend_w - op); ++ match += oend_w - op; ++ op = oend_w; ++ } ++ while (op < oMatchEnd) ++ *op++ = *match++; ++ } else { ++ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength - 8); /* works even if matchLength < 8 */ ++ } ++ return sequenceLength; ++} ++ ++static size_t INIT ZSTD_decompressSequencesLong(ZSTD_DCtx *dctx, void *dst, size_t maxDstSize, const void *seqStart, size_t seqSize) ++{ ++ const BYTE *ip = (const BYTE *)seqStart; ++ const BYTE *const iend = ip + seqSize; ++ BYTE *const ostart = (BYTE * const)dst; ++ BYTE *const oend = ostart + maxDstSize; ++ BYTE *op = ostart; ++ const BYTE *litPtr = dctx->litPtr; ++ const BYTE *const litEnd = litPtr + dctx->litSize; ++ const BYTE *const base = (const BYTE *)(dctx->base); ++ const BYTE *const vBase = (const BYTE *)(dctx->vBase); ++ const BYTE *const dictEnd = (const BYTE *)(dctx->dictEnd); ++ unsigned const windowSize = dctx->fParams.windowSize; ++ int nbSeq; ++ ++ /* Build Decoding Tables */ ++ { ++ size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); ++ if (ZSTD_isError(seqHSize)) ++ return seqHSize; ++ ip += seqHSize; ++ } ++ ++ /* Regen sequences */ ++ if (nbSeq) { ++#define STORED_SEQS 4 ++#define STOSEQ_MASK (STORED_SEQS - 1) ++#define ADVANCED_SEQS 4 ++ seq_t *sequences = (seq_t *)dctx->entropy.workspace; ++ int const seqAdvance = MIN(nbSeq, ADVANCED_SEQS); ++ seqState_t seqState; ++ int seqNb; ++ ZSTD_STATIC_ASSERT(sizeof(dctx->entropy.workspace) >= sizeof(seq_t) * STORED_SEQS); ++ dctx->fseEntropy = 1; ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ seqState.prevOffset[i] = dctx->entropy.rep[i]; ++ } ++ seqState.base = base; ++ seqState.pos = (size_t)(op - base); ++ seqState.gotoDict = (uPtrDiff)dictEnd - (uPtrDiff)base; /* cast to avoid undefined behaviour */ ++ CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend - ip), corruption_detected); ++ FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); ++ FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ++ FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); ++ ++ /* prepare in advance */ ++ for (seqNb = 0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && seqNb < seqAdvance; seqNb++) { ++ sequences[seqNb] = ZSTD_decodeSequenceLong(&seqState, windowSize); ++ } ++ if (seqNb < seqAdvance) ++ return ERROR(corruption_detected); ++ ++ /* decode and decompress */ ++ for (; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && seqNb < nbSeq; seqNb++) { ++ seq_t const sequence = ZSTD_decodeSequenceLong(&seqState, windowSize); ++ size_t const oneSeqSize = ++ ZSTD_execSequenceLong(op, oend, sequences[(seqNb - ADVANCED_SEQS) & STOSEQ_MASK], &litPtr, litEnd, base, vBase, dictEnd); ++ if (ZSTD_isError(oneSeqSize)) ++ return oneSeqSize; ++ ZSTD_PREFETCH(sequence.match); ++ sequences[seqNb & STOSEQ_MASK] = sequence; ++ op += oneSeqSize; ++ } ++ if (seqNb < nbSeq) ++ return ERROR(corruption_detected); ++ ++ /* finish queue */ ++ seqNb -= seqAdvance; ++ for (; seqNb < nbSeq; seqNb++) { ++ size_t const oneSeqSize = ZSTD_execSequenceLong(op, oend, sequences[seqNb & STOSEQ_MASK], &litPtr, litEnd, base, vBase, dictEnd); ++ if (ZSTD_isError(oneSeqSize)) ++ return oneSeqSize; ++ op += oneSeqSize; ++ } ++ ++ /* save reps for next block */ ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); ++ } ++ } ++ ++ /* last literal segment */ ++ { ++ size_t const lastLLSize = litEnd - litPtr; ++ if (lastLLSize > (size_t)(oend - op)) ++ return ERROR(dstSize_tooSmall); ++ memcpy(op, litPtr, lastLLSize); ++ op += lastLLSize; ++ } ++ ++ return op - ostart; ++} ++ ++static size_t INIT ZSTD_decompressBlock_internal(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ /* blockType == blockCompressed */ ++ const BYTE *ip = (const BYTE *)src; ++ ++ if (srcSize >= ZSTD_BLOCKSIZE_ABSOLUTEMAX) ++ return ERROR(srcSize_wrong); ++ ++ /* Decode literals section */ ++ { ++ size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize); ++ if (ZSTD_isError(litCSize)) ++ return litCSize; ++ ip += litCSize; ++ srcSize -= litCSize; ++ } ++ if (sizeof(size_t) > 4) /* do not enable prefetching on 32-bits x86, as it's performance detrimental */ ++ /* likely because of register pressure */ ++ /* if that's the correct cause, then 32-bits ARM should be affected differently */ ++ /* it would be good to test this on ARM real hardware, to see if prefetch version improves speed */ ++ if (dctx->fParams.windowSize > (1 << 23)) ++ return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize); ++ return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize); ++} ++ ++static void INIT ZSTD_checkContinuity(ZSTD_DCtx *dctx, const void *dst) ++{ ++ if (dst != dctx->previousDstEnd) { /* not contiguous */ ++ dctx->dictEnd = dctx->previousDstEnd; ++ dctx->vBase = (const char *)dst - ((const char *)(dctx->previousDstEnd) - (const char *)(dctx->base)); ++ dctx->base = dst; ++ dctx->previousDstEnd = dst; ++ } ++} ++ ++size_t INIT ZSTD_decompressBlock(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ ++ size_t dSize; ++ ZSTD_checkContinuity(dctx, dst); ++ dSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize); ++ dctx->previousDstEnd = (char *)dst + dSize; ++ return dSize; ++} ++ ++/** ZSTD_insertBlock() : ++ insert `src` block into `dctx` history. Useful to track uncompressed blocks. */ ++size_t INIT ZSTD_insertBlock(ZSTD_DCtx *dctx, const void *blockStart, size_t blockSize) ++{ ++ ZSTD_checkContinuity(dctx, blockStart); ++ dctx->previousDstEnd = (const char *)blockStart + blockSize; ++ return blockSize; ++} ++ ++size_t INIT ZSTD_generateNxBytes(void *dst, size_t dstCapacity, BYTE byte, size_t length) ++{ ++ if (length > dstCapacity) ++ return ERROR(dstSize_tooSmall); ++ memset(dst, byte, length); ++ return length; ++} ++ ++/** ZSTD_findFrameCompressedSize() : ++ * compatible with legacy mode ++ * `src` must point to the start of a ZSTD frame, ZSTD legacy frame, or skippable frame ++ * `srcSize` must be at least as large as the frame contained ++ * @return : the compressed size of the frame starting at `src` */ ++size_t INIT ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) ++{ ++ if (srcSize >= ZSTD_skippableHeaderSize && (ZSTD_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { ++ return ZSTD_skippableHeaderSize + ZSTD_readLE32((const BYTE *)src + 4); ++ } else { ++ const BYTE *ip = (const BYTE *)src; ++ const BYTE *const ipstart = ip; ++ size_t remainingSize = srcSize; ++ ZSTD_frameParams fParams; ++ ++ size_t const headerSize = ZSTD_frameHeaderSize(ip, remainingSize); ++ if (ZSTD_isError(headerSize)) ++ return headerSize; ++ ++ /* Frame Header */ ++ { ++ size_t const ret = ZSTD_getFrameParams(&fParams, ip, remainingSize); ++ if (ZSTD_isError(ret)) ++ return ret; ++ if (ret > 0) ++ return ERROR(srcSize_wrong); ++ } ++ ++ ip += headerSize; ++ remainingSize -= headerSize; ++ ++ /* Loop on each block */ ++ while (1) { ++ blockProperties_t blockProperties; ++ size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); ++ if (ZSTD_isError(cBlockSize)) ++ return cBlockSize; ++ ++ if (ZSTD_blockHeaderSize + cBlockSize > remainingSize) ++ return ERROR(srcSize_wrong); ++ ++ ip += ZSTD_blockHeaderSize + cBlockSize; ++ remainingSize -= ZSTD_blockHeaderSize + cBlockSize; ++ ++ if (blockProperties.lastBlock) ++ break; ++ } ++ ++ if (fParams.checksumFlag) { /* Frame content checksum */ ++ if (remainingSize < 4) ++ return ERROR(srcSize_wrong); ++ ip += 4; ++ remainingSize -= 4; ++ } ++ ++ return ip - ipstart; ++ } ++} ++ ++/*! ZSTD_decompressFrame() : ++* @dctx must be properly initialized */ ++static size_t INIT ZSTD_decompressFrame(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void **srcPtr, size_t *srcSizePtr) ++{ ++ const BYTE *ip = (const BYTE *)(*srcPtr); ++ BYTE *const ostart = (BYTE * const)dst; ++ BYTE *const oend = ostart + dstCapacity; ++ BYTE *op = ostart; ++ size_t remainingSize = *srcSizePtr; ++ ++ /* check */ ++ if (remainingSize < ZSTD_frameHeaderSize_min + ZSTD_blockHeaderSize) ++ return ERROR(srcSize_wrong); ++ ++ /* Frame Header */ ++ { ++ size_t const frameHeaderSize = ZSTD_frameHeaderSize(ip, ZSTD_frameHeaderSize_prefix); ++ if (ZSTD_isError(frameHeaderSize)) ++ return frameHeaderSize; ++ if (remainingSize < frameHeaderSize + ZSTD_blockHeaderSize) ++ return ERROR(srcSize_wrong); ++ CHECK_F(ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize)); ++ ip += frameHeaderSize; ++ remainingSize -= frameHeaderSize; ++ } ++ ++ /* Loop on each block */ ++ while (1) { ++ size_t decodedSize; ++ blockProperties_t blockProperties; ++ size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); ++ if (ZSTD_isError(cBlockSize)) ++ return cBlockSize; ++ ++ ip += ZSTD_blockHeaderSize; ++ remainingSize -= ZSTD_blockHeaderSize; ++ if (cBlockSize > remainingSize) ++ return ERROR(srcSize_wrong); ++ ++ switch (blockProperties.blockType) { ++ case bt_compressed: decodedSize = ZSTD_decompressBlock_internal(dctx, op, oend - op, ip, cBlockSize); break; ++ case bt_raw: decodedSize = ZSTD_copyRawBlock(op, oend - op, ip, cBlockSize); break; ++ case bt_rle: decodedSize = ZSTD_generateNxBytes(op, oend - op, *ip, blockProperties.origSize); break; ++ case bt_reserved: ++ default: return ERROR(corruption_detected); ++ } ++ ++ if (ZSTD_isError(decodedSize)) ++ return decodedSize; ++ if (dctx->fParams.checksumFlag) ++ xxh64_update(&dctx->xxhState, op, decodedSize); ++ op += decodedSize; ++ ip += cBlockSize; ++ remainingSize -= cBlockSize; ++ if (blockProperties.lastBlock) ++ break; ++ } ++ ++ if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */ ++ U32 const checkCalc = (U32)xxh64_digest(&dctx->xxhState); ++ U32 checkRead; ++ if (remainingSize < 4) ++ return ERROR(checksum_wrong); ++ checkRead = ZSTD_readLE32(ip); ++ if (checkRead != checkCalc) ++ return ERROR(checksum_wrong); ++ ip += 4; ++ remainingSize -= 4; ++ } ++ ++ /* Allow caller to get size read */ ++ *srcPtr = ip; ++ *srcSizePtr = remainingSize; ++ return op - ostart; ++} ++ ++static const void INIT *ZSTD_DDictDictContent(const ZSTD_DDict *ddict); ++static size_t INIT ZSTD_DDictDictSize(const ZSTD_DDict *ddict); ++ ++static size_t INIT ZSTD_decompressMultiFrame(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const void *dict, size_t dictSize, ++ const ZSTD_DDict *ddict) ++{ ++ void *const dststart = dst; ++ ++ if (ddict) { ++ if (dict) { ++ /* programmer error, these two cases should be mutually exclusive */ ++ return ERROR(GENERIC); ++ } ++ ++ dict = ZSTD_DDictDictContent(ddict); ++ dictSize = ZSTD_DDictDictSize(ddict); ++ } ++ ++ while (srcSize >= ZSTD_frameHeaderSize_prefix) { ++ U32 magicNumber; ++ ++ magicNumber = ZSTD_readLE32(src); ++ if (magicNumber != ZSTD_MAGICNUMBER) { ++ if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { ++ size_t skippableSize; ++ if (srcSize < ZSTD_skippableHeaderSize) ++ return ERROR(srcSize_wrong); ++ skippableSize = ZSTD_readLE32((const BYTE *)src + 4) + ZSTD_skippableHeaderSize; ++ if (srcSize < skippableSize) { ++ return ERROR(srcSize_wrong); ++ } ++ ++ src = (const BYTE *)src + skippableSize; ++ srcSize -= skippableSize; ++ continue; ++ } else { ++ return ERROR(prefix_unknown); ++ } ++ } ++ ++ if (ddict) { ++ /* we were called from ZSTD_decompress_usingDDict */ ++ ZSTD_refDDict(dctx, ddict); ++ } else { ++ /* this will initialize correctly with no dict if dict == NULL, so ++ * use this in all cases but ddict */ ++ CHECK_F(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize)); ++ } ++ ZSTD_checkContinuity(dctx, dst); ++ ++ { ++ const size_t res = ZSTD_decompressFrame(dctx, dst, dstCapacity, &src, &srcSize); ++ if (ZSTD_isError(res)) ++ return res; ++ /* don't need to bounds check this, ZSTD_decompressFrame will have ++ * already */ ++ dst = (BYTE *)dst + res; ++ dstCapacity -= res; ++ } ++ } ++ ++ if (srcSize) ++ return ERROR(srcSize_wrong); /* input not entirely consumed */ ++ ++ return (BYTE *)dst - (BYTE *)dststart; ++} ++ ++size_t INIT ZSTD_decompress_usingDict(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const void *dict, size_t dictSize) ++{ ++ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, dict, dictSize, NULL); ++} ++ ++size_t INIT ZSTD_decompressDCtx(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ ++ return ZSTD_decompress_usingDict(dctx, dst, dstCapacity, src, srcSize, NULL, 0); ++} ++ ++/*-************************************** ++* Advanced Streaming Decompression API ++* Bufferless and synchronous ++****************************************/ ++size_t INIT ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx *dctx) { return dctx->expected; } ++ ++ZSTD_nextInputType_e INIT ZSTD_nextInputType(ZSTD_DCtx *dctx) ++{ ++ switch (dctx->stage) { ++ default: /* should not happen */ ++ case ZSTDds_getFrameHeaderSize: ++ case ZSTDds_decodeFrameHeader: return ZSTDnit_frameHeader; ++ case ZSTDds_decodeBlockHeader: return ZSTDnit_blockHeader; ++ case ZSTDds_decompressBlock: return ZSTDnit_block; ++ case ZSTDds_decompressLastBlock: return ZSTDnit_lastBlock; ++ case ZSTDds_checkChecksum: return ZSTDnit_checksum; ++ case ZSTDds_decodeSkippableHeader: ++ case ZSTDds_skipFrame: return ZSTDnit_skippableFrame; ++ } ++} ++ ++int INIT ZSTD_isSkipFrame(ZSTD_DCtx *dctx) { return dctx->stage == ZSTDds_skipFrame; } /* for zbuff */ ++ ++/** ZSTD_decompressContinue() : ++* @return : nb of bytes generated into `dst` (necessarily <= `dstCapacity) ++* or an error code, which can be tested using ZSTD_isError() */ ++size_t INIT ZSTD_decompressContinue(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ ++ /* Sanity check */ ++ if (srcSize != dctx->expected) ++ return ERROR(srcSize_wrong); ++ if (dstCapacity) ++ ZSTD_checkContinuity(dctx, dst); ++ ++ switch (dctx->stage) { ++ case ZSTDds_getFrameHeaderSize: ++ if (srcSize != ZSTD_frameHeaderSize_prefix) ++ return ERROR(srcSize_wrong); /* impossible */ ++ if ((ZSTD_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */ ++ memcpy(dctx->headerBuffer, src, ZSTD_frameHeaderSize_prefix); ++ dctx->expected = ZSTD_skippableHeaderSize - ZSTD_frameHeaderSize_prefix; /* magic number + skippable frame length */ ++ dctx->stage = ZSTDds_decodeSkippableHeader; ++ return 0; ++ } ++ dctx->headerSize = ZSTD_frameHeaderSize(src, ZSTD_frameHeaderSize_prefix); ++ if (ZSTD_isError(dctx->headerSize)) ++ return dctx->headerSize; ++ memcpy(dctx->headerBuffer, src, ZSTD_frameHeaderSize_prefix); ++ if (dctx->headerSize > ZSTD_frameHeaderSize_prefix) { ++ dctx->expected = dctx->headerSize - ZSTD_frameHeaderSize_prefix; ++ dctx->stage = ZSTDds_decodeFrameHeader; ++ return 0; ++ } ++ dctx->expected = 0; /* not necessary to copy more */ ++ /* fall through */ ++ ++ case ZSTDds_decodeFrameHeader: ++ memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected); ++ CHECK_F(ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize)); ++ dctx->expected = ZSTD_blockHeaderSize; ++ dctx->stage = ZSTDds_decodeBlockHeader; ++ return 0; ++ ++ case ZSTDds_decodeBlockHeader: { ++ blockProperties_t bp; ++ size_t const cBlockSize = ZSTD_getcBlockSize(src, ZSTD_blockHeaderSize, &bp); ++ if (ZSTD_isError(cBlockSize)) ++ return cBlockSize; ++ dctx->expected = cBlockSize; ++ dctx->bType = bp.blockType; ++ dctx->rleSize = bp.origSize; ++ if (cBlockSize) { ++ dctx->stage = bp.lastBlock ? ZSTDds_decompressLastBlock : ZSTDds_decompressBlock; ++ return 0; ++ } ++ /* empty block */ ++ if (bp.lastBlock) { ++ if (dctx->fParams.checksumFlag) { ++ dctx->expected = 4; ++ dctx->stage = ZSTDds_checkChecksum; ++ } else { ++ dctx->expected = 0; /* end of frame */ ++ dctx->stage = ZSTDds_getFrameHeaderSize; ++ } ++ } else { ++ dctx->expected = 3; /* go directly to next header */ ++ dctx->stage = ZSTDds_decodeBlockHeader; ++ } ++ return 0; ++ } ++ case ZSTDds_decompressLastBlock: ++ case ZSTDds_decompressBlock: { ++ size_t rSize; ++ switch (dctx->bType) { ++ case bt_compressed: rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize); break; ++ case bt_raw: rSize = ZSTD_copyRawBlock(dst, dstCapacity, src, srcSize); break; ++ case bt_rle: rSize = ZSTD_setRleBlock(dst, dstCapacity, src, srcSize, dctx->rleSize); break; ++ case bt_reserved: /* should never happen */ ++ default: return ERROR(corruption_detected); ++ } ++ if (ZSTD_isError(rSize)) ++ return rSize; ++ if (dctx->fParams.checksumFlag) ++ xxh64_update(&dctx->xxhState, dst, rSize); ++ ++ if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */ ++ if (dctx->fParams.checksumFlag) { /* another round for frame checksum */ ++ dctx->expected = 4; ++ dctx->stage = ZSTDds_checkChecksum; ++ } else { ++ dctx->expected = 0; /* ends here */ ++ dctx->stage = ZSTDds_getFrameHeaderSize; ++ } ++ } else { ++ dctx->stage = ZSTDds_decodeBlockHeader; ++ dctx->expected = ZSTD_blockHeaderSize; ++ dctx->previousDstEnd = (char *)dst + rSize; ++ } ++ return rSize; ++ } ++ case ZSTDds_checkChecksum: { ++ U32 const h32 = (U32)xxh64_digest(&dctx->xxhState); ++ U32 const check32 = ZSTD_readLE32(src); /* srcSize == 4, guaranteed by dctx->expected */ ++ if (check32 != h32) ++ return ERROR(checksum_wrong); ++ dctx->expected = 0; ++ dctx->stage = ZSTDds_getFrameHeaderSize; ++ return 0; ++ } ++ case ZSTDds_decodeSkippableHeader: { ++ memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected); ++ dctx->expected = ZSTD_readLE32(dctx->headerBuffer + 4); ++ dctx->stage = ZSTDds_skipFrame; ++ return 0; ++ } ++ case ZSTDds_skipFrame: { ++ dctx->expected = 0; ++ dctx->stage = ZSTDds_getFrameHeaderSize; ++ return 0; ++ } ++ default: ++ return ERROR(GENERIC); /* impossible */ ++ } ++} ++ ++static size_t INIT ZSTD_refDictContent(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) ++{ ++ dctx->dictEnd = dctx->previousDstEnd; ++ dctx->vBase = (const char *)dict - ((const char *)(dctx->previousDstEnd) - (const char *)(dctx->base)); ++ dctx->base = dict; ++ dctx->previousDstEnd = (const char *)dict + dictSize; ++ return 0; ++} ++ ++/* ZSTD_loadEntropy() : ++ * dict : must point at beginning of a valid zstd dictionary ++ * @return : size of entropy tables read */ ++static size_t INIT ZSTD_loadEntropy(ZSTD_entropyTables_t *entropy, const void *const dict, size_t const dictSize) ++{ ++ const BYTE *dictPtr = (const BYTE *)dict; ++ const BYTE *const dictEnd = dictPtr + dictSize; ++ ++ if (dictSize <= 8) ++ return ERROR(dictionary_corrupted); ++ dictPtr += 8; /* skip header = magic + dictID */ ++ ++ { ++ size_t const hSize = HUF_readDTableX4_wksp(entropy->hufTable, dictPtr, dictEnd - dictPtr, entropy->workspace, sizeof(entropy->workspace)); ++ if (HUF_isError(hSize)) ++ return ERROR(dictionary_corrupted); ++ dictPtr += hSize; ++ } ++ ++ { ++ short offcodeNCount[MaxOff + 1]; ++ U32 offcodeMaxValue = MaxOff, offcodeLog; ++ size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd - dictPtr); ++ if (FSE_isError(offcodeHeaderSize)) ++ return ERROR(dictionary_corrupted); ++ if (offcodeLog > OffFSELog) ++ return ERROR(dictionary_corrupted); ++ CHECK_E(FSE_buildDTable_wksp(entropy->OFTable, offcodeNCount, offcodeMaxValue, offcodeLog, entropy->workspace, sizeof(entropy->workspace)), dictionary_corrupted); ++ dictPtr += offcodeHeaderSize; ++ } ++ ++ { ++ short matchlengthNCount[MaxML + 1]; ++ unsigned matchlengthMaxValue = MaxML, matchlengthLog; ++ size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd - dictPtr); ++ if (FSE_isError(matchlengthHeaderSize)) ++ return ERROR(dictionary_corrupted); ++ if (matchlengthLog > MLFSELog) ++ return ERROR(dictionary_corrupted); ++ CHECK_E(FSE_buildDTable_wksp(entropy->MLTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, entropy->workspace, sizeof(entropy->workspace)), dictionary_corrupted); ++ dictPtr += matchlengthHeaderSize; ++ } ++ ++ { ++ short litlengthNCount[MaxLL + 1]; ++ unsigned litlengthMaxValue = MaxLL, litlengthLog; ++ size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd - dictPtr); ++ if (FSE_isError(litlengthHeaderSize)) ++ return ERROR(dictionary_corrupted); ++ if (litlengthLog > LLFSELog) ++ return ERROR(dictionary_corrupted); ++ CHECK_E(FSE_buildDTable_wksp(entropy->LLTable, litlengthNCount, litlengthMaxValue, litlengthLog, entropy->workspace, sizeof(entropy->workspace)), dictionary_corrupted); ++ dictPtr += litlengthHeaderSize; ++ } ++ ++ if (dictPtr + 12 > dictEnd) ++ return ERROR(dictionary_corrupted); ++ { ++ int i; ++ size_t const dictContentSize = (size_t)(dictEnd - (dictPtr + 12)); ++ for (i = 0; i < 3; i++) { ++ U32 const rep = ZSTD_readLE32(dictPtr); ++ dictPtr += 4; ++ if (rep == 0 || rep >= dictContentSize) ++ return ERROR(dictionary_corrupted); ++ entropy->rep[i] = rep; ++ } ++ } ++ ++ return dictPtr - (const BYTE *)dict; ++} ++ ++static size_t INIT ZSTD_decompress_insertDictionary(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) ++{ ++ if (dictSize < 8) ++ return ZSTD_refDictContent(dctx, dict, dictSize); ++ { ++ U32 const magic = ZSTD_readLE32(dict); ++ if (magic != ZSTD_DICT_MAGIC) { ++ return ZSTD_refDictContent(dctx, dict, dictSize); /* pure content mode */ ++ } ++ } ++ dctx->dictID = ZSTD_readLE32((const char *)dict + 4); ++ ++ /* load entropy tables */ ++ { ++ size_t const eSize = ZSTD_loadEntropy(&dctx->entropy, dict, dictSize); ++ if (ZSTD_isError(eSize)) ++ return ERROR(dictionary_corrupted); ++ dict = (const char *)dict + eSize; ++ dictSize -= eSize; ++ } ++ dctx->litEntropy = dctx->fseEntropy = 1; ++ ++ /* reference dictionary content */ ++ return ZSTD_refDictContent(dctx, dict, dictSize); ++} ++ ++size_t INIT ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) ++{ ++ CHECK_F(ZSTD_decompressBegin(dctx)); ++ if (dict && dictSize) ++ CHECK_E(ZSTD_decompress_insertDictionary(dctx, dict, dictSize), dictionary_corrupted); ++ return 0; ++} ++ ++/* ====== ZSTD_DDict ====== */ ++ ++struct ZSTD_DDict_s { ++ void *dictBuffer; ++ const void *dictContent; ++ size_t dictSize; ++ ZSTD_entropyTables_t entropy; ++ U32 dictID; ++ U32 entropyPresent; ++ ZSTD_customMem cMem; ++}; /* typedef'd to ZSTD_DDict within "zstd.h" */ ++ ++size_t INIT ZSTD_DDictWorkspaceBound(void) { return ZSTD_ALIGN(sizeof(ZSTD_stack)) + ZSTD_ALIGN(sizeof(ZSTD_DDict)); } ++ ++static const void INIT *ZSTD_DDictDictContent(const ZSTD_DDict *ddict) { return ddict->dictContent; } ++ ++static size_t INIT ZSTD_DDictDictSize(const ZSTD_DDict *ddict) { return ddict->dictSize; } ++ ++static void INIT ZSTD_refDDict(ZSTD_DCtx *dstDCtx, const ZSTD_DDict *ddict) ++{ ++ ZSTD_decompressBegin(dstDCtx); /* init */ ++ if (ddict) { /* support refDDict on NULL */ ++ dstDCtx->dictID = ddict->dictID; ++ dstDCtx->base = ddict->dictContent; ++ dstDCtx->vBase = ddict->dictContent; ++ dstDCtx->dictEnd = (const BYTE *)ddict->dictContent + ddict->dictSize; ++ dstDCtx->previousDstEnd = dstDCtx->dictEnd; ++ if (ddict->entropyPresent) { ++ dstDCtx->litEntropy = 1; ++ dstDCtx->fseEntropy = 1; ++ dstDCtx->LLTptr = ddict->entropy.LLTable; ++ dstDCtx->MLTptr = ddict->entropy.MLTable; ++ dstDCtx->OFTptr = ddict->entropy.OFTable; ++ dstDCtx->HUFptr = ddict->entropy.hufTable; ++ dstDCtx->entropy.rep[0] = ddict->entropy.rep[0]; ++ dstDCtx->entropy.rep[1] = ddict->entropy.rep[1]; ++ dstDCtx->entropy.rep[2] = ddict->entropy.rep[2]; ++ } else { ++ dstDCtx->litEntropy = 0; ++ dstDCtx->fseEntropy = 0; ++ } ++ } ++} ++ ++static size_t INIT ZSTD_loadEntropy_inDDict(ZSTD_DDict *ddict) ++{ ++ ddict->dictID = 0; ++ ddict->entropyPresent = 0; ++ if (ddict->dictSize < 8) ++ return 0; ++ { ++ U32 const magic = ZSTD_readLE32(ddict->dictContent); ++ if (magic != ZSTD_DICT_MAGIC) ++ return 0; /* pure content mode */ ++ } ++ ddict->dictID = ZSTD_readLE32((const char *)ddict->dictContent + 4); ++ ++ /* load entropy tables */ ++ CHECK_E(ZSTD_loadEntropy(&ddict->entropy, ddict->dictContent, ddict->dictSize), dictionary_corrupted); ++ ddict->entropyPresent = 1; ++ return 0; ++} ++ ++static ZSTD_DDict INIT *ZSTD_createDDict_advanced(const void *dict, size_t dictSize, unsigned byReference, ZSTD_customMem customMem) ++{ ++ if (!customMem.customAlloc || !customMem.customFree) ++ return NULL; ++ ++ { ++ ZSTD_DDict *const ddict = (ZSTD_DDict *)ZSTD_malloc(sizeof(ZSTD_DDict), customMem); ++ if (!ddict) ++ return NULL; ++ ddict->cMem = customMem; ++ ++ if ((byReference) || (!dict) || (!dictSize)) { ++ ddict->dictBuffer = NULL; ++ ddict->dictContent = dict; ++ } else { ++ void *const internalBuffer = ZSTD_malloc(dictSize, customMem); ++ if (!internalBuffer) { ++ ZSTD_freeDDict(ddict); ++ return NULL; ++ } ++ memcpy(internalBuffer, dict, dictSize); ++ ddict->dictBuffer = internalBuffer; ++ ddict->dictContent = internalBuffer; ++ } ++ ddict->dictSize = dictSize; ++ ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ ++ /* parse dictionary content */ ++ { ++ size_t const errorCode = ZSTD_loadEntropy_inDDict(ddict); ++ if (ZSTD_isError(errorCode)) { ++ ZSTD_freeDDict(ddict); ++ return NULL; ++ } ++ } ++ ++ return ddict; ++ } ++} ++ ++/*! ZSTD_initDDict() : ++* Create a digested dictionary, to start decompression without startup delay. ++* `dict` content is copied inside DDict. ++* Consequently, `dict` can be released after `ZSTD_DDict` creation */ ++ZSTD_DDict INIT *ZSTD_initDDict(const void *dict, size_t dictSize, void *workspace, size_t workspaceSize) ++{ ++ ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); ++ return ZSTD_createDDict_advanced(dict, dictSize, 1, stackMem); ++} ++ ++size_t INIT ZSTD_freeDDict(ZSTD_DDict *ddict) ++{ ++ if (ddict == NULL) ++ return 0; /* support free on NULL */ ++ { ++ ZSTD_customMem const cMem = ddict->cMem; ++ ZSTD_free(ddict->dictBuffer, cMem); ++ ZSTD_free(ddict, cMem); ++ return 0; ++ } ++} ++ ++/*! ZSTD_getDictID_fromDict() : ++ * Provides the dictID stored within dictionary. ++ * if @return == 0, the dictionary is not conformant with Zstandard specification. ++ * It can still be loaded, but as a content-only dictionary. */ ++unsigned INIT ZSTD_getDictID_fromDict(const void *dict, size_t dictSize) ++{ ++ if (dictSize < 8) ++ return 0; ++ if (ZSTD_readLE32(dict) != ZSTD_DICT_MAGIC) ++ return 0; ++ return ZSTD_readLE32((const char *)dict + 4); ++} ++ ++/*! ZSTD_getDictID_fromDDict() : ++ * Provides the dictID of the dictionary loaded into `ddict`. ++ * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty. ++ * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */ ++unsigned INIT ZSTD_getDictID_fromDDict(const ZSTD_DDict *ddict) ++{ ++ if (ddict == NULL) ++ return 0; ++ return ZSTD_getDictID_fromDict(ddict->dictContent, ddict->dictSize); ++} ++ ++/*! ZSTD_getDictID_fromFrame() : ++ * Provides the dictID required to decompressed the frame stored within `src`. ++ * If @return == 0, the dictID could not be decoded. ++ * This could for one of the following reasons : ++ * - The frame does not require a dictionary to be decoded (most common case). ++ * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information. ++ * Note : this use case also happens when using a non-conformant dictionary. ++ * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`). ++ * - This is not a Zstandard frame. ++ * When identifying the exact failure cause, it's possible to used ZSTD_getFrameParams(), which will provide a more precise error code. */ ++unsigned INIT ZSTD_getDictID_fromFrame(const void *src, size_t srcSize) ++{ ++ ZSTD_frameParams zfp = {0, 0, 0, 0}; ++ size_t const hError = ZSTD_getFrameParams(&zfp, src, srcSize); ++ if (ZSTD_isError(hError)) ++ return 0; ++ return zfp.dictID; ++} ++ ++/*! ZSTD_decompress_usingDDict() : ++* Decompression using a pre-digested Dictionary ++* Use dictionary without significant overhead. */ ++size_t INIT ZSTD_decompress_usingDDict(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const ZSTD_DDict *ddict) ++{ ++ /* pass content and size in case legacy frames are encountered */ ++ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, NULL, 0, ddict); ++} ++ ++/*===================================== ++* Streaming decompression ++*====================================*/ ++ ++typedef enum { zdss_init, zdss_loadHeader, zdss_read, zdss_load, zdss_flush } ZSTD_dStreamStage; ++ ++/* *** Resource management *** */ ++struct ZSTD_DStream_s { ++ ZSTD_DCtx *dctx; ++ ZSTD_DDict *ddictLocal; ++ const ZSTD_DDict *ddict; ++ ZSTD_frameParams fParams; ++ ZSTD_dStreamStage stage; ++ char *inBuff; ++ size_t inBuffSize; ++ size_t inPos; ++ size_t maxWindowSize; ++ char *outBuff; ++ size_t outBuffSize; ++ size_t outStart; ++ size_t outEnd; ++ size_t blockSize; ++ BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; /* tmp buffer to store frame header */ ++ size_t lhSize; ++ ZSTD_customMem customMem; ++ void *legacyContext; ++ U32 previousLegacyVersion; ++ U32 legacyVersion; ++ U32 hostageByte; ++}; /* typedef'd to ZSTD_DStream within "zstd.h" */ ++ ++size_t INIT ZSTD_DStreamWorkspaceBound(size_t maxWindowSize) ++{ ++ size_t const blockSize = MIN(maxWindowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); ++ size_t const inBuffSize = blockSize; ++ size_t const outBuffSize = maxWindowSize + blockSize + WILDCOPY_OVERLENGTH * 2; ++ return ZSTD_DCtxWorkspaceBound() + ZSTD_ALIGN(sizeof(ZSTD_DStream)) + ZSTD_ALIGN(inBuffSize) + ZSTD_ALIGN(outBuffSize); ++} ++ ++static ZSTD_DStream INIT *ZSTD_createDStream_advanced(ZSTD_customMem customMem) ++{ ++ ZSTD_DStream *zds; ++ ++ if (!customMem.customAlloc || !customMem.customFree) ++ return NULL; ++ ++ zds = (ZSTD_DStream *)ZSTD_malloc(sizeof(ZSTD_DStream), customMem); ++ if (zds == NULL) ++ return NULL; ++ memset(zds, 0, sizeof(ZSTD_DStream)); ++ memcpy(&zds->customMem, &customMem, sizeof(ZSTD_customMem)); ++ zds->dctx = ZSTD_createDCtx_advanced(customMem); ++ if (zds->dctx == NULL) { ++ ZSTD_freeDStream(zds); ++ return NULL; ++ } ++ zds->stage = zdss_init; ++ zds->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT; ++ return zds; ++} ++ ++ZSTD_DStream INIT *ZSTD_initDStream(size_t maxWindowSize, void *workspace, size_t workspaceSize) ++{ ++ ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); ++ ZSTD_DStream *zds = ZSTD_createDStream_advanced(stackMem); ++ if (!zds) { ++ return NULL; ++ } ++ ++ zds->maxWindowSize = maxWindowSize; ++ zds->stage = zdss_loadHeader; ++ zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; ++ ZSTD_freeDDict(zds->ddictLocal); ++ zds->ddictLocal = NULL; ++ zds->ddict = zds->ddictLocal; ++ zds->legacyVersion = 0; ++ zds->hostageByte = 0; ++ ++ { ++ size_t const blockSize = MIN(zds->maxWindowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); ++ size_t const neededOutSize = zds->maxWindowSize + blockSize + WILDCOPY_OVERLENGTH * 2; ++ ++ zds->inBuff = (char *)ZSTD_malloc(blockSize, zds->customMem); ++ zds->inBuffSize = blockSize; ++ zds->outBuff = (char *)ZSTD_malloc(neededOutSize, zds->customMem); ++ zds->outBuffSize = neededOutSize; ++ if (zds->inBuff == NULL || zds->outBuff == NULL) { ++ ZSTD_freeDStream(zds); ++ return NULL; ++ } ++ } ++ return zds; ++} ++ ++ZSTD_DStream INIT *ZSTD_initDStream_usingDDict(size_t maxWindowSize, const ZSTD_DDict *ddict, void *workspace, size_t workspaceSize) ++{ ++ ZSTD_DStream *zds = ZSTD_initDStream(maxWindowSize, workspace, workspaceSize); ++ if (zds) { ++ zds->ddict = ddict; ++ } ++ return zds; ++} ++ ++size_t INIT ZSTD_freeDStream(ZSTD_DStream *zds) ++{ ++ if (zds == NULL) ++ return 0; /* support free on null */ ++ { ++ ZSTD_customMem const cMem = zds->customMem; ++ ZSTD_freeDCtx(zds->dctx); ++ zds->dctx = NULL; ++ ZSTD_freeDDict(zds->ddictLocal); ++ zds->ddictLocal = NULL; ++ ZSTD_free(zds->inBuff, cMem); ++ zds->inBuff = NULL; ++ ZSTD_free(zds->outBuff, cMem); ++ zds->outBuff = NULL; ++ ZSTD_free(zds, cMem); ++ return 0; ++ } ++} ++ ++/* *** Initialization *** */ ++ ++size_t INIT ZSTD_DStreamInSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX + ZSTD_blockHeaderSize; } ++size_t INIT ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX; } ++ ++size_t INIT ZSTD_resetDStream(ZSTD_DStream *zds) ++{ ++ zds->stage = zdss_loadHeader; ++ zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; ++ zds->legacyVersion = 0; ++ zds->hostageByte = 0; ++ return ZSTD_frameHeaderSize_prefix; ++} ++ ++/* ***** Decompression ***** */ ++ ++ZSTD_STATIC size_t INIT ZSTD_limitCopy(void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ ++ size_t const length = MIN(dstCapacity, srcSize); ++ memcpy(dst, src, length); ++ return length; ++} ++ ++size_t INIT ZSTD_decompressStream(ZSTD_DStream *zds, ZSTD_outBuffer *output, ZSTD_inBuffer *input) ++{ ++ const char *const istart = (const char *)(input->src) + input->pos; ++ const char *const iend = (const char *)(input->src) + input->size; ++ const char *ip = istart; ++ char *const ostart = (char *)(output->dst) + output->pos; ++ char *const oend = (char *)(output->dst) + output->size; ++ char *op = ostart; ++ U32 someMoreWork = 1; ++ ++ while (someMoreWork) { ++ switch (zds->stage) { ++ case zdss_init: ++ ZSTD_resetDStream(zds); /* transparent reset on starting decoding a new frame */ ++ /* fall through */ ++ ++ case zdss_loadHeader: { ++ size_t const hSize = ZSTD_getFrameParams(&zds->fParams, zds->headerBuffer, zds->lhSize); ++ if (ZSTD_isError(hSize)) ++ return hSize; ++ if (hSize != 0) { /* need more input */ ++ size_t const toLoad = hSize - zds->lhSize; /* if hSize!=0, hSize > zds->lhSize */ ++ if (toLoad > (size_t)(iend - ip)) { /* not enough input to load full header */ ++ memcpy(zds->headerBuffer + zds->lhSize, ip, iend - ip); ++ zds->lhSize += iend - ip; ++ input->pos = input->size; ++ return (MAX(ZSTD_frameHeaderSize_min, hSize) - zds->lhSize) + ++ ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ ++ } ++ memcpy(zds->headerBuffer + zds->lhSize, ip, toLoad); ++ zds->lhSize = hSize; ++ ip += toLoad; ++ break; ++ } ++ ++ /* check for single-pass mode opportunity */ ++ if (zds->fParams.frameContentSize && zds->fParams.windowSize /* skippable frame if == 0 */ ++ && (U64)(size_t)(oend - op) >= zds->fParams.frameContentSize) { ++ size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend - istart); ++ if (cSize <= (size_t)(iend - istart)) { ++ size_t const decompressedSize = ZSTD_decompress_usingDDict(zds->dctx, op, oend - op, istart, cSize, zds->ddict); ++ if (ZSTD_isError(decompressedSize)) ++ return decompressedSize; ++ ip = istart + cSize; ++ op += decompressedSize; ++ zds->dctx->expected = 0; ++ zds->stage = zdss_init; ++ someMoreWork = 0; ++ break; ++ } ++ } ++ ++ /* Consume header */ ++ ZSTD_refDDict(zds->dctx, zds->ddict); ++ { ++ size_t const h1Size = ZSTD_nextSrcSizeToDecompress(zds->dctx); /* == ZSTD_frameHeaderSize_prefix */ ++ CHECK_F(ZSTD_decompressContinue(zds->dctx, NULL, 0, zds->headerBuffer, h1Size)); ++ { ++ size_t const h2Size = ZSTD_nextSrcSizeToDecompress(zds->dctx); ++ CHECK_F(ZSTD_decompressContinue(zds->dctx, NULL, 0, zds->headerBuffer + h1Size, h2Size)); ++ } ++ } ++ ++ zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN); ++ if (zds->fParams.windowSize > zds->maxWindowSize) ++ return ERROR(frameParameter_windowTooLarge); ++ ++ /* Buffers are preallocated, but double check */ ++ { ++ size_t const blockSize = MIN(zds->maxWindowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); ++ size_t const neededOutSize = zds->maxWindowSize + blockSize + WILDCOPY_OVERLENGTH * 2; ++ if (zds->inBuffSize < blockSize) { ++ return ERROR(GENERIC); ++ } ++ if (zds->outBuffSize < neededOutSize) { ++ return ERROR(GENERIC); ++ } ++ zds->blockSize = blockSize; ++ } ++ zds->stage = zdss_read; ++ } ++ /* fall through */ ++ ++ case zdss_read: { ++ size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->dctx); ++ if (neededInSize == 0) { /* end of frame */ ++ zds->stage = zdss_init; ++ someMoreWork = 0; ++ break; ++ } ++ if ((size_t)(iend - ip) >= neededInSize) { /* decode directly from src */ ++ const int isSkipFrame = ZSTD_isSkipFrame(zds->dctx); ++ size_t const decodedSize = ZSTD_decompressContinue(zds->dctx, zds->outBuff + zds->outStart, ++ (isSkipFrame ? 0 : zds->outBuffSize - zds->outStart), ip, neededInSize); ++ if (ZSTD_isError(decodedSize)) ++ return decodedSize; ++ ip += neededInSize; ++ if (!decodedSize && !isSkipFrame) ++ break; /* this was just a header */ ++ zds->outEnd = zds->outStart + decodedSize; ++ zds->stage = zdss_flush; ++ break; ++ } ++ if (ip == iend) { ++ someMoreWork = 0; ++ break; ++ } /* no more input */ ++ zds->stage = zdss_load; ++ /* pass-through */ ++ } ++ /* fall through */ ++ ++ case zdss_load: { ++ size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->dctx); ++ size_t const toLoad = neededInSize - zds->inPos; /* should always be <= remaining space within inBuff */ ++ size_t loadedSize; ++ if (toLoad > zds->inBuffSize - zds->inPos) ++ return ERROR(corruption_detected); /* should never happen */ ++ loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, iend - ip); ++ ip += loadedSize; ++ zds->inPos += loadedSize; ++ if (loadedSize < toLoad) { ++ someMoreWork = 0; ++ break; ++ } /* not enough input, wait for more */ ++ ++ /* decode loaded input */ ++ { ++ const int isSkipFrame = ZSTD_isSkipFrame(zds->dctx); ++ size_t const decodedSize = ZSTD_decompressContinue(zds->dctx, zds->outBuff + zds->outStart, zds->outBuffSize - zds->outStart, ++ zds->inBuff, neededInSize); ++ if (ZSTD_isError(decodedSize)) ++ return decodedSize; ++ zds->inPos = 0; /* input is consumed */ ++ if (!decodedSize && !isSkipFrame) { ++ zds->stage = zdss_read; ++ break; ++ } /* this was just a header */ ++ zds->outEnd = zds->outStart + decodedSize; ++ zds->stage = zdss_flush; ++ /* pass-through */ ++ } ++ } ++ /* fall through */ ++ ++ case zdss_flush: { ++ size_t const toFlushSize = zds->outEnd - zds->outStart; ++ size_t const flushedSize = ZSTD_limitCopy(op, oend - op, zds->outBuff + zds->outStart, toFlushSize); ++ op += flushedSize; ++ zds->outStart += flushedSize; ++ if (flushedSize == toFlushSize) { /* flush completed */ ++ zds->stage = zdss_read; ++ if (zds->outStart + zds->blockSize > zds->outBuffSize) ++ zds->outStart = zds->outEnd = 0; ++ break; ++ } ++ /* cannot complete flush */ ++ someMoreWork = 0; ++ break; ++ } ++ default: ++ return ERROR(GENERIC); /* impossible */ ++ } ++ } ++ ++ /* result */ ++ input->pos += (size_t)(ip - istart); ++ output->pos += (size_t)(op - ostart); ++ { ++ size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zds->dctx); ++ if (!nextSrcSizeHint) { /* frame fully decoded */ ++ if (zds->outEnd == zds->outStart) { /* output fully flushed */ ++ if (zds->hostageByte) { ++ if (input->pos >= input->size) { ++ zds->stage = zdss_read; ++ return 1; ++ } /* can't release hostage (not present) */ ++ input->pos++; /* release hostage */ ++ } ++ return 0; ++ } ++ if (!zds->hostageByte) { /* output not fully flushed; keep last byte as hostage; will be released when all output is flushed */ ++ input->pos--; /* note : pos > 0, otherwise, impossible to finish reading last block */ ++ zds->hostageByte = 1; ++ } ++ return 1; ++ } ++ nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zds->dctx) == ZSTDnit_block); /* preload header of next block */ ++ if (zds->inPos > nextSrcSizeHint) ++ return ERROR(GENERIC); /* should never happen */ ++ nextSrcSizeHint -= zds->inPos; /* already loaded*/ ++ return nextSrcSizeHint; ++ } ++} +diff --git a/xen/common/zstd/entropy_common.c b/xen/common/zstd/entropy_common.c +new file mode 100644 +index 0000000000..bcdb57982b +--- /dev/null ++++ b/xen/common/zstd/entropy_common.c +@@ -0,0 +1,243 @@ ++/* ++ * Common functions of New Generation Entropy library ++ * Copyright (C) 2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++ ++/* ************************************* ++* Dependencies ++***************************************/ ++#include "error_private.h" /* ERR_*, ERROR */ ++#include "fse.h" ++#include "huf.h" ++#include "mem.h" ++ ++/*=== Version ===*/ ++unsigned INIT FSE_versionNumber(void) { return FSE_VERSION_NUMBER; } ++ ++/*=== Error Management ===*/ ++unsigned INIT FSE_isError(size_t code) { return ERR_isError(code); } ++ ++unsigned INIT HUF_isError(size_t code) { return ERR_isError(code); } ++ ++/*-************************************************************** ++* FSE NCount encoding-decoding ++****************************************************************/ ++size_t INIT FSE_readNCount(short *normalizedCounter, unsigned *maxSVPtr, unsigned *tableLogPtr, const void *headerBuffer, size_t hbSize) ++{ ++ const BYTE *const istart = (const BYTE *)headerBuffer; ++ const BYTE *const iend = istart + hbSize; ++ const BYTE *ip = istart; ++ int nbBits; ++ int remaining; ++ int threshold; ++ U32 bitStream; ++ int bitCount; ++ unsigned charnum = 0; ++ int previous0 = 0; ++ ++ if (hbSize < 4) ++ return ERROR(srcSize_wrong); ++ bitStream = ZSTD_readLE32(ip); ++ nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ ++ if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) ++ return ERROR(tableLog_tooLarge); ++ bitStream >>= 4; ++ bitCount = 4; ++ *tableLogPtr = nbBits; ++ remaining = (1 << nbBits) + 1; ++ threshold = 1 << nbBits; ++ nbBits++; ++ ++ while ((remaining > 1) & (charnum <= *maxSVPtr)) { ++ if (previous0) { ++ unsigned n0 = charnum; ++ while ((bitStream & 0xFFFF) == 0xFFFF) { ++ n0 += 24; ++ if (ip < iend - 5) { ++ ip += 2; ++ bitStream = ZSTD_readLE32(ip) >> bitCount; ++ } else { ++ bitStream >>= 16; ++ bitCount += 16; ++ } ++ } ++ while ((bitStream & 3) == 3) { ++ n0 += 3; ++ bitStream >>= 2; ++ bitCount += 2; ++ } ++ n0 += bitStream & 3; ++ bitCount += 2; ++ if (n0 > *maxSVPtr) ++ return ERROR(maxSymbolValue_tooSmall); ++ while (charnum < n0) ++ normalizedCounter[charnum++] = 0; ++ if ((ip <= iend - 7) || (ip + (bitCount >> 3) <= iend - 4)) { ++ ip += bitCount >> 3; ++ bitCount &= 7; ++ bitStream = ZSTD_readLE32(ip) >> bitCount; ++ } else { ++ bitStream >>= 2; ++ } ++ } ++ { ++ int const max = (2 * threshold - 1) - remaining; ++ int count; ++ ++ if ((bitStream & (threshold - 1)) < (U32)max) { ++ count = bitStream & (threshold - 1); ++ bitCount += nbBits - 1; ++ } else { ++ count = bitStream & (2 * threshold - 1); ++ if (count >= threshold) ++ count -= max; ++ bitCount += nbBits; ++ } ++ ++ count--; /* extra accuracy */ ++ remaining -= count < 0 ? -count : count; /* -1 means +1 */ ++ normalizedCounter[charnum++] = (short)count; ++ previous0 = !count; ++ while (remaining < threshold) { ++ nbBits--; ++ threshold >>= 1; ++ } ++ ++ if ((ip <= iend - 7) || (ip + (bitCount >> 3) <= iend - 4)) { ++ ip += bitCount >> 3; ++ bitCount &= 7; ++ } else { ++ bitCount -= (int)(8 * (iend - 4 - ip)); ++ ip = iend - 4; ++ } ++ bitStream = ZSTD_readLE32(ip) >> (bitCount & 31); ++ } ++ } /* while ((remaining>1) & (charnum<=*maxSVPtr)) */ ++ if (remaining != 1) ++ return ERROR(corruption_detected); ++ if (bitCount > 32) ++ return ERROR(corruption_detected); ++ *maxSVPtr = charnum - 1; ++ ++ ip += (bitCount + 7) >> 3; ++ return ip - istart; ++} ++ ++/*! HUF_readStats() : ++ Read compact Huffman tree, saved by HUF_writeCTable(). ++ `huffWeight` is destination buffer. ++ `rankStats` is assumed to be a table of at least HUF_TABLELOG_MAX U32. ++ @return : size read from `src` , or an error Code . ++ Note : Needed by HUF_readCTable() and HUF_readDTableX?() . ++*/ ++size_t INIT HUF_readStats_wksp(BYTE *huffWeight, size_t hwSize, U32 *rankStats, U32 *nbSymbolsPtr, U32 *tableLogPtr, const void *src, size_t srcSize, void *workspace, size_t workspaceSize) ++{ ++ U32 weightTotal; ++ const BYTE *ip = (const BYTE *)src; ++ size_t iSize; ++ size_t oSize; ++ ++ if (!srcSize) ++ return ERROR(srcSize_wrong); ++ iSize = ip[0]; ++ /* memset(huffWeight, 0, hwSize); */ /* is not necessary, even though some analyzer complain ... */ ++ ++ if (iSize >= 128) { /* special header */ ++ oSize = iSize - 127; ++ iSize = ((oSize + 1) / 2); ++ if (iSize + 1 > srcSize) ++ return ERROR(srcSize_wrong); ++ if (oSize >= hwSize) ++ return ERROR(corruption_detected); ++ ip += 1; ++ { ++ U32 n; ++ for (n = 0; n < oSize; n += 2) { ++ huffWeight[n] = ip[n / 2] >> 4; ++ huffWeight[n + 1] = ip[n / 2] & 15; ++ } ++ } ++ } else { /* header compressed with FSE (normal case) */ ++ if (iSize + 1 > srcSize) ++ return ERROR(srcSize_wrong); ++ oSize = FSE_decompress_wksp(huffWeight, hwSize - 1, ip + 1, iSize, 6, workspace, workspaceSize); /* max (hwSize-1) values decoded, as last one is implied */ ++ if (FSE_isError(oSize)) ++ return oSize; ++ } ++ ++ /* collect weight stats */ ++ memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32)); ++ weightTotal = 0; ++ { ++ U32 n; ++ for (n = 0; n < oSize; n++) { ++ if (huffWeight[n] >= HUF_TABLELOG_MAX) ++ return ERROR(corruption_detected); ++ rankStats[huffWeight[n]]++; ++ weightTotal += (1 << huffWeight[n]) >> 1; ++ } ++ } ++ if (weightTotal == 0) ++ return ERROR(corruption_detected); ++ ++ /* get last non-null symbol weight (implied, total must be 2^n) */ ++ { ++ U32 const tableLog = BIT_highbit32(weightTotal) + 1; ++ if (tableLog > HUF_TABLELOG_MAX) ++ return ERROR(corruption_detected); ++ *tableLogPtr = tableLog; ++ /* determine last weight */ ++ { ++ U32 const total = 1 << tableLog; ++ U32 const rest = total - weightTotal; ++ U32 const verif = 1 << BIT_highbit32(rest); ++ U32 const lastWeight = BIT_highbit32(rest) + 1; ++ if (verif != rest) ++ return ERROR(corruption_detected); /* last value must be a clean power of 2 */ ++ huffWeight[oSize] = (BYTE)lastWeight; ++ rankStats[lastWeight]++; ++ } ++ } ++ ++ /* check tree construction validity */ ++ if ((rankStats[1] < 2) || (rankStats[1] & 1)) ++ return ERROR(corruption_detected); /* by construction : at least 2 elts of rank 1, must be even */ ++ ++ /* results */ ++ *nbSymbolsPtr = (U32)(oSize + 1); ++ return iSize + 1; ++} +diff --git a/xen/common/zstd/error_private.h b/xen/common/zstd/error_private.h +new file mode 100644 +index 0000000000..ecbfe51dfb +--- /dev/null ++++ b/xen/common/zstd/error_private.h +@@ -0,0 +1,53 @@ ++/** ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++/* Note : this module is expected to remain private, do not expose it */ ++ ++#ifndef ERROR_H_MODULE ++#define ERROR_H_MODULE ++ ++/* **************************************** ++* Dependencies ++******************************************/ ++#include /* size_t */ ++#include /* enum list */ ++ ++/* **************************************** ++* Compiler-specific ++******************************************/ ++#define ERR_STATIC static __attribute__((unused)) ++ ++/*-**************************************** ++* Customization (error_public.h) ++******************************************/ ++typedef ZSTD_ErrorCode ERR_enum; ++#define PREFIX(name) ZSTD_error_##name ++ ++/*-**************************************** ++* Error codes handling ++******************************************/ ++#define ERROR(name) ((size_t)-PREFIX(name)) ++ ++ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); } ++ ++ERR_STATIC ERR_enum ERR_getErrorCode(size_t code) ++{ ++ if (!ERR_isError(code)) ++ return (ERR_enum)0; ++ return (ERR_enum)(0 - code); ++} ++ ++#endif /* ERROR_H_MODULE */ +diff --git a/xen/common/zstd/fse.h b/xen/common/zstd/fse.h +new file mode 100644 +index 0000000000..b86717c34d +--- /dev/null ++++ b/xen/common/zstd/fse.h +@@ -0,0 +1,575 @@ ++/* ++ * FSE : Finite State Entropy codec ++ * Public Prototypes declaration ++ * Copyright (C) 2013-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++#ifndef FSE_H ++#define FSE_H ++ ++/*-***************************************** ++* Dependencies ++******************************************/ ++#include /* size_t, ptrdiff_t */ ++ ++/*-***************************************** ++* FSE_PUBLIC_API : control library symbols visibility ++******************************************/ ++#define FSE_PUBLIC_API ++ ++/*------ Version ------*/ ++#define FSE_VERSION_MAJOR 0 ++#define FSE_VERSION_MINOR 9 ++#define FSE_VERSION_RELEASE 0 ++ ++#define FSE_LIB_VERSION FSE_VERSION_MAJOR.FSE_VERSION_MINOR.FSE_VERSION_RELEASE ++#define FSE_QUOTE(str) #str ++#define FSE_EXPAND_AND_QUOTE(str) FSE_QUOTE(str) ++#define FSE_VERSION_STRING FSE_EXPAND_AND_QUOTE(FSE_LIB_VERSION) ++ ++#define FSE_VERSION_NUMBER (FSE_VERSION_MAJOR * 100 * 100 + FSE_VERSION_MINOR * 100 + FSE_VERSION_RELEASE) ++FSE_PUBLIC_API unsigned FSE_versionNumber(void); /**< library version number; to be used when checking dll version */ ++ ++/*-***************************************** ++* Tool functions ++******************************************/ ++FSE_PUBLIC_API size_t FSE_compressBound(size_t size); /* maximum compressed size */ ++ ++/* Error Management */ ++FSE_PUBLIC_API unsigned FSE_isError(size_t code); /* tells if a return value is an error code */ ++ ++/*-***************************************** ++* FSE detailed API ++******************************************/ ++/*! ++FSE_compress() does the following: ++1. count symbol occurrence from source[] into table count[] ++2. normalize counters so that sum(count[]) == Power_of_2 (2^tableLog) ++3. save normalized counters to memory buffer using writeNCount() ++4. build encoding table 'CTable' from normalized counters ++5. encode the data stream using encoding table 'CTable' ++ ++FSE_decompress() does the following: ++1. read normalized counters with readNCount() ++2. build decoding table 'DTable' from normalized counters ++3. decode the data stream using decoding table 'DTable' ++ ++The following API allows targeting specific sub-functions for advanced tasks. ++For example, it's possible to compress several blocks using the same 'CTable', ++or to save and provide normalized distribution using external method. ++*/ ++ ++/* *** COMPRESSION *** */ ++/*! FSE_optimalTableLog(): ++ dynamically downsize 'tableLog' when conditions are met. ++ It saves CPU time, by using smaller tables, while preserving or even improving compression ratio. ++ @return : recommended tableLog (necessarily <= 'maxTableLog') */ ++FSE_PUBLIC_API unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); ++ ++/*! FSE_normalizeCount(): ++ normalize counts so that sum(count[]) == Power_of_2 (2^tableLog) ++ 'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1). ++ @return : tableLog, ++ or an errorCode, which can be tested using FSE_isError() */ ++FSE_PUBLIC_API size_t FSE_normalizeCount(short *normalizedCounter, unsigned tableLog, const unsigned *count, size_t srcSize, unsigned maxSymbolValue); ++ ++/*! FSE_NCountWriteBound(): ++ Provides the maximum possible size of an FSE normalized table, given 'maxSymbolValue' and 'tableLog'. ++ Typically useful for allocation purpose. */ ++FSE_PUBLIC_API size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog); ++ ++/*! FSE_writeNCount(): ++ Compactly save 'normalizedCounter' into 'buffer'. ++ @return : size of the compressed table, ++ or an errorCode, which can be tested using FSE_isError(). */ ++FSE_PUBLIC_API size_t FSE_writeNCount(void *buffer, size_t bufferSize, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); ++ ++/*! Constructor and Destructor of FSE_CTable. ++ Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */ ++typedef unsigned FSE_CTable; /* don't allocate that. It's only meant to be more restrictive than void* */ ++ ++/*! FSE_compress_usingCTable(): ++ Compress `src` using `ct` into `dst` which must be already allocated. ++ @return : size of compressed data (<= `dstCapacity`), ++ or 0 if compressed data could not fit into `dst`, ++ or an errorCode, which can be tested using FSE_isError() */ ++FSE_PUBLIC_API size_t FSE_compress_usingCTable(void *dst, size_t dstCapacity, const void *src, size_t srcSize, const FSE_CTable *ct); ++ ++/*! ++Tutorial : ++---------- ++The first step is to count all symbols. FSE_count() does this job very fast. ++Result will be saved into 'count', a table of unsigned int, which must be already allocated, and have 'maxSymbolValuePtr[0]+1' cells. ++'src' is a table of bytes of size 'srcSize'. All values within 'src' MUST be <= maxSymbolValuePtr[0] ++maxSymbolValuePtr[0] will be updated, with its real value (necessarily <= original value) ++FSE_count() will return the number of occurrence of the most frequent symbol. ++This can be used to know if there is a single symbol within 'src', and to quickly evaluate its compressibility. ++If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()). ++ ++The next step is to normalize the frequencies. ++FSE_normalizeCount() will ensure that sum of frequencies is == 2 ^'tableLog'. ++It also guarantees a minimum of 1 to any Symbol with frequency >= 1. ++You can use 'tableLog'==0 to mean "use default tableLog value". ++If you are unsure of which tableLog value to use, you can ask FSE_optimalTableLog(), ++which will provide the optimal valid tableLog given sourceSize, maxSymbolValue, and a user-defined maximum (0 means "default"). ++ ++The result of FSE_normalizeCount() will be saved into a table, ++called 'normalizedCounter', which is a table of signed short. ++'normalizedCounter' must be already allocated, and have at least 'maxSymbolValue+1' cells. ++The return value is tableLog if everything proceeded as expected. ++It is 0 if there is a single symbol within distribution. ++If there is an error (ex: invalid tableLog value), the function will return an ErrorCode (which can be tested using FSE_isError()). ++ ++'normalizedCounter' can be saved in a compact manner to a memory area using FSE_writeNCount(). ++'buffer' must be already allocated. ++For guaranteed success, buffer size must be at least FSE_headerBound(). ++The result of the function is the number of bytes written into 'buffer'. ++If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError(); ex : buffer size too small). ++ ++'normalizedCounter' can then be used to create the compression table 'CTable'. ++The space required by 'CTable' must be already allocated, using FSE_createCTable(). ++You can then use FSE_buildCTable() to fill 'CTable'. ++If there is an error, both functions will return an ErrorCode (which can be tested using FSE_isError()). ++ ++'CTable' can then be used to compress 'src', with FSE_compress_usingCTable(). ++Similar to FSE_count(), the convention is that 'src' is assumed to be a table of char of size 'srcSize' ++The function returns the size of compressed data (without header), necessarily <= `dstCapacity`. ++If it returns '0', compressed data could not fit into 'dst'. ++If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()). ++*/ ++ ++/* *** DECOMPRESSION *** */ ++ ++/*! FSE_readNCount(): ++ Read compactly saved 'normalizedCounter' from 'rBuffer'. ++ @return : size read from 'rBuffer', ++ or an errorCode, which can be tested using FSE_isError(). ++ maxSymbolValuePtr[0] and tableLogPtr[0] will also be updated with their respective values */ ++FSE_PUBLIC_API size_t FSE_readNCount(short *normalizedCounter, unsigned *maxSymbolValuePtr, unsigned *tableLogPtr, const void *rBuffer, size_t rBuffSize); ++ ++/*! Constructor and Destructor of FSE_DTable. ++ Note that its size depends on 'tableLog' */ ++typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be more restrictive than void* */ ++ ++/*! FSE_buildDTable(): ++ Builds 'dt', which must be already allocated, using FSE_createDTable(). ++ return : 0, or an errorCode, which can be tested using FSE_isError() */ ++FSE_PUBLIC_API size_t FSE_buildDTable_wksp(FSE_DTable *dt, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workspace, size_t workspaceSize); ++ ++/*! FSE_decompress_usingDTable(): ++ Decompress compressed source `cSrc` of size `cSrcSize` using `dt` ++ into `dst` which must be already allocated. ++ @return : size of regenerated data (necessarily <= `dstCapacity`), ++ or an errorCode, which can be tested using FSE_isError() */ ++FSE_PUBLIC_API size_t FSE_decompress_usingDTable(void *dst, size_t dstCapacity, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt); ++ ++/*! ++Tutorial : ++---------- ++(Note : these functions only decompress FSE-compressed blocks. ++ If block is uncompressed, use memcpy() instead ++ If block is a single repeated byte, use memset() instead ) ++ ++The first step is to obtain the normalized frequencies of symbols. ++This can be performed by FSE_readNCount() if it was saved using FSE_writeNCount(). ++'normalizedCounter' must be already allocated, and have at least 'maxSymbolValuePtr[0]+1' cells of signed short. ++In practice, that means it's necessary to know 'maxSymbolValue' beforehand, ++or size the table to handle worst case situations (typically 256). ++FSE_readNCount() will provide 'tableLog' and 'maxSymbolValue'. ++The result of FSE_readNCount() is the number of bytes read from 'rBuffer'. ++Note that 'rBufferSize' must be at least 4 bytes, even if useful information is less than that. ++If there is an error, the function will return an error code, which can be tested using FSE_isError(). ++ ++The next step is to build the decompression tables 'FSE_DTable' from 'normalizedCounter'. ++This is performed by the function FSE_buildDTable(). ++The space required by 'FSE_DTable' must be already allocated using FSE_createDTable(). ++If there is an error, the function will return an error code, which can be tested using FSE_isError(). ++ ++`FSE_DTable` can then be used to decompress `cSrc`, with FSE_decompress_usingDTable(). ++`cSrcSize` must be strictly correct, otherwise decompression will fail. ++FSE_decompress_usingDTable() result will tell how many bytes were regenerated (<=`dstCapacity`). ++If there is an error, the function will return an error code, which can be tested using FSE_isError(). (ex: dst buffer too small) ++*/ ++ ++/* *** Dependency *** */ ++#include "bitstream.h" ++ ++/* ***************************************** ++* Static allocation ++*******************************************/ ++/* FSE buffer bounds */ ++#define FSE_NCOUNTBOUND 512 ++#define FSE_BLOCKBOUND(size) (size + (size >> 7)) ++#define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ ++ ++/* It is possible to statically allocate FSE CTable/DTable as a table of FSE_CTable/FSE_DTable using below macros */ ++#define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) (1 + (1 << (maxTableLog - 1)) + ((maxSymbolValue + 1) * 2)) ++#define FSE_DTABLE_SIZE_U32(maxTableLog) (1 + (1 << maxTableLog)) ++ ++/* ***************************************** ++* FSE advanced API ++*******************************************/ ++/* FSE_count_wksp() : ++ * Same as FSE_count(), but using an externally provided scratch buffer. ++ * `workSpace` size must be table of >= `1024` unsigned ++ */ ++size_t FSE_count_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *source, size_t sourceSize, unsigned *workSpace); ++ ++/* FSE_countFast_wksp() : ++ * Same as FSE_countFast(), but using an externally provided scratch buffer. ++ * `workSpace` must be a table of minimum `1024` unsigned ++ */ ++size_t FSE_countFast_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *src, size_t srcSize, unsigned *workSpace); ++ ++/*! FSE_count_simple ++ * Same as FSE_countFast(), but does not use any additional memory (not even on stack). ++ * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr` (presuming it's also the size of `count`). ++*/ ++size_t FSE_count_simple(unsigned *count, unsigned *maxSymbolValuePtr, const void *src, size_t srcSize); ++ ++unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus); ++/**< same as FSE_optimalTableLog(), which used `minus==2` */ ++ ++size_t FSE_buildCTable_raw(FSE_CTable *ct, unsigned nbBits); ++/**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */ ++ ++size_t FSE_buildCTable_rle(FSE_CTable *ct, unsigned char symbolValue); ++/**< build a fake FSE_CTable, designed to compress always the same symbolValue */ ++ ++/* FSE_buildCTable_wksp() : ++ * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`). ++ * `wkspSize` must be >= `(1<= BIT_DStream_completed ++ ++When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. ++Checking if DStream has reached its end is performed by : ++ BIT_endOfDStream(&DStream); ++Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. ++ FSE_endOfDState(&DState); ++*/ ++ ++/* ***************************************** ++* FSE unsafe API ++*******************************************/ ++static unsigned char FSE_decodeSymbolFast(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD); ++/* faster, but works only if nbBits is always >= 1 (otherwise, result will be corrupted) */ ++ ++/* ***************************************** ++* Implementation of inlined functions ++*******************************************/ ++typedef struct { ++ int deltaFindState; ++ U32 deltaNbBits; ++} FSE_symbolCompressionTransform; /* total 8 bytes */ ++ ++ZSTD_STATIC void FSE_initCState(FSE_CState_t *statePtr, const FSE_CTable *ct) ++{ ++ const void *ptr = ct; ++ const U16 *u16ptr = (const U16 *)ptr; ++ const U32 tableLog = ZSTD_read16(ptr); ++ statePtr->value = (ptrdiff_t)1 << tableLog; ++ statePtr->stateTable = u16ptr + 2; ++ statePtr->symbolTT = ((const U32 *)ct + 1 + (tableLog ? (1 << (tableLog - 1)) : 1)); ++ statePtr->stateLog = tableLog; ++} ++ ++/*! FSE_initCState2() : ++* Same as FSE_initCState(), but the first symbol to include (which will be the last to be read) ++* uses the smallest state value possible, saving the cost of this symbol */ ++ZSTD_STATIC void FSE_initCState2(FSE_CState_t *statePtr, const FSE_CTable *ct, U32 symbol) ++{ ++ FSE_initCState(statePtr, ct); ++ { ++ const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform *)(statePtr->symbolTT))[symbol]; ++ const U16 *stateTable = (const U16 *)(statePtr->stateTable); ++ U32 nbBitsOut = (U32)((symbolTT.deltaNbBits + (1 << 15)) >> 16); ++ statePtr->value = (nbBitsOut << 16) - symbolTT.deltaNbBits; ++ statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState]; ++ } ++} ++ ++ZSTD_STATIC void FSE_encodeSymbol(BIT_CStream_t *bitC, FSE_CState_t *statePtr, U32 symbol) ++{ ++ const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform *)(statePtr->symbolTT))[symbol]; ++ const U16 *const stateTable = (const U16 *)(statePtr->stateTable); ++ U32 nbBitsOut = (U32)((statePtr->value + symbolTT.deltaNbBits) >> 16); ++ BIT_addBits(bitC, statePtr->value, nbBitsOut); ++ statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState]; ++} ++ ++ZSTD_STATIC void FSE_flushCState(BIT_CStream_t *bitC, const FSE_CState_t *statePtr) ++{ ++ BIT_addBits(bitC, statePtr->value, statePtr->stateLog); ++ BIT_flushBits(bitC); ++} ++ ++/* ====== Decompression ====== */ ++ ++typedef struct { ++ U16 tableLog; ++ U16 fastMode; ++} FSE_DTableHeader; /* sizeof U32 */ ++ ++typedef struct { ++ unsigned short newState; ++ unsigned char symbol; ++ unsigned char nbBits; ++} FSE_decode_t; /* size == U32 */ ++ ++ZSTD_STATIC void FSE_initDState(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD, const FSE_DTable *dt) ++{ ++ const void *ptr = dt; ++ const FSE_DTableHeader *const DTableH = (const FSE_DTableHeader *)ptr; ++ DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); ++ BIT_reloadDStream(bitD); ++ DStatePtr->table = dt + 1; ++} ++ ++ZSTD_STATIC BYTE FSE_peekSymbol(const FSE_DState_t *DStatePtr) ++{ ++ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; ++ return DInfo.symbol; ++} ++ ++ZSTD_STATIC void FSE_updateState(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD) ++{ ++ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; ++ U32 const nbBits = DInfo.nbBits; ++ size_t const lowBits = BIT_readBits(bitD, nbBits); ++ DStatePtr->state = DInfo.newState + lowBits; ++} ++ ++ZSTD_STATIC BYTE FSE_decodeSymbol(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD) ++{ ++ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; ++ U32 const nbBits = DInfo.nbBits; ++ BYTE const symbol = DInfo.symbol; ++ size_t const lowBits = BIT_readBits(bitD, nbBits); ++ ++ DStatePtr->state = DInfo.newState + lowBits; ++ return symbol; ++} ++ ++/*! FSE_decodeSymbolFast() : ++ unsafe, only works if no symbol has a probability > 50% */ ++ZSTD_STATIC BYTE FSE_decodeSymbolFast(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD) ++{ ++ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; ++ U32 const nbBits = DInfo.nbBits; ++ BYTE const symbol = DInfo.symbol; ++ size_t const lowBits = BIT_readBitsFast(bitD, nbBits); ++ ++ DStatePtr->state = DInfo.newState + lowBits; ++ return symbol; ++} ++ ++ZSTD_STATIC unsigned FSE_endOfDState(const FSE_DState_t *DStatePtr) { return DStatePtr->state == 0; } ++ ++/* ************************************************************** ++* Tuning parameters ++****************************************************************/ ++/*!MEMORY_USAGE : ++* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) ++* Increasing memory usage improves compression ratio ++* Reduced memory usage can improve speed, due to cache effect ++* Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ ++#ifndef FSE_MAX_MEMORY_USAGE ++#define FSE_MAX_MEMORY_USAGE 14 ++#endif ++#ifndef FSE_DEFAULT_MEMORY_USAGE ++#define FSE_DEFAULT_MEMORY_USAGE 13 ++#endif ++ ++/*!FSE_MAX_SYMBOL_VALUE : ++* Maximum symbol value authorized. ++* Required for proper stack allocation */ ++#ifndef FSE_MAX_SYMBOL_VALUE ++#define FSE_MAX_SYMBOL_VALUE 255 ++#endif ++ ++/* ************************************************************** ++* template functions type & suffix ++****************************************************************/ ++#define FSE_FUNCTION_TYPE BYTE ++#define FSE_FUNCTION_EXTENSION ++#define FSE_DECODE_TYPE FSE_decode_t ++ ++/* *************************************************************** ++* Constants ++*****************************************************************/ ++#define FSE_MAX_TABLELOG (FSE_MAX_MEMORY_USAGE - 2) ++#define FSE_MAX_TABLESIZE (1U << FSE_MAX_TABLELOG) ++#define FSE_MAXTABLESIZE_MASK (FSE_MAX_TABLESIZE - 1) ++#define FSE_DEFAULT_TABLELOG (FSE_DEFAULT_MEMORY_USAGE - 2) ++#define FSE_MIN_TABLELOG 5 ++ ++#define FSE_TABLELOG_ABSOLUTE_MAX 15 ++#if FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX ++#error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported" ++#endif ++ ++#define FSE_TABLESTEP(tableSize) ((tableSize >> 1) + (tableSize >> 3) + 3) ++ ++#endif /* FSE_H */ +diff --git a/xen/common/zstd/fse_decompress.c b/xen/common/zstd/fse_decompress.c +new file mode 100644 +index 0000000000..041a5a1f0a +--- /dev/null ++++ b/xen/common/zstd/fse_decompress.c +@@ -0,0 +1,323 @@ ++/* ++ * FSE : Finite State Entropy decoder ++ * Copyright (C) 2013-2015, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++ ++/* ************************************************************** ++* Compiler specifics ++****************************************************************/ ++#define FORCE_INLINE static always_inline ++ ++/* ************************************************************** ++* Includes ++****************************************************************/ ++#include "bitstream.h" ++#include "fse.h" ++#include "zstd_internal.h" ++#include /* memcpy, memset */ ++ ++/* ************************************************************** ++* Error Management ++****************************************************************/ ++#define FSE_isError ERR_isError ++#define FSE_STATIC_ASSERT(c) \ ++ { \ ++ enum { FSE_static_assert = 1 / (int)(!!(c)) }; \ ++ } /* use only *after* variable declarations */ ++ ++/* ************************************************************** ++* Templates ++****************************************************************/ ++/* ++ designed to be included ++ for type-specific functions (template emulation in C) ++ Objective is to write these functions only once, for improved maintenance ++*/ ++ ++/* safety checks */ ++#ifndef FSE_FUNCTION_EXTENSION ++#error "FSE_FUNCTION_EXTENSION must be defined" ++#endif ++#ifndef FSE_FUNCTION_TYPE ++#error "FSE_FUNCTION_TYPE must be defined" ++#endif ++ ++/* Function names */ ++#define FSE_CAT(X, Y) X##Y ++#define FSE_FUNCTION_NAME(X, Y) FSE_CAT(X, Y) ++#define FSE_TYPE_NAME(X, Y) FSE_CAT(X, Y) ++ ++/* Function templates */ ++ ++size_t INIT FSE_buildDTable_wksp(FSE_DTable *dt, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workspace, size_t workspaceSize) ++{ ++ void *const tdPtr = dt + 1; /* because *dt is unsigned, 32-bits aligned on 32-bits */ ++ FSE_DECODE_TYPE *const tableDecode = (FSE_DECODE_TYPE *)(tdPtr); ++ U16 *symbolNext = (U16 *)workspace; ++ ++ U32 const maxSV1 = maxSymbolValue + 1; ++ U32 const tableSize = 1 << tableLog; ++ U32 highThreshold = tableSize - 1; ++ ++ /* Sanity Checks */ ++ if (workspaceSize < sizeof(U16) * (FSE_MAX_SYMBOL_VALUE + 1)) ++ return ERROR(tableLog_tooLarge); ++ if (maxSymbolValue > FSE_MAX_SYMBOL_VALUE) ++ return ERROR(maxSymbolValue_tooLarge); ++ if (tableLog > FSE_MAX_TABLELOG) ++ return ERROR(tableLog_tooLarge); ++ ++ /* Init, lay down lowprob symbols */ ++ { ++ FSE_DTableHeader DTableH; ++ DTableH.tableLog = (U16)tableLog; ++ DTableH.fastMode = 1; ++ { ++ S16 const largeLimit = (S16)(1 << (tableLog - 1)); ++ U32 s; ++ for (s = 0; s < maxSV1; s++) { ++ if (normalizedCounter[s] == -1) { ++ tableDecode[highThreshold--].symbol = (FSE_FUNCTION_TYPE)s; ++ symbolNext[s] = 1; ++ } else { ++ if (normalizedCounter[s] >= largeLimit) ++ DTableH.fastMode = 0; ++ symbolNext[s] = normalizedCounter[s]; ++ } ++ } ++ } ++ memcpy(dt, &DTableH, sizeof(DTableH)); ++ } ++ ++ /* Spread symbols */ ++ { ++ U32 const tableMask = tableSize - 1; ++ U32 const step = FSE_TABLESTEP(tableSize); ++ U32 s, position = 0; ++ for (s = 0; s < maxSV1; s++) { ++ int i; ++ for (i = 0; i < normalizedCounter[s]; i++) { ++ tableDecode[position].symbol = (FSE_FUNCTION_TYPE)s; ++ position = (position + step) & tableMask; ++ while (position > highThreshold) ++ position = (position + step) & tableMask; /* lowprob area */ ++ } ++ } ++ if (position != 0) ++ return ERROR(GENERIC); /* position must reach all cells once, otherwise normalizedCounter is incorrect */ ++ } ++ ++ /* Build Decoding table */ ++ { ++ U32 u; ++ for (u = 0; u < tableSize; u++) { ++ FSE_FUNCTION_TYPE const symbol = (FSE_FUNCTION_TYPE)(tableDecode[u].symbol); ++ U16 nextState = symbolNext[symbol]++; ++ tableDecode[u].nbBits = (BYTE)(tableLog - BIT_highbit32((U32)nextState)); ++ tableDecode[u].newState = (U16)((nextState << tableDecode[u].nbBits) - tableSize); ++ } ++ } ++ ++ return 0; ++} ++ ++/*-******************************************************* ++* Decompression (Byte symbols) ++*********************************************************/ ++size_t INIT FSE_buildDTable_rle(FSE_DTable *dt, BYTE symbolValue) ++{ ++ void *ptr = dt; ++ FSE_DTableHeader *const DTableH = (FSE_DTableHeader *)ptr; ++ void *dPtr = dt + 1; ++ FSE_decode_t *const cell = (FSE_decode_t *)dPtr; ++ ++ DTableH->tableLog = 0; ++ DTableH->fastMode = 0; ++ ++ cell->newState = 0; ++ cell->symbol = symbolValue; ++ cell->nbBits = 0; ++ ++ return 0; ++} ++ ++size_t INIT FSE_buildDTable_raw(FSE_DTable *dt, unsigned nbBits) ++{ ++ void *ptr = dt; ++ FSE_DTableHeader *const DTableH = (FSE_DTableHeader *)ptr; ++ void *dPtr = dt + 1; ++ FSE_decode_t *const dinfo = (FSE_decode_t *)dPtr; ++ const unsigned tableSize = 1 << nbBits; ++ const unsigned tableMask = tableSize - 1; ++ const unsigned maxSV1 = tableMask + 1; ++ unsigned s; ++ ++ /* Sanity checks */ ++ if (nbBits < 1) ++ return ERROR(GENERIC); /* min size */ ++ ++ /* Build Decoding Table */ ++ DTableH->tableLog = (U16)nbBits; ++ DTableH->fastMode = 1; ++ for (s = 0; s < maxSV1; s++) { ++ dinfo[s].newState = 0; ++ dinfo[s].symbol = (BYTE)s; ++ dinfo[s].nbBits = (BYTE)nbBits; ++ } ++ ++ return 0; ++} ++ ++FORCE_INLINE size_t FSE_decompress_usingDTable_generic(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt, ++ const unsigned fast) ++{ ++ BYTE *const ostart = (BYTE *)dst; ++ BYTE *op = ostart; ++ BYTE *const omax = op + maxDstSize; ++ BYTE *const olimit = omax - 3; ++ ++ BIT_DStream_t bitD; ++ FSE_DState_t state1; ++ FSE_DState_t state2; ++ ++ /* Init */ ++ CHECK_F(BIT_initDStream(&bitD, cSrc, cSrcSize)); ++ ++ FSE_initDState(&state1, &bitD, dt); ++ FSE_initDState(&state2, &bitD, dt); ++ ++#define FSE_GETSYMBOL(statePtr) fast ? FSE_decodeSymbolFast(statePtr, &bitD) : FSE_decodeSymbol(statePtr, &bitD) ++ ++ /* 4 symbols per loop */ ++ for (; (BIT_reloadDStream(&bitD) == BIT_DStream_unfinished) & (op < olimit); op += 4) { ++ op[0] = FSE_GETSYMBOL(&state1); ++ ++ if (FSE_MAX_TABLELOG * 2 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */ ++ BIT_reloadDStream(&bitD); ++ ++ op[1] = FSE_GETSYMBOL(&state2); ++ ++ if (FSE_MAX_TABLELOG * 4 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */ ++ { ++ if (BIT_reloadDStream(&bitD) > BIT_DStream_unfinished) { ++ op += 2; ++ break; ++ } ++ } ++ ++ op[2] = FSE_GETSYMBOL(&state1); ++ ++ if (FSE_MAX_TABLELOG * 2 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */ ++ BIT_reloadDStream(&bitD); ++ ++ op[3] = FSE_GETSYMBOL(&state2); ++ } ++ ++ /* tail */ ++ /* note : BIT_reloadDStream(&bitD) >= FSE_DStream_partiallyFilled; Ends at exactly BIT_DStream_completed */ ++ while (1) { ++ if (op > (omax - 2)) ++ return ERROR(dstSize_tooSmall); ++ *op++ = FSE_GETSYMBOL(&state1); ++ if (BIT_reloadDStream(&bitD) == BIT_DStream_overflow) { ++ *op++ = FSE_GETSYMBOL(&state2); ++ break; ++ } ++ ++ if (op > (omax - 2)) ++ return ERROR(dstSize_tooSmall); ++ *op++ = FSE_GETSYMBOL(&state2); ++ if (BIT_reloadDStream(&bitD) == BIT_DStream_overflow) { ++ *op++ = FSE_GETSYMBOL(&state1); ++ break; ++ } ++ } ++ ++ return op - ostart; ++} ++ ++size_t INIT FSE_decompress_usingDTable(void *dst, size_t originalSize, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt) ++{ ++ const void *ptr = dt; ++ const FSE_DTableHeader *DTableH = (const FSE_DTableHeader *)ptr; ++ const U32 fastMode = DTableH->fastMode; ++ ++ /* select fast mode (static) */ ++ if (fastMode) ++ return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1); ++ return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0); ++} ++ ++size_t INIT FSE_decompress_wksp(void *dst, size_t dstCapacity, const void *cSrc, size_t cSrcSize, unsigned maxLog, void *workspace, size_t workspaceSize) ++{ ++ const BYTE *const istart = (const BYTE *)cSrc; ++ const BYTE *ip = istart; ++ unsigned tableLog; ++ unsigned maxSymbolValue = FSE_MAX_SYMBOL_VALUE; ++ size_t NCountLength; ++ ++ FSE_DTable *dt; ++ short *counting; ++ size_t spaceUsed32 = 0; ++ ++ FSE_STATIC_ASSERT(sizeof(FSE_DTable) == sizeof(U32)); ++ ++ dt = (FSE_DTable *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += FSE_DTABLE_SIZE_U32(maxLog); ++ counting = (short *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += ALIGN(sizeof(short) * (FSE_MAX_SYMBOL_VALUE + 1), sizeof(U32)) >> 2; ++ ++ if ((spaceUsed32 << 2) > workspaceSize) ++ return ERROR(tableLog_tooLarge); ++ workspace = (U32 *)workspace + spaceUsed32; ++ workspaceSize -= (spaceUsed32 << 2); ++ ++ /* normal FSE decoding mode */ ++ NCountLength = FSE_readNCount(counting, &maxSymbolValue, &tableLog, istart, cSrcSize); ++ if (FSE_isError(NCountLength)) ++ return NCountLength; ++ // if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); /* too small input size; supposed to be already checked in NCountLength, only remaining ++ // case : NCountLength==cSrcSize */ ++ if (tableLog > maxLog) ++ return ERROR(tableLog_tooLarge); ++ ip += NCountLength; ++ cSrcSize -= NCountLength; ++ ++ CHECK_F(FSE_buildDTable_wksp(dt, counting, maxSymbolValue, tableLog, workspace, workspaceSize)); ++ ++ return FSE_decompress_usingDTable(dst, dstCapacity, ip, cSrcSize, dt); /* always return, even if it is an error code */ ++} +diff --git a/xen/common/zstd/huf.h b/xen/common/zstd/huf.h +new file mode 100644 +index 0000000000..a9d522c7bb +--- /dev/null ++++ b/xen/common/zstd/huf.h +@@ -0,0 +1,212 @@ ++/* ++ * Huffman coder, part of New Generation Entropy library ++ * header file ++ * Copyright (C) 2013-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++#ifndef HUF_H_298734234 ++#define HUF_H_298734234 ++ ++/* *** Dependencies *** */ ++#include /* size_t */ ++ ++/* *** Tool functions *** */ ++#define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ ++size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ ++ ++/* Error Management */ ++unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */ ++ ++/* *** Advanced function *** */ ++ ++/** HUF_compress4X_wksp() : ++* Same as HUF_compress2(), but uses externally allocated `workSpace`, which must be a table of >= 1024 unsigned */ ++size_t HUF_compress4X_wksp(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, ++ size_t wkspSize); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ ++ ++/* *** Dependencies *** */ ++#include "mem.h" /* U32 */ ++ ++/* *** Constants *** */ ++#define HUF_TABLELOG_MAX 12 /* max configured tableLog (for static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ ++#define HUF_TABLELOG_DEFAULT 11 /* tableLog by default, when not specified */ ++#define HUF_SYMBOLVALUE_MAX 255 ++ ++#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ ++#if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX) ++#error "HUF_TABLELOG_MAX is too large !" ++#endif ++ ++/* **************************************** ++* Static allocation ++******************************************/ ++/* HUF buffer bounds */ ++#define HUF_CTABLEBOUND 129 ++#define HUF_BLOCKBOUND(size) (size + (size >> 8) + 8) /* only true if incompressible pre-filtered with fast heuristic */ ++#define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ ++ ++/* static allocation of HUF's Compression Table */ ++#define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \ ++ U32 name##hb[maxSymbolValue + 1]; \ ++ void *name##hv = &(name##hb); \ ++ HUF_CElt *name = (HUF_CElt *)(name##hv) /* no final ; */ ++ ++/* static allocation of HUF's DTable */ ++typedef U32 HUF_DTable; ++#define HUF_DTABLE_SIZE(maxTableLog) (1 + (1 << (maxTableLog))) ++#define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = {((U32)((maxTableLog)-1) * 0x01000001)} ++#define HUF_CREATE_STATIC_DTABLEX4(DTable, maxTableLog) HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = {((U32)(maxTableLog)*0x01000001)} ++ ++/* The workspace must have alignment at least 4 and be at least this large */ ++#define HUF_COMPRESS_WORKSPACE_SIZE (6 << 10) ++#define HUF_COMPRESS_WORKSPACE_SIZE_U32 (HUF_COMPRESS_WORKSPACE_SIZE / sizeof(U32)) ++ ++/* The workspace must have alignment at least 4 and be at least this large */ ++#define HUF_DECOMPRESS_WORKSPACE_SIZE (3 << 10) ++#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32)) ++ ++/* **************************************** ++* Advanced decompression functions ++******************************************/ ++size_t HUF_decompress4X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize); /**< decodes RLE and uncompressed */ ++size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, ++ size_t workspaceSize); /**< considers RLE and uncompressed as errors */ ++size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, ++ size_t workspaceSize); /**< single-symbol decoder */ ++size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, ++ size_t workspaceSize); /**< double-symbols decoder */ ++ ++/* **************************************** ++* HUF detailed API ++******************************************/ ++/*! ++HUF_compress() does the following: ++1. count symbol occurrence from source[] into table count[] using FSE_count() ++2. (optional) refine tableLog using HUF_optimalTableLog() ++3. build Huffman table from count using HUF_buildCTable() ++4. save Huffman table to memory buffer using HUF_writeCTable_wksp() ++5. encode the data stream using HUF_compress4X_usingCTable() ++ ++The following API allows targeting specific sub-functions for advanced tasks. ++For example, it's possible to compress several blocks using the same 'CTable', ++or to save and regenerate 'CTable' using external methods. ++*/ ++/* FSE_count() : find it within "fse.h" */ ++unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); ++typedef struct HUF_CElt_s HUF_CElt; /* incomplete type */ ++size_t HUF_writeCTable_wksp(void *dst, size_t maxDstSize, const HUF_CElt *CTable, unsigned maxSymbolValue, unsigned huffLog, void *workspace, size_t workspaceSize); ++size_t HUF_compress4X_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const HUF_CElt *CTable); ++ ++typedef enum { ++ HUF_repeat_none, /**< Cannot use the previous table */ ++ HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, ++ 4}X_repeat */ ++ HUF_repeat_valid /**< Can use the previous table and it is asumed to be valid */ ++} HUF_repeat; ++/** HUF_compress4X_repeat() : ++* Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. ++* If it uses hufTable it does not modify hufTable or repeat. ++* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. ++* If preferRepeat then the old table will always be used if valid. */ ++size_t HUF_compress4X_repeat(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, ++ size_t wkspSize, HUF_CElt *hufTable, HUF_repeat *repeat, ++ int preferRepeat); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ ++ ++/** HUF_buildCTable_wksp() : ++ * Same as HUF_buildCTable(), but using externally allocated scratch buffer. ++ * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned. ++ */ ++size_t HUF_buildCTable_wksp(HUF_CElt *tree, const U32 *count, U32 maxSymbolValue, U32 maxNbBits, void *workSpace, size_t wkspSize); ++ ++/*! HUF_readStats() : ++ Read compact Huffman tree, saved by HUF_writeCTable(). ++ `huffWeight` is destination buffer. ++ @return : size read from `src` , or an error Code . ++ Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */ ++size_t HUF_readStats_wksp(BYTE *huffWeight, size_t hwSize, U32 *rankStats, U32 *nbSymbolsPtr, U32 *tableLogPtr, const void *src, size_t srcSize, ++ void *workspace, size_t workspaceSize); ++ ++/** HUF_readCTable() : ++* Loading a CTable saved with HUF_writeCTable() */ ++size_t HUF_readCTable_wksp(HUF_CElt *CTable, unsigned maxSymbolValue, const void *src, size_t srcSize, void *workspace, size_t workspaceSize); ++ ++/* ++HUF_decompress() does the following: ++1. select the decompression algorithm (X2, X4) based on pre-computed heuristics ++2. build Huffman table from save, using HUF_readDTableXn() ++3. decode 1 or 4 segments in parallel using HUF_decompressSXn_usingDTable ++*/ ++ ++/** HUF_selectDecoder() : ++* Tells which decoder is likely to decode faster, ++* based on a set of pre-determined metrics. ++* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 . ++* Assumption : 0 < cSrcSize < dstSize <= 128 KB */ ++U32 HUF_selectDecoder(size_t dstSize, size_t cSrcSize); ++ ++size_t HUF_readDTableX2_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize); ++size_t HUF_readDTableX4_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize); ++ ++size_t HUF_decompress4X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); ++size_t HUF_decompress4X2_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); ++size_t HUF_decompress4X4_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); ++ ++/* single stream variants */ ++ ++size_t HUF_compress1X_wksp(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, ++ size_t wkspSize); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ ++size_t HUF_compress1X_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const HUF_CElt *CTable); ++/** HUF_compress1X_repeat() : ++* Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. ++* If it uses hufTable it does not modify hufTable or repeat. ++* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. ++* If preferRepeat then the old table will always be used if valid. */ ++size_t HUF_compress1X_repeat(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, ++ size_t wkspSize, HUF_CElt *hufTable, HUF_repeat *repeat, ++ int preferRepeat); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ ++ ++size_t HUF_decompress1X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize); ++size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, ++ size_t workspaceSize); /**< single-symbol decoder */ ++size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, ++ size_t workspaceSize); /**< double-symbols decoder */ ++ ++size_t HUF_decompress1X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, ++ const HUF_DTable *DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */ ++size_t HUF_decompress1X2_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); ++size_t HUF_decompress1X4_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); ++ ++#endif /* HUF_H_298734234 */ +diff --git a/xen/common/zstd/huf_decompress.c b/xen/common/zstd/huf_decompress.c +new file mode 100644 +index 0000000000..f79603a12f +--- /dev/null ++++ b/xen/common/zstd/huf_decompress.c +@@ -0,0 +1,958 @@ ++/* ++ * Huffman decoder, part of New Generation Entropy library ++ * Copyright (C) 2013-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++ ++/* ************************************************************** ++* Compiler specifics ++****************************************************************/ ++#define FORCE_INLINE static always_inline ++ ++/* ************************************************************** ++* Dependencies ++****************************************************************/ ++#include "bitstream.h" /* BIT_* */ ++#include "fse.h" /* header compression */ ++#include "huf.h" ++#include /* memcpy, memset */ ++ ++/* ************************************************************** ++* Error Management ++****************************************************************/ ++#define HUF_STATIC_ASSERT(c) \ ++ { \ ++ enum { HUF_static_assert = 1 / (int)(!!(c)) }; \ ++ } /* use only *after* variable declarations */ ++ ++/*-***************************/ ++/* generic DTableDesc */ ++/*-***************************/ ++ ++typedef struct { ++ BYTE maxTableLog; ++ BYTE tableType; ++ BYTE tableLog; ++ BYTE reserved; ++} DTableDesc; ++ ++static DTableDesc INIT HUF_getDTableDesc(const HUF_DTable *table) ++{ ++ DTableDesc dtd; ++ memcpy(&dtd, table, sizeof(dtd)); ++ return dtd; ++} ++ ++/*-***************************/ ++/* single-symbol decoding */ ++/*-***************************/ ++ ++typedef struct { ++ BYTE byte; ++ BYTE nbBits; ++} HUF_DEltX2; /* single-symbol decoding */ ++ ++size_t INIT HUF_readDTableX2_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize) ++{ ++ U32 tableLog = 0; ++ U32 nbSymbols = 0; ++ size_t iSize; ++ void *const dtPtr = DTable + 1; ++ HUF_DEltX2 *const dt = (HUF_DEltX2 *)dtPtr; ++ ++ U32 *rankVal; ++ BYTE *huffWeight; ++ size_t spaceUsed32 = 0; ++ ++ rankVal = (U32 *)workspace + spaceUsed32; ++ spaceUsed32 += HUF_TABLELOG_ABSOLUTEMAX + 1; ++ huffWeight = (BYTE *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; ++ ++ if ((spaceUsed32 << 2) > workspaceSize) ++ return ERROR(tableLog_tooLarge); ++ workspace = (U32 *)workspace + spaceUsed32; ++ workspaceSize -= (spaceUsed32 << 2); ++ ++ HUF_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable)); ++ /* memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */ ++ ++ iSize = HUF_readStats_wksp(huffWeight, HUF_SYMBOLVALUE_MAX + 1, rankVal, &nbSymbols, &tableLog, src, srcSize, workspace, workspaceSize); ++ if (HUF_isError(iSize)) ++ return iSize; ++ ++ /* Table header */ ++ { ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ if (tableLog > (U32)(dtd.maxTableLog + 1)) ++ return ERROR(tableLog_tooLarge); /* DTable too small, Huffman tree cannot fit in */ ++ dtd.tableType = 0; ++ dtd.tableLog = (BYTE)tableLog; ++ memcpy(DTable, &dtd, sizeof(dtd)); ++ } ++ ++ /* Calculate starting value for each rank */ ++ { ++ U32 n, nextRankStart = 0; ++ for (n = 1; n < tableLog + 1; n++) { ++ U32 const curr = nextRankStart; ++ nextRankStart += (rankVal[n] << (n - 1)); ++ rankVal[n] = curr; ++ } ++ } ++ ++ /* fill DTable */ ++ { ++ U32 n; ++ for (n = 0; n < nbSymbols; n++) { ++ U32 const w = huffWeight[n]; ++ U32 const length = (1 << w) >> 1; ++ U32 u; ++ HUF_DEltX2 D; ++ D.byte = (BYTE)n; ++ D.nbBits = (BYTE)(tableLog + 1 - w); ++ for (u = rankVal[w]; u < rankVal[w] + length; u++) ++ dt[u] = D; ++ rankVal[w] += length; ++ } ++ } ++ ++ return iSize; ++} ++ ++static BYTE INIT HUF_decodeSymbolX2(BIT_DStream_t *Dstream, const HUF_DEltX2 *dt, const U32 dtLog) ++{ ++ size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ ++ BYTE const c = dt[val].byte; ++ BIT_skipBits(Dstream, dt[val].nbBits); ++ return c; ++} ++ ++#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) *ptr++ = HUF_decodeSymbolX2(DStreamPtr, dt, dtLog) ++ ++#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ ++ if (ZSTD_64bits() || (HUF_TABLELOG_MAX <= 12)) \ ++ HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) ++ ++#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ ++ if (ZSTD_64bits()) \ ++ HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) ++ ++FORCE_INLINE size_t HUF_decodeStreamX2(BYTE *p, BIT_DStream_t *const bitDPtr, BYTE *const pEnd, const HUF_DEltX2 *const dt, const U32 dtLog) ++{ ++ BYTE *const pStart = p; ++ ++ /* up to 4 symbols at a time */ ++ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd - 4)) { ++ HUF_DECODE_SYMBOLX2_2(p, bitDPtr); ++ HUF_DECODE_SYMBOLX2_1(p, bitDPtr); ++ HUF_DECODE_SYMBOLX2_2(p, bitDPtr); ++ HUF_DECODE_SYMBOLX2_0(p, bitDPtr); ++ } ++ ++ /* closer to the end */ ++ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd)) ++ HUF_DECODE_SYMBOLX2_0(p, bitDPtr); ++ ++ /* no more data to retrieve from bitstream, hence no need to reload */ ++ while (p < pEnd) ++ HUF_DECODE_SYMBOLX2_0(p, bitDPtr); ++ ++ return pEnd - pStart; ++} ++ ++static size_t INIT HUF_decompress1X2_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ BYTE *op = (BYTE *)dst; ++ BYTE *const oend = op + dstSize; ++ const void *dtPtr = DTable + 1; ++ const HUF_DEltX2 *const dt = (const HUF_DEltX2 *)dtPtr; ++ BIT_DStream_t bitD; ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ U32 const dtLog = dtd.tableLog; ++ ++ { ++ size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ ++ HUF_decodeStreamX2(op, &bitD, oend, dt, dtLog); ++ ++ /* check */ ++ if (!BIT_endOfDStream(&bitD)) ++ return ERROR(corruption_detected); ++ ++ return dstSize; ++} ++ ++size_t INIT HUF_decompress1X2_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ if (dtd.tableType != 0) ++ return ERROR(GENERIC); ++ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); ++} ++ ++size_t INIT HUF_decompress1X2_DCtx_wksp(HUF_DTable *DCtx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ const BYTE *ip = (const BYTE *)cSrc; ++ ++ size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, workspace, workspaceSize); ++ if (HUF_isError(hSize)) ++ return hSize; ++ if (hSize >= cSrcSize) ++ return ERROR(srcSize_wrong); ++ ip += hSize; ++ cSrcSize -= hSize; ++ ++ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx); ++} ++ ++static size_t INIT HUF_decompress4X2_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ /* Check */ ++ if (cSrcSize < 10) ++ return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ ++ ++ { ++ const BYTE *const istart = (const BYTE *)cSrc; ++ BYTE *const ostart = (BYTE *)dst; ++ BYTE *const oend = ostart + dstSize; ++ const void *const dtPtr = DTable + 1; ++ const HUF_DEltX2 *const dt = (const HUF_DEltX2 *)dtPtr; ++ ++ /* Init */ ++ BIT_DStream_t bitD1; ++ BIT_DStream_t bitD2; ++ BIT_DStream_t bitD3; ++ BIT_DStream_t bitD4; ++ size_t const length1 = ZSTD_readLE16(istart); ++ size_t const length2 = ZSTD_readLE16(istart + 2); ++ size_t const length3 = ZSTD_readLE16(istart + 4); ++ size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); ++ const BYTE *const istart1 = istart + 6; /* jumpTable */ ++ const BYTE *const istart2 = istart1 + length1; ++ const BYTE *const istart3 = istart2 + length2; ++ const BYTE *const istart4 = istart3 + length3; ++ const size_t segmentSize = (dstSize + 3) / 4; ++ BYTE *const opStart2 = ostart + segmentSize; ++ BYTE *const opStart3 = opStart2 + segmentSize; ++ BYTE *const opStart4 = opStart3 + segmentSize; ++ BYTE *op1 = ostart; ++ BYTE *op2 = opStart2; ++ BYTE *op3 = opStart3; ++ BYTE *op4 = opStart4; ++ U32 endSignal; ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ U32 const dtLog = dtd.tableLog; ++ ++ if (length4 > cSrcSize) ++ return ERROR(corruption_detected); /* overflow */ ++ { ++ size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ ++ /* 16-32 symbols per loop (4-8 symbols per stream) */ ++ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); ++ for (; (endSignal == BIT_DStream_unfinished) && (op4 < (oend - 7));) { ++ HUF_DECODE_SYMBOLX2_2(op1, &bitD1); ++ HUF_DECODE_SYMBOLX2_2(op2, &bitD2); ++ HUF_DECODE_SYMBOLX2_2(op3, &bitD3); ++ HUF_DECODE_SYMBOLX2_2(op4, &bitD4); ++ HUF_DECODE_SYMBOLX2_1(op1, &bitD1); ++ HUF_DECODE_SYMBOLX2_1(op2, &bitD2); ++ HUF_DECODE_SYMBOLX2_1(op3, &bitD3); ++ HUF_DECODE_SYMBOLX2_1(op4, &bitD4); ++ HUF_DECODE_SYMBOLX2_2(op1, &bitD1); ++ HUF_DECODE_SYMBOLX2_2(op2, &bitD2); ++ HUF_DECODE_SYMBOLX2_2(op3, &bitD3); ++ HUF_DECODE_SYMBOLX2_2(op4, &bitD4); ++ HUF_DECODE_SYMBOLX2_0(op1, &bitD1); ++ HUF_DECODE_SYMBOLX2_0(op2, &bitD2); ++ HUF_DECODE_SYMBOLX2_0(op3, &bitD3); ++ HUF_DECODE_SYMBOLX2_0(op4, &bitD4); ++ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); ++ } ++ ++ /* check corruption */ ++ if (op1 > opStart2) ++ return ERROR(corruption_detected); ++ if (op2 > opStart3) ++ return ERROR(corruption_detected); ++ if (op3 > opStart4) ++ return ERROR(corruption_detected); ++ /* note : op4 supposed already verified within main loop */ ++ ++ /* finish bitStreams one by one */ ++ HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog); ++ HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog); ++ HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog); ++ HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog); ++ ++ /* check */ ++ endSignal = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); ++ if (!endSignal) ++ return ERROR(corruption_detected); ++ ++ /* decoded size */ ++ return dstSize; ++ } ++} ++ ++size_t INIT HUF_decompress4X2_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ if (dtd.tableType != 0) ++ return ERROR(GENERIC); ++ return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); ++} ++ ++size_t INIT HUF_decompress4X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ const BYTE *ip = (const BYTE *)cSrc; ++ ++ size_t const hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, workspace, workspaceSize); ++ if (HUF_isError(hSize)) ++ return hSize; ++ if (hSize >= cSrcSize) ++ return ERROR(srcSize_wrong); ++ ip += hSize; ++ cSrcSize -= hSize; ++ ++ return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx); ++} ++ ++/* *************************/ ++/* double-symbols decoding */ ++/* *************************/ ++typedef struct { ++ U16 sequence; ++ BYTE nbBits; ++ BYTE length; ++} HUF_DEltX4; /* double-symbols decoding */ ++ ++typedef struct { ++ BYTE symbol; ++ BYTE weight; ++} sortedSymbol_t; ++ ++/* HUF_fillDTableX4Level2() : ++ * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */ ++static void INIT HUF_fillDTableX4Level2(HUF_DEltX4 *DTable, U32 sizeLog, const U32 consumed, const U32 *rankValOrigin, const int minWeight, ++ const sortedSymbol_t *sortedSymbols, const U32 sortedListSize, U32 nbBitsBaseline, U16 baseSeq) ++{ ++ HUF_DEltX4 DElt; ++ U32 rankVal[HUF_TABLELOG_MAX + 1]; ++ ++ /* get pre-calculated rankVal */ ++ memcpy(rankVal, rankValOrigin, sizeof(rankVal)); ++ ++ /* fill skipped values */ ++ if (minWeight > 1) { ++ U32 i, skipSize = rankVal[minWeight]; ++ ZSTD_writeLE16(&(DElt.sequence), baseSeq); ++ DElt.nbBits = (BYTE)(consumed); ++ DElt.length = 1; ++ for (i = 0; i < skipSize; i++) ++ DTable[i] = DElt; ++ } ++ ++ /* fill DTable */ ++ { ++ U32 s; ++ for (s = 0; s < sortedListSize; s++) { /* note : sortedSymbols already skipped */ ++ const U32 symbol = sortedSymbols[s].symbol; ++ const U32 weight = sortedSymbols[s].weight; ++ const U32 nbBits = nbBitsBaseline - weight; ++ const U32 length = 1 << (sizeLog - nbBits); ++ const U32 start = rankVal[weight]; ++ U32 i = start; ++ const U32 end = start + length; ++ ++ ZSTD_writeLE16(&(DElt.sequence), (U16)(baseSeq + (symbol << 8))); ++ DElt.nbBits = (BYTE)(nbBits + consumed); ++ DElt.length = 2; ++ do { ++ DTable[i++] = DElt; ++ } while (i < end); /* since length >= 1 */ ++ ++ rankVal[weight] += length; ++ } ++ } ++} ++ ++typedef U32 rankVal_t[HUF_TABLELOG_MAX][HUF_TABLELOG_MAX + 1]; ++typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1]; ++ ++static void INIT HUF_fillDTableX4(HUF_DEltX4 *DTable, const U32 targetLog, const sortedSymbol_t *sortedList, const U32 sortedListSize, const U32 *rankStart, ++ rankVal_t rankValOrigin, const U32 maxWeight, const U32 nbBitsBaseline) ++{ ++ U32 rankVal[HUF_TABLELOG_MAX + 1]; ++ const int scaleLog = nbBitsBaseline - targetLog; /* note : targetLog >= srcLog, hence scaleLog <= 1 */ ++ const U32 minBits = nbBitsBaseline - maxWeight; ++ U32 s; ++ ++ memcpy(rankVal, rankValOrigin, sizeof(rankVal)); ++ ++ /* fill DTable */ ++ for (s = 0; s < sortedListSize; s++) { ++ const U16 symbol = sortedList[s].symbol; ++ const U32 weight = sortedList[s].weight; ++ const U32 nbBits = nbBitsBaseline - weight; ++ const U32 start = rankVal[weight]; ++ const U32 length = 1 << (targetLog - nbBits); ++ ++ if (targetLog - nbBits >= minBits) { /* enough room for a second symbol */ ++ U32 sortedRank; ++ int minWeight = nbBits + scaleLog; ++ if (minWeight < 1) ++ minWeight = 1; ++ sortedRank = rankStart[minWeight]; ++ HUF_fillDTableX4Level2(DTable + start, targetLog - nbBits, nbBits, rankValOrigin[nbBits], minWeight, sortedList + sortedRank, ++ sortedListSize - sortedRank, nbBitsBaseline, symbol); ++ } else { ++ HUF_DEltX4 DElt; ++ ZSTD_writeLE16(&(DElt.sequence), symbol); ++ DElt.nbBits = (BYTE)(nbBits); ++ DElt.length = 1; ++ { ++ U32 const end = start + length; ++ U32 u; ++ for (u = start; u < end; u++) ++ DTable[u] = DElt; ++ } ++ } ++ rankVal[weight] += length; ++ } ++} ++ ++size_t INIT HUF_readDTableX4_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize) ++{ ++ U32 tableLog, maxW, sizeOfSort, nbSymbols; ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ U32 const maxTableLog = dtd.maxTableLog; ++ size_t iSize; ++ void *dtPtr = DTable + 1; /* force compiler to avoid strict-aliasing */ ++ HUF_DEltX4 *const dt = (HUF_DEltX4 *)dtPtr; ++ U32 *rankStart; ++ ++ rankValCol_t *rankVal; ++ U32 *rankStats; ++ U32 *rankStart0; ++ sortedSymbol_t *sortedSymbol; ++ BYTE *weightList; ++ size_t spaceUsed32 = 0; ++ ++ HUF_STATIC_ASSERT((sizeof(rankValCol_t) & 3) == 0); ++ ++ rankVal = (rankValCol_t *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += (sizeof(rankValCol_t) * HUF_TABLELOG_MAX) >> 2; ++ rankStats = (U32 *)workspace + spaceUsed32; ++ spaceUsed32 += HUF_TABLELOG_MAX + 1; ++ rankStart0 = (U32 *)workspace + spaceUsed32; ++ spaceUsed32 += HUF_TABLELOG_MAX + 2; ++ sortedSymbol = (sortedSymbol_t *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += ALIGN(sizeof(sortedSymbol_t) * (HUF_SYMBOLVALUE_MAX + 1), sizeof(U32)) >> 2; ++ weightList = (BYTE *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; ++ ++ if ((spaceUsed32 << 2) > workspaceSize) ++ return ERROR(tableLog_tooLarge); ++ workspace = (U32 *)workspace + spaceUsed32; ++ workspaceSize -= (spaceUsed32 << 2); ++ ++ rankStart = rankStart0 + 1; ++ memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1)); ++ ++ HUF_STATIC_ASSERT(sizeof(HUF_DEltX4) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */ ++ if (maxTableLog > HUF_TABLELOG_MAX) ++ return ERROR(tableLog_tooLarge); ++ /* memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */ ++ ++ iSize = HUF_readStats_wksp(weightList, HUF_SYMBOLVALUE_MAX + 1, rankStats, &nbSymbols, &tableLog, src, srcSize, workspace, workspaceSize); ++ if (HUF_isError(iSize)) ++ return iSize; ++ ++ /* check result */ ++ if (tableLog > maxTableLog) ++ return ERROR(tableLog_tooLarge); /* DTable can't fit code depth */ ++ ++ /* find maxWeight */ ++ for (maxW = tableLog; rankStats[maxW] == 0; maxW--) { ++ } /* necessarily finds a solution before 0 */ ++ ++ /* Get start index of each weight */ ++ { ++ U32 w, nextRankStart = 0; ++ for (w = 1; w < maxW + 1; w++) { ++ U32 curr = nextRankStart; ++ nextRankStart += rankStats[w]; ++ rankStart[w] = curr; ++ } ++ rankStart[0] = nextRankStart; /* put all 0w symbols at the end of sorted list*/ ++ sizeOfSort = nextRankStart; ++ } ++ ++ /* sort symbols by weight */ ++ { ++ U32 s; ++ for (s = 0; s < nbSymbols; s++) { ++ U32 const w = weightList[s]; ++ U32 const r = rankStart[w]++; ++ sortedSymbol[r].symbol = (BYTE)s; ++ sortedSymbol[r].weight = (BYTE)w; ++ } ++ rankStart[0] = 0; /* forget 0w symbols; this is beginning of weight(1) */ ++ } ++ ++ /* Build rankVal */ ++ { ++ U32 *const rankVal0 = rankVal[0]; ++ { ++ int const rescale = (maxTableLog - tableLog) - 1; /* tableLog <= maxTableLog */ ++ U32 nextRankVal = 0; ++ U32 w; ++ for (w = 1; w < maxW + 1; w++) { ++ U32 curr = nextRankVal; ++ nextRankVal += rankStats[w] << (w + rescale); ++ rankVal0[w] = curr; ++ } ++ } ++ { ++ U32 const minBits = tableLog + 1 - maxW; ++ U32 consumed; ++ for (consumed = minBits; consumed < maxTableLog - minBits + 1; consumed++) { ++ U32 *const rankValPtr = rankVal[consumed]; ++ U32 w; ++ for (w = 1; w < maxW + 1; w++) { ++ rankValPtr[w] = rankVal0[w] >> consumed; ++ } ++ } ++ } ++ } ++ ++ HUF_fillDTableX4(dt, maxTableLog, sortedSymbol, sizeOfSort, rankStart0, rankVal, maxW, tableLog + 1); ++ ++ dtd.tableLog = (BYTE)maxTableLog; ++ dtd.tableType = 1; ++ memcpy(DTable, &dtd, sizeof(dtd)); ++ return iSize; ++} ++ ++static U32 INIT HUF_decodeSymbolX4(void *op, BIT_DStream_t *DStream, const HUF_DEltX4 *dt, const U32 dtLog) ++{ ++ size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ ++ memcpy(op, dt + val, 2); ++ BIT_skipBits(DStream, dt[val].nbBits); ++ return dt[val].length; ++} ++ ++static U32 INIT HUF_decodeLastSymbolX4(void *op, BIT_DStream_t *DStream, const HUF_DEltX4 *dt, const U32 dtLog) ++{ ++ size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ ++ memcpy(op, dt + val, 1); ++ if (dt[val].length == 1) ++ BIT_skipBits(DStream, dt[val].nbBits); ++ else { ++ if (DStream->bitsConsumed < (sizeof(DStream->bitContainer) * 8)) { ++ BIT_skipBits(DStream, dt[val].nbBits); ++ if (DStream->bitsConsumed > (sizeof(DStream->bitContainer) * 8)) ++ /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */ ++ DStream->bitsConsumed = (sizeof(DStream->bitContainer) * 8); ++ } ++ } ++ return 1; ++} ++ ++#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) ++ ++#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \ ++ if (ZSTD_64bits() || (HUF_TABLELOG_MAX <= 12)) \ ++ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) ++ ++#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \ ++ if (ZSTD_64bits()) \ ++ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) ++ ++FORCE_INLINE size_t HUF_decodeStreamX4(BYTE *p, BIT_DStream_t *bitDPtr, BYTE *const pEnd, const HUF_DEltX4 *const dt, const U32 dtLog) ++{ ++ BYTE *const pStart = p; ++ ++ /* up to 8 symbols at a time */ ++ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd - (sizeof(bitDPtr->bitContainer) - 1))) { ++ HUF_DECODE_SYMBOLX4_2(p, bitDPtr); ++ HUF_DECODE_SYMBOLX4_1(p, bitDPtr); ++ HUF_DECODE_SYMBOLX4_2(p, bitDPtr); ++ HUF_DECODE_SYMBOLX4_0(p, bitDPtr); ++ } ++ ++ /* closer to end : up to 2 symbols at a time */ ++ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd - 2)) ++ HUF_DECODE_SYMBOLX4_0(p, bitDPtr); ++ ++ while (p <= pEnd - 2) ++ HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ ++ ++ if (p < pEnd) ++ p += HUF_decodeLastSymbolX4(p, bitDPtr, dt, dtLog); ++ ++ return p - pStart; ++} ++ ++static size_t INIT HUF_decompress1X4_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ BIT_DStream_t bitD; ++ ++ /* Init */ ++ { ++ size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ ++ /* decode */ ++ { ++ BYTE *const ostart = (BYTE *)dst; ++ BYTE *const oend = ostart + dstSize; ++ const void *const dtPtr = DTable + 1; /* force compiler to not use strict-aliasing */ ++ const HUF_DEltX4 *const dt = (const HUF_DEltX4 *)dtPtr; ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ HUF_decodeStreamX4(ostart, &bitD, oend, dt, dtd.tableLog); ++ } ++ ++ /* check */ ++ if (!BIT_endOfDStream(&bitD)) ++ return ERROR(corruption_detected); ++ ++ /* decoded size */ ++ return dstSize; ++} ++ ++size_t INIT HUF_decompress1X4_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ if (dtd.tableType != 1) ++ return ERROR(GENERIC); ++ return HUF_decompress1X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); ++} ++ ++size_t INIT HUF_decompress1X4_DCtx_wksp(HUF_DTable *DCtx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ const BYTE *ip = (const BYTE *)cSrc; ++ ++ size_t const hSize = HUF_readDTableX4_wksp(DCtx, cSrc, cSrcSize, workspace, workspaceSize); ++ if (HUF_isError(hSize)) ++ return hSize; ++ if (hSize >= cSrcSize) ++ return ERROR(srcSize_wrong); ++ ip += hSize; ++ cSrcSize -= hSize; ++ ++ return HUF_decompress1X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx); ++} ++ ++static size_t INIT HUF_decompress4X4_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ if (cSrcSize < 10) ++ return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ ++ ++ { ++ const BYTE *const istart = (const BYTE *)cSrc; ++ BYTE *const ostart = (BYTE *)dst; ++ BYTE *const oend = ostart + dstSize; ++ const void *const dtPtr = DTable + 1; ++ const HUF_DEltX4 *const dt = (const HUF_DEltX4 *)dtPtr; ++ ++ /* Init */ ++ BIT_DStream_t bitD1; ++ BIT_DStream_t bitD2; ++ BIT_DStream_t bitD3; ++ BIT_DStream_t bitD4; ++ size_t const length1 = ZSTD_readLE16(istart); ++ size_t const length2 = ZSTD_readLE16(istart + 2); ++ size_t const length3 = ZSTD_readLE16(istart + 4); ++ size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); ++ const BYTE *const istart1 = istart + 6; /* jumpTable */ ++ const BYTE *const istart2 = istart1 + length1; ++ const BYTE *const istart3 = istart2 + length2; ++ const BYTE *const istart4 = istart3 + length3; ++ size_t const segmentSize = (dstSize + 3) / 4; ++ BYTE *const opStart2 = ostart + segmentSize; ++ BYTE *const opStart3 = opStart2 + segmentSize; ++ BYTE *const opStart4 = opStart3 + segmentSize; ++ BYTE *op1 = ostart; ++ BYTE *op2 = opStart2; ++ BYTE *op3 = opStart3; ++ BYTE *op4 = opStart4; ++ U32 endSignal; ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ U32 const dtLog = dtd.tableLog; ++ ++ if (length4 > cSrcSize) ++ return ERROR(corruption_detected); /* overflow */ ++ { ++ size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ ++ /* 16-32 symbols per loop (4-8 symbols per stream) */ ++ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); ++ for (; (endSignal == BIT_DStream_unfinished) & (op4 < (oend - (sizeof(bitD4.bitContainer) - 1)));) { ++ HUF_DECODE_SYMBOLX4_2(op1, &bitD1); ++ HUF_DECODE_SYMBOLX4_2(op2, &bitD2); ++ HUF_DECODE_SYMBOLX4_2(op3, &bitD3); ++ HUF_DECODE_SYMBOLX4_2(op4, &bitD4); ++ HUF_DECODE_SYMBOLX4_1(op1, &bitD1); ++ HUF_DECODE_SYMBOLX4_1(op2, &bitD2); ++ HUF_DECODE_SYMBOLX4_1(op3, &bitD3); ++ HUF_DECODE_SYMBOLX4_1(op4, &bitD4); ++ HUF_DECODE_SYMBOLX4_2(op1, &bitD1); ++ HUF_DECODE_SYMBOLX4_2(op2, &bitD2); ++ HUF_DECODE_SYMBOLX4_2(op3, &bitD3); ++ HUF_DECODE_SYMBOLX4_2(op4, &bitD4); ++ HUF_DECODE_SYMBOLX4_0(op1, &bitD1); ++ HUF_DECODE_SYMBOLX4_0(op2, &bitD2); ++ HUF_DECODE_SYMBOLX4_0(op3, &bitD3); ++ HUF_DECODE_SYMBOLX4_0(op4, &bitD4); ++ ++ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); ++ } ++ ++ /* check corruption */ ++ if (op1 > opStart2) ++ return ERROR(corruption_detected); ++ if (op2 > opStart3) ++ return ERROR(corruption_detected); ++ if (op3 > opStart4) ++ return ERROR(corruption_detected); ++ /* note : op4 already verified within main loop */ ++ ++ /* finish bitStreams one by one */ ++ HUF_decodeStreamX4(op1, &bitD1, opStart2, dt, dtLog); ++ HUF_decodeStreamX4(op2, &bitD2, opStart3, dt, dtLog); ++ HUF_decodeStreamX4(op3, &bitD3, opStart4, dt, dtLog); ++ HUF_decodeStreamX4(op4, &bitD4, oend, dt, dtLog); ++ ++ /* check */ ++ { ++ U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); ++ if (!endCheck) ++ return ERROR(corruption_detected); ++ } ++ ++ /* decoded size */ ++ return dstSize; ++ } ++} ++ ++size_t INIT HUF_decompress4X4_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ if (dtd.tableType != 1) ++ return ERROR(GENERIC); ++ return HUF_decompress4X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); ++} ++ ++size_t INIT HUF_decompress4X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ const BYTE *ip = (const BYTE *)cSrc; ++ ++ size_t hSize = HUF_readDTableX4_wksp(dctx, cSrc, cSrcSize, workspace, workspaceSize); ++ if (HUF_isError(hSize)) ++ return hSize; ++ if (hSize >= cSrcSize) ++ return ERROR(srcSize_wrong); ++ ip += hSize; ++ cSrcSize -= hSize; ++ ++ return HUF_decompress4X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx); ++} ++ ++/* ********************************/ ++/* Generic decompression selector */ ++/* ********************************/ ++ ++size_t INIT HUF_decompress1X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable) ++ : HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable); ++} ++ ++size_t INIT HUF_decompress4X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable) ++ : HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable); ++} ++ ++typedef struct { ++ U32 tableTime; ++ U32 decode256Time; ++} algo_time_t; ++static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, quad */] = { ++ /* single, double, quad */ ++ {{0, 0}, {1, 1}, {2, 2}}, /* Q==0 : impossible */ ++ {{0, 0}, {1, 1}, {2, 2}}, /* Q==1 : impossible */ ++ {{38, 130}, {1313, 74}, {2151, 38}}, /* Q == 2 : 12-18% */ ++ {{448, 128}, {1353, 74}, {2238, 41}}, /* Q == 3 : 18-25% */ ++ {{556, 128}, {1353, 74}, {2238, 47}}, /* Q == 4 : 25-32% */ ++ {{714, 128}, {1418, 74}, {2436, 53}}, /* Q == 5 : 32-38% */ ++ {{883, 128}, {1437, 74}, {2464, 61}}, /* Q == 6 : 38-44% */ ++ {{897, 128}, {1515, 75}, {2622, 68}}, /* Q == 7 : 44-50% */ ++ {{926, 128}, {1613, 75}, {2730, 75}}, /* Q == 8 : 50-56% */ ++ {{947, 128}, {1729, 77}, {3359, 77}}, /* Q == 9 : 56-62% */ ++ {{1107, 128}, {2083, 81}, {4006, 84}}, /* Q ==10 : 62-69% */ ++ {{1177, 128}, {2379, 87}, {4785, 88}}, /* Q ==11 : 69-75% */ ++ {{1242, 128}, {2415, 93}, {5155, 84}}, /* Q ==12 : 75-81% */ ++ {{1349, 128}, {2644, 106}, {5260, 106}}, /* Q ==13 : 81-87% */ ++ {{1455, 128}, {2422, 124}, {4174, 124}}, /* Q ==14 : 87-93% */ ++ {{722, 128}, {1891, 145}, {1936, 146}}, /* Q ==15 : 93-99% */ ++}; ++ ++/** HUF_selectDecoder() : ++* Tells which decoder is likely to decode faster, ++* based on a set of pre-determined metrics. ++* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 . ++* Assumption : 0 < cSrcSize < dstSize <= 128 KB */ ++U32 INIT HUF_selectDecoder(size_t dstSize, size_t cSrcSize) ++{ ++ /* decoder timing evaluation */ ++ U32 const Q = (U32)(cSrcSize * 16 / dstSize); /* Q < 16 since dstSize > cSrcSize */ ++ U32 const D256 = (U32)(dstSize >> 8); ++ U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256); ++ U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256); ++ DTime1 += DTime1 >> 3; /* advantage to algorithm using less memory, for cache eviction */ ++ ++ return DTime1 < DTime0; ++} ++ ++typedef size_t (*decompressionAlgo)(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize); ++ ++size_t INIT HUF_decompress4X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ /* validation checks */ ++ if (dstSize == 0) ++ return ERROR(dstSize_tooSmall); ++ if (cSrcSize > dstSize) ++ return ERROR(corruption_detected); /* invalid */ ++ if (cSrcSize == dstSize) { ++ memcpy(dst, cSrc, dstSize); ++ return dstSize; ++ } /* not compressed */ ++ if (cSrcSize == 1) { ++ memset(dst, *(const BYTE *)cSrc, dstSize); ++ return dstSize; ++ } /* RLE */ ++ ++ { ++ U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); ++ return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize) ++ : HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize); ++ } ++} ++ ++size_t INIT HUF_decompress4X_hufOnly_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ /* validation checks */ ++ if (dstSize == 0) ++ return ERROR(dstSize_tooSmall); ++ if ((cSrcSize >= dstSize) || (cSrcSize <= 1)) ++ return ERROR(corruption_detected); /* invalid */ ++ ++ { ++ U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); ++ return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize) ++ : HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize); ++ } ++} ++ ++size_t INIT HUF_decompress1X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ /* validation checks */ ++ if (dstSize == 0) ++ return ERROR(dstSize_tooSmall); ++ if (cSrcSize > dstSize) ++ return ERROR(corruption_detected); /* invalid */ ++ if (cSrcSize == dstSize) { ++ memcpy(dst, cSrc, dstSize); ++ return dstSize; ++ } /* not compressed */ ++ if (cSrcSize == 1) { ++ memset(dst, *(const BYTE *)cSrc, dstSize); ++ return dstSize; ++ } /* RLE */ ++ ++ { ++ U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); ++ return algoNb ? HUF_decompress1X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize) ++ : HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize); ++ } ++} +diff --git a/xen/common/zstd/mem.h b/xen/common/zstd/mem.h +new file mode 100644 +index 0000000000..d2fa444687 +--- /dev/null ++++ b/xen/common/zstd/mem.h +@@ -0,0 +1,151 @@ ++/** ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++#ifndef MEM_H_MODULE ++#define MEM_H_MODULE ++ ++/*-**************************************** ++* Dependencies ++******************************************/ ++#include /* memcpy */ ++#include /* size_t, ptrdiff_t */ ++#include "private.h" ++ ++/*-**************************************** ++* Compiler specifics ++******************************************/ ++#define ZSTD_STATIC static inline ++ ++/*-************************************************************** ++* Basic Types ++*****************************************************************/ ++typedef uint8_t BYTE; ++typedef uint16_t U16; ++typedef int16_t S16; ++typedef uint32_t U32; ++typedef int32_t S32; ++typedef uint64_t U64; ++typedef int64_t S64; ++typedef ptrdiff_t iPtrDiff; ++typedef uintptr_t uPtrDiff; ++ ++/*-************************************************************** ++* Memory I/O ++*****************************************************************/ ++ZSTD_STATIC unsigned ZSTD_32bits(void) { return sizeof(size_t) == 4; } ++ZSTD_STATIC unsigned ZSTD_64bits(void) { return sizeof(size_t) == 8; } ++ ++#if defined(__LITTLE_ENDIAN) ++#define ZSTD_LITTLE_ENDIAN 1 ++#else ++#define ZSTD_LITTLE_ENDIAN 0 ++#endif ++ ++ZSTD_STATIC unsigned ZSTD_isLittleEndian(void) { return ZSTD_LITTLE_ENDIAN; } ++ ++ZSTD_STATIC U16 ZSTD_read16(const void *memPtr) { return get_unaligned((const U16 *)memPtr); } ++ ++ZSTD_STATIC U32 ZSTD_read32(const void *memPtr) { return get_unaligned((const U32 *)memPtr); } ++ ++ZSTD_STATIC U64 ZSTD_read64(const void *memPtr) { return get_unaligned((const U64 *)memPtr); } ++ ++ZSTD_STATIC size_t ZSTD_readST(const void *memPtr) { return get_unaligned((const size_t *)memPtr); } ++ ++ZSTD_STATIC void ZSTD_write16(void *memPtr, U16 value) { put_unaligned(value, (U16 *)memPtr); } ++ ++ZSTD_STATIC void ZSTD_write32(void *memPtr, U32 value) { put_unaligned(value, (U32 *)memPtr); } ++ ++ZSTD_STATIC void ZSTD_write64(void *memPtr, U64 value) { put_unaligned(value, (U64 *)memPtr); } ++ ++/*=== Little endian r/w ===*/ ++ ++ZSTD_STATIC U16 ZSTD_readLE16(const void *memPtr) { return get_unaligned_le16(memPtr); } ++ ++ZSTD_STATIC void ZSTD_writeLE16(void *memPtr, U16 val) { put_unaligned_le16(val, memPtr); } ++ ++ZSTD_STATIC U32 ZSTD_readLE24(const void *memPtr) { return ZSTD_readLE16(memPtr) + (((const BYTE *)memPtr)[2] << 16); } ++ ++ZSTD_STATIC void ZSTD_writeLE24(void *memPtr, U32 val) ++{ ++ ZSTD_writeLE16(memPtr, (U16)val); ++ ((BYTE *)memPtr)[2] = (BYTE)(val >> 16); ++} ++ ++ZSTD_STATIC U32 ZSTD_readLE32(const void *memPtr) { return get_unaligned_le32(memPtr); } ++ ++ZSTD_STATIC void ZSTD_writeLE32(void *memPtr, U32 val32) { put_unaligned_le32(val32, memPtr); } ++ ++ZSTD_STATIC U64 ZSTD_readLE64(const void *memPtr) { return get_unaligned_le64(memPtr); } ++ ++ZSTD_STATIC void ZSTD_writeLE64(void *memPtr, U64 val64) { put_unaligned_le64(val64, memPtr); } ++ ++ZSTD_STATIC size_t ZSTD_readLEST(const void *memPtr) ++{ ++ if (ZSTD_32bits()) ++ return (size_t)ZSTD_readLE32(memPtr); ++ else ++ return (size_t)ZSTD_readLE64(memPtr); ++} ++ ++ZSTD_STATIC void ZSTD_writeLEST(void *memPtr, size_t val) ++{ ++ if (ZSTD_32bits()) ++ ZSTD_writeLE32(memPtr, (U32)val); ++ else ++ ZSTD_writeLE64(memPtr, (U64)val); ++} ++ ++/*=== Big endian r/w ===*/ ++ ++ZSTD_STATIC U32 ZSTD_readBE32(const void *memPtr) { return get_unaligned_be32(memPtr); } ++ ++ZSTD_STATIC void ZSTD_writeBE32(void *memPtr, U32 val32) { put_unaligned_be32(val32, memPtr); } ++ ++ZSTD_STATIC U64 ZSTD_readBE64(const void *memPtr) { return get_unaligned_be64(memPtr); } ++ ++ZSTD_STATIC void ZSTD_writeBE64(void *memPtr, U64 val64) { put_unaligned_be64(val64, memPtr); } ++ ++ZSTD_STATIC size_t ZSTD_readBEST(const void *memPtr) ++{ ++ if (ZSTD_32bits()) ++ return (size_t)ZSTD_readBE32(memPtr); ++ else ++ return (size_t)ZSTD_readBE64(memPtr); ++} ++ ++ZSTD_STATIC void ZSTD_writeBEST(void *memPtr, size_t val) ++{ ++ if (ZSTD_32bits()) ++ ZSTD_writeBE32(memPtr, (U32)val); ++ else ++ ZSTD_writeBE64(memPtr, (U64)val); ++} ++ ++/* function safe only for comparisons */ ++ZSTD_STATIC U32 ZSTD_readMINMATCH(const void *memPtr, U32 length) ++{ ++ switch (length) { ++ default: ++ case 4: return ZSTD_read32(memPtr); ++ case 3: ++ if (ZSTD_isLittleEndian()) ++ return ZSTD_read32(memPtr) << 8; ++ else ++ return ZSTD_read32(memPtr) >> 8; ++ } ++} ++ ++#endif /* MEM_H_MODULE */ +diff --git a/xen/common/zstd/private.h b/xen/common/zstd/private.h +new file mode 100644 +index 0000000000..fac4d3c095 +--- /dev/null ++++ b/xen/common/zstd/private.h +@@ -0,0 +1,105 @@ ++#ifndef ZSTD_PRIVATE_H ++#define ZSTD_PRIVATE_H ++ ++#include ++#include ++#include ++ ++typedef ssize_t __attribute__((__mode__(__pointer__))) ptrdiff_t; ++ ++/* from kernel include/linux/unaligned/access_ok.h */ ++ ++static always_inline u16 get_unaligned_le16(const void *p) ++{ ++ return le16_to_cpup((__le16 *)p); ++} ++ ++static always_inline u32 get_unaligned_le32(const void *p) ++{ ++ return le32_to_cpup((__le32 *)p); ++} ++ ++static always_inline u64 get_unaligned_le64(const void *p) ++{ ++ return le64_to_cpup((__le64 *)p); ++} ++ ++static always_inline u32 get_unaligned_be32(const void *p) ++{ ++ return be32_to_cpup((__be32 *)p); ++} ++ ++static always_inline u64 get_unaligned_be64(const void *p) ++{ ++ return be64_to_cpup((__be64 *)p); ++} ++ ++static always_inline void put_unaligned_le16(u16 val, void *p) ++{ ++ *((__le16 *)p) = cpu_to_le16(val); ++} ++ ++static always_inline void put_unaligned_le32(u32 val, void *p) ++{ ++ *((__le32 *)p) = cpu_to_le32(val); ++} ++ ++static always_inline void put_unaligned_le64(u64 val, void *p) ++{ ++ *((__le64 *)p) = cpu_to_le64(val); ++} ++ ++static always_inline void put_unaligned_be32(u32 val, void *p) ++{ ++ *((__be32 *)p) = cpu_to_be32(val); ++} ++ ++static always_inline void put_unaligned_be64(u64 val, void *p) ++{ ++ *((__be64 *)p) = cpu_to_be64(val); ++} ++ ++ ++/* from kernel include/asm-generic/unaligned.h with linux/unaligned/generic.h ++ assuming little endian */ ++ ++extern void __bad_unaligned_access_size(void); ++ ++#define get_unaligned(ptr) ((__force typeof(*(ptr)))({ \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)), \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)), \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)), \ ++ __bad_unaligned_access_size())))); \ ++ })) ++ ++#define put_unaligned(val, ptr) ({ \ ++ void *__gu_p = (ptr); \ ++ switch (sizeof(*(ptr))) { \ ++ case 1: \ ++ *(u8 *)__gu_p = (__force u8)(val); \ ++ break; \ ++ case 2: \ ++ put_unaligned_le16((__force u16)(val), __gu_p); \ ++ break; \ ++ case 4: \ ++ put_unaligned_le32((__force u32)(val), __gu_p); \ ++ break; \ ++ case 8: \ ++ put_unaligned_le64((__force u64)(val), __gu_p); \ ++ break; \ ++ default: \ ++ __bad_unaligned_access_size(); \ ++ break; \ ++ } \ ++ (void)0; }) ++ ++ ++/* from kernel linux/kernel.h and uapi/linux/kernel.h */ ++ ++#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) ++#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) ++#define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) ++#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) ++ ++#endif /* ZSTD_PRIVATE_H */ +diff --git a/xen/common/zstd/zstd_common.c b/xen/common/zstd/zstd_common.c +new file mode 100644 +index 0000000000..1b13903538 +--- /dev/null ++++ b/xen/common/zstd/zstd_common.c +@@ -0,0 +1,74 @@ ++/** ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++/*-************************************* ++* Dependencies ++***************************************/ ++#include "error_private.h" ++#include "zstd_internal.h" /* declaration of ZSTD_isError, ZSTD_getErrorName, ZSTD_getErrorCode, ZSTD_getErrorString, ZSTD_versionNumber */ ++ ++/*=************************************************************** ++* Custom allocator ++****************************************************************/ ++ ++#define stack_push(stack, size) \ ++ ({ \ ++ void *const ptr = ZSTD_PTR_ALIGN((stack)->ptr); \ ++ (stack)->ptr = (char *)ptr + (size); \ ++ (stack)->ptr <= (stack)->end ? ptr : NULL; \ ++ }) ++ ++ZSTD_customMem INIT ZSTD_initStack(void *workspace, size_t workspaceSize) ++{ ++ ZSTD_customMem stackMem = {ZSTD_stackAlloc, ZSTD_stackFree, workspace}; ++ ZSTD_stack *stack = (ZSTD_stack *)workspace; ++ /* Verify preconditions */ ++ if (!workspace || workspaceSize < sizeof(ZSTD_stack) || workspace != ZSTD_PTR_ALIGN(workspace)) { ++ ZSTD_customMem error = {NULL, NULL, NULL}; ++ return error; ++ } ++ /* Initialize the stack */ ++ stack->ptr = workspace; ++ stack->end = (char *)workspace + workspaceSize; ++ stack_push(stack, sizeof(ZSTD_stack)); ++ return stackMem; ++} ++ ++void INIT *ZSTD_stackAllocAll(void *opaque, size_t *size) ++{ ++ ZSTD_stack *stack = (ZSTD_stack *)opaque; ++ *size = (BYTE const *)stack->end - (BYTE *)ZSTD_PTR_ALIGN(stack->ptr); ++ return stack_push(stack, *size); ++} ++ ++void INIT *ZSTD_stackAlloc(void *opaque, size_t size) ++{ ++ ZSTD_stack *stack = (ZSTD_stack *)opaque; ++ return stack_push(stack, size); ++} ++void INIT ZSTD_stackFree(void *opaque, void *address) ++{ ++ (void)opaque; ++ (void)address; ++} ++ ++void INIT *ZSTD_malloc(size_t size, ZSTD_customMem customMem) { return customMem.customAlloc(customMem.opaque, size); } ++ ++void INIT ZSTD_free(void *ptr, ZSTD_customMem customMem) ++{ ++ if (ptr != NULL) ++ customMem.customFree(customMem.opaque, ptr); ++} +diff --git a/xen/common/zstd/zstd_internal.h b/xen/common/zstd/zstd_internal.h +new file mode 100644 +index 0000000000..1b13840c44 +--- /dev/null ++++ b/xen/common/zstd/zstd_internal.h +@@ -0,0 +1,265 @@ ++/** ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++#ifndef ZSTD_CCOMMON_H_MODULE ++#define ZSTD_CCOMMON_H_MODULE ++ ++/*-******************************************************* ++* Compiler specifics ++*********************************************************/ ++#define FORCE_INLINE static always_inline ++#define FORCE_NOINLINE static noinline ++ ++/*-************************************* ++* Dependencies ++***************************************/ ++#include "error_private.h" ++#include "mem.h" ++#include ++#include ++ ++/*-************************************* ++* shared macros ++***************************************/ ++#define CHECK_F(f) \ ++ { \ ++ size_t const errcod = f; \ ++ if (ERR_isError(errcod)) \ ++ return errcod; \ ++ } /* check and Forward error code */ ++#define CHECK_E(f, e) \ ++ { \ ++ size_t const errcod = f; \ ++ if (ERR_isError(errcod)) \ ++ return ERROR(e); \ ++ } /* check and send Error code */ ++#define ZSTD_STATIC_ASSERT(c) \ ++ { \ ++ enum { ZSTD_static_assert = 1 / (int)(!!(c)) }; \ ++ } ++ ++/*-************************************* ++* Common constants ++***************************************/ ++#define ZSTD_OPT_NUM (1 << 12) ++#define ZSTD_DICT_MAGIC 0xEC30A437 /* v0.7+ */ ++ ++#define ZSTD_REP_NUM 3 /* number of repcodes */ ++#define ZSTD_REP_CHECK (ZSTD_REP_NUM) /* number of repcodes to check by the optimal parser */ ++#define ZSTD_REP_MOVE (ZSTD_REP_NUM - 1) ++#define ZSTD_REP_MOVE_OPT (ZSTD_REP_NUM) ++static const U32 repStartValue[ZSTD_REP_NUM] = {1, 4, 8}; ++ ++#define BIT7 128 ++#define BIT6 64 ++#define BIT5 32 ++#define BIT4 16 ++#define BIT1 2 ++#define BIT0 1 ++ ++#define ZSTD_WINDOWLOG_ABSOLUTEMIN 10 ++static const size_t ZSTD_fcs_fieldSize[4] = {0, 2, 4, 8}; ++static const size_t ZSTD_did_fieldSize[4] = {0, 1, 2, 4}; ++ ++#define ZSTD_BLOCKHEADERSIZE 3 /* C standard doesn't allow `static const` variable to be init using another `static const` variable */ ++static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE; ++typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e; ++ ++#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ ++#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ ++ ++#define HufLog 12 ++typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e; ++ ++#define LONGNBSEQ 0x7F00 ++ ++#define MINMATCH 3 ++#define EQUAL_READ32 4 ++ ++#define Litbits 8 ++#define MaxLit ((1 << Litbits) - 1) ++#define MaxML 52 ++#define MaxLL 35 ++#define MaxOff 28 ++#define MaxSeq MAX(MaxLL, MaxML) /* Assumption : MaxOff < MaxLL,MaxML */ ++#define MLFSELog 9 ++#define LLFSELog 9 ++#define OffFSELog 8 ++ ++static const U32 LL_bits[MaxLL + 1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; ++static const S16 LL_defaultNorm[MaxLL + 1] = {4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, -1, -1, -1, -1}; ++#define LL_DEFAULTNORMLOG 6 /* for static allocation */ ++static const U32 LL_defaultNormLog = LL_DEFAULTNORMLOG; ++ ++static const U32 ML_bits[MaxML + 1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; ++static const S16 ML_defaultNorm[MaxML + 1] = {1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1}; ++#define ML_DEFAULTNORMLOG 6 /* for static allocation */ ++static const U32 ML_defaultNormLog = ML_DEFAULTNORMLOG; ++ ++static const S16 OF_defaultNorm[MaxOff + 1] = {1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1}; ++#define OF_DEFAULTNORMLOG 5 /* for static allocation */ ++static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG; ++ ++/*-******************************************* ++* Shared functions to include for inlining ++*********************************************/ ++ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) { ++ /* ++ * zstd relies heavily on gcc being able to analyze and inline this ++ * memcpy() call, since it is called in a tight loop. Preboot mode ++ * is compiled in freestanding mode, which stops gcc from analyzing ++ * memcpy(). Use __builtin_memcpy() to tell gcc to analyze this as a ++ * regular memcpy(). ++ */ ++ __builtin_memcpy(dst, src, 8); ++} ++/*! ZSTD_wildcopy() : ++* custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */ ++#define WILDCOPY_OVERLENGTH 8 ++ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length) ++{ ++ const BYTE* ip = (const BYTE*)src; ++ BYTE* op = (BYTE*)dst; ++ BYTE* const oend = op + length; ++#if defined(GCC_VERSION) && GCC_VERSION >= 70000 && GCC_VERSION < 70200 ++ /* ++ * Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388. ++ * Avoid the bad case where the loop only runs once by handling the ++ * special case separately. This doesn't trigger the bug because it ++ * doesn't involve pointer/integer overflow. ++ */ ++ if (length <= 8) ++ return ZSTD_copy8(dst, src); ++#endif ++ do { ++ ZSTD_copy8(op, ip); ++ op += 8; ++ ip += 8; ++ } while (op < oend); ++} ++ ++/*-******************************************* ++* Private interfaces ++*********************************************/ ++typedef struct ZSTD_stats_s ZSTD_stats_t; ++ ++typedef struct { ++ U32 off; ++ U32 len; ++} ZSTD_match_t; ++ ++typedef struct { ++ U32 price; ++ U32 off; ++ U32 mlen; ++ U32 litlen; ++ U32 rep[ZSTD_REP_NUM]; ++} ZSTD_optimal_t; ++ ++typedef struct seqDef_s { ++ U32 offset; ++ U16 litLength; ++ U16 matchLength; ++} seqDef; ++ ++typedef struct { ++ seqDef *sequencesStart; ++ seqDef *sequences; ++ BYTE *litStart; ++ BYTE *lit; ++ BYTE *llCode; ++ BYTE *mlCode; ++ BYTE *ofCode; ++ U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */ ++ U32 longLengthPos; ++ /* opt */ ++ ZSTD_optimal_t *priceTable; ++ ZSTD_match_t *matchTable; ++ U32 *matchLengthFreq; ++ U32 *litLengthFreq; ++ U32 *litFreq; ++ U32 *offCodeFreq; ++ U32 matchLengthSum; ++ U32 matchSum; ++ U32 litLengthSum; ++ U32 litSum; ++ U32 offCodeSum; ++ U32 log2matchLengthSum; ++ U32 log2matchSum; ++ U32 log2litLengthSum; ++ U32 log2litSum; ++ U32 log2offCodeSum; ++ U32 factor; ++ U32 staticPrices; ++ U32 cachedPrice; ++ U32 cachedLitLength; ++ const BYTE *cachedLiterals; ++} seqStore_t; ++ ++const seqStore_t *ZSTD_getSeqStore(const ZSTD_CCtx *ctx); ++void ZSTD_seqToCodes(const seqStore_t *seqStorePtr); ++int ZSTD_isSkipFrame(ZSTD_DCtx *dctx); ++ ++/*= Custom memory allocation functions */ ++typedef void *(*ZSTD_allocFunction)(void *opaque, size_t size); ++typedef void (*ZSTD_freeFunction)(void *opaque, void *address); ++typedef struct { ++ ZSTD_allocFunction customAlloc; ++ ZSTD_freeFunction customFree; ++ void *opaque; ++} ZSTD_customMem; ++ ++void *ZSTD_malloc(size_t size, ZSTD_customMem customMem); ++void ZSTD_free(void *ptr, ZSTD_customMem customMem); ++ ++/*====== stack allocation ======*/ ++ ++typedef struct { ++ void *ptr; ++ const void *end; ++} ZSTD_stack; ++ ++#define ZSTD_ALIGN(x) ALIGN(x, sizeof(size_t)) ++#define ZSTD_PTR_ALIGN(p) PTR_ALIGN(p, sizeof(size_t)) ++ ++ZSTD_customMem ZSTD_initStack(void *workspace, size_t workspaceSize); ++ ++void *ZSTD_stackAllocAll(void *opaque, size_t *size); ++void *ZSTD_stackAlloc(void *opaque, size_t size); ++void ZSTD_stackFree(void *opaque, void *address); ++ ++/*====== common function ======*/ ++ ++ZSTD_STATIC U32 ZSTD_highbit32(U32 val) { return 31 - __builtin_clz(val); } ++ ++/* hidden functions */ ++ ++/* ZSTD_invalidateRepCodes() : ++ * ensures next compression will not use repcodes from previous block. ++ * Note : only works with regular variant; ++ * do not use with extDict variant ! */ ++void ZSTD_invalidateRepCodes(ZSTD_CCtx *cctx); ++ ++size_t ZSTD_freeCCtx(ZSTD_CCtx *cctx); ++size_t ZSTD_freeDCtx(ZSTD_DCtx *dctx); ++size_t ZSTD_freeCDict(ZSTD_CDict *cdict); ++size_t ZSTD_freeDDict(ZSTD_DDict *cdict); ++size_t ZSTD_freeCStream(ZSTD_CStream *zcs); ++size_t ZSTD_freeDStream(ZSTD_DStream *zds); ++ ++#endif /* ZSTD_CCOMMON_H_MODULE */ +diff --git a/xen/common/zstd/zstd_opt.h b/xen/common/zstd/zstd_opt.h +new file mode 100644 +index 0000000000..55e1b4cba8 +--- /dev/null ++++ b/xen/common/zstd/zstd_opt.h +@@ -0,0 +1,1014 @@ ++/** ++ * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++/* Note : this file is intended to be included within zstd_compress.c */ ++ ++#ifndef ZSTD_OPT_H_91842398743 ++#define ZSTD_OPT_H_91842398743 ++ ++#define ZSTD_LITFREQ_ADD 2 ++#define ZSTD_FREQ_DIV 4 ++#define ZSTD_MAX_PRICE (1 << 30) ++ ++/*-************************************* ++* Price functions for optimal parser ++***************************************/ ++FORCE_INLINE void ZSTD_setLog2Prices(seqStore_t *ssPtr) ++{ ++ ssPtr->log2matchLengthSum = ZSTD_highbit32(ssPtr->matchLengthSum + 1); ++ ssPtr->log2litLengthSum = ZSTD_highbit32(ssPtr->litLengthSum + 1); ++ ssPtr->log2litSum = ZSTD_highbit32(ssPtr->litSum + 1); ++ ssPtr->log2offCodeSum = ZSTD_highbit32(ssPtr->offCodeSum + 1); ++ ssPtr->factor = 1 + ((ssPtr->litSum >> 5) / ssPtr->litLengthSum) + ((ssPtr->litSum << 1) / (ssPtr->litSum + ssPtr->matchSum)); ++} ++ ++ZSTD_STATIC void ZSTD_rescaleFreqs(seqStore_t *ssPtr, const BYTE *src, size_t srcSize) ++{ ++ unsigned u; ++ ++ ssPtr->cachedLiterals = NULL; ++ ssPtr->cachedPrice = ssPtr->cachedLitLength = 0; ++ ssPtr->staticPrices = 0; ++ ++ if (ssPtr->litLengthSum == 0) { ++ if (srcSize <= 1024) ++ ssPtr->staticPrices = 1; ++ ++ for (u = 0; u <= MaxLit; u++) ++ ssPtr->litFreq[u] = 0; ++ for (u = 0; u < srcSize; u++) ++ ssPtr->litFreq[src[u]]++; ++ ++ ssPtr->litSum = 0; ++ ssPtr->litLengthSum = MaxLL + 1; ++ ssPtr->matchLengthSum = MaxML + 1; ++ ssPtr->offCodeSum = (MaxOff + 1); ++ ssPtr->matchSum = (ZSTD_LITFREQ_ADD << Litbits); ++ ++ for (u = 0; u <= MaxLit; u++) { ++ ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u] >> ZSTD_FREQ_DIV); ++ ssPtr->litSum += ssPtr->litFreq[u]; ++ } ++ for (u = 0; u <= MaxLL; u++) ++ ssPtr->litLengthFreq[u] = 1; ++ for (u = 0; u <= MaxML; u++) ++ ssPtr->matchLengthFreq[u] = 1; ++ for (u = 0; u <= MaxOff; u++) ++ ssPtr->offCodeFreq[u] = 1; ++ } else { ++ ssPtr->matchLengthSum = 0; ++ ssPtr->litLengthSum = 0; ++ ssPtr->offCodeSum = 0; ++ ssPtr->matchSum = 0; ++ ssPtr->litSum = 0; ++ ++ for (u = 0; u <= MaxLit; u++) { ++ ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u] >> (ZSTD_FREQ_DIV + 1)); ++ ssPtr->litSum += ssPtr->litFreq[u]; ++ } ++ for (u = 0; u <= MaxLL; u++) { ++ ssPtr->litLengthFreq[u] = 1 + (ssPtr->litLengthFreq[u] >> (ZSTD_FREQ_DIV + 1)); ++ ssPtr->litLengthSum += ssPtr->litLengthFreq[u]; ++ } ++ for (u = 0; u <= MaxML; u++) { ++ ssPtr->matchLengthFreq[u] = 1 + (ssPtr->matchLengthFreq[u] >> ZSTD_FREQ_DIV); ++ ssPtr->matchLengthSum += ssPtr->matchLengthFreq[u]; ++ ssPtr->matchSum += ssPtr->matchLengthFreq[u] * (u + 3); ++ } ++ ssPtr->matchSum *= ZSTD_LITFREQ_ADD; ++ for (u = 0; u <= MaxOff; u++) { ++ ssPtr->offCodeFreq[u] = 1 + (ssPtr->offCodeFreq[u] >> ZSTD_FREQ_DIV); ++ ssPtr->offCodeSum += ssPtr->offCodeFreq[u]; ++ } ++ } ++ ++ ZSTD_setLog2Prices(ssPtr); ++} ++ ++FORCE_INLINE U32 ZSTD_getLiteralPrice(seqStore_t *ssPtr, U32 litLength, const BYTE *literals) ++{ ++ U32 price, u; ++ ++ if (ssPtr->staticPrices) ++ return ZSTD_highbit32((U32)litLength + 1) + (litLength * 6); ++ ++ if (litLength == 0) ++ return ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[0] + 1); ++ ++ /* literals */ ++ if (ssPtr->cachedLiterals == literals) { ++ U32 const additional = litLength - ssPtr->cachedLitLength; ++ const BYTE *literals2 = ssPtr->cachedLiterals + ssPtr->cachedLitLength; ++ price = ssPtr->cachedPrice + additional * ssPtr->log2litSum; ++ for (u = 0; u < additional; u++) ++ price -= ZSTD_highbit32(ssPtr->litFreq[literals2[u]] + 1); ++ ssPtr->cachedPrice = price; ++ ssPtr->cachedLitLength = litLength; ++ } else { ++ price = litLength * ssPtr->log2litSum; ++ for (u = 0; u < litLength; u++) ++ price -= ZSTD_highbit32(ssPtr->litFreq[literals[u]] + 1); ++ ++ if (litLength >= 12) { ++ ssPtr->cachedLiterals = literals; ++ ssPtr->cachedPrice = price; ++ ssPtr->cachedLitLength = litLength; ++ } ++ } ++ ++ /* literal Length */ ++ { ++ const BYTE LL_deltaCode = 19; ++ const BYTE llCode = (litLength > 63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; ++ price += LL_bits[llCode] + ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[llCode] + 1); ++ } ++ ++ return price; ++} ++ ++FORCE_INLINE U32 ZSTD_getPrice(seqStore_t *seqStorePtr, U32 litLength, const BYTE *literals, U32 offset, U32 matchLength, const int ultra) ++{ ++ /* offset */ ++ U32 price; ++ BYTE const offCode = (BYTE)ZSTD_highbit32(offset + 1); ++ ++ if (seqStorePtr->staticPrices) ++ return ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + ZSTD_highbit32((U32)matchLength + 1) + 16 + offCode; ++ ++ price = offCode + seqStorePtr->log2offCodeSum - ZSTD_highbit32(seqStorePtr->offCodeFreq[offCode] + 1); ++ if (!ultra && offCode >= 20) ++ price += (offCode - 19) * 2; ++ ++ /* match Length */ ++ { ++ const BYTE ML_deltaCode = 36; ++ const BYTE mlCode = (matchLength > 127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; ++ price += ML_bits[mlCode] + seqStorePtr->log2matchLengthSum - ZSTD_highbit32(seqStorePtr->matchLengthFreq[mlCode] + 1); ++ } ++ ++ return price + ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + seqStorePtr->factor; ++} ++ ++ZSTD_STATIC void ZSTD_updatePrice(seqStore_t *seqStorePtr, U32 litLength, const BYTE *literals, U32 offset, U32 matchLength) ++{ ++ U32 u; ++ ++ /* literals */ ++ seqStorePtr->litSum += litLength * ZSTD_LITFREQ_ADD; ++ for (u = 0; u < litLength; u++) ++ seqStorePtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD; ++ ++ /* literal Length */ ++ { ++ const BYTE LL_deltaCode = 19; ++ const BYTE llCode = (litLength > 63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; ++ seqStorePtr->litLengthFreq[llCode]++; ++ seqStorePtr->litLengthSum++; ++ } ++ ++ /* match offset */ ++ { ++ BYTE const offCode = (BYTE)ZSTD_highbit32(offset + 1); ++ seqStorePtr->offCodeSum++; ++ seqStorePtr->offCodeFreq[offCode]++; ++ } ++ ++ /* match Length */ ++ { ++ const BYTE ML_deltaCode = 36; ++ const BYTE mlCode = (matchLength > 127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; ++ seqStorePtr->matchLengthFreq[mlCode]++; ++ seqStorePtr->matchLengthSum++; ++ } ++ ++ ZSTD_setLog2Prices(seqStorePtr); ++} ++ ++#define SET_PRICE(pos, mlen_, offset_, litlen_, price_) \ ++ { \ ++ while (last_pos < pos) { \ ++ opt[last_pos + 1].price = ZSTD_MAX_PRICE; \ ++ last_pos++; \ ++ } \ ++ opt[pos].mlen = mlen_; \ ++ opt[pos].off = offset_; \ ++ opt[pos].litlen = litlen_; \ ++ opt[pos].price = price_; \ ++ } ++ ++/* Update hashTable3 up to ip (excluded) ++ Assumption : always within prefix (i.e. not within extDict) */ ++FORCE_INLINE ++U32 ZSTD_insertAndFindFirstIndexHash3(ZSTD_CCtx *zc, const BYTE *ip) ++{ ++ U32 *const hashTable3 = zc->hashTable3; ++ U32 const hashLog3 = zc->hashLog3; ++ const BYTE *const base = zc->base; ++ U32 idx = zc->nextToUpdate3; ++ const U32 target = zc->nextToUpdate3 = (U32)(ip - base); ++ const size_t hash3 = ZSTD_hash3Ptr(ip, hashLog3); ++ ++ while (idx < target) { ++ hashTable3[ZSTD_hash3Ptr(base + idx, hashLog3)] = idx; ++ idx++; ++ } ++ ++ return hashTable3[hash3]; ++} ++ ++/*-************************************* ++* Binary Tree search ++***************************************/ ++static U32 ZSTD_insertBtAndGetAllMatches(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, U32 nbCompares, const U32 mls, U32 extDict, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ const BYTE *const base = zc->base; ++ const U32 curr = (U32)(ip - base); ++ const U32 hashLog = zc->params.cParams.hashLog; ++ const size_t h = ZSTD_hashPtr(ip, hashLog, mls); ++ U32 *const hashTable = zc->hashTable; ++ U32 matchIndex = hashTable[h]; ++ U32 *const bt = zc->chainTable; ++ const U32 btLog = zc->params.cParams.chainLog - 1; ++ const U32 btMask = (1U << btLog) - 1; ++ size_t commonLengthSmaller = 0, commonLengthLarger = 0; ++ const BYTE *const dictBase = zc->dictBase; ++ const U32 dictLimit = zc->dictLimit; ++ const BYTE *const dictEnd = dictBase + dictLimit; ++ const BYTE *const prefixStart = base + dictLimit; ++ const U32 btLow = btMask >= curr ? 0 : curr - btMask; ++ const U32 windowLow = zc->lowLimit; ++ U32 *smallerPtr = bt + 2 * (curr & btMask); ++ U32 *largerPtr = bt + 2 * (curr & btMask) + 1; ++ U32 matchEndIdx = curr + 8; ++ U32 dummy32; /* to be nullified at the end */ ++ U32 mnum = 0; ++ ++ const U32 minMatch = (mls == 3) ? 3 : 4; ++ size_t bestLength = minMatchLen - 1; ++ ++ if (minMatch == 3) { /* HC3 match finder */ ++ U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(zc, ip); ++ if (matchIndex3 > windowLow && (curr - matchIndex3 < (1 << 18))) { ++ const BYTE *match; ++ size_t currMl = 0; ++ if ((!extDict) || matchIndex3 >= dictLimit) { ++ match = base + matchIndex3; ++ if (match[bestLength] == ip[bestLength]) ++ currMl = ZSTD_count(ip, match, iLimit); ++ } else { ++ match = dictBase + matchIndex3; ++ if (ZSTD_readMINMATCH(match, MINMATCH) == ++ ZSTD_readMINMATCH(ip, MINMATCH)) /* assumption : matchIndex3 <= dictLimit-4 (by table construction) */ ++ currMl = ZSTD_count_2segments(ip + MINMATCH, match + MINMATCH, iLimit, dictEnd, prefixStart) + MINMATCH; ++ } ++ ++ /* save best solution */ ++ if (currMl > bestLength) { ++ bestLength = currMl; ++ matches[mnum].off = ZSTD_REP_MOVE_OPT + curr - matchIndex3; ++ matches[mnum].len = (U32)currMl; ++ mnum++; ++ if (currMl > ZSTD_OPT_NUM) ++ goto update; ++ if (ip + currMl == iLimit) ++ goto update; /* best possible, and avoid read overflow*/ ++ } ++ } ++ } ++ ++ hashTable[h] = curr; /* Update Hash Table */ ++ ++ while (nbCompares-- && (matchIndex > windowLow)) { ++ U32 *nextPtr = bt + 2 * (matchIndex & btMask); ++ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ ++ const BYTE *match; ++ ++ if ((!extDict) || (matchIndex + matchLength >= dictLimit)) { ++ match = base + matchIndex; ++ if (match[matchLength] == ip[matchLength]) { ++ matchLength += ZSTD_count(ip + matchLength + 1, match + matchLength + 1, iLimit) + 1; ++ } ++ } else { ++ match = dictBase + matchIndex; ++ matchLength += ZSTD_count_2segments(ip + matchLength, match + matchLength, iLimit, dictEnd, prefixStart); ++ if (matchIndex + matchLength >= dictLimit) ++ match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ ++ } ++ ++ if (matchLength > bestLength) { ++ if (matchLength > matchEndIdx - matchIndex) ++ matchEndIdx = matchIndex + (U32)matchLength; ++ bestLength = matchLength; ++ matches[mnum].off = ZSTD_REP_MOVE_OPT + curr - matchIndex; ++ matches[mnum].len = (U32)matchLength; ++ mnum++; ++ if (matchLength > ZSTD_OPT_NUM) ++ break; ++ if (ip + matchLength == iLimit) /* equal : no way to know if inf or sup */ ++ break; /* drop, to guarantee consistency (miss a little bit of compression) */ ++ } ++ ++ if (match[matchLength] < ip[matchLength]) { ++ /* match is smaller than curr */ ++ *smallerPtr = matchIndex; /* update smaller idx */ ++ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ ++ if (matchIndex <= btLow) { ++ smallerPtr = &dummy32; ++ break; ++ } /* beyond tree size, stop the search */ ++ smallerPtr = nextPtr + 1; /* new "smaller" => larger of match */ ++ matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to curr) */ ++ } else { ++ /* match is larger than curr */ ++ *largerPtr = matchIndex; ++ commonLengthLarger = matchLength; ++ if (matchIndex <= btLow) { ++ largerPtr = &dummy32; ++ break; ++ } /* beyond tree size, stop the search */ ++ largerPtr = nextPtr; ++ matchIndex = nextPtr[0]; ++ } ++ } ++ ++ *smallerPtr = *largerPtr = 0; ++ ++update: ++ zc->nextToUpdate = (matchEndIdx > curr + 8) ? matchEndIdx - 8 : curr + 1; ++ return mnum; ++} ++ ++/** Tree updater, providing best match */ ++static U32 ZSTD_BtGetAllMatches(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, const U32 maxNbAttempts, const U32 mls, ZSTD_match_t *matches, ++ const U32 minMatchLen) ++{ ++ if (ip < zc->base + zc->nextToUpdate) ++ return 0; /* skipped area */ ++ ZSTD_updateTree(zc, ip, iLimit, maxNbAttempts, mls); ++ return ZSTD_insertBtAndGetAllMatches(zc, ip, iLimit, maxNbAttempts, mls, 0, matches, minMatchLen); ++} ++ ++static U32 ZSTD_BtGetAllMatches_selectMLS(ZSTD_CCtx *zc, /* Index table will be updated */ ++ const BYTE *ip, const BYTE *const iHighLimit, const U32 maxNbAttempts, const U32 matchLengthSearch, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ switch (matchLengthSearch) { ++ case 3: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 3, matches, minMatchLen); ++ default: ++ case 4: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 4, matches, minMatchLen); ++ case 5: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 5, matches, minMatchLen); ++ case 7: ++ case 6: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 6, matches, minMatchLen); ++ } ++} ++ ++/** Tree updater, providing best match */ ++static U32 ZSTD_BtGetAllMatches_extDict(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, const U32 maxNbAttempts, const U32 mls, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ if (ip < zc->base + zc->nextToUpdate) ++ return 0; /* skipped area */ ++ ZSTD_updateTree_extDict(zc, ip, iLimit, maxNbAttempts, mls); ++ return ZSTD_insertBtAndGetAllMatches(zc, ip, iLimit, maxNbAttempts, mls, 1, matches, minMatchLen); ++} ++ ++static U32 ZSTD_BtGetAllMatches_selectMLS_extDict(ZSTD_CCtx *zc, /* Index table will be updated */ ++ const BYTE *ip, const BYTE *const iHighLimit, const U32 maxNbAttempts, const U32 matchLengthSearch, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ switch (matchLengthSearch) { ++ case 3: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 3, matches, minMatchLen); ++ default: ++ case 4: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 4, matches, minMatchLen); ++ case 5: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 5, matches, minMatchLen); ++ case 7: ++ case 6: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 6, matches, minMatchLen); ++ } ++} ++ ++/*-******************************* ++* Optimal parser ++*********************************/ ++FORCE_INLINE ++void ZSTD_compressBlock_opt_generic(ZSTD_CCtx *ctx, const void *src, size_t srcSize, const int ultra) ++{ ++ seqStore_t *seqStorePtr = &(ctx->seqStore); ++ const BYTE *const istart = (const BYTE *)src; ++ const BYTE *ip = istart; ++ const BYTE *anchor = istart; ++ const BYTE *const iend = istart + srcSize; ++ const BYTE *const ilimit = iend - 8; ++ const BYTE *const base = ctx->base; ++ const BYTE *const prefixStart = base + ctx->dictLimit; ++ ++ const U32 maxSearches = 1U << ctx->params.cParams.searchLog; ++ const U32 sufficient_len = ctx->params.cParams.targetLength; ++ const U32 mls = ctx->params.cParams.searchLength; ++ const U32 minMatch = (ctx->params.cParams.searchLength == 3) ? 3 : 4; ++ ++ ZSTD_optimal_t *opt = seqStorePtr->priceTable; ++ ZSTD_match_t *matches = seqStorePtr->matchTable; ++ const BYTE *inr; ++ U32 offset, rep[ZSTD_REP_NUM]; ++ ++ /* init */ ++ ctx->nextToUpdate3 = ctx->nextToUpdate; ++ ZSTD_rescaleFreqs(seqStorePtr, (const BYTE *)src, srcSize); ++ ip += (ip == prefixStart); ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ rep[i] = ctx->rep[i]; ++ } ++ ++ /* Match Loop */ ++ while (ip < ilimit) { ++ U32 cur, match_num, last_pos, litlen, price; ++ U32 u, mlen, best_mlen, best_off, litLength; ++ memset(opt, 0, sizeof(ZSTD_optimal_t)); ++ last_pos = 0; ++ litlen = (U32)(ip - anchor); ++ ++ /* check repCode */ ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (ip == anchor); ++ for (i = (ip == anchor); i < last_i; i++) { ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : rep[i]; ++ if ((repCur > 0) && (repCur < (S32)(ip - prefixStart)) && ++ (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repCur, minMatch))) { ++ mlen = (U32)ZSTD_count(ip + minMatch, ip + minMatch - repCur, iend) + minMatch; ++ if (mlen > sufficient_len || mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ best_off = i - (ip == anchor); ++ do { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */ ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS(ctx, ip, iend, maxSearches, mls, matches, minMatch); ++ ++ if (!last_pos && !match_num) { ++ ip++; ++ continue; ++ } ++ ++ if (match_num && (matches[match_num - 1].len > sufficient_len || matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ ++ /* set prices using matches at position = 0 */ ++ best_mlen = (last_pos) ? last_pos : minMatch; ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ while (mlen <= best_mlen) { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, matches[u].off, litlen, price); /* note : macro modifies last_pos */ ++ mlen++; ++ } ++ } ++ ++ if (last_pos < minMatch) { ++ ip++; ++ continue; ++ } ++ ++ /* initialize opt[0] */ ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ opt[0].rep[i] = rep[i]; ++ } ++ opt[0].mlen = 1; ++ opt[0].litlen = litlen; ++ ++ /* check further positions */ ++ for (cur = 1; cur <= last_pos; cur++) { ++ inr = ip + cur; ++ ++ if (opt[cur - 1].mlen == 1) { ++ litlen = opt[cur - 1].litlen + 1; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - litlen); ++ } else ++ price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor); ++ } else { ++ litlen = 1; ++ price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - 1); ++ } ++ ++ if (cur > last_pos || price <= opt[cur].price) ++ SET_PRICE(cur, 1, 0, litlen, price); ++ ++ if (cur == last_pos) ++ break; ++ ++ if (inr > ilimit) /* last match must start at a minimum distance of 8 from oend */ ++ continue; ++ ++ mlen = opt[cur].mlen; ++ if (opt[cur].off > ZSTD_REP_MOVE_OPT) { ++ opt[cur].rep[2] = opt[cur - mlen].rep[1]; ++ opt[cur].rep[1] = opt[cur - mlen].rep[0]; ++ opt[cur].rep[0] = opt[cur].off - ZSTD_REP_MOVE_OPT; ++ } else { ++ opt[cur].rep[2] = (opt[cur].off > 1) ? opt[cur - mlen].rep[1] : opt[cur - mlen].rep[2]; ++ opt[cur].rep[1] = (opt[cur].off > 0) ? opt[cur - mlen].rep[0] : opt[cur - mlen].rep[1]; ++ opt[cur].rep[0] = ++ ((opt[cur].off == ZSTD_REP_MOVE_OPT) && (mlen != 1)) ? (opt[cur - mlen].rep[0] - 1) : (opt[cur - mlen].rep[opt[cur].off]); ++ } ++ ++ best_mlen = minMatch; ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (mlen != 1); ++ for (i = (opt[cur].mlen != 1); i < last_i; i++) { /* check rep */ ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (opt[cur].rep[0] - 1) : opt[cur].rep[i]; ++ if ((repCur > 0) && (repCur < (S32)(inr - prefixStart)) && ++ (ZSTD_readMINMATCH(inr, minMatch) == ZSTD_readMINMATCH(inr - repCur, minMatch))) { ++ mlen = (U32)ZSTD_count(inr + minMatch, inr + minMatch - repCur, iend) + minMatch; ++ ++ if (mlen > sufficient_len || cur + mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ best_off = i - (opt[cur].mlen != 1); ++ if (mlen > best_mlen) ++ best_mlen = mlen; ++ ++ do { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr - litlen, ++ best_off, mlen - MINMATCH, ultra); ++ } else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || price <= opt[cur + mlen].price) ++ SET_PRICE(cur + mlen, mlen, i, litlen, price); ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS(ctx, inr, iend, maxSearches, mls, matches, best_mlen); ++ ++ if (match_num > 0 && (matches[match_num - 1].len > sufficient_len || cur + matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ /* set prices using matches at position = cur */ ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ ++ while (mlen <= best_mlen) { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip + cur - litlen, ++ matches[u].off - 1, mlen - MINMATCH, ultra); ++ else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || (price < opt[cur + mlen].price)) ++ SET_PRICE(cur + mlen, mlen, matches[u].off, litlen, price); ++ ++ mlen++; ++ } ++ } ++ } ++ ++ best_mlen = opt[last_pos].mlen; ++ best_off = opt[last_pos].off; ++ cur = last_pos - best_mlen; ++ ++ /* store sequence */ ++_storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ ++ opt[0].mlen = 1; ++ ++ while (1) { ++ mlen = opt[cur].mlen; ++ offset = opt[cur].off; ++ opt[cur].mlen = best_mlen; ++ opt[cur].off = best_off; ++ best_mlen = mlen; ++ best_off = offset; ++ if (mlen > cur) ++ break; ++ cur -= mlen; ++ } ++ ++ for (u = 0; u <= last_pos;) { ++ u += opt[u].mlen; ++ } ++ ++ for (cur = 0; cur < last_pos;) { ++ mlen = opt[cur].mlen; ++ if (mlen == 1) { ++ ip++; ++ cur++; ++ continue; ++ } ++ offset = opt[cur].off; ++ cur += mlen; ++ litLength = (U32)(ip - anchor); ++ ++ if (offset > ZSTD_REP_MOVE_OPT) { ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = offset - ZSTD_REP_MOVE_OPT; ++ offset--; ++ } else { ++ if (offset != 0) { ++ best_off = (offset == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : (rep[offset]); ++ if (offset != 1) ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = best_off; ++ } ++ if (litLength == 0) ++ offset--; ++ } ++ ++ ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ anchor = ip = ip + mlen; ++ } ++ } /* for (cur=0; cur < last_pos; ) */ ++ ++ /* Save reps for next block */ ++ { ++ int i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ ctx->repToConfirm[i] = rep[i]; ++ } ++ ++ /* Last Literals */ ++ { ++ size_t const lastLLSize = iend - anchor; ++ memcpy(seqStorePtr->lit, anchor, lastLLSize); ++ seqStorePtr->lit += lastLLSize; ++ } ++} ++ ++FORCE_INLINE ++void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx *ctx, const void *src, size_t srcSize, const int ultra) ++{ ++ seqStore_t *seqStorePtr = &(ctx->seqStore); ++ const BYTE *const istart = (const BYTE *)src; ++ const BYTE *ip = istart; ++ const BYTE *anchor = istart; ++ const BYTE *const iend = istart + srcSize; ++ const BYTE *const ilimit = iend - 8; ++ const BYTE *const base = ctx->base; ++ const U32 lowestIndex = ctx->lowLimit; ++ const U32 dictLimit = ctx->dictLimit; ++ const BYTE *const prefixStart = base + dictLimit; ++ const BYTE *const dictBase = ctx->dictBase; ++ const BYTE *const dictEnd = dictBase + dictLimit; ++ ++ const U32 maxSearches = 1U << ctx->params.cParams.searchLog; ++ const U32 sufficient_len = ctx->params.cParams.targetLength; ++ const U32 mls = ctx->params.cParams.searchLength; ++ const U32 minMatch = (ctx->params.cParams.searchLength == 3) ? 3 : 4; ++ ++ ZSTD_optimal_t *opt = seqStorePtr->priceTable; ++ ZSTD_match_t *matches = seqStorePtr->matchTable; ++ const BYTE *inr; ++ ++ /* init */ ++ U32 offset, rep[ZSTD_REP_NUM]; ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ rep[i] = ctx->rep[i]; ++ } ++ ++ ctx->nextToUpdate3 = ctx->nextToUpdate; ++ ZSTD_rescaleFreqs(seqStorePtr, (const BYTE *)src, srcSize); ++ ip += (ip == prefixStart); ++ ++ /* Match Loop */ ++ while (ip < ilimit) { ++ U32 cur, match_num, last_pos, litlen, price; ++ U32 u, mlen, best_mlen, best_off, litLength; ++ U32 curr = (U32)(ip - base); ++ memset(opt, 0, sizeof(ZSTD_optimal_t)); ++ last_pos = 0; ++ opt[0].litlen = (U32)(ip - anchor); ++ ++ /* check repCode */ ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (ip == anchor); ++ for (i = (ip == anchor); i < last_i; i++) { ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : rep[i]; ++ const U32 repIndex = (U32)(curr - repCur); ++ const BYTE *const repBase = repIndex < dictLimit ? dictBase : base; ++ const BYTE *const repMatch = repBase + repIndex; ++ if ((repCur > 0 && repCur <= (S32)curr) && ++ (((U32)((dictLimit - 1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ ++ && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch))) { ++ /* repcode detected we should take it */ ++ const BYTE *const repEnd = repIndex < dictLimit ? dictEnd : iend; ++ mlen = (U32)ZSTD_count_2segments(ip + minMatch, repMatch + minMatch, iend, repEnd, prefixStart) + minMatch; ++ ++ if (mlen > sufficient_len || mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ ++ best_off = i - (ip == anchor); ++ litlen = opt[0].litlen; ++ do { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */ ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS_extDict(ctx, ip, iend, maxSearches, mls, matches, minMatch); /* first search (depth 0) */ ++ ++ if (!last_pos && !match_num) { ++ ip++; ++ continue; ++ } ++ ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ opt[0].rep[i] = rep[i]; ++ } ++ opt[0].mlen = 1; ++ ++ if (match_num && (matches[match_num - 1].len > sufficient_len || matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ ++ best_mlen = (last_pos) ? last_pos : minMatch; ++ ++ /* set prices using matches at position = 0 */ ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ litlen = opt[0].litlen; ++ while (mlen <= best_mlen) { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, matches[u].off, litlen, price); ++ mlen++; ++ } ++ } ++ ++ if (last_pos < minMatch) { ++ ip++; ++ continue; ++ } ++ ++ /* check further positions */ ++ for (cur = 1; cur <= last_pos; cur++) { ++ inr = ip + cur; ++ ++ if (opt[cur - 1].mlen == 1) { ++ litlen = opt[cur - 1].litlen + 1; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - litlen); ++ } else ++ price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor); ++ } else { ++ litlen = 1; ++ price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - 1); ++ } ++ ++ if (cur > last_pos || price <= opt[cur].price) ++ SET_PRICE(cur, 1, 0, litlen, price); ++ ++ if (cur == last_pos) ++ break; ++ ++ if (inr > ilimit) /* last match must start at a minimum distance of 8 from oend */ ++ continue; ++ ++ mlen = opt[cur].mlen; ++ if (opt[cur].off > ZSTD_REP_MOVE_OPT) { ++ opt[cur].rep[2] = opt[cur - mlen].rep[1]; ++ opt[cur].rep[1] = opt[cur - mlen].rep[0]; ++ opt[cur].rep[0] = opt[cur].off - ZSTD_REP_MOVE_OPT; ++ } else { ++ opt[cur].rep[2] = (opt[cur].off > 1) ? opt[cur - mlen].rep[1] : opt[cur - mlen].rep[2]; ++ opt[cur].rep[1] = (opt[cur].off > 0) ? opt[cur - mlen].rep[0] : opt[cur - mlen].rep[1]; ++ opt[cur].rep[0] = ++ ((opt[cur].off == ZSTD_REP_MOVE_OPT) && (mlen != 1)) ? (opt[cur - mlen].rep[0] - 1) : (opt[cur - mlen].rep[opt[cur].off]); ++ } ++ ++ best_mlen = minMatch; ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (mlen != 1); ++ for (i = (mlen != 1); i < last_i; i++) { ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (opt[cur].rep[0] - 1) : opt[cur].rep[i]; ++ const U32 repIndex = (U32)(curr + cur - repCur); ++ const BYTE *const repBase = repIndex < dictLimit ? dictBase : base; ++ const BYTE *const repMatch = repBase + repIndex; ++ if ((repCur > 0 && repCur <= (S32)(curr + cur)) && ++ (((U32)((dictLimit - 1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ ++ && (ZSTD_readMINMATCH(inr, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch))) { ++ /* repcode detected */ ++ const BYTE *const repEnd = repIndex < dictLimit ? dictEnd : iend; ++ mlen = (U32)ZSTD_count_2segments(inr + minMatch, repMatch + minMatch, iend, repEnd, prefixStart) + minMatch; ++ ++ if (mlen > sufficient_len || cur + mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ best_off = i - (opt[cur].mlen != 1); ++ if (mlen > best_mlen) ++ best_mlen = mlen; ++ ++ do { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr - litlen, ++ best_off, mlen - MINMATCH, ultra); ++ } else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || price <= opt[cur + mlen].price) ++ SET_PRICE(cur + mlen, mlen, i, litlen, price); ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS_extDict(ctx, inr, iend, maxSearches, mls, matches, minMatch); ++ ++ if (match_num > 0 && (matches[match_num - 1].len > sufficient_len || cur + matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ /* set prices using matches at position = cur */ ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ ++ while (mlen <= best_mlen) { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip + cur - litlen, ++ matches[u].off - 1, mlen - MINMATCH, ultra); ++ else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || (price < opt[cur + mlen].price)) ++ SET_PRICE(cur + mlen, mlen, matches[u].off, litlen, price); ++ ++ mlen++; ++ } ++ } ++ } /* for (cur = 1; cur <= last_pos; cur++) */ ++ ++ best_mlen = opt[last_pos].mlen; ++ best_off = opt[last_pos].off; ++ cur = last_pos - best_mlen; ++ ++ /* store sequence */ ++_storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ ++ opt[0].mlen = 1; ++ ++ while (1) { ++ mlen = opt[cur].mlen; ++ offset = opt[cur].off; ++ opt[cur].mlen = best_mlen; ++ opt[cur].off = best_off; ++ best_mlen = mlen; ++ best_off = offset; ++ if (mlen > cur) ++ break; ++ cur -= mlen; ++ } ++ ++ for (u = 0; u <= last_pos;) { ++ u += opt[u].mlen; ++ } ++ ++ for (cur = 0; cur < last_pos;) { ++ mlen = opt[cur].mlen; ++ if (mlen == 1) { ++ ip++; ++ cur++; ++ continue; ++ } ++ offset = opt[cur].off; ++ cur += mlen; ++ litLength = (U32)(ip - anchor); ++ ++ if (offset > ZSTD_REP_MOVE_OPT) { ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = offset - ZSTD_REP_MOVE_OPT; ++ offset--; ++ } else { ++ if (offset != 0) { ++ best_off = (offset == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : (rep[offset]); ++ if (offset != 1) ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = best_off; ++ } ++ ++ if (litLength == 0) ++ offset--; ++ } ++ ++ ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ anchor = ip = ip + mlen; ++ } ++ } /* for (cur=0; cur < last_pos; ) */ ++ ++ /* Save reps for next block */ ++ { ++ int i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ ctx->repToConfirm[i] = rep[i]; ++ } ++ ++ /* Last Literals */ ++ { ++ size_t lastLLSize = iend - anchor; ++ memcpy(seqStorePtr->lit, anchor, lastLLSize); ++ seqStorePtr->lit += lastLLSize; ++ } ++} ++ ++#endif /* ZSTD_OPT_H_91842398743 */ +diff --git a/xen/include/xen/decompress.h b/xen/include/xen/decompress.h +index b2955faa4b..f5bc17f2b6 100644 +--- a/xen/include/xen/decompress.h ++++ b/xen/include/xen/decompress.h +@@ -31,7 +31,7 @@ typedef int decompress_fn(unsigned char *inbuf, unsigned int len, + * dependent). + */ + +-decompress_fn bunzip2, unxz, unlzma, unlzo, unlz4; ++decompress_fn bunzip2, unxz, unlzma, unlzo, unlz4, unzstd; + + int decompress(void *inbuf, unsigned int len, void *outbuf); + +diff --git a/xen/include/xen/xxhash.h b/xen/include/xen/xxhash.h +new file mode 100644 +index 0000000000..13ddc616d1 +--- /dev/null ++++ b/xen/include/xen/xxhash.h +@@ -0,0 +1,259 @@ ++/* ++ * xxHash - Extremely Fast Hash algorithm ++ * Copyright (C) 2012-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at: ++ * - xxHash homepage: https://cyan4973.github.io/xxHash/ ++ * - xxHash source repository: https://github.com/Cyan4973/xxHash ++ */ ++ ++/* ++ * Notice extracted from xxHash homepage: ++ * ++ * xxHash is an extremely fast Hash algorithm, running at RAM speed limits. ++ * It also successfully passes all tests from the SMHasher suite. ++ * ++ * Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 ++ * Duo @3GHz) ++ * ++ * Name Speed Q.Score Author ++ * xxHash 5.4 GB/s 10 ++ * CrapWow 3.2 GB/s 2 Andrew ++ * MumurHash 3a 2.7 GB/s 10 Austin Appleby ++ * SpookyHash 2.0 GB/s 10 Bob Jenkins ++ * SBox 1.4 GB/s 9 Bret Mulvey ++ * Lookup3 1.2 GB/s 9 Bob Jenkins ++ * SuperFastHash 1.2 GB/s 1 Paul Hsieh ++ * CityHash64 1.05 GB/s 10 Pike & Alakuijala ++ * FNV 0.55 GB/s 5 Fowler, Noll, Vo ++ * CRC32 0.43 GB/s 9 ++ * MD5-32 0.33 GB/s 10 Ronald L. Rivest ++ * SHA1-32 0.28 GB/s 10 ++ * ++ * Q.Score is a measure of quality of the hash function. ++ * It depends on successfully passing SMHasher test set. ++ * 10 is a perfect score. ++ * ++ * A 64-bits version, named xxh64 offers much better speed, ++ * but for 64-bits applications only. ++ * Name Speed on 64 bits Speed on 32 bits ++ * xxh64 13.8 GB/s 1.9 GB/s ++ * xxh32 6.8 GB/s 6.0 GB/s ++ */ ++ ++#ifndef XXHASH_H ++#define XXHASH_H ++ ++#include ++ ++/*-**************************** ++ * Simple Hash Functions ++ *****************************/ ++ ++/** ++ * xxh32() - calculate the 32-bit hash of the input with a given seed. ++ * ++ * @input: The data to hash. ++ * @length: The length of the data to hash. ++ * @seed: The seed can be used to alter the result predictably. ++ * ++ * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s ++ * ++ * Return: The 32-bit hash of the data. ++ */ ++uint32_t xxh32(const void *input, size_t length, uint32_t seed); ++ ++/** ++ * xxh64() - calculate the 64-bit hash of the input with a given seed. ++ * ++ * @input: The data to hash. ++ * @length: The length of the data to hash. ++ * @seed: The seed can be used to alter the result predictably. ++ * ++ * This function runs 2x faster on 64-bit systems, but slower on 32-bit systems. ++ * ++ * Return: The 64-bit hash of the data. ++ */ ++uint64_t xxh64(const void *input, size_t length, uint64_t seed); ++ ++/** ++ * xxhash() - calculate wordsize hash of the input with a given seed ++ * @input: The data to hash. ++ * @length: The length of the data to hash. ++ * @seed: The seed can be used to alter the result predictably. ++ * ++ * If the hash does not need to be comparable between machines with ++ * different word sizes, this function will call whichever of xxh32() ++ * or xxh64() is faster. ++ * ++ * Return: wordsize hash of the data. ++ */ ++ ++static inline unsigned long xxhash(const void *input, size_t length, ++ uint64_t seed) ++{ ++#if BITS_PER_LONG == 64 ++ return xxh64(input, length, seed); ++#else ++ return xxh32(input, length, seed); ++#endif ++} ++ ++/*-**************************** ++ * Streaming Hash Functions ++ *****************************/ ++ ++/* ++ * These definitions are only meant to allow allocation of XXH state ++ * statically, on stack, or in a struct for example. ++ * Do not use members directly. ++ */ ++ ++/** ++ * struct xxh32_state - private xxh32 state, do not use members directly ++ */ ++struct xxh32_state { ++ uint32_t total_len_32; ++ uint32_t large_len; ++ uint32_t v1; ++ uint32_t v2; ++ uint32_t v3; ++ uint32_t v4; ++ uint32_t mem32[4]; ++ uint32_t memsize; ++}; ++ ++/** ++ * struct xxh32_state - private xxh64 state, do not use members directly ++ */ ++struct xxh64_state { ++ uint64_t total_len; ++ uint64_t v1; ++ uint64_t v2; ++ uint64_t v3; ++ uint64_t v4; ++ uint64_t mem64[4]; ++ uint32_t memsize; ++}; ++ ++/** ++ * xxh32_reset() - reset the xxh32 state to start a new hashing operation ++ * ++ * @state: The xxh32 state to reset. ++ * @seed: Initialize the hash state with this seed. ++ * ++ * Call this function on any xxh32_state to prepare for a new hashing operation. ++ */ ++void xxh32_reset(struct xxh32_state *state, uint32_t seed); ++ ++/** ++ * xxh32_update() - hash the data given and update the xxh32 state ++ * ++ * @state: The xxh32 state to update. ++ * @input: The data to hash. ++ * @length: The length of the data to hash. ++ * ++ * After calling xxh32_reset() call xxh32_update() as many times as necessary. ++ * ++ * Return: Zero on success, otherwise an error code. ++ */ ++int xxh32_update(struct xxh32_state *state, const void *input, size_t length); ++ ++/** ++ * xxh32_digest() - produce the current xxh32 hash ++ * ++ * @state: Produce the current xxh32 hash of this state. ++ * ++ * A hash value can be produced at any time. It is still possible to continue ++ * inserting input into the hash state after a call to xxh32_digest(), and ++ * generate new hashes later on, by calling xxh32_digest() again. ++ * ++ * Return: The xxh32 hash stored in the state. ++ */ ++uint32_t xxh32_digest(const struct xxh32_state *state); ++ ++/** ++ * xxh64_reset() - reset the xxh64 state to start a new hashing operation ++ * ++ * @state: The xxh64 state to reset. ++ * @seed: Initialize the hash state with this seed. ++ */ ++void xxh64_reset(struct xxh64_state *state, uint64_t seed); ++ ++/** ++ * xxh64_update() - hash the data given and update the xxh64 state ++ * @state: The xxh64 state to update. ++ * @input: The data to hash. ++ * @length: The length of the data to hash. ++ * ++ * After calling xxh64_reset() call xxh64_update() as many times as necessary. ++ * ++ * Return: Zero on success, otherwise an error code. ++ */ ++int xxh64_update(struct xxh64_state *state, const void *input, size_t length); ++ ++/** ++ * xxh64_digest() - produce the current xxh64 hash ++ * ++ * @state: Produce the current xxh64 hash of this state. ++ * ++ * A hash value can be produced at any time. It is still possible to continue ++ * inserting input into the hash state after a call to xxh64_digest(), and ++ * generate new hashes later on, by calling xxh64_digest() again. ++ * ++ * Return: The xxh64 hash stored in the state. ++ */ ++uint64_t xxh64_digest(const struct xxh64_state *state); ++ ++/*-************************** ++ * Utils ++ ***************************/ ++ ++/** ++ * xxh32_copy_state() - copy the source state into the destination state ++ * ++ * @src: The source xxh32 state. ++ * @dst: The destination xxh32 state. ++ */ ++void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src); ++ ++/** ++ * xxh64_copy_state() - copy the source state into the destination state ++ * ++ * @src: The source xxh64 state. ++ * @dst: The destination xxh64 state. ++ */ ++void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src); ++ ++#endif /* XXHASH_H */ +diff --git a/xen/include/xen/zstd.h b/xen/include/xen/zstd.h +new file mode 100644 +index 0000000000..eb33582a18 +--- /dev/null ++++ b/xen/include/xen/zstd.h +@@ -0,0 +1,1157 @@ ++/* ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++#ifndef ZSTD_H ++#define ZSTD_H ++ ++/* ====== Dependency ======*/ ++#include /* size_t */ ++ ++ ++/*-***************************************************************************** ++ * Introduction ++ * ++ * zstd, short for Zstandard, is a fast lossless compression algorithm, ++ * targeting real-time compression scenarios at zlib-level and better ++ * compression ratios. The zstd compression library provides in-memory ++ * compression and decompression functions. The library supports compression ++ * levels from 1 up to ZSTD_maxCLevel() which is 22. Levels >= 20, labeled ++ * ultra, should be used with caution, as they require more memory. ++ * Compression can be done in: ++ * - a single step, reusing a context (described as Explicit memory management) ++ * - unbounded multiple steps (described as Streaming compression) ++ * The compression ratio achievable on small data can be highly improved using ++ * compression with a dictionary in: ++ * - a single step (described as Simple dictionary API) ++ * - a single step, reusing a dictionary (described as Fast dictionary API) ++ ******************************************************************************/ ++ ++/*====== Helper functions ======*/ ++ ++/** ++ * enum ZSTD_ErrorCode - zstd error codes ++ * ++ * Functions that return size_t can be checked for errors using ZSTD_isError() ++ * and the ZSTD_ErrorCode can be extracted using ZSTD_getErrorCode(). ++ */ ++typedef enum { ++ ZSTD_error_no_error, ++ ZSTD_error_GENERIC, ++ ZSTD_error_prefix_unknown, ++ ZSTD_error_version_unsupported, ++ ZSTD_error_parameter_unknown, ++ ZSTD_error_frameParameter_unsupported, ++ ZSTD_error_frameParameter_unsupportedBy32bits, ++ ZSTD_error_frameParameter_windowTooLarge, ++ ZSTD_error_compressionParameter_unsupported, ++ ZSTD_error_init_missing, ++ ZSTD_error_memory_allocation, ++ ZSTD_error_stage_wrong, ++ ZSTD_error_dstSize_tooSmall, ++ ZSTD_error_srcSize_wrong, ++ ZSTD_error_corruption_detected, ++ ZSTD_error_checksum_wrong, ++ ZSTD_error_tableLog_tooLarge, ++ ZSTD_error_maxSymbolValue_tooLarge, ++ ZSTD_error_maxSymbolValue_tooSmall, ++ ZSTD_error_dictionary_corrupted, ++ ZSTD_error_dictionary_wrong, ++ ZSTD_error_dictionaryCreation_failed, ++ ZSTD_error_maxCode ++} ZSTD_ErrorCode; ++ ++/** ++ * ZSTD_maxCLevel() - maximum compression level available ++ * ++ * Return: Maximum compression level available. ++ */ ++int ZSTD_maxCLevel(void); ++/** ++ * ZSTD_compressBound() - maximum compressed size in worst case scenario ++ * @srcSize: The size of the data to compress. ++ * ++ * Return: The maximum compressed size in the worst case scenario. ++ */ ++size_t ZSTD_compressBound(size_t srcSize); ++/** ++ * ZSTD_isError() - tells if a size_t function result is an error code ++ * @code: The function result to check for error. ++ * ++ * Return: Non-zero iff the code is an error. ++ */ ++static __attribute__((unused)) unsigned int ZSTD_isError(size_t code) ++{ ++ return code > (size_t)-ZSTD_error_maxCode; ++} ++/** ++ * ZSTD_getErrorCode() - translates an error function result to a ZSTD_ErrorCode ++ * @functionResult: The result of a function for which ZSTD_isError() is true. ++ * ++ * Return: The ZSTD_ErrorCode corresponding to the functionResult or 0 ++ * if the functionResult isn't an error. ++ */ ++static __attribute__((unused)) ZSTD_ErrorCode ZSTD_getErrorCode( ++ size_t functionResult) ++{ ++ if (!ZSTD_isError(functionResult)) ++ return (ZSTD_ErrorCode)0; ++ return (ZSTD_ErrorCode)(0 - functionResult); ++} ++ ++/** ++ * enum ZSTD_strategy - zstd compression search strategy ++ * ++ * From faster to stronger. ++ */ ++typedef enum { ++ ZSTD_fast, ++ ZSTD_dfast, ++ ZSTD_greedy, ++ ZSTD_lazy, ++ ZSTD_lazy2, ++ ZSTD_btlazy2, ++ ZSTD_btopt, ++ ZSTD_btopt2 ++} ZSTD_strategy; ++ ++/** ++ * struct ZSTD_compressionParameters - zstd compression parameters ++ * @windowLog: Log of the largest match distance. Larger means more ++ * compression, and more memory needed during decompression. ++ * @chainLog: Fully searched segment. Larger means more compression, slower, ++ * and more memory (useless for fast). ++ * @hashLog: Dispatch table. Larger means more compression, ++ * slower, and more memory. ++ * @searchLog: Number of searches. Larger means more compression and slower. ++ * @searchLength: Match length searched. Larger means faster decompression, ++ * sometimes less compression. ++ * @targetLength: Acceptable match size for optimal parser (only). Larger means ++ * more compression, and slower. ++ * @strategy: The zstd compression strategy. ++ */ ++typedef struct { ++ unsigned int windowLog; ++ unsigned int chainLog; ++ unsigned int hashLog; ++ unsigned int searchLog; ++ unsigned int searchLength; ++ unsigned int targetLength; ++ ZSTD_strategy strategy; ++} ZSTD_compressionParameters; ++ ++/** ++ * struct ZSTD_frameParameters - zstd frame parameters ++ * @contentSizeFlag: Controls whether content size will be present in the frame ++ * header (when known). ++ * @checksumFlag: Controls whether a 32-bit checksum is generated at the end ++ * of the frame for error detection. ++ * @noDictIDFlag: Controls whether dictID will be saved into the frame header ++ * when using dictionary compression. ++ * ++ * The default value is all fields set to 0. ++ */ ++typedef struct { ++ unsigned int contentSizeFlag; ++ unsigned int checksumFlag; ++ unsigned int noDictIDFlag; ++} ZSTD_frameParameters; ++ ++/** ++ * struct ZSTD_parameters - zstd parameters ++ * @cParams: The compression parameters. ++ * @fParams: The frame parameters. ++ */ ++typedef struct { ++ ZSTD_compressionParameters cParams; ++ ZSTD_frameParameters fParams; ++} ZSTD_parameters; ++ ++/** ++ * ZSTD_getCParams() - returns ZSTD_compressionParameters for selected level ++ * @compressionLevel: The compression level from 1 to ZSTD_maxCLevel(). ++ * @estimatedSrcSize: The estimated source size to compress or 0 if unknown. ++ * @dictSize: The dictionary size or 0 if a dictionary isn't being used. ++ * ++ * Return: The selected ZSTD_compressionParameters. ++ */ ++ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, ++ unsigned long long estimatedSrcSize, size_t dictSize); ++ ++/** ++ * ZSTD_getParams() - returns ZSTD_parameters for selected level ++ * @compressionLevel: The compression level from 1 to ZSTD_maxCLevel(). ++ * @estimatedSrcSize: The estimated source size to compress or 0 if unknown. ++ * @dictSize: The dictionary size or 0 if a dictionary isn't being used. ++ * ++ * The same as ZSTD_getCParams() except also selects the default frame ++ * parameters (all zero). ++ * ++ * Return: The selected ZSTD_parameters. ++ */ ++ZSTD_parameters ZSTD_getParams(int compressionLevel, ++ unsigned long long estimatedSrcSize, size_t dictSize); ++ ++/*-************************************* ++ * Explicit memory management ++ **************************************/ ++ ++/** ++ * ZSTD_CCtxWorkspaceBound() - amount of memory needed to initialize a ZSTD_CCtx ++ * @cParams: The compression parameters to be used for compression. ++ * ++ * If multiple compression parameters might be used, the caller must call ++ * ZSTD_CCtxWorkspaceBound() for each set of parameters and use the maximum ++ * size. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initCCtx(). ++ */ ++size_t ZSTD_CCtxWorkspaceBound(ZSTD_compressionParameters cParams); ++ ++/** ++ * struct ZSTD_CCtx - the zstd compression context ++ * ++ * When compressing many times it is recommended to allocate a context just once ++ * and reuse it for each successive compression operation. ++ */ ++typedef struct ZSTD_CCtx_s ZSTD_CCtx; ++/** ++ * ZSTD_initCCtx() - initialize a zstd compression context ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. Use ZSTD_CCtxWorkspaceBound() to ++ * determine how large the workspace must be. ++ * ++ * Return: A compression context emplaced into workspace. ++ */ ++ZSTD_CCtx *ZSTD_initCCtx(void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_compressCCtx() - compress src into dst ++ * @ctx: The context. Must have been initialized with a workspace at ++ * least as large as ZSTD_CCtxWorkspaceBound(params.cParams). ++ * @dst: The buffer to compress src into. ++ * @dstCapacity: The size of the destination buffer. May be any size, but ++ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. ++ * @src: The data to compress. ++ * @srcSize: The size of the data to compress. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * ++ * Return: The compressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compressCCtx(ZSTD_CCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, ZSTD_parameters params); ++ ++/** ++ * ZSTD_DCtxWorkspaceBound() - amount of memory needed to initialize a ZSTD_DCtx ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initDCtx(). ++ */ ++size_t ZSTD_DCtxWorkspaceBound(void); ++ ++/** ++ * struct ZSTD_DCtx - the zstd decompression context ++ * ++ * When decompressing many times it is recommended to allocate a context just ++ * once and reuse it for each successive decompression operation. ++ */ ++typedef struct ZSTD_DCtx_s ZSTD_DCtx; ++/** ++ * ZSTD_initDCtx() - initialize a zstd decompression context ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. Use ZSTD_DCtxWorkspaceBound() to ++ * determine how large the workspace must be. ++ * ++ * Return: A decompression context emplaced into workspace. ++ */ ++ZSTD_DCtx *ZSTD_initDCtx(void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_decompressDCtx() - decompress zstd compressed src into dst ++ * @ctx: The decompression context. ++ * @dst: The buffer to decompress src into. ++ * @dstCapacity: The size of the destination buffer. Must be at least as large ++ * as the decompressed size. If the caller cannot upper bound the ++ * decompressed size, then it's better to use the streaming API. ++ * @src: The zstd compressed data to decompress. Multiple concatenated ++ * frames and skippable frames are allowed. ++ * @srcSize: The exact size of the data to decompress. ++ * ++ * Return: The decompressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_decompressDCtx(ZSTD_DCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++ ++/*-************************ ++ * Simple dictionary API ++ **************************/ ++ ++/** ++ * ZSTD_compress_usingDict() - compress src into dst using a dictionary ++ * @ctx: The context. Must have been initialized with a workspace at ++ * least as large as ZSTD_CCtxWorkspaceBound(params.cParams). ++ * @dst: The buffer to compress src into. ++ * @dstCapacity: The size of the destination buffer. May be any size, but ++ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. ++ * @src: The data to compress. ++ * @srcSize: The size of the data to compress. ++ * @dict: The dictionary to use for compression. ++ * @dictSize: The size of the dictionary. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * ++ * Compression using a predefined dictionary. The same dictionary must be used ++ * during decompression. ++ * ++ * Return: The compressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compress_usingDict(ZSTD_CCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, const void *dict, size_t dictSize, ++ ZSTD_parameters params); ++ ++/** ++ * ZSTD_decompress_usingDict() - decompress src into dst using a dictionary ++ * @ctx: The decompression context. ++ * @dst: The buffer to decompress src into. ++ * @dstCapacity: The size of the destination buffer. Must be at least as large ++ * as the decompressed size. If the caller cannot upper bound the ++ * decompressed size, then it's better to use the streaming API. ++ * @src: The zstd compressed data to decompress. Multiple concatenated ++ * frames and skippable frames are allowed. ++ * @srcSize: The exact size of the data to decompress. ++ * @dict: The dictionary to use for decompression. The same dictionary ++ * must've been used to compress the data. ++ * @dictSize: The size of the dictionary. ++ * ++ * Return: The decompressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_decompress_usingDict(ZSTD_DCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, const void *dict, size_t dictSize); ++ ++/*-************************** ++ * Fast dictionary API ++ ***************************/ ++ ++/** ++ * ZSTD_CDictWorkspaceBound() - memory needed to initialize a ZSTD_CDict ++ * @cParams: The compression parameters to be used for compression. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initCDict(). ++ */ ++size_t ZSTD_CDictWorkspaceBound(ZSTD_compressionParameters cParams); ++ ++/** ++ * struct ZSTD_CDict - a digested dictionary to be used for compression ++ */ ++typedef struct ZSTD_CDict_s ZSTD_CDict; ++ ++/** ++ * ZSTD_initCDict() - initialize a digested dictionary for compression ++ * @dictBuffer: The dictionary to digest. The buffer is referenced by the ++ * ZSTD_CDict so it must outlive the returned ZSTD_CDict. ++ * @dictSize: The size of the dictionary. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * @workspace: The workspace. It must outlive the returned ZSTD_CDict. ++ * @workspaceSize: The workspace size. Must be at least ++ * ZSTD_CDictWorkspaceBound(params.cParams). ++ * ++ * When compressing multiple messages / blocks with the same dictionary it is ++ * recommended to load it just once. The ZSTD_CDict merely references the ++ * dictBuffer, so it must outlive the returned ZSTD_CDict. ++ * ++ * Return: The digested dictionary emplaced into workspace. ++ */ ++ZSTD_CDict *ZSTD_initCDict(const void *dictBuffer, size_t dictSize, ++ ZSTD_parameters params, void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_compress_usingCDict() - compress src into dst using a ZSTD_CDict ++ * @ctx: The context. Must have been initialized with a workspace at ++ * least as large as ZSTD_CCtxWorkspaceBound(cParams) where ++ * cParams are the compression parameters used to initialize the ++ * cdict. ++ * @dst: The buffer to compress src into. ++ * @dstCapacity: The size of the destination buffer. May be any size, but ++ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. ++ * @src: The data to compress. ++ * @srcSize: The size of the data to compress. ++ * @cdict: The digested dictionary to use for compression. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * ++ * Compression using a digested dictionary. The same dictionary must be used ++ * during decompression. ++ * ++ * Return: The compressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compress_usingCDict(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, const ZSTD_CDict *cdict); ++ ++ ++/** ++ * ZSTD_DDictWorkspaceBound() - memory needed to initialize a ZSTD_DDict ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initDDict(). ++ */ ++size_t ZSTD_DDictWorkspaceBound(void); ++ ++/** ++ * struct ZSTD_DDict - a digested dictionary to be used for decompression ++ */ ++typedef struct ZSTD_DDict_s ZSTD_DDict; ++ ++/** ++ * ZSTD_initDDict() - initialize a digested dictionary for decompression ++ * @dictBuffer: The dictionary to digest. The buffer is referenced by the ++ * ZSTD_DDict so it must outlive the returned ZSTD_DDict. ++ * @dictSize: The size of the dictionary. ++ * @workspace: The workspace. It must outlive the returned ZSTD_DDict. ++ * @workspaceSize: The workspace size. Must be at least ++ * ZSTD_DDictWorkspaceBound(). ++ * ++ * When decompressing multiple messages / blocks with the same dictionary it is ++ * recommended to load it just once. The ZSTD_DDict merely references the ++ * dictBuffer, so it must outlive the returned ZSTD_DDict. ++ * ++ * Return: The digested dictionary emplaced into workspace. ++ */ ++ZSTD_DDict *ZSTD_initDDict(const void *dictBuffer, size_t dictSize, ++ void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_decompress_usingDDict() - decompress src into dst using a ZSTD_DDict ++ * @ctx: The decompression context. ++ * @dst: The buffer to decompress src into. ++ * @dstCapacity: The size of the destination buffer. Must be at least as large ++ * as the decompressed size. If the caller cannot upper bound the ++ * decompressed size, then it's better to use the streaming API. ++ * @src: The zstd compressed data to decompress. Multiple concatenated ++ * frames and skippable frames are allowed. ++ * @srcSize: The exact size of the data to decompress. ++ * @ddict: The digested dictionary to use for decompression. The same ++ * dictionary must've been used to compress the data. ++ * ++ * Return: The decompressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_decompress_usingDDict(ZSTD_DCtx *dctx, void *dst, ++ size_t dstCapacity, const void *src, size_t srcSize, ++ const ZSTD_DDict *ddict); ++ ++ ++/*-************************** ++ * Streaming ++ ***************************/ ++ ++/** ++ * struct ZSTD_inBuffer - input buffer for streaming ++ * @src: Start of the input buffer. ++ * @size: Size of the input buffer. ++ * @pos: Position where reading stopped. Will be updated. ++ * Necessarily 0 <= pos <= size. ++ */ ++typedef struct ZSTD_inBuffer_s { ++ const void *src; ++ size_t size; ++ size_t pos; ++} ZSTD_inBuffer; ++ ++/** ++ * struct ZSTD_outBuffer - output buffer for streaming ++ * @dst: Start of the output buffer. ++ * @size: Size of the output buffer. ++ * @pos: Position where writing stopped. Will be updated. ++ * Necessarily 0 <= pos <= size. ++ */ ++typedef struct ZSTD_outBuffer_s { ++ void *dst; ++ size_t size; ++ size_t pos; ++} ZSTD_outBuffer; ++ ++ ++ ++/*-***************************************************************************** ++ * Streaming compression - HowTo ++ * ++ * A ZSTD_CStream object is required to track streaming operation. ++ * Use ZSTD_initCStream() to initialize a ZSTD_CStream object. ++ * ZSTD_CStream objects can be reused multiple times on consecutive compression ++ * operations. It is recommended to re-use ZSTD_CStream in situations where many ++ * streaming operations will be achieved consecutively. Use one separate ++ * ZSTD_CStream per thread for parallel execution. ++ * ++ * Use ZSTD_compressStream() repetitively to consume input stream. ++ * The function will automatically update both `pos` fields. ++ * Note that it may not consume the entire input, in which case `pos < size`, ++ * and it's up to the caller to present again remaining data. ++ * It returns a hint for the preferred number of bytes to use as an input for ++ * the next function call. ++ * ++ * At any moment, it's possible to flush whatever data remains within internal ++ * buffer, using ZSTD_flushStream(). `output->pos` will be updated. There might ++ * still be some content left within the internal buffer if `output->size` is ++ * too small. It returns the number of bytes left in the internal buffer and ++ * must be called until it returns 0. ++ * ++ * ZSTD_endStream() instructs to finish a frame. It will perform a flush and ++ * write frame epilogue. The epilogue is required for decoders to consider a ++ * frame completed. Similar to ZSTD_flushStream(), it may not be able to flush ++ * the full content if `output->size` is too small. In which case, call again ++ * ZSTD_endStream() to complete the flush. It returns the number of bytes left ++ * in the internal buffer and must be called until it returns 0. ++ ******************************************************************************/ ++ ++/** ++ * ZSTD_CStreamWorkspaceBound() - memory needed to initialize a ZSTD_CStream ++ * @cParams: The compression parameters to be used for compression. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initCStream() and ZSTD_initCStream_usingCDict(). ++ */ ++size_t ZSTD_CStreamWorkspaceBound(ZSTD_compressionParameters cParams); ++ ++/** ++ * struct ZSTD_CStream - the zstd streaming compression context ++ */ ++typedef struct ZSTD_CStream_s ZSTD_CStream; ++ ++/*===== ZSTD_CStream management functions =====*/ ++/** ++ * ZSTD_initCStream() - initialize a zstd streaming compression context ++ * @params: The zstd compression parameters. ++ * @pledgedSrcSize: If params.fParams.contentSizeFlag == 1 then the caller must ++ * pass the source size (zero means empty source). Otherwise, ++ * the caller may optionally pass the source size, or zero if ++ * unknown. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. ++ * Use ZSTD_CStreamWorkspaceBound(params.cParams) to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming compression context. ++ */ ++ZSTD_CStream *ZSTD_initCStream(ZSTD_parameters params, ++ unsigned long long pledgedSrcSize, void *workspace, ++ size_t workspaceSize); ++ ++/** ++ * ZSTD_initCStream_usingCDict() - initialize a streaming compression context ++ * @cdict: The digested dictionary to use for compression. ++ * @pledgedSrcSize: Optionally the source size, or zero if unknown. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. Call ZSTD_CStreamWorkspaceBound() ++ * with the cParams used to initialize the cdict to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming compression context. ++ */ ++ZSTD_CStream *ZSTD_initCStream_usingCDict(const ZSTD_CDict *cdict, ++ unsigned long long pledgedSrcSize, void *workspace, ++ size_t workspaceSize); ++ ++/*===== Streaming compression functions =====*/ ++/** ++ * ZSTD_resetCStream() - reset the context using parameters from creation ++ * @zcs: The zstd streaming compression context to reset. ++ * @pledgedSrcSize: Optionally the source size, or zero if unknown. ++ * ++ * Resets the context using the parameters from creation. Skips dictionary ++ * loading, since it can be reused. If `pledgedSrcSize` is non-zero the frame ++ * content size is always written into the frame header. ++ * ++ * Return: Zero or an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_resetCStream(ZSTD_CStream *zcs, unsigned long long pledgedSrcSize); ++/** ++ * ZSTD_compressStream() - streaming compress some of input into output ++ * @zcs: The zstd streaming compression context. ++ * @output: Destination buffer. `output->pos` is updated to indicate how much ++ * compressed data was written. ++ * @input: Source buffer. `input->pos` is updated to indicate how much data was ++ * read. Note that it may not consume the entire input, in which case ++ * `input->pos < input->size`, and it's up to the caller to present ++ * remaining data again. ++ * ++ * The `input` and `output` buffers may be any size. Guaranteed to make some ++ * forward progress if `input` and `output` are not empty. ++ * ++ * Return: A hint for the number of bytes to use as the input for the next ++ * function call or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compressStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output, ++ ZSTD_inBuffer *input); ++/** ++ * ZSTD_flushStream() - flush internal buffers into output ++ * @zcs: The zstd streaming compression context. ++ * @output: Destination buffer. `output->pos` is updated to indicate how much ++ * compressed data was written. ++ * ++ * ZSTD_flushStream() must be called until it returns 0, meaning all the data ++ * has been flushed. Since ZSTD_flushStream() causes a block to be ended, ++ * calling it too often will degrade the compression ratio. ++ * ++ * Return: The number of bytes still present within internal buffers or an ++ * error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_flushStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output); ++/** ++ * ZSTD_endStream() - flush internal buffers into output and end the frame ++ * @zcs: The zstd streaming compression context. ++ * @output: Destination buffer. `output->pos` is updated to indicate how much ++ * compressed data was written. ++ * ++ * ZSTD_endStream() must be called until it returns 0, meaning all the data has ++ * been flushed and the frame epilogue has been written. ++ * ++ * Return: The number of bytes still present within internal buffers or an ++ * error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_endStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output); ++ ++/** ++ * ZSTD_CStreamInSize() - recommended size for the input buffer ++ * ++ * Return: The recommended size for the input buffer. ++ */ ++size_t ZSTD_CStreamInSize(void); ++/** ++ * ZSTD_CStreamOutSize() - recommended size for the output buffer ++ * ++ * When the output buffer is at least this large, it is guaranteed to be large ++ * enough to flush at least one complete compressed block. ++ * ++ * Return: The recommended size for the output buffer. ++ */ ++size_t ZSTD_CStreamOutSize(void); ++ ++ ++ ++/*-***************************************************************************** ++ * Streaming decompression - HowTo ++ * ++ * A ZSTD_DStream object is required to track streaming operations. ++ * Use ZSTD_initDStream() to initialize a ZSTD_DStream object. ++ * ZSTD_DStream objects can be re-used multiple times. ++ * ++ * Use ZSTD_decompressStream() repetitively to consume your input. ++ * The function will update both `pos` fields. ++ * If `input->pos < input->size`, some input has not been consumed. ++ * It's up to the caller to present again remaining data. ++ * If `output->pos < output->size`, decoder has flushed everything it could. ++ * Returns 0 iff a frame is completely decoded and fully flushed. ++ * Otherwise it returns a suggested next input size that will never load more ++ * than the current frame. ++ ******************************************************************************/ ++ ++/** ++ * ZSTD_DStreamWorkspaceBound() - memory needed to initialize a ZSTD_DStream ++ * @maxWindowSize: The maximum window size allowed for compressed frames. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initDStream() and ZSTD_initDStream_usingDDict(). ++ */ ++size_t ZSTD_DStreamWorkspaceBound(size_t maxWindowSize); ++ ++/** ++ * struct ZSTD_DStream - the zstd streaming decompression context ++ */ ++typedef struct ZSTD_DStream_s ZSTD_DStream; ++/*===== ZSTD_DStream management functions =====*/ ++/** ++ * ZSTD_initDStream() - initialize a zstd streaming decompression context ++ * @maxWindowSize: The maximum window size allowed for compressed frames. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. ++ * Use ZSTD_DStreamWorkspaceBound(maxWindowSize) to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming decompression context. ++ */ ++ZSTD_DStream *ZSTD_initDStream(size_t maxWindowSize, void *workspace, ++ size_t workspaceSize); ++/** ++ * ZSTD_initDStream_usingDDict() - initialize streaming decompression context ++ * @maxWindowSize: The maximum window size allowed for compressed frames. ++ * @ddict: The digested dictionary to use for decompression. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. ++ * Use ZSTD_DStreamWorkspaceBound(maxWindowSize) to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming decompression context. ++ */ ++ZSTD_DStream *ZSTD_initDStream_usingDDict(size_t maxWindowSize, ++ const ZSTD_DDict *ddict, void *workspace, size_t workspaceSize); ++ ++/*===== Streaming decompression functions =====*/ ++/** ++ * ZSTD_resetDStream() - reset the context using parameters from creation ++ * @zds: The zstd streaming decompression context to reset. ++ * ++ * Resets the context using the parameters from creation. Skips dictionary ++ * loading, since it can be reused. ++ * ++ * Return: Zero or an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_resetDStream(ZSTD_DStream *zds); ++/** ++ * ZSTD_decompressStream() - streaming decompress some of input into output ++ * @zds: The zstd streaming decompression context. ++ * @output: Destination buffer. `output.pos` is updated to indicate how much ++ * decompressed data was written. ++ * @input: Source buffer. `input.pos` is updated to indicate how much data was ++ * read. Note that it may not consume the entire input, in which case ++ * `input.pos < input.size`, and it's up to the caller to present ++ * remaining data again. ++ * ++ * The `input` and `output` buffers may be any size. Guaranteed to make some ++ * forward progress if `input` and `output` are not empty. ++ * ZSTD_decompressStream() will not consume the last byte of the frame until ++ * the entire frame is flushed. ++ * ++ * Return: Returns 0 iff a frame is completely decoded and fully flushed. ++ * Otherwise returns a hint for the number of bytes to use as the input ++ * for the next function call or an error, which can be checked using ++ * ZSTD_isError(). The size hint will never load more than the frame. ++ */ ++size_t ZSTD_decompressStream(ZSTD_DStream *zds, ZSTD_outBuffer *output, ++ ZSTD_inBuffer *input); ++ ++/** ++ * ZSTD_DStreamInSize() - recommended size for the input buffer ++ * ++ * Return: The recommended size for the input buffer. ++ */ ++size_t ZSTD_DStreamInSize(void); ++/** ++ * ZSTD_DStreamOutSize() - recommended size for the output buffer ++ * ++ * When the output buffer is at least this large, it is guaranteed to be large ++ * enough to flush at least one complete decompressed block. ++ * ++ * Return: The recommended size for the output buffer. ++ */ ++size_t ZSTD_DStreamOutSize(void); ++ ++ ++/* --- Constants ---*/ ++#define ZSTD_MAGICNUMBER 0xFD2FB528 /* >= v0.8.0 */ ++#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50U ++ ++#define ZSTD_CONTENTSIZE_UNKNOWN (0ULL - 1) ++#define ZSTD_CONTENTSIZE_ERROR (0ULL - 2) ++ ++#define ZSTD_WINDOWLOG_MAX_32 27 ++#define ZSTD_WINDOWLOG_MAX_64 27 ++#define ZSTD_WINDOWLOG_MAX \ ++ ((unsigned int)(sizeof(size_t) == 4 \ ++ ? ZSTD_WINDOWLOG_MAX_32 \ ++ : ZSTD_WINDOWLOG_MAX_64)) ++#define ZSTD_WINDOWLOG_MIN 10 ++#define ZSTD_HASHLOG_MAX ZSTD_WINDOWLOG_MAX ++#define ZSTD_HASHLOG_MIN 6 ++#define ZSTD_CHAINLOG_MAX (ZSTD_WINDOWLOG_MAX+1) ++#define ZSTD_CHAINLOG_MIN ZSTD_HASHLOG_MIN ++#define ZSTD_HASHLOG3_MAX 17 ++#define ZSTD_SEARCHLOG_MAX (ZSTD_WINDOWLOG_MAX-1) ++#define ZSTD_SEARCHLOG_MIN 1 ++/* only for ZSTD_fast, other strategies are limited to 6 */ ++#define ZSTD_SEARCHLENGTH_MAX 7 ++/* only for ZSTD_btopt, other strategies are limited to 4 */ ++#define ZSTD_SEARCHLENGTH_MIN 3 ++#define ZSTD_TARGETLENGTH_MIN 4 ++#define ZSTD_TARGETLENGTH_MAX 999 ++ ++/* for static allocation */ ++#define ZSTD_FRAMEHEADERSIZE_MAX 18 ++#define ZSTD_FRAMEHEADERSIZE_MIN 6 ++static const size_t ZSTD_frameHeaderSize_prefix = 5; ++static const size_t ZSTD_frameHeaderSize_min = ZSTD_FRAMEHEADERSIZE_MIN; ++static const size_t ZSTD_frameHeaderSize_max = ZSTD_FRAMEHEADERSIZE_MAX; ++/* magic number + skippable frame length */ ++static const size_t ZSTD_skippableHeaderSize = 8; ++ ++ ++/*-************************************* ++ * Compressed size functions ++ **************************************/ ++ ++/** ++ * ZSTD_findFrameCompressedSize() - returns the size of a compressed frame ++ * @src: Source buffer. It should point to the start of a zstd encoded frame ++ * or a skippable frame. ++ * @srcSize: The size of the source buffer. It must be at least as large as the ++ * size of the frame. ++ * ++ * Return: The compressed size of the frame pointed to by `src` or an error, ++ * which can be check with ZSTD_isError(). ++ * Suitable to pass to ZSTD_decompress() or similar functions. ++ */ ++size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize); ++ ++/*-************************************* ++ * Decompressed size functions ++ **************************************/ ++/** ++ * ZSTD_getFrameContentSize() - returns the content size in a zstd frame header ++ * @src: It should point to the start of a zstd encoded frame. ++ * @srcSize: The size of the source buffer. It must be at least as large as the ++ * frame header. `ZSTD_frameHeaderSize_max` is always large enough. ++ * ++ * Return: The frame content size stored in the frame header if known. ++ * `ZSTD_CONTENTSIZE_UNKNOWN` if the content size isn't stored in the ++ * frame header. `ZSTD_CONTENTSIZE_ERROR` on invalid input. ++ */ ++unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize); ++ ++/** ++ * ZSTD_findDecompressedSize() - returns decompressed size of a series of frames ++ * @src: It should point to the start of a series of zstd encoded and/or ++ * skippable frames. ++ * @srcSize: The exact size of the series of frames. ++ * ++ * If any zstd encoded frame in the series doesn't have the frame content size ++ * set, `ZSTD_CONTENTSIZE_UNKNOWN` is returned. But frame content size is always ++ * set when using ZSTD_compress(). The decompressed size can be very large. ++ * If the source is untrusted, the decompressed size could be wrong or ++ * intentionally modified. Always ensure the result fits within the ++ * application's authorized limits. ZSTD_findDecompressedSize() handles multiple ++ * frames, and so it must traverse the input to read each frame header. This is ++ * efficient as most of the data is skipped, however it does mean that all frame ++ * data must be present and valid. ++ * ++ * Return: Decompressed size of all the data contained in the frames if known. ++ * `ZSTD_CONTENTSIZE_UNKNOWN` if the decompressed size is unknown. ++ * `ZSTD_CONTENTSIZE_ERROR` if an error occurred. ++ */ ++unsigned long long ZSTD_findDecompressedSize(const void *src, size_t srcSize); ++ ++/*-************************************* ++ * Advanced compression functions ++ **************************************/ ++/** ++ * ZSTD_checkCParams() - ensure parameter values remain within authorized range ++ * @cParams: The zstd compression parameters. ++ * ++ * Return: Zero or an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams); ++ ++/** ++ * ZSTD_adjustCParams() - optimize parameters for a given srcSize and dictSize ++ * @srcSize: Optionally the estimated source size, or zero if unknown. ++ * @dictSize: Optionally the estimated dictionary size, or zero if unknown. ++ * ++ * Return: The optimized parameters. ++ */ ++ZSTD_compressionParameters ZSTD_adjustCParams( ++ ZSTD_compressionParameters cParams, unsigned long long srcSize, ++ size_t dictSize); ++ ++/*--- Advanced decompression functions ---*/ ++ ++/** ++ * ZSTD_isFrame() - returns true iff the buffer starts with a valid frame ++ * @buffer: The source buffer to check. ++ * @size: The size of the source buffer, must be at least 4 bytes. ++ * ++ * Return: True iff the buffer starts with a zstd or skippable frame identifier. ++ */ ++unsigned int ZSTD_isFrame(const void *buffer, size_t size); ++ ++/** ++ * ZSTD_getDictID_fromDict() - returns the dictionary id stored in a dictionary ++ * @dict: The dictionary buffer. ++ * @dictSize: The size of the dictionary buffer. ++ * ++ * Return: The dictionary id stored within the dictionary or 0 if the ++ * dictionary is not a zstd dictionary. If it returns 0 the ++ * dictionary can still be loaded as a content-only dictionary. ++ */ ++unsigned int ZSTD_getDictID_fromDict(const void *dict, size_t dictSize); ++ ++/** ++ * ZSTD_getDictID_fromDDict() - returns the dictionary id stored in a ZSTD_DDict ++ * @ddict: The ddict to find the id of. ++ * ++ * Return: The dictionary id stored within `ddict` or 0 if the dictionary is not ++ * a zstd dictionary. If it returns 0 `ddict` will be loaded as a ++ * content-only dictionary. ++ */ ++unsigned int ZSTD_getDictID_fromDDict(const ZSTD_DDict *ddict); ++ ++/** ++ * ZSTD_getDictID_fromFrame() - returns the dictionary id stored in a zstd frame ++ * @src: Source buffer. It must be a zstd encoded frame. ++ * @srcSize: The size of the source buffer. It must be at least as large as the ++ * frame header. `ZSTD_frameHeaderSize_max` is always large enough. ++ * ++ * Return: The dictionary id required to decompress the frame stored within ++ * `src` or 0 if the dictionary id could not be decoded. It can return ++ * 0 if the frame does not require a dictionary, the dictionary id ++ * wasn't stored in the frame, `src` is not a zstd frame, or `srcSize` ++ * is too small. ++ */ ++unsigned int ZSTD_getDictID_fromFrame(const void *src, size_t srcSize); ++ ++/** ++ * struct ZSTD_frameParams - zstd frame parameters stored in the frame header ++ * @frameContentSize: The frame content size, or 0 if not present. ++ * @windowSize: The window size, or 0 if the frame is a skippable frame. ++ * @dictID: The dictionary id, or 0 if not present. ++ * @checksumFlag: Whether a checksum was used. ++ */ ++typedef struct { ++ unsigned long long frameContentSize; ++ unsigned int windowSize; ++ unsigned int dictID; ++ unsigned int checksumFlag; ++} ZSTD_frameParams; ++ ++/** ++ * ZSTD_getFrameParams() - extracts parameters from a zstd or skippable frame ++ * @fparamsPtr: On success the frame parameters are written here. ++ * @src: The source buffer. It must point to a zstd or skippable frame. ++ * @srcSize: The size of the source buffer. `ZSTD_frameHeaderSize_max` is ++ * always large enough to succeed. ++ * ++ * Return: 0 on success. If more data is required it returns how many bytes ++ * must be provided to make forward progress. Otherwise it returns ++ * an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_getFrameParams(ZSTD_frameParams *fparamsPtr, const void *src, ++ size_t srcSize); ++ ++/*-***************************************************************************** ++ * Buffer-less and synchronous inner streaming functions ++ * ++ * This is an advanced API, giving full control over buffer management, for ++ * users which need direct control over memory. ++ * But it's also a complex one, with many restrictions (documented below). ++ * Prefer using normal streaming API for an easier experience ++ ******************************************************************************/ ++ ++/*-***************************************************************************** ++ * Buffer-less streaming compression (synchronous mode) ++ * ++ * A ZSTD_CCtx object is required to track streaming operations. ++ * Use ZSTD_initCCtx() to initialize a context. ++ * ZSTD_CCtx object can be re-used multiple times within successive compression ++ * operations. ++ * ++ * Start by initializing a context. ++ * Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary ++ * compression, ++ * or ZSTD_compressBegin_advanced(), for finer parameter control. ++ * It's also possible to duplicate a reference context which has already been ++ * initialized, using ZSTD_copyCCtx() ++ * ++ * Then, consume your input using ZSTD_compressContinue(). ++ * There are some important considerations to keep in mind when using this ++ * advanced function : ++ * - ZSTD_compressContinue() has no internal buffer. It uses externally provided ++ * buffer only. ++ * - Interface is synchronous : input is consumed entirely and produce 1+ ++ * (or more) compressed blocks. ++ * - Caller must ensure there is enough space in `dst` to store compressed data ++ * under worst case scenario. Worst case evaluation is provided by ++ * ZSTD_compressBound(). ++ * ZSTD_compressContinue() doesn't guarantee recover after a failed ++ * compression. ++ * - ZSTD_compressContinue() presumes prior input ***is still accessible and ++ * unmodified*** (up to maximum distance size, see WindowLog). ++ * It remembers all previous contiguous blocks, plus one separated memory ++ * segment (which can itself consists of multiple contiguous blocks) ++ * - ZSTD_compressContinue() detects that prior input has been overwritten when ++ * `src` buffer overlaps. In which case, it will "discard" the relevant memory ++ * section from its history. ++ * ++ * Finish a frame with ZSTD_compressEnd(), which will write the last block(s) ++ * and optional checksum. It's possible to use srcSize==0, in which case, it ++ * will write a final empty block to end the frame. Without last block mark, ++ * frames will be considered unfinished (corrupted) by decoders. ++ * ++ * `ZSTD_CCtx` object can be re-used (ZSTD_compressBegin()) to compress some new ++ * frame. ++ ******************************************************************************/ ++ ++/*===== Buffer-less streaming compression functions =====*/ ++size_t ZSTD_compressBegin(ZSTD_CCtx *cctx, int compressionLevel); ++size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx *cctx, const void *dict, ++ size_t dictSize, int compressionLevel); ++size_t ZSTD_compressBegin_advanced(ZSTD_CCtx *cctx, const void *dict, ++ size_t dictSize, ZSTD_parameters params, ++ unsigned long long pledgedSrcSize); ++size_t ZSTD_copyCCtx(ZSTD_CCtx *cctx, const ZSTD_CCtx *preparedCCtx, ++ unsigned long long pledgedSrcSize); ++size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx *cctx, const ZSTD_CDict *cdict, ++ unsigned long long pledgedSrcSize); ++size_t ZSTD_compressContinue(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++size_t ZSTD_compressEnd(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++ ++ ++ ++/*-***************************************************************************** ++ * Buffer-less streaming decompression (synchronous mode) ++ * ++ * A ZSTD_DCtx object is required to track streaming operations. ++ * Use ZSTD_initDCtx() to initialize a context. ++ * A ZSTD_DCtx object can be re-used multiple times. ++ * ++ * First typical operation is to retrieve frame parameters, using ++ * ZSTD_getFrameParams(). It fills a ZSTD_frameParams structure which provide ++ * important information to correctly decode the frame, such as the minimum ++ * rolling buffer size to allocate to decompress data (`windowSize`), and the ++ * dictionary ID used. ++ * Note: content size is optional, it may not be present. 0 means unknown. ++ * Note that these values could be wrong, either because of data malformation, ++ * or because an attacker is spoofing deliberate false information. As a ++ * consequence, check that values remain within valid application range, ++ * especially `windowSize`, before allocation. Each application can set its own ++ * limit, depending on local restrictions. For extended interoperability, it is ++ * recommended to support at least 8 MB. ++ * Frame parameters are extracted from the beginning of the compressed frame. ++ * Data fragment must be large enough to ensure successful decoding, typically ++ * `ZSTD_frameHeaderSize_max` bytes. ++ * Result: 0: successful decoding, the `ZSTD_frameParams` structure is filled. ++ * >0: `srcSize` is too small, provide at least this many bytes. ++ * errorCode, which can be tested using ZSTD_isError(). ++ * ++ * Start decompression, with ZSTD_decompressBegin() or ++ * ZSTD_decompressBegin_usingDict(). Alternatively, you can copy a prepared ++ * context, using ZSTD_copyDCtx(). ++ * ++ * Then use ZSTD_nextSrcSizeToDecompress() and ZSTD_decompressContinue() ++ * alternatively. ++ * ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' ++ * to ZSTD_decompressContinue(). ++ * ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will ++ * fail. ++ * ++ * The result of ZSTD_decompressContinue() is the number of bytes regenerated ++ * within 'dst' (necessarily <= dstCapacity). It can be zero, which is not an ++ * error; it just means ZSTD_decompressContinue() has decoded some metadata ++ * item. It can also be an error code, which can be tested with ZSTD_isError(). ++ * ++ * ZSTD_decompressContinue() needs previous data blocks during decompression, up ++ * to `windowSize`. They should preferably be located contiguously, prior to ++ * current block. Alternatively, a round buffer of sufficient size is also ++ * possible. Sufficient size is determined by frame parameters. ++ * ZSTD_decompressContinue() is very sensitive to contiguity, if 2 blocks don't ++ * follow each other, make sure that either the compressor breaks contiguity at ++ * the same place, or that previous contiguous segment is large enough to ++ * properly handle maximum back-reference. ++ * ++ * A frame is fully decoded when ZSTD_nextSrcSizeToDecompress() returns zero. ++ * Context can then be reset to start a new decompression. ++ * ++ * Note: it's possible to know if next input to present is a header or a block, ++ * using ZSTD_nextInputType(). This information is not required to properly ++ * decode a frame. ++ * ++ * == Special case: skippable frames == ++ * ++ * Skippable frames allow integration of user-defined data into a flow of ++ * concatenated frames. Skippable frames will be ignored (skipped) by a ++ * decompressor. The format of skippable frames is as follows: ++ * a) Skippable frame ID - 4 Bytes, Little endian format, any value from ++ * 0x184D2A50 to 0x184D2A5F ++ * b) Frame Size - 4 Bytes, Little endian format, unsigned 32-bits ++ * c) Frame Content - any content (User Data) of length equal to Frame Size ++ * For skippable frames ZSTD_decompressContinue() always returns 0. ++ * For skippable frames ZSTD_getFrameParams() returns fparamsPtr->windowLog==0 ++ * what means that a frame is skippable. ++ * Note: If fparamsPtr->frameContentSize==0, it is ambiguous: the frame might ++ * actually be a zstd encoded frame with no content. For purposes of ++ * decompression, it is valid in both cases to skip the frame using ++ * ZSTD_findFrameCompressedSize() to find its size in bytes. ++ * It also returns frame size as fparamsPtr->frameContentSize. ++ ******************************************************************************/ ++ ++/*===== Buffer-less streaming decompression functions =====*/ ++size_t ZSTD_decompressBegin(ZSTD_DCtx *dctx); ++size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, ++ size_t dictSize); ++void ZSTD_copyDCtx(ZSTD_DCtx *dctx, const ZSTD_DCtx *preparedDCtx); ++size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx *dctx); ++size_t ZSTD_decompressContinue(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++typedef enum { ++ ZSTDnit_frameHeader, ++ ZSTDnit_blockHeader, ++ ZSTDnit_block, ++ ZSTDnit_lastBlock, ++ ZSTDnit_checksum, ++ ZSTDnit_skippableFrame ++} ZSTD_nextInputType_e; ++ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx *dctx); ++ ++/*-***************************************************************************** ++ * Block functions ++ * ++ * Block functions produce and decode raw zstd blocks, without frame metadata. ++ * Frame metadata cost is typically ~18 bytes, which can be non-negligible for ++ * very small blocks (< 100 bytes). User will have to take in charge required ++ * information to regenerate data, such as compressed and content sizes. ++ * ++ * A few rules to respect: ++ * - Compressing and decompressing require a context structure ++ * + Use ZSTD_initCCtx() and ZSTD_initDCtx() ++ * - It is necessary to init context before starting ++ * + compression : ZSTD_compressBegin() ++ * + decompression : ZSTD_decompressBegin() ++ * + variants _usingDict() are also allowed ++ * + copyCCtx() and copyDCtx() work too ++ * - Block size is limited, it must be <= ZSTD_getBlockSizeMax() ++ * + If you need to compress more, cut data into multiple blocks ++ * + Consider using the regular ZSTD_compress() instead, as frame metadata ++ * costs become negligible when source size is large. ++ * - When a block is considered not compressible enough, ZSTD_compressBlock() ++ * result will be zero. In which case, nothing is produced into `dst`. ++ * + User must test for such outcome and deal directly with uncompressed data ++ * + ZSTD_decompressBlock() doesn't accept uncompressed data as input!!! ++ * + In case of multiple successive blocks, decoder must be informed of ++ * uncompressed block existence to follow proper history. Use ++ * ZSTD_insertBlock() in such a case. ++ ******************************************************************************/ ++ ++/* Define for static allocation */ ++#define ZSTD_BLOCKSIZE_ABSOLUTEMAX (128 * 1024) ++/*===== Raw zstd block functions =====*/ ++size_t ZSTD_getBlockSizeMax(ZSTD_CCtx *cctx); ++size_t ZSTD_compressBlock(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++size_t ZSTD_decompressBlock(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++size_t ZSTD_insertBlock(ZSTD_DCtx *dctx, const void *blockStart, ++ size_t blockSize); ++ ++#endif /* ZSTD_H */