From nobody Thu May 16 14:52:48 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1571663680; cv=none; d=zoho.com; s=zohoarc; b=brKRDt9eRBOAUN13EP8dISyRFIwckIMjQVESIA1zxagMlKSr664N6sUXTp0ycOKnw5cQ0rsSaFJtBll4XWVIdbE08avh2AOj3hviut3am6wyL+CCJseXJCbLFy10VTnRpfLWklK92UaRo37wFgFX+JbvpyMRiqNE3RDwwWuFrGo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1571663680; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=HcrjH3sNkg5tJMOGbMqP3HwOk717tt39vP2aT2I+UU0=; b=cPLNu6VFPuQlkH33r/reDaiB7EmztDJ7sDz4P2DTLqTfr0T97sZP6u5bxGTmbKjYyKx32CvqmeDk8o1W68uoXZKYrJDoplw+3V9Z9M05pOo4B9W4/79DFmGfY17R4fzlsP5RjMrEfWjrq93fLmqkLqc4t3rFFFH1aPY+3QvGq2A= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1571663680474856.907885083161; Mon, 21 Oct 2019 06:14:40 -0700 (PDT) Received: from localhost ([::1]:40872 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMXW9-0006jq-Do for importer@patchew.org; Mon, 21 Oct 2019 09:14:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34490) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMXUC-0005O1-FI for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:38 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iMXUA-0003pF-FG for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:36 -0400 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:23900) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1iMXU8-0003oC-Op for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:34 -0400 Received: from pps.filterd (m0098410.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id x9LD99ch104354 for ; Mon, 21 Oct 2019 09:12:31 -0400 Received: from e06smtp03.uk.ibm.com (e06smtp03.uk.ibm.com [195.75.94.99]) by mx0a-001b2d01.pphosted.com with ESMTP id 2vsbftwbje-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Mon, 21 Oct 2019 09:12:31 -0400 Received: from localhost by e06smtp03.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 21 Oct 2019 14:12:28 +0100 Received: from b06avi18878370.portsmouth.uk.ibm.com (9.149.26.194) by e06smtp03.uk.ibm.com (192.168.101.133) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Mon, 21 Oct 2019 14:12:24 +0100 Received: from b06wcsmtp001.portsmouth.uk.ibm.com (b06wcsmtp001.portsmouth.uk.ibm.com [9.149.105.160]) by b06avi18878370.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id x9LDCNGt17105246 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 21 Oct 2019 13:12:23 GMT Received: from b06wcsmtp001.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id B4662A405C; Mon, 21 Oct 2019 13:12:23 +0000 (GMT) Received: from b06wcsmtp001.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 95690A405F; Mon, 21 Oct 2019 13:12:23 +0000 (GMT) Received: from smtp.tls.ibm.com (unknown [9.101.4.1]) by b06wcsmtp001.portsmouth.uk.ibm.com (Postfix) with ESMTP; Mon, 21 Oct 2019 13:12:23 +0000 (GMT) Received: from yukon.kaod.org.ibmuc.com (sig-9-145-86-58.uk.ibm.com [9.145.86.58]) by smtp.tls.ibm.com (Postfix) with ESMTP id DA40F2201BC; Mon, 21 Oct 2019 15:12:22 +0200 (CEST) From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= To: David Gibson Subject: [PATCH 1/5] ppc/pnv: Add a PNOR model Date: Mon, 21 Oct 2019 15:12:11 +0200 X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191021131215.3693-1-clg@kaod.org> References: <20191021131215.3693-1-clg@kaod.org> MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 19102113-0012-0000-0000-0000035AF16B X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 19102113-0013-0000-0000-0000219618A8 Message-Id: <20191021131215.3693-2-clg@kaod.org> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-10-21_04:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1034 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1908290000 definitions=main-1910210128 Content-Transfer-Encoding: quoted-printable X-MIME-Autoconverted: from 8bit to quoted-printable by mx0a-001b2d01.pphosted.com id x9LD99ch104354 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.156.1 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-devel@nongnu.org, Greg Kurz , =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= , qemu-ppc@nongnu.org, Joel Stanley , "Marty E . Plummer" , =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" From: C=C3=A9dric Le Goater On a POWERPC PowerNV system, the host firmware is stored in a PNOR flash chip which contents is mapped on the LPC bus. This model adds a simple dummy device to map the contents of a block device in the host address space. Signed-off-by: C=C3=A9dric Le Goater --- include/hw/ppc/pnv.h | 3 + include/hw/ppc/pnv_pnor.h | 25 +++++++ hw/ppc/pnv.c | 14 ++++ hw/ppc/pnv_pnor.c | 135 ++++++++++++++++++++++++++++++++++++++ hw/ppc/Makefile.objs | 4 +- 5 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 include/hw/ppc/pnv_pnor.h create mode 100644 hw/ppc/pnv_pnor.c diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 5e01a9f3df95..e2f20f2b0bc4 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -24,6 +24,7 @@ #include "hw/sysbus.h" #include "hw/ipmi/ipmi.h" #include "hw/ppc/pnv_lpc.h" +#include "hw/ppc/pnv_pnor.h" #include "hw/ppc/pnv_psi.h" #include "hw/ppc/pnv_occ.h" #include "hw/ppc/pnv_homer.h" @@ -173,6 +174,8 @@ typedef struct PnvMachineState { =20 IPMIBmc *bmc; Notifier powerdown_notifier; + + PnvPnor *pnor; } PnvMachineState; =20 static inline bool pnv_chip_is_power9(const PnvChip *chip) diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h new file mode 100644 index 000000000000..dec811695c8d --- /dev/null +++ b/include/hw/ppc/pnv_pnor.h @@ -0,0 +1,25 @@ +/* + * QEMU PowerNV PNOR simple model + * + * Copyright (c) 2019, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ +#ifndef _PPC_PNV_PNOR_H +#define _PPC_PNV_PNOR_H + +#define TYPE_PNV_PNOR "pnv-pnor" +#define PNV_PNOR(obj) OBJECT_CHECK(PnvPnor, (obj), TYPE_PNV_PNOR) + +typedef struct PnvPnor { + SysBusDevice parent_obj; + + BlockBackend *blk; + + uint8_t *storage; + uint32_t size; + MemoryRegion mmio; +} PnvPnor; + +#endif /* _PPC_PNV_PNOR_H */ diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 1a22dfd46031..b74528eba42a 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -44,6 +44,7 @@ #include "hw/ppc/xics.h" #include "hw/qdev-properties.h" #include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_pnor.h" =20 #include "hw/isa/isa.h" #include "hw/boards.h" @@ -633,6 +634,8 @@ static void pnv_init(MachineState *machine) long fw_size; int i; char *chip_typename; + DriveInfo *pnor =3D drive_get(IF_MTD, 0, 0); + DeviceState *dev; =20 /* allocate RAM */ if (machine->ram_size < (1 * GiB)) { @@ -644,6 +647,17 @@ static void pnv_init(MachineState *machine) machine->ram_size); memory_region_add_subregion(get_system_memory(), 0, ram); =20 + /* + * Create our simple PNOR device + */ + dev =3D qdev_create(NULL, TYPE_PNV_PNOR); + if (pnor) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(pnor), + &error_abort); + } + qdev_init_nofail(dev); + pnv->pnor =3D PNV_PNOR(dev); + /* load skiboot firmware */ if (bios_name =3D=3D NULL) { bios_name =3D FW_FILE_NAME; diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c new file mode 100644 index 000000000000..bfb1e92b0392 --- /dev/null +++ b/hw/ppc/pnv_pnor.c @@ -0,0 +1,135 @@ +/* + * QEMU PowerNV PNOR simple model + * + * Copyright (c) 2015-2019, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" +#include "hw/loader.h" +#include "hw/ppc/pnv_pnor.h" +#include "hw/qdev-properties.h" + +static uint64_t pnv_pnor_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvPnor *s =3D PNV_PNOR(opaque); + uint64_t ret =3D 0; + int i; + + for (i =3D 0; i < size; i++) { + ret |=3D (uint64_t) s->storage[addr + i] << (8 * (size - i - 1)); + } + + return ret; +} + +static void pnv_pnor_update(PnvPnor *s, int offset, int size) +{ + int offset_end; + + if (s->blk) { + return; + } + + offset_end =3D offset + size; + offset =3D QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); + offset_end =3D QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); + + blk_pwrite(s->blk, offset, s->storage + offset, + offset_end - offset, 0); +} + +static void pnv_pnor_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + PnvPnor *s =3D PNV_PNOR(opaque); + int i; + + for (i =3D 0; i < size; i++) { + s->storage[addr + i] =3D (data >> (8 * (size - i - 1))) & 0xFF; + } + pnv_pnor_update(s, addr, size); +} + +/* + * TODO: Check endianness: skiboot is BIG, Aspeed AHB is LITTLE, flash + * is BIG. + */ +static const MemoryRegionOps pnv_pnor_ops =3D { + .read =3D pnv_pnor_read, + .write =3D pnv_pnor_write, + .endianness =3D DEVICE_BIG_ENDIAN, + .valid =3D { + .min_access_size =3D 1, + .max_access_size =3D 4, + }, +}; + +static void pnv_pnor_realize(DeviceState *dev, Error **errp) +{ + PnvPnor *s =3D PNV_PNOR(dev); + int ret; + + if (s->blk) { + uint64_t perm =3D BLK_PERM_CONSISTENT_READ | + (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE); + ret =3D blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp); + if (ret < 0) { + return; + } + + s->size =3D blk_getlength(s->blk); + if (s->size <=3D 0) { + error_setg(errp, "failed to get flash size"); + return; + } + + s->storage =3D blk_blockalign(s->blk, s->size); + + if (blk_pread(s->blk, 0, s->storage, s->size) !=3D s->size) { + error_setg(errp, "failed to read the initial flash content"); + return; + } + } else { + s->storage =3D blk_blockalign(NULL, s->size); + memset(s->storage, 0xFF, s->size); + } + + memory_region_init_io(&s->mmio, OBJECT(s), &pnv_pnor_ops, s, + TYPE_PNV_PNOR, s->size); +} + +static Property pnv_pnor_properties[] =3D { + DEFINE_PROP_UINT32("size", PnvPnor, size, 128 << 20), + DEFINE_PROP_DRIVE("drive", PnvPnor, blk), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_pnor_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->realize =3D pnv_pnor_realize; + dc->props =3D pnv_pnor_properties; +} + +static const TypeInfo pnv_pnor_info =3D { + .name =3D TYPE_PNV_PNOR, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(PnvPnor), + .class_init =3D pnv_pnor_class_init, +}; + +static void pnv_pnor_register_types(void) +{ + type_register_static(&pnv_pnor_info); +} + +type_init(pnv_pnor_register_types) diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 580bb4f0dd4c..101e9fc59185 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -9,7 +9,9 @@ obj-$(CONFIG_PSERIES) +=3D spapr_tpm_proxy.o obj-$(CONFIG_SPAPR_RNG) +=3D spapr_rng.o # IBM PowerNV obj-$(CONFIG_POWERNV) +=3D pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.= o pnv_occ.o pnv_bmc.o -obj-$(CONFIG_POWERNV) +=3D pnv_homer.o +obj-$(CONFIG_POWERNV) +=3D pnv_homer.o pnv_pnor.o + + ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y +=3D spapr_pci_vfio.o spapr_pci_nvlink2.o endif --=20 2.21.0 From nobody Thu May 16 14:52:48 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1571664274; cv=none; d=zoho.com; s=zohoarc; b=Om6EXSbys68n2YFCdSekA4lawuJIbLBEuqHb+CfmsRLLnNewZnMv217m92ayOWe2stx9VhABTunub/AwbDzV4qtQHqfrM/+/riIzhtopN8/DBKVP9u1cmCUCZg3qxd8edy/3Xg9/2M0DsHH2WYuB0xmUqfYr6kLzyhsYwLNBwlg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1571664274; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=QIzIo3r0c5B/2ZV8PPDaDx/ojj/T7dm7Gy3/oA5Cesw=; b=gefeaUvNAMgMzqvTUa56DfbWmIZgOz6ZOWpLK1SCfBAOtGvlASMRmjAxc1DKDs7uXPIEUyND5ycsEMygLHuwMQVE9bDIenq9Cb8i+Hj8qP+5tioaWqZi7chG71OfpmuqaBVK61KTYwdlZkJuFI3iJgHJTkOAy0ERDKPMb73qk2U= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1571664274640482.19445843097947; Mon, 21 Oct 2019 06:24:34 -0700 (PDT) Received: from localhost ([::1]:41178 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMXc0-0004s8-7E for importer@patchew.org; Mon, 21 Oct 2019 09:20:40 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34575) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMXUI-0005TA-Ow for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:48 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iMXUC-0003qC-EM for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:42 -0400 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:5970) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1iMXUA-0003oi-EO for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:36 -0400 Received: from pps.filterd (m0098404.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id x9LD8hTD132975 for ; Mon, 21 Oct 2019 09:12:32 -0400 Received: from e06smtp03.uk.ibm.com (e06smtp03.uk.ibm.com [195.75.94.99]) by mx0a-001b2d01.pphosted.com with ESMTP id 2vsd518q6c-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Mon, 21 Oct 2019 09:12:32 -0400 Received: from localhost by e06smtp03.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 21 Oct 2019 14:12:28 +0100 Received: from b06cxnps4076.portsmouth.uk.ibm.com (9.149.109.198) by e06smtp03.uk.ibm.com (192.168.101.133) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Mon, 21 Oct 2019 14:12:25 +0100 Received: from d06av24.portsmouth.uk.ibm.com (d06av24.portsmouth.uk.ibm.com [9.149.105.60]) by b06cxnps4076.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id x9LDCOPe42139832 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 21 Oct 2019 13:12:24 GMT Received: from d06av24.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 65E234203F; Mon, 21 Oct 2019 13:12:24 +0000 (GMT) Received: from d06av24.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 485EE4204C; Mon, 21 Oct 2019 13:12:24 +0000 (GMT) Received: from smtp.tls.ibm.com (unknown [9.101.4.1]) by d06av24.portsmouth.uk.ibm.com (Postfix) with ESMTP; Mon, 21 Oct 2019 13:12:24 +0000 (GMT) Received: from yukon.kaod.org.ibmuc.com (sig-9-145-86-58.uk.ibm.com [9.145.86.58]) by smtp.tls.ibm.com (Postfix) with ESMTP id 8B53F22016B; Mon, 21 Oct 2019 15:12:23 +0200 (CEST) From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= To: David Gibson Subject: [PATCH 2/5] ipmi: Add support to customize OEM functions Date: Mon, 21 Oct 2019 15:12:12 +0200 X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191021131215.3693-1-clg@kaod.org> References: <20191021131215.3693-1-clg@kaod.org> MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 19102113-0012-0000-0000-0000035AF16C X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 19102113-0013-0000-0000-0000219618A9 Message-Id: <20191021131215.3693-3-clg@kaod.org> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-10-21_04:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1034 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1908290000 definitions=main-1910210128 Content-Transfer-Encoding: quoted-printable X-MIME-Autoconverted: from 8bit to quoted-printable by mx0a-001b2d01.pphosted.com id x9LD8hTD132975 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.156.1 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Corey Minyard , qemu-devel@nongnu.org, Greg Kurz , qemu-ppc@nongnu.org, Joel Stanley , "Marty E . Plummer" , =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" The routine ipmi_register_oem_netfn() lets external modules register command handlers for OEM functions. Required for the PowerNV machine. Cc: Corey Minyard Signed-off-by: C=C3=A9dric Le Goater Acked-by: Corey Minyard Reviewed-by: Corey Minyard --- include/hw/ipmi/ipmi.h | 36 ++++++++++++++++++++++++++++++++++++ hw/ipmi/ipmi_bmc_sim.c | 41 ++++++----------------------------------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 6f2413b39b4a..cb7203b06767 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -265,4 +265,40 @@ int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, const struct ipmi_sdr_compact **sdr, uint16_t *nextr= ec); void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log); =20 +typedef struct IPMIBmcSim IPMIBmcSim; + +typedef struct RspBuffer { + uint8_t buffer[MAX_IPMI_MSG_SIZE]; + unsigned int len; +} RspBuffer; + +static inline void rsp_buffer_set_error(RspBuffer *rsp, uint8_t byte) +{ + rsp->buffer[2] =3D byte; +} + +/* Add a byte to the response. */ +static inline void rsp_buffer_push(RspBuffer *rsp, uint8_t byte) +{ + if (rsp->len >=3D sizeof(rsp->buffer)) { + rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_TRUNCATED); + return; + } + rsp->buffer[rsp->len++] =3D byte; +} + +typedef struct IPMICmdHandler { + void (*cmd_handler)(IPMIBmcSim *s, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp); + unsigned int cmd_len_min; +} IPMICmdHandler; + +typedef struct IPMINetfn { + unsigned int cmd_nums; + const IPMICmdHandler *cmd_handlers; +} IPMINetfn; + +int ipmi_register_oem_netfn(IPMIBmc *b, const IPMINetfn *netfnd); + #endif diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 71e56f3b13d1..770aace55b08 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -98,6 +98,7 @@ #define IPMI_CMD_GET_SEL_TIME 0x48 #define IPMI_CMD_SET_SEL_TIME 0x49 =20 +#define IPMI_NETFN_OEM 0x3a =20 /* Same as a timespec struct. */ struct ipmi_time { @@ -167,23 +168,8 @@ typedef struct IPMISensor { #define MAX_SENSORS 20 #define IPMI_WATCHDOG_SENSOR 0 =20 -typedef struct IPMIBmcSim IPMIBmcSim; -typedef struct RspBuffer RspBuffer; - #define MAX_NETFNS 64 =20 -typedef struct IPMICmdHandler { - void (*cmd_handler)(IPMIBmcSim *s, - uint8_t *cmd, unsigned int cmd_len, - RspBuffer *rsp); - unsigned int cmd_len_min; -} IPMICmdHandler; - -typedef struct IPMINetfn { - unsigned int cmd_nums; - const IPMICmdHandler *cmd_handlers; -} IPMINetfn; - typedef struct IPMIRcvBufEntry { QTAILQ_ENTRY(IPMIRcvBufEntry) entry; uint8_t len; @@ -279,28 +265,8 @@ struct IPMIBmcSim { #define IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN 2 #define IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE 3 =20 -struct RspBuffer { - uint8_t buffer[MAX_IPMI_MSG_SIZE]; - unsigned int len; -}; - #define RSP_BUFFER_INITIALIZER { } =20 -static inline void rsp_buffer_set_error(RspBuffer *rsp, uint8_t byte) -{ - rsp->buffer[2] =3D byte; -} - -/* Add a byte to the response. */ -static inline void rsp_buffer_push(RspBuffer *rsp, uint8_t byte) -{ - if (rsp->len >=3D sizeof(rsp->buffer)) { - rsp_buffer_set_error(rsp, IPMI_CC_REQUEST_DATA_TRUNCATED); - return; - } - rsp->buffer[rsp->len++] =3D byte; -} - static inline void rsp_buffer_pushmore(RspBuffer *rsp, uint8_t *bytes, unsigned int n) { @@ -640,6 +606,11 @@ static int ipmi_register_netfn(IPMIBmcSim *s, unsigned= int netfn, return 0; } =20 +int ipmi_register_oem_netfn(IPMIBmc *b, const IPMINetfn *netfnd) +{ + return ipmi_register_netfn(IPMI_BMC_SIMULATOR(b), IPMI_NETFN_OEM, netf= nd); +} + static const IPMICmdHandler *ipmi_get_handler(IPMIBmcSim *ibs, unsigned int netfn, unsigned int cmd) --=20 2.21.0 From nobody Thu May 16 14:52:48 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1571664651; cv=none; d=zoho.com; s=zohoarc; b=MLw0IPCT5wUgTS8BZCGbLxftY9w90aKexiUTUhxbfCn01jSvOF8KGKHr5oNJ34Gl8MCdUamJVSj1y10OrhtDOXvbeTc0GXBFsmx6tkDixQ319jWdT79xvEXZMgGHDZYkvD1lvzwpO2t8LTe9ekGbgdVfTHWsn/GjTunW35Wa+P8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1571664651; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=HsLen5z42k0dYnuMv3MjiuCsamHw82dzbCdQTinuWg8=; b=WPRFcgHBaCaX939QcYdaZTc/JQGnrI1/RlvPItfh+ttHNLySV4FsRydv6QHww0AwVCFSImb2Foso4ifRU32uDsYxHRFUthV97x1K8IsIJQzMj5sWp1xM+NPFE7j0up+3k7GNtO+8IJ25QQby6Sh8HI1rRp0CS3Nl6UyiM35fcZs= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1571664651505929.1945579969126; Mon, 21 Oct 2019 06:30:51 -0700 (PDT) Received: from localhost ([::1]:40956 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMXa8-0002CO-4a for importer@patchew.org; Mon, 21 Oct 2019 09:18:44 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34533) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMXUE-0005Qb-KL for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:42 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iMXUC-0003rA-Ta for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:38 -0400 Received: from mx0b-001b2d01.pphosted.com ([148.163.158.5]:53480) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1iMXUC-0003qG-Or for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:36 -0400 Received: from pps.filterd (m0127361.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id x9LD8Vl4153784 for ; Mon, 21 Oct 2019 09:12:36 -0400 Received: from e06smtp01.uk.ibm.com (e06smtp01.uk.ibm.com [195.75.94.97]) by mx0a-001b2d01.pphosted.com with ESMTP id 2vs6d16haf-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Mon, 21 Oct 2019 09:12:35 -0400 Received: from localhost by e06smtp01.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 21 Oct 2019 14:12:29 +0100 Received: from b06cxnps3074.portsmouth.uk.ibm.com (9.149.109.194) by e06smtp01.uk.ibm.com (192.168.101.131) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Mon, 21 Oct 2019 14:12:26 +0100 Received: from d06av23.portsmouth.uk.ibm.com (d06av23.portsmouth.uk.ibm.com [9.149.105.59]) by b06cxnps3074.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id x9LDCPx236045020 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 21 Oct 2019 13:12:25 GMT Received: from d06av23.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 087B9A405B; Mon, 21 Oct 2019 13:12:25 +0000 (GMT) Received: from d06av23.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id E0703A4055; Mon, 21 Oct 2019 13:12:24 +0000 (GMT) Received: from smtp.tls.ibm.com (unknown [9.101.4.1]) by d06av23.portsmouth.uk.ibm.com (Postfix) with ESMTP; Mon, 21 Oct 2019 13:12:24 +0000 (GMT) Received: from yukon.kaod.org.ibmuc.com (sig-9-145-86-58.uk.ibm.com [9.145.86.58]) by smtp.tls.ibm.com (Postfix) with ESMTP id 3CFD52201BC; Mon, 21 Oct 2019 15:12:24 +0200 (CEST) From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= To: David Gibson Subject: [PATCH 3/5] ppc/pnv: Add HIOMAP commands Date: Mon, 21 Oct 2019 15:12:13 +0200 X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191021131215.3693-1-clg@kaod.org> References: <20191021131215.3693-1-clg@kaod.org> MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 19102113-4275-0000-0000-00000374F811 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 19102113-4276-0000-0000-0000388818AD Message-Id: <20191021131215.3693-4-clg@kaod.org> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-10-21_04:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1034 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1908290000 definitions=main-1910210128 Content-Transfer-Encoding: quoted-printable X-MIME-Autoconverted: from 8bit to quoted-printable by mx0a-001b2d01.pphosted.com id x9LD8Vl4153784 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.158.5 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-devel@nongnu.org, Greg Kurz , qemu-ppc@nongnu.org, Joel Stanley , "Marty E . Plummer" , =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" This activates HIOMAP support on the QEMU PowerNV machine. The PnvPnor model is used to access the flash contents. The model simply maps the contents at a fix offset and enables or disables the mapping. HIOMAP Protocol description : https://github.com/openbmc/hiomapd/blob/master/Documentation/protocol.md Signed-off-by: C=C3=A9dric Le Goater Reviewed-by: Joel Stanley --- include/hw/ppc/pnv.h | 1 + include/hw/ppc/pnv_pnor.h | 5 ++ hw/ppc/pnv.c | 1 + hw/ppc/pnv_bmc.c | 101 ++++++++++++++++++++++++++++++++++++++ hw/ppc/pnv_lpc.c | 13 +++++ 5 files changed, 121 insertions(+) diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index e2f20f2b0bc4..aa9d5dd16df1 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -209,6 +209,7 @@ static inline PnvChip *pnv_get_chip(PnvMachineState *pn= v, uint32_t chip_id) */ void pnv_dt_bmc_sensors(IPMIBmc *bmc, void *fdt); void pnv_bmc_powerdown(IPMIBmc *bmc); +int pnv_bmc_hiomap(IPMIBmc *bmc); =20 /* * POWER8 MMIO base addresses diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h index dec811695c8d..c3dd28643cae 100644 --- a/include/hw/ppc/pnv_pnor.h +++ b/include/hw/ppc/pnv_pnor.h @@ -9,6 +9,11 @@ #ifndef _PPC_PNV_PNOR_H #define _PPC_PNV_PNOR_H =20 +/* + * PNOR offset on the LPC FW address space + */ +#define PNOR_SPI_OFFSET 0x0c000000UL + #define TYPE_PNV_PNOR "pnv-pnor" #define PNV_PNOR(obj) OBJECT_CHECK(PnvPnor, (obj), TYPE_PNV_PNOR) =20 diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index b74528eba42a..7019273f1cdd 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -566,6 +566,7 @@ static void pnv_reset(MachineState *machine) obj =3D object_resolve_path_type("", "ipmi-bmc-sim", NULL); if (obj) { pnv->bmc =3D IPMI_BMC(obj); + pnv_bmc_hiomap(pnv->bmc); } =20 fdt =3D pnv_dt_create(machine); diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index dc5e918cb79e..1bcf22a9aad8 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -114,3 +114,104 @@ void pnv_dt_bmc_sensors(IPMIBmc *bmc, void *fdt) sdr->sensor_type))); } } + +/* + * HIOMAP protocol handler + */ +#define HIOMAP_C_RESET 1 +#define HIOMAP_C_GET_INFO 2 +#define HIOMAP_C_GET_FLASH_INFO 3 +#define HIOMAP_C_CREATE_READ_WINDOW 4 +#define HIOMAP_C_CLOSE_WINDOW 5 +#define HIOMAP_C_CREATE_WRITE_WINDOW 6 +#define HIOMAP_C_MARK_DIRTY 7 +#define HIOMAP_C_FLUSH 8 +#define HIOMAP_C_ACK 9 +#define HIOMAP_C_ERASE 10 +#define HIOMAP_C_DEVICE_NAME 11 +#define HIOMAP_C_LOCK 12 + +#define BLOCK_SHIFT 12 /* 4K */ + +static uint16_t bytes_to_blocks(uint32_t bytes) +{ + return bytes >> BLOCK_SHIFT; +} + +static void hiomap_cmd(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + PnvMachineState *pnv =3D PNV_MACHINE(qdev_get_machine()); + PnvPnor *pnor =3D pnv->pnor; + uint32_t pnor_size =3D pnor->size; + uint32_t pnor_addr =3D PNOR_SPI_OFFSET; + bool readonly =3D false; + + rsp_buffer_push(rsp, cmd[2]); + rsp_buffer_push(rsp, cmd[3]); + + switch (cmd[2]) { + case HIOMAP_C_MARK_DIRTY: + case HIOMAP_C_FLUSH: + case HIOMAP_C_ERASE: + case HIOMAP_C_ACK: + break; + + case HIOMAP_C_GET_INFO: + rsp_buffer_push(rsp, 2); /* Version 2 */ + rsp_buffer_push(rsp, BLOCK_SHIFT); /* block size */ + rsp_buffer_push(rsp, 0); /* Timeout */ + rsp_buffer_push(rsp, 0); /* Timeout */ + break; + + case HIOMAP_C_GET_FLASH_INFO: + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8); + rsp_buffer_push(rsp, 0x01); /* erase size */ + rsp_buffer_push(rsp, 0x00); /* erase size */ + break; + + case HIOMAP_C_CREATE_READ_WINDOW: + readonly =3D true; + /* Fall through */ + + case HIOMAP_C_CREATE_WRITE_WINDOW: + memory_region_set_readonly(&pnor->mmio, readonly); + memory_region_set_enabled(&pnor->mmio, true); + + rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) >> 8); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8); + rsp_buffer_push(rsp, 0x00); /* offset */ + rsp_buffer_push(rsp, 0x00); /* offset */ + break; + + case HIOMAP_C_CLOSE_WINDOW: + memory_region_set_enabled(&pnor->mmio, false); + break; + + case HIOMAP_C_DEVICE_NAME: + case HIOMAP_C_RESET: + case HIOMAP_C_LOCK: + default: + qemu_log_mask(LOG_GUEST_ERROR, "HIOMAP: unknow command %02X\n", cm= d[2]); + break; + } +} + +#define HIOMAP 0x5a + +static const IPMICmdHandler hiomap_cmds[] =3D { + [HIOMAP] =3D { hiomap_cmd, 3 }, +}; + +static const IPMINetfn hiomap_netfn =3D { + .cmd_nums =3D ARRAY_SIZE(hiomap_cmds), + .cmd_handlers =3D hiomap_cmds +}; + +int pnv_bmc_hiomap(IPMIBmc *bmc) +{ + return ipmi_register_oem_netfn(bmc, &hiomap_netfn); +} diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 9466d4a1be3b..287fa8ab091b 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -801,6 +801,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool = use_cpld, Error **errp) ISABus *isa_bus; qemu_irq *irqs; qemu_irq_handler handler; + PnvMachineState *pnv =3D PNV_MACHINE(qdev_get_machine()); =20 /* let isa_bus_new() create its own bridge on SysBus otherwise * devices speficied on the command line won't find the bus and @@ -825,5 +826,17 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool= use_cpld, Error **errp) irqs =3D qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS); =20 isa_bus_irqs(isa_bus, irqs); + + /* + * TODO: Map PNOR on the LPC FW address space on demand ? + */ + memory_region_add_subregion(&lpc->isa_fw, PNOR_SPI_OFFSET, + &pnv->pnor->mmio); + /* + * Start disabled. The HIOMAP protocol will activate the mapping + * with HIOMAP_C_CREATE_WRITE_WINDOW + */ + memory_region_set_enabled(&pnv->pnor->mmio, false); + return isa_bus; } --=20 2.21.0 From nobody Thu May 16 14:52:48 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1571664280; cv=none; d=zoho.com; s=zohoarc; b=RRFyPHX04ZgtbS/dMoKCh8Mrc8cNYsaEc7BvKU2AP0fDKN5LDq2LwIo9enCTKJwTT3nkN/WdtZ3n6UVm71N27czUK4rcfMiW2B+zqkarwhZXPHD91yba5/eNP6KAnR5HQvYtRhXEKvqDjtt/wMdjX3oj83W8sRm8TPRerGYd4UY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1571664280; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=KZY2D3K4xdM5ctIAMU93PIZqj0wu0j4qPt1RNgg4rtc=; b=U3itMoQTneSRCU2DdYA1G/Er3YBTfp80xeoOHez30eKdvqwxyHzaaRgbhmiIlUd0NQDNic9B0Px0KKutuOYjG+/sbPAJ9JLoGihreSS2micXKGRI73Fi6YeDA367l5NEsscJ7ZzbpbdELAcEbBiH+GKhJucPIuWu4MgaL96XKsg= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1571664280570505.0780694015767; Mon, 21 Oct 2019 06:24:40 -0700 (PDT) Received: from localhost ([::1]:40974 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMXam-00034y-AH for importer@patchew.org; Mon, 21 Oct 2019 09:19:24 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34592) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMXUM-0005UC-IL for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:55 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iMXUC-0003qQ-HC for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:46 -0400 Received: from mx0b-001b2d01.pphosted.com ([148.163.158.5]:42014 helo=mx0a-001b2d01.pphosted.com) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1iMXUA-0003oz-El for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:36 -0400 Received: from pps.filterd (m0098416.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id x9LD8WFH033379 for ; Mon, 21 Oct 2019 09:12:33 -0400 Received: from e06smtp03.uk.ibm.com (e06smtp03.uk.ibm.com [195.75.94.99]) by mx0b-001b2d01.pphosted.com with ESMTP id 2vscek2jpn-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Mon, 21 Oct 2019 09:12:32 -0400 Received: from localhost by e06smtp03.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 21 Oct 2019 14:12:30 +0100 Received: from b06cxnps3075.portsmouth.uk.ibm.com (9.149.109.195) by e06smtp03.uk.ibm.com (192.168.101.133) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Mon, 21 Oct 2019 14:12:26 +0100 Received: from b06wcsmtp001.portsmouth.uk.ibm.com (b06wcsmtp001.portsmouth.uk.ibm.com [9.149.105.160]) by b06cxnps3075.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id x9LDCQ8b48496788 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 21 Oct 2019 13:12:26 GMT Received: from b06wcsmtp001.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id E65E0A4062; Mon, 21 Oct 2019 13:12:25 +0000 (GMT) Received: from b06wcsmtp001.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id AE8BCA4060; Mon, 21 Oct 2019 13:12:25 +0000 (GMT) Received: from smtp.tls.ibm.com (unknown [9.101.4.1]) by b06wcsmtp001.portsmouth.uk.ibm.com (Postfix) with ESMTP; Mon, 21 Oct 2019 13:12:25 +0000 (GMT) Received: from yukon.kaod.org.ibmuc.com (sig-9-145-86-58.uk.ibm.com [9.145.86.58]) by smtp.tls.ibm.com (Postfix) with ESMTP id D76DD22016B; Mon, 21 Oct 2019 15:12:24 +0200 (CEST) From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= To: David Gibson Subject: [PATCH 4/5] libxz: Add XZ Embedded library for PPC Date: Mon, 21 Oct 2019 15:12:14 +0200 X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191021131215.3693-1-clg@kaod.org> References: <20191021131215.3693-1-clg@kaod.org> MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 19102113-0012-0000-0000-0000035AF16E X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 19102113-0013-0000-0000-0000219618AB Message-Id: <20191021131215.3693-5-clg@kaod.org> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-10-21_04:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=9 phishscore=0 bulkscore=0 spamscore=0 clxscore=1034 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1908290000 definitions=main-1910210128 Content-Transfer-Encoding: quoted-printable X-MIME-Autoconverted: from 8bit to quoted-printable by mx0b-001b2d01.pphosted.com id x9LD8WFH033379 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.158.5 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-devel@nongnu.org, Greg Kurz , qemu-ppc@nongnu.org, Joel Stanley , "Marty E . Plummer" , =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" version 20130513 from https://tukaani.org/xz/embedded.html Signed-off-by: C=C3=A9dric Le Goater --- libxz/xz.h | 304 ++++++++++ libxz/xz_config.h | 124 ++++ libxz/xz_lzma2.h | 204 +++++++ libxz/xz_private.h | 156 +++++ libxz/xz_stream.h | 62 ++ libxz/xz_crc32.c | 59 ++ libxz/xz_dec_lzma2.c | 1171 ++++++++++++++++++++++++++++++++++++++ libxz/xz_dec_stream.c | 847 +++++++++++++++++++++++++++ libxz/Makefile.objs | 1 + target/ppc/Makefile.objs | 1 + 10 files changed, 2929 insertions(+) create mode 100644 libxz/xz.h create mode 100644 libxz/xz_config.h create mode 100644 libxz/xz_lzma2.h create mode 100644 libxz/xz_private.h create mode 100644 libxz/xz_stream.h create mode 100644 libxz/xz_crc32.c create mode 100644 libxz/xz_dec_lzma2.c create mode 100644 libxz/xz_dec_stream.c create mode 100644 libxz/Makefile.objs diff --git a/libxz/xz.h b/libxz/xz.h new file mode 100644 index 000000000000..75df73e39e53 --- /dev/null +++ b/libxz/xz.h @@ -0,0 +1,304 @@ +/* + * XZ decompressor + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_H +#define XZ_H + +#ifdef __KERNEL__ + #include + #include +#else + #include + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* In Linux, this is used to make extern functions static when needed. */ +#ifndef XZ_EXTERN + #define XZ_EXTERN extern +#endif + +/** + * enum xz_mode - Operation mode + * + * @XZ_SINGLE: Single-call mode. This uses less RAM than + * than multi-call modes, because the LZMA2 + * dictionary doesn't need to be allocated as + * part of the decoder state. All required data + * structures are allocated at initialization, + * so xz_dec_run() cannot return XZ_MEM_ERROR. + * @XZ_PREALLOC: Multi-call mode with preallocated LZMA2 + * dictionary buffer. All data structures are + * allocated at initialization, so xz_dec_run() + * cannot return XZ_MEM_ERROR. + * @XZ_DYNALLOC: Multi-call mode. The LZMA2 dictionary is + * allocated once the required size has been + * parsed from the stream headers. If the + * allocation fails, xz_dec_run() will return + * XZ_MEM_ERROR. + * + * It is possible to enable support only for a subset of the above + * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC, + * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled + * with support for all operation modes, but the preboot code may + * be built with fewer features to minimize code size. + */ +enum xz_mode { + XZ_SINGLE, + XZ_PREALLOC, + XZ_DYNALLOC +}; + +/** + * enum xz_ret - Return codes + * @XZ_OK: Everything is OK so far. More input or more + * output space is required to continue. This + * return code is possible only in multi-call mode + * (XZ_PREALLOC or XZ_DYNALLOC). + * @XZ_STREAM_END: Operation finished successfully. + * @XZ_UNSUPPORTED_CHECK: Integrity check type is not supported. Decoding + * is still possible in multi-call mode by simply + * calling xz_dec_run() again. + * Note that this return value is used only if + * XZ_DEC_ANY_CHECK was defined at build time, + * which is not used in the kernel. Unsupported + * check types return XZ_OPTIONS_ERROR if + * XZ_DEC_ANY_CHECK was not defined at build time. + * @XZ_MEM_ERROR: Allocating memory failed. This return code is + * possible only if the decoder was initialized + * with XZ_DYNALLOC. The amount of memory that was + * tried to be allocated was no more than the + * dict_max argument given to xz_dec_init(). + * @XZ_MEMLIMIT_ERROR: A bigger LZMA2 dictionary would be needed than + * allowed by the dict_max argument given to + * xz_dec_init(). This return value is possible + * only in multi-call mode (XZ_PREALLOC or + * XZ_DYNALLOC); the single-call mode (XZ_SINGLE) + * ignores the dict_max argument. + * @XZ_FORMAT_ERROR: File format was not recognized (wrong magic + * bytes). + * @XZ_OPTIONS_ERROR: This implementation doesn't support the reques= ted + * compression options. In the decoder this means + * that the header CRC32 matches, but the header + * itself specifies something that we don't suppo= rt. + * @XZ_DATA_ERROR: Compressed data is corrupt. + * @XZ_BUF_ERROR: Cannot make any progress. Details are slightly + * different between multi-call and single-call + * mode; more information below. + * + * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls + * to XZ code cannot consume any input and cannot produce any new output. + * This happens when there is no new input available, or the output buffer + * is full while at least one output byte is still pending. Assuming your + * code is not buggy, you can get this error only when decoding a compress= ed + * stream that is truncated or otherwise corrupt. + * + * In single-call mode, XZ_BUF_ERROR is returned only when the output buff= er + * is too small or the compressed input is corrupt in a way that makes the + * decoder produce more output than the caller expected. When it is + * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR + * is used instead of XZ_BUF_ERROR. + */ +enum xz_ret { + XZ_OK, + XZ_STREAM_END, + XZ_UNSUPPORTED_CHECK, + XZ_MEM_ERROR, + XZ_MEMLIMIT_ERROR, + XZ_FORMAT_ERROR, + XZ_OPTIONS_ERROR, + XZ_DATA_ERROR, + XZ_BUF_ERROR +}; + +/** + * struct xz_buf - Passing input and output buffers to XZ code + * @in: Beginning of the input buffer. This may be NULL if and only + * if in_pos is equal to in_size. + * @in_pos: Current position in the input buffer. This must not exceed + * in_size. + * @in_size: Size of the input buffer + * @out: Beginning of the output buffer. This may be NULL if and on= ly + * if out_pos is equal to out_size. + * @out_pos: Current position in the output buffer. This must not exceed + * out_size. + * @out_size: Size of the output buffer + * + * Only the contents of the output buffer from out[out_pos] onward, and + * the variables in_pos and out_pos are modified by the XZ code. + */ +struct xz_buf { + const uint8_t *in; + size_t in_pos; + size_t in_size; + + uint8_t *out; + size_t out_pos; + size_t out_size; +}; + +/** + * struct xz_dec - Opaque type to hold the XZ decoder state + */ +struct xz_dec; + +/** + * xz_dec_init() - Allocate and initialize a XZ decoder state + * @mode: Operation mode + * @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for + * multi-call decoding. This is ignored in single-call mode + * (mode =3D=3D XZ_SINGLE). LZMA2 dictionary is always 2^n by= tes + * or 2^n + 2^(n-1) bytes (the latter sizes are less common + * in practice), so other values for dict_max don't make sens= e. + * In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 Ki= B, + * 512 KiB, and 1 MiB are probably the only reasonable values, + * except for kernel and initramfs images where a bigger + * dictionary can be fine and useful. + * + * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at + * once. The caller must provide enough output space or the decoding will + * fail. The output space is used as the dictionary buffer, which is why + * there is no need to allocate the dictionary as part of the decoder's + * internal state. + * + * Because the output buffer is used as the workspace, streams encoded usi= ng + * a big dictionary are not a problem in single-call mode. It is enough th= at + * the output buffer is big enough to hold the actual uncompressed data; it + * can be smaller than the dictionary size stored in the stream headers. + * + * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max by= tes + * of memory is preallocated for the LZMA2 dictionary. This way there is no + * risk that xz_dec_run() could run out of memory, since xz_dec_run() will + * never allocate any memory. Instead, if the preallocated dictionary is t= oo + * small for decoding the given input stream, xz_dec_run() will return + * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will= be + * decoded to avoid allocating excessive amount of memory for the dictiona= ry. + * + * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC): + * dict_max specifies the maximum allowed dictionary size that xz_dec_run() + * may allocate once it has parsed the dictionary size from the stream + * headers. This way excessive allocations can be avoided while still + * limiting the maximum memory usage to a sane value to prevent running the + * system out of memory when decompressing streams from untrusted sources. + * + * On success, xz_dec_init() returns a pointer to struct xz_dec, which is + * ready to be used with xz_dec_run(). If memory allocation fails, + * xz_dec_init() returns NULL. + */ +XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max); + +/** + * xz_dec_run() - Run the XZ decoder + * @s: Decoder state allocated using xz_dec_init() + * @b: Input and output buffers + * + * The possible return values depend on build options and operation mode. + * See enum xz_ret for details. + * + * Note that if an error occurs in single-call mode (return value is not + * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the + * contents of the output buffer from b->out[b->out_pos] onward are + * undefined. This is true even after XZ_BUF_ERROR, because with some filt= er + * chains, there may be a second pass over the output buffer, and this pass + * cannot be properly done if the output buffer is truncated. Thus, you + * cannot give the single-call decoder a too small buffer and then expect = to + * get that amount valid data from the beginning of the stream. You must u= se + * the multi-call decoder if you don't want to uncompress the whole stream. + */ +XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b); + +/** + * xz_dec_reset() - Reset an already allocated decoder state + * @s: Decoder state allocated using xz_dec_init() + * + * This function can be used to reset the multi-call decoder state without + * freeing and reallocating memory with xz_dec_end() and xz_dec_init(). + * + * In single-call mode, xz_dec_reset() is always called in the beginning of + * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in + * multi-call mode. + */ +XZ_EXTERN void xz_dec_reset(struct xz_dec *s); + +/** + * xz_dec_end() - Free the memory allocated for the decoder state + * @s: Decoder state allocated using xz_dec_init(). If s is NULL, + * this function does nothing. + */ +XZ_EXTERN void xz_dec_end(struct xz_dec *s); + +/* + * Standalone build (userspace build or in-kernel build for boot time use) + * needs a CRC32 implementation. For normal in-kernel use, kernel's own + * CRC32 module is used instead, and users of this module don't need to + * care about the functions below. + */ +#ifndef XZ_INTERNAL_CRC32 + #ifdef __KERNEL__ + #define XZ_INTERNAL_CRC32 0 + #else + #define XZ_INTERNAL_CRC32 1 + #endif +#endif + +/* + * If CRC64 support has been enabled with XZ_USE_CRC64, a CRC64 + * implementation is needed too. + */ +#ifndef XZ_USE_CRC64 + #undef XZ_INTERNAL_CRC64 + #define XZ_INTERNAL_CRC64 0 +#endif +#ifndef XZ_INTERNAL_CRC64 + #ifdef __KERNEL__ + #error Using CRC64 in the kernel has not been implemented. + #else + #define XZ_INTERNAL_CRC64 1 + #endif +#endif + +#if XZ_INTERNAL_CRC32 +/* + * This must be called before any other xz_* function to initialize + * the CRC32 lookup table. + */ +XZ_EXTERN void xz_crc32_init(void); + +/* + * Update CRC32 value using the polynomial from IEEE-802.3. To start a new + * calculation, the third argument must be zero. To continue the calculati= on, + * the previously returned value is passed as the third argument. + */ +XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc); +#endif + +#if XZ_INTERNAL_CRC64 +/* + * This must be called before any other xz_* function (except xz_crc32_ini= t()) + * to initialize the CRC64 lookup table. + */ +XZ_EXTERN void xz_crc64_init(void); + +/* + * Update CRC64 value using the polynomial from ECMA-182. To start a new + * calculation, the third argument must be zero. To continue the calculati= on, + * the previously returned value is passed as the third argument. + */ +XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libxz/xz_config.h b/libxz/xz_config.h new file mode 100644 index 000000000000..a380be02c143 --- /dev/null +++ b/libxz/xz_config.h @@ -0,0 +1,124 @@ +/* + * Private includes and definitions for userspace use of XZ Embedded + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_CONFIG_H +#define XZ_CONFIG_H + +/* Uncomment to enable CRC64 support. */ +/* #define XZ_USE_CRC64 */ + +/* Uncomment as needed to enable BCJ filter decoders. */ +/* #define XZ_DEC_X86 */ +/* #define XZ_DEC_POWERPC */ +/* #define XZ_DEC_IA64 */ +/* #define XZ_DEC_ARM */ +/* #define XZ_DEC_ARMTHUMB */ +/* #define XZ_DEC_SPARC */ + +/* + * MSVC doesn't support modern C but XZ Embedded is mostly C89 + * so these are enough. + */ +#ifdef _MSC_VER +typedef unsigned char bool; + #define true 1 + #define false 0 + #define inline __inline +#else + #include +#endif + +#include +#include + +#include "xz.h" + +#define kmalloc(size, flags) malloc(size) +#define kfree(ptr) free(ptr) +#define vmalloc(size) malloc(size) +#define vfree(ptr) free(ptr) + +#define memeq(a, b, size) (memcmp(a, b, size) =3D=3D 0) +#define memzero(buf, size) memset(buf, 0, size) + +#ifndef min + #define min(x, y) ((x) < (y) ? (x) : (y)) +#endif +#define min_t(type, x, y) min(x, y) + +/* + * Some functions have been marked with __always_inline to keep the + * performance reasonable even when the compiler is optimizing for + * small code size. You may be able to save a few bytes by #defining + * __always_inline to plain inline, but don't complain if the code + * becomes slow. + * + * NOTE: System headers on GNU/Linux may #define this macro already, + * so if you want to change it, you need to #undef it first. + */ +#ifndef __always_inline + #ifdef __GNUC__ + #define __always_inline \ + inline __attribute__((__always_inline__)) + #else + #define __always_inline inline + #endif +#endif + +/* Inline functions to access unaligned unsigned 32-bit integers */ +#ifndef get_unaligned_le32 +static inline uint32_t get_unaligned_le32(const uint8_t *buf) +{ + return (uint32_t)buf[0] + | ((uint32_t)buf[1] << 8) + | ((uint32_t)buf[2] << 16) + | ((uint32_t)buf[3] << 24); +} +#endif + +#ifndef get_unaligned_be32 +static inline uint32_t get_unaligned_be32(const uint8_t *buf) +{ + return (uint32_t)(buf[0] << 24) + | ((uint32_t)buf[1] << 16) + | ((uint32_t)buf[2] << 8) + | (uint32_t)buf[3]; +} +#endif + +#ifndef put_unaligned_le32 +static inline void put_unaligned_le32(uint32_t val, uint8_t *buf) +{ + buf[0] =3D (uint8_t)val; + buf[1] =3D (uint8_t)(val >> 8); + buf[2] =3D (uint8_t)(val >> 16); + buf[3] =3D (uint8_t)(val >> 24); +} +#endif + +#ifndef put_unaligned_be32 +static inline void put_unaligned_be32(uint32_t val, uint8_t *buf) +{ + buf[0] =3D (uint8_t)(val >> 24); + buf[1] =3D (uint8_t)(val >> 16); + buf[2] =3D (uint8_t)(val >> 8); + buf[3] =3D (uint8_t)val; +} +#endif + +/* + * Use get_unaligned_le32() also for aligned access for simplicity. On + * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr)) + * could save a few bytes in code size. + */ +#ifndef get_le32 + #define get_le32 get_unaligned_le32 +#endif + +#endif diff --git a/libxz/xz_lzma2.h b/libxz/xz_lzma2.h new file mode 100644 index 000000000000..aa05d956319e --- /dev/null +++ b/libxz/xz_lzma2.h @@ -0,0 +1,204 @@ +/* + * LZMA2 definitions + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_LZMA2_H +#define XZ_LZMA2_H + +/* Range coder constants */ +#define RC_SHIFT_BITS 8 +#define RC_TOP_BITS 24 +#define RC_TOP_VALUE (1 << RC_TOP_BITS) +#define RC_BIT_MODEL_TOTAL_BITS 11 +#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS) +#define RC_MOVE_BITS 5 + +/* + * Maximum number of position states. A position state is the lowest pb + * number of bits of the current uncompressed offset. In some places there + * are different sets of probabilities for different position states. + */ +#define POS_STATES_MAX (1 << 4) + +/* + * This enum is used to track which LZMA symbols have occurred most recent= ly + * and in which order. This information is used to predict the next symbol. + * + * Symbols: + * - Literal: One 8-bit byte + * - Match: Repeat a chunk of data at some distance + * - Long repeat: Multi-byte match at a recently seen distance + * - Short repeat: One-byte repeat at a recently seen distance + * + * The symbol names are in from STATE_oldest_older_previous. REP means + * either short or long repeated match, and NONLIT means any non-literal. + */ +enum lzma_state { + STATE_LIT_LIT, + STATE_MATCH_LIT_LIT, + STATE_REP_LIT_LIT, + STATE_SHORTREP_LIT_LIT, + STATE_MATCH_LIT, + STATE_REP_LIT, + STATE_SHORTREP_LIT, + STATE_LIT_MATCH, + STATE_LIT_LONGREP, + STATE_LIT_SHORTREP, + STATE_NONLIT_MATCH, + STATE_NONLIT_REP +}; + +/* Total number of states */ +#define STATES 12 + +/* The lowest 7 states indicate that the previous state was a literal. */ +#define LIT_STATES 7 + +/* Indicate that the latest symbol was a literal. */ +static inline void lzma_state_literal(enum lzma_state *state) +{ + if (*state <=3D STATE_SHORTREP_LIT_LIT) + *state =3D STATE_LIT_LIT; + else if (*state <=3D STATE_LIT_SHORTREP) + *state -=3D 3; + else + *state -=3D 6; +} + +/* Indicate that the latest symbol was a match. */ +static inline void lzma_state_match(enum lzma_state *state) +{ + *state =3D *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH; +} + +/* Indicate that the latest state was a long repeated match. */ +static inline void lzma_state_long_rep(enum lzma_state *state) +{ + *state =3D *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP; +} + +/* Indicate that the latest symbol was a short match. */ +static inline void lzma_state_short_rep(enum lzma_state *state) +{ + *state =3D *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP; +} + +/* Test if the previous symbol was a literal. */ +static inline bool lzma_state_is_literal(enum lzma_state state) +{ + return state < LIT_STATES; +} + +/* Each literal coder is divided in three sections: + * - 0x001-0x0FF: Without match byte + * - 0x101-0x1FF: With match byte; match bit is 0 + * - 0x201-0x2FF: With match byte; match bit is 1 + * + * Match byte is used when the previous LZMA symbol was something else than + * a literal (that is, it was some kind of match). + */ +#define LITERAL_CODER_SIZE 0x300 + +/* Maximum number of literal coders */ +#define LITERAL_CODERS_MAX (1 << 4) + +/* Minimum length of a match is two bytes. */ +#define MATCH_LEN_MIN 2 + +/* Match length is encoded with 4, 5, or 10 bits. + * + * Length Bits + * 2-9 4 =3D Choice=3D0 + 3 bits + * 10-17 5 =3D Choice=3D1 + Choice2=3D0 + 3 bits + * 18-273 10 =3D Choice=3D1 + Choice2=3D1 + 8 bits + */ +#define LEN_LOW_BITS 3 +#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS) +#define LEN_MID_BITS 3 +#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS) +#define LEN_HIGH_BITS 8 +#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS) +#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS) + +/* + * Maximum length of a match is 273 which is a result of the encoding + * described above. + */ +#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1) + +/* + * Different sets of probabilities are used for match distances that have + * very short match length: Lengths of 2, 3, and 4 bytes have a separate + * set of probabilities for each length. The matches with longer length + * use a shared set of probabilities. + */ +#define DIST_STATES 4 + +/* + * Get the index of the appropriate probability array for decoding + * the distance slot. + */ +static inline uint32_t lzma_get_dist_state(uint32_t len) +{ + return len < DIST_STATES + MATCH_LEN_MIN + ? len - MATCH_LEN_MIN : DIST_STATES - 1; +} + +/* + * The highest two bits of a 32-bit match distance are encoded using six b= its. + * This six-bit value is called a distance slot. This way encoding a 32-bit + * value takes 6-36 bits, larger values taking more bits. + */ +#define DIST_SLOT_BITS 6 +#define DIST_SLOTS (1 << DIST_SLOT_BITS) + +/* Match distances up to 127 are fully encoded using probabilities. Since + * the highest two bits (distance slot) are always encoded using six bits, + * the distances 0-3 don't need any additional bits to encode, since the + * distance slot itself is the same as the actual distance. DIST_MODEL_STA= RT + * indicates the first distance slot where at least one additional bit is + * needed. + */ +#define DIST_MODEL_START 4 + +/* + * Match distances greater than 127 are encoded in three pieces: + * - distance slot: the highest two bits + * - direct bits: 2-26 bits below the highest two bits + * - alignment bits: four lowest bits + * + * Direct bits don't use any probabilities. + * + * The distance slot value of 14 is for distances 128-191. + */ +#define DIST_MODEL_END 14 + +/* Distance slots that indicate a distance <=3D 127. */ +#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2) +#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS) + +/* + * For match distances greater than 127, only the highest two bits and the + * lowest four bits (alignment) is encoded using probabilities. + */ +#define ALIGN_BITS 4 +#define ALIGN_SIZE (1 << ALIGN_BITS) +#define ALIGN_MASK (ALIGN_SIZE - 1) + +/* Total number of all probability variables */ +#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE) + +/* + * LZMA remembers the four most recent match distances. Reusing these + * distances tends to take less space than re-encoding the actual + * distance value. + */ +#define REPS 4 + +#endif diff --git a/libxz/xz_private.h b/libxz/xz_private.h new file mode 100644 index 000000000000..49c366554086 --- /dev/null +++ b/libxz/xz_private.h @@ -0,0 +1,156 @@ +/* + * Private includes and definitions + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_PRIVATE_H +#define XZ_PRIVATE_H + +#ifdef __KERNEL__ + #include + #include + #include + /* XZ_PREBOOT may be defined only via decompress_unxz.c. */ + #ifndef XZ_PREBOOT + #include + #include + #include + #ifdef CONFIG_XZ_DEC_X86 + #define XZ_DEC_X86 + #endif + #ifdef CONFIG_XZ_DEC_POWERPC + #define XZ_DEC_POWERPC + #endif + #ifdef CONFIG_XZ_DEC_IA64 + #define XZ_DEC_IA64 + #endif + #ifdef CONFIG_XZ_DEC_ARM + #define XZ_DEC_ARM + #endif + #ifdef CONFIG_XZ_DEC_ARMTHUMB + #define XZ_DEC_ARMTHUMB + #endif + #ifdef CONFIG_XZ_DEC_SPARC + #define XZ_DEC_SPARC + #endif + #define memeq(a, b, size) (memcmp(a, b, size) =3D=3D 0) + #define memzero(buf, size) memset(buf, 0, size) + #endif + #define get_le32(p) le32_to_cpup((const uint32_t *)(p)) +#else + /* + * For userspace builds, use a separate header to define the required + * macros and functions. This makes it easier to adapt the code into + * different environments and avoids clutter in the Linux kernel tree. + */ + #include "xz_config.h" +#endif + +/* If no specific decoding mode is requested, enable support for all modes= . */ +#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \ + && !defined(XZ_DEC_DYNALLOC) + #define XZ_DEC_SINGLE + #define XZ_DEC_PREALLOC + #define XZ_DEC_DYNALLOC +#endif + +/* + * The DEC_IS_foo(mode) macros are used in "if" statements. If only some + * of the supported modes are enabled, these macros will evaluate to true = or + * false at compile time and thus allow the compiler to omit unneeded code. + */ +#ifdef XZ_DEC_SINGLE + #define DEC_IS_SINGLE(mode) ((mode) =3D=3D XZ_SINGLE) +#else + #define DEC_IS_SINGLE(mode) (false) +#endif + +#ifdef XZ_DEC_PREALLOC + #define DEC_IS_PREALLOC(mode) ((mode) =3D=3D XZ_PREALLOC) +#else + #define DEC_IS_PREALLOC(mode) (false) +#endif + +#ifdef XZ_DEC_DYNALLOC + #define DEC_IS_DYNALLOC(mode) ((mode) =3D=3D XZ_DYNALLOC) +#else + #define DEC_IS_DYNALLOC(mode) (false) +#endif + +#if !defined(XZ_DEC_SINGLE) + #define DEC_IS_MULTI(mode) (true) +#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC) + #define DEC_IS_MULTI(mode) ((mode) !=3D XZ_SINGLE) +#else + #define DEC_IS_MULTI(mode) (false) +#endif + +/* + * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ. + * XZ_DEC_BCJ is used to enable generic support for BCJ decoders. + */ +#ifndef XZ_DEC_BCJ + #if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \ + || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \ + || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \ + || defined(XZ_DEC_SPARC) + #define XZ_DEC_BCJ + #endif +#endif + +/* + * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used + * before calling xz_dec_lzma2_run(). + */ +XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, + uint32_t dict_max); + +/* + * Decode the LZMA2 properties (one byte) and reset the decoder. Return + * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is n= ot + * big enough, and XZ_OPTIONS_ERROR if props indicates something that this + * decoder doesn't support. + */ +XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, + uint8_t props); + +/* Decode raw LZMA2 stream from b->in to b->out. */ +XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, + struct xz_buf *b); + +/* Free the memory allocated for the LZMA2 decoder. */ +XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s); + +#ifdef XZ_DEC_BCJ +/* + * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before + * calling xz_dec_bcj_run(). + */ +XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call); + +/* + * Decode the Filter ID of a BCJ filter. This implementation doesn't + * support custom start offsets, so no decoding of Filter Properties + * is needed. Returns XZ_OK if the given Filter ID is supported. + * Otherwise XZ_OPTIONS_ERROR is returned. + */ +XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id); + +/* + * Decode raw BCJ + LZMA2 stream. This must be used only if there actually= is + * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_ru= n() + * must be called directly. + */ +XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s, + struct xz_dec_lzma2 *lzma2, + struct xz_buf *b); + +/* Free the memory allocated for the BCJ filters. */ +#define xz_dec_bcj_end(s) kfree(s) +#endif + +#endif diff --git a/libxz/xz_stream.h b/libxz/xz_stream.h new file mode 100644 index 000000000000..54fda5389497 --- /dev/null +++ b/libxz/xz_stream.h @@ -0,0 +1,62 @@ +/* + * Definitions for handling the .xz file format + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#ifndef XZ_STREAM_H +#define XZ_STREAM_H + +#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32 + #include + #undef crc32 + #define xz_crc32(buf, size, crc) \ + (~crc32_le(~(uint32_t)(crc), buf, size)) +#endif + +/* + * See the .xz file format specification at + * http://tukaani.org/xz/xz-file-format.txt + * to understand the container format. + */ + +#define STREAM_HEADER_SIZE 12 + +#define HEADER_MAGIC "\3757zXZ" +#define HEADER_MAGIC_SIZE 6 + +#define FOOTER_MAGIC "YZ" +#define FOOTER_MAGIC_SIZE 2 + +/* + * Variable-length integer can hold a 63-bit unsigned integer or a special + * value indicating that the value is unknown. + * + * Experimental: vli_type can be defined to uint32_t to save a few bytes + * in code size (no effect on speed). Doing so limits the uncompressed and + * compressed size of the file to less than 256 MiB and may also weaken + * error detection slightly. + */ +typedef uint64_t vli_type; + +#define VLI_MAX ((vli_type)-1 / 2) +#define VLI_UNKNOWN ((vli_type)-1) + +/* Maximum encoded size of a VLI */ +#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7) + +/* Integrity Check types */ +enum xz_check { + XZ_CHECK_NONE =3D 0, + XZ_CHECK_CRC32 =3D 1, + XZ_CHECK_CRC64 =3D 4, + XZ_CHECK_SHA256 =3D 10 +}; + +/* Maximum possible Check ID */ +#define XZ_CHECK_MAX 15 + +#endif diff --git a/libxz/xz_crc32.c b/libxz/xz_crc32.c new file mode 100644 index 000000000000..34532d14fd4c --- /dev/null +++ b/libxz/xz_crc32.c @@ -0,0 +1,59 @@ +/* + * CRC32 using the polynomial from IEEE-802.3 + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +/* + * This is not the fastest implementation, but it is pretty compact. + * The fastest versions of xz_crc32() on modern CPUs without hardware + * accelerated CRC instruction are 3-5 times as fast as this version, + * but they are bigger and use more memory for the lookup table. + */ + +#include "xz_private.h" + +/* + * STATIC_RW_DATA is used in the pre-boot environment on some architecture= s. + * See for details. + */ +#ifndef STATIC_RW_DATA +# define STATIC_RW_DATA static +#endif + +STATIC_RW_DATA uint32_t xz_crc32_table[256]; + +XZ_EXTERN void xz_crc32_init(void) +{ + const uint32_t poly =3D 0xEDB88320; + + uint32_t i; + uint32_t j; + uint32_t r; + + for (i =3D 0; i < 256; ++i) { + r =3D i; + for (j =3D 0; j < 8; ++j) + r =3D (r >> 1) ^ (poly & ~((r & 1) - 1)); + + xz_crc32_table[i] =3D r; + } + + return; +} + +XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc) +{ + crc =3D ~crc; + + while (size !=3D 0) { + crc =3D xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8); + --size; + } + + return ~crc; +} diff --git a/libxz/xz_dec_lzma2.c b/libxz/xz_dec_lzma2.c new file mode 100644 index 000000000000..e9fb006c87fe --- /dev/null +++ b/libxz/xz_dec_lzma2.c @@ -0,0 +1,1171 @@ +/* + * LZMA2 decoder + * + * Authors: Lasse Collin + * Igor Pavlov + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" +#include "xz_lzma2.h" + +/* + * Range decoder initialization eats the first five bytes of each LZMA chu= nk. + */ +#define RC_INIT_BYTES 5 + +/* + * Minimum number of usable input buffer to safely decode one LZMA symbol. + * The worst case is that we decode 22 bits using probabilities and 26 + * direct bits. This may decode at maximum of 20 bytes of input. However, + * lzma_main() does an extra normalization before returning, thus we + * need to put 21 here. + */ +#define LZMA_IN_REQUIRED 21 + +/* + * Dictionary (history buffer) + * + * These are always true: + * start <=3D pos <=3D full <=3D end + * pos <=3D limit <=3D end + * + * In multi-call mode, also these are true: + * end =3D=3D size + * size <=3D size_max + * allocated <=3D size + * + * Most of these variables are size_t to support single-call mode, + * in which the dictionary variables address the actual output + * buffer directly. + */ +struct dictionary { + /* Beginning of the history buffer */ + uint8_t *buf; + + /* Old position in buf (before decoding more data) */ + size_t start; + + /* Position in buf */ + size_t pos; + + /* + * How full dictionary is. This is used to detect corrupt input th= at + * would read beyond the beginning of the uncompressed stream. + */ + size_t full; + + /* Write limit; we don't write to buf[limit] or later bytes. */ + size_t limit; + + /* + * End of the dictionary buffer. In multi-call mode, this is + * the same as the dictionary size. In single-call mode, this + * indicates the size of the output buffer. + */ + size_t end; + + /* + * Size of the dictionary as specified in Block Header. This is us= ed + * together with "full" to detect corrupt input that would make us + * read beyond the beginning of the uncompressed stream. + */ + uint32_t size; + + /* + * Maximum allowed dictionary size in multi-call mode. + * This is ignored in single-call mode. + */ + uint32_t size_max; + + /* + * Amount of memory currently allocated for the dictionary. + * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC, + * size_max is always the same as the allocated size.) + */ + uint32_t allocated; + + /* Operation mode */ + enum xz_mode mode; +}; + +/* Range decoder */ +struct rc_dec { + uint32_t range; + uint32_t code; + + /* + * Number of initializing bytes remaining to be read + * by rc_read_init(). + */ + uint32_t init_bytes_left; + + /* + * Buffer from which we read our input. It can be either + * temp.buf or the caller-provided input buffer. + */ + const uint8_t *in; + size_t in_pos; + size_t in_limit; +}; + +/* Probabilities for a length decoder. */ +struct lzma_len_dec { + /* Probability of match length being at least 10 */ + uint16_t choice; + + /* Probability of match length being at least 18 */ + uint16_t choice2; + + /* Probabilities for match lengths 2-9 */ + uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS]; + + /* Probabilities for match lengths 10-17 */ + uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS]; + + /* Probabilities for match lengths 18-273 */ + uint16_t high[LEN_HIGH_SYMBOLS]; +}; + +struct lzma_dec { + /* Distances of latest four matches */ + uint32_t rep0; + uint32_t rep1; + uint32_t rep2; + uint32_t rep3; + + /* Types of the most recently seen LZMA symbols */ + enum lzma_state state; + + /* + * Length of a match. This is updated so that dict_repeat can + * be called again to finish repeating the whole match. + */ + uint32_t len; + + /* + * LZMA properties or related bit masks (number of literal + * context bits, a mask dervied from the number of literal + * position bits, and a mask dervied from the number + * position bits) + */ + uint32_t lc; + uint32_t literal_pos_mask; /* (1 << lp) - 1 */ + uint32_t pos_mask; /* (1 << pb) - 1 */ + + /* If 1, it's a match. Otherwise it's a single 8-bit literal. */ + uint16_t is_match[STATES][POS_STATES_MAX]; + + /* If 1, it's a repeated match. The distance is one of rep0 .. rep= 3. */ + uint16_t is_rep[STATES]; + + /* + * If 0, distance of a repeated match is rep0. + * Otherwise check is_rep1. + */ + uint16_t is_rep0[STATES]; + + /* + * If 0, distance of a repeated match is rep1. + * Otherwise check is_rep2. + */ + uint16_t is_rep1[STATES]; + + /* If 0, distance of a repeated match is rep2. Otherwise it is rep= 3. */ + uint16_t is_rep2[STATES]; + + /* + * If 1, the repeated match has length of one byte. Otherwise + * the length is decoded from rep_len_decoder. + */ + uint16_t is_rep0_long[STATES][POS_STATES_MAX]; + + /* + * Probability tree for the highest two bits of the match + * distance. There is a separate probability tree for match + * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273]. + */ + uint16_t dist_slot[DIST_STATES][DIST_SLOTS]; + + /* + * Probility trees for additional bits for match distance + * when the distance is in the range [4, 127]. + */ + uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END]; + + /* + * Probability tree for the lowest four bits of a match + * distance that is equal to or greater than 128. + */ + uint16_t dist_align[ALIGN_SIZE]; + + /* Length of a normal match */ + struct lzma_len_dec match_len_dec; + + /* Length of a repeated match */ + struct lzma_len_dec rep_len_dec; + + /* Probabilities of literals */ + uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE]; +}; + +struct lzma2_dec { + /* Position in xz_dec_lzma2_run(). */ + enum lzma2_seq { + SEQ_CONTROL, + SEQ_UNCOMPRESSED_1, + SEQ_UNCOMPRESSED_2, + SEQ_COMPRESSED_0, + SEQ_COMPRESSED_1, + SEQ_PROPERTIES, + SEQ_LZMA_PREPARE, + SEQ_LZMA_RUN, + SEQ_COPY + } sequence; + + /* Next position after decoding the compressed size of the chunk. = */ + enum lzma2_seq next_sequence; + + /* Uncompressed size of LZMA chunk (2 MiB at maximum) */ + uint32_t uncompressed; + + /* + * Compressed size of LZMA chunk or compressed/uncompressed + * size of uncompressed chunk (64 KiB at maximum) + */ + uint32_t compressed; + + /* + * True if dictionary reset is needed. This is false before + * the first chunk (LZMA or uncompressed). + */ + bool need_dict_reset; + + /* + * True if new LZMA properties are needed. This is false + * before the first LZMA chunk. + */ + bool need_props; +}; + +struct xz_dec_lzma2 { + /* + * The order below is important on x86 to reduce code size and + * it shouldn't hurt on other platforms. Everything up to and + * including lzma.pos_mask are in the first 128 bytes on x86-32, + * which allows using smaller instructions to access those + * variables. On x86-64, fewer variables fit into the first 128 + * bytes, but this is still the best order without sacrificing + * the readability by splitting the structures. + */ + struct rc_dec rc; + struct dictionary dict; + struct lzma2_dec lzma2; + struct lzma_dec lzma; + + /* + * Temporary buffer which holds small number of input bytes between + * decoder calls. See lzma2_lzma() for details. + */ + struct { + uint32_t size; + uint8_t buf[3 * LZMA_IN_REQUIRED]; + } temp; +}; + +/************** + * Dictionary * + **************/ + +/* + * Reset the dictionary state. When in single-call mode, set up the beginn= ing + * of the dictionary to point to the actual output buffer. + */ +static void dict_reset(struct dictionary *dict, struct xz_buf *b) +{ + if (DEC_IS_SINGLE(dict->mode)) { + dict->buf =3D b->out + b->out_pos; + dict->end =3D b->out_size - b->out_pos; + } + + dict->start =3D 0; + dict->pos =3D 0; + dict->limit =3D 0; + dict->full =3D 0; +} + +/* Set dictionary write limit */ +static void dict_limit(struct dictionary *dict, size_t out_max) +{ + if (dict->end - dict->pos <=3D out_max) + dict->limit =3D dict->end; + else + dict->limit =3D dict->pos + out_max; +} + +/* Return true if at least one byte can be written into the dictionary. */ +static inline bool dict_has_space(const struct dictionary *dict) +{ + return dict->pos < dict->limit; +} + +/* + * Get a byte from the dictionary at the given distance. The distance is + * assumed to valid, or as a special case, zero when the dictionary is + * still empty. This special case is needed for single-call decoding to + * avoid writing a '\0' to the end of the destination buffer. + */ +static inline uint32_t dict_get(const struct dictionary *dict, uint32_t di= st) +{ + size_t offset =3D dict->pos - dist - 1; + + if (dist >=3D dict->pos) + offset +=3D dict->end; + + return dict->full > 0 ? dict->buf[offset] : 0; +} + +/* + * Put one byte into the dictionary. It is assumed that there is space for= it. + */ +static inline void dict_put(struct dictionary *dict, uint8_t byte) +{ + dict->buf[dict->pos++] =3D byte; + + if (dict->full < dict->pos) + dict->full =3D dict->pos; +} + +/* + * Repeat given number of bytes from the given distance. If the distance is + * invalid, false is returned. On success, true is returned and *len is + * updated to indicate how many bytes were left to be repeated. + */ +static bool dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t d= ist) +{ + size_t back; + uint32_t left; + + if (dist >=3D dict->full || dist >=3D dict->size) + return false; + + left =3D min_t(size_t, dict->limit - dict->pos, *len); + *len -=3D left; + + back =3D dict->pos - dist - 1; + if (dist >=3D dict->pos) + back +=3D dict->end; + + do { + dict->buf[dict->pos++] =3D dict->buf[back++]; + if (back =3D=3D dict->end) + back =3D 0; + } while (--left > 0); + + if (dict->full < dict->pos) + dict->full =3D dict->pos; + + return true; +} + +/* Copy uncompressed data as is from input to dictionary and output buffer= s. */ +static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b, + uint32_t *left) +{ + size_t copy_size; + + while (*left > 0 && b->in_pos < b->in_size + && b->out_pos < b->out_size) { + copy_size =3D min(b->in_size - b->in_pos, + b->out_size - b->out_pos); + if (copy_size > dict->end - dict->pos) + copy_size =3D dict->end - dict->pos; + if (copy_size > *left) + copy_size =3D *left; + + *left -=3D copy_size; + + memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size= ); + dict->pos +=3D copy_size; + + if (dict->full < dict->pos) + dict->full =3D dict->pos; + + if (DEC_IS_MULTI(dict->mode)) { + if (dict->pos =3D=3D dict->end) + dict->pos =3D 0; + + memcpy(b->out + b->out_pos, b->in + b->in_pos, + copy_size); + } + + dict->start =3D dict->pos; + + b->out_pos +=3D copy_size; + b->in_pos +=3D copy_size; + } +} + +/* + * Flush pending data from dictionary to b->out. It is assumed that there = is + * enough space in b->out. This is guaranteed because caller uses dict_lim= it() + * before decoding data into the dictionary. + */ +static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b) +{ + size_t copy_size =3D dict->pos - dict->start; + + if (DEC_IS_MULTI(dict->mode)) { + if (dict->pos =3D=3D dict->end) + dict->pos =3D 0; + + memcpy(b->out + b->out_pos, dict->buf + dict->start, + copy_size); + } + + dict->start =3D dict->pos; + b->out_pos +=3D copy_size; + return copy_size; +} + +/***************** + * Range decoder * + *****************/ + +/* Reset the range decoder. */ +static void rc_reset(struct rc_dec *rc) +{ + rc->range =3D (uint32_t)-1; + rc->code =3D 0; + rc->init_bytes_left =3D RC_INIT_BYTES; +} + +/* + * Read the first five initial bytes into rc->code if they haven't been + * read already. (Yes, the first byte gets completely ignored.) + */ +static bool rc_read_init(struct rc_dec *rc, struct xz_buf *b) +{ + while (rc->init_bytes_left > 0) { + if (b->in_pos =3D=3D b->in_size) + return false; + + rc->code =3D (rc->code << 8) + b->in[b->in_pos++]; + --rc->init_bytes_left; + } + + return true; +} + +/* Return true if there may not be enough input for the next decoding loop= . */ +static inline bool rc_limit_exceeded(const struct rc_dec *rc) +{ + return rc->in_pos > rc->in_limit; +} + +/* + * Return true if it is possible (from point of view of range decoder) that + * we have reached the end of the LZMA chunk. + */ +static inline bool rc_is_finished(const struct rc_dec *rc) +{ + return rc->code =3D=3D 0; +} + +/* Read the next input byte if needed. */ +static __always_inline void rc_normalize(struct rc_dec *rc) +{ + if (rc->range < RC_TOP_VALUE) { + rc->range <<=3D RC_SHIFT_BITS; + rc->code =3D (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_p= os++]; + } +} + +/* + * Decode one bit. In some versions, this function has been splitted in th= ree + * functions so that the compiler is supposed to be able to more easily av= oid + * an extra branch. In this particular version of the LZMA decoder, this + * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3 + * on x86). Using a non-splitted version results in nicer looking code too. + * + * NOTE: This must return an int. Do not make it return a bool or the speed + * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't ca= re, + * and it generates 10-20 % faster code than GCC 3.x from this file anyway= .) + */ +static __always_inline int rc_bit(struct rc_dec *rc, uint16_t *prob) +{ + uint32_t bound; + int bit; + + rc_normalize(rc); + bound =3D (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob; + if (rc->code < bound) { + rc->range =3D bound; + *prob +=3D (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS; + bit =3D 0; + } else { + rc->range -=3D bound; + rc->code -=3D bound; + *prob -=3D *prob >> RC_MOVE_BITS; + bit =3D 1; + } + + return bit; +} + +/* Decode a bittree starting from the most significant bit. */ +static __always_inline uint32_t rc_bittree(struct rc_dec *rc, + uint16_t *probs, uint32_t limit) +{ + uint32_t symbol =3D 1; + + do { + if (rc_bit(rc, &probs[symbol])) + symbol =3D (symbol << 1) + 1; + else + symbol <<=3D 1; + } while (symbol < limit); + + return symbol; +} + +/* Decode a bittree starting from the least significant bit. */ +static __always_inline void rc_bittree_reverse(struct rc_dec *rc, + uint16_t *probs, + uint32_t *dest, uint32_t li= mit) +{ + uint32_t symbol =3D 1; + uint32_t i =3D 0; + + do { + if (rc_bit(rc, &probs[symbol])) { + symbol =3D (symbol << 1) + 1; + *dest +=3D 1 << i; + } else { + symbol <<=3D 1; + } + } while (++i < limit); +} + +/* Decode direct bits (fixed fifty-fifty probability) */ +static inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t l= imit) +{ + uint32_t mask; + + do { + rc_normalize(rc); + rc->range >>=3D 1; + rc->code -=3D rc->range; + mask =3D (uint32_t)0 - (rc->code >> 31); + rc->code +=3D rc->range & mask; + *dest =3D (*dest << 1) + (mask + 1); + } while (--limit > 0); +} + +/******** + * LZMA * + ********/ + +/* Get pointer to literal coder probability array. */ +static uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s) +{ + uint32_t prev_byte =3D dict_get(&s->dict, 0); + uint32_t low =3D prev_byte >> (8 - s->lzma.lc); + uint32_t high =3D (s->dict.pos & s->lzma.literal_pos_mask) << s->l= zma.lc; + return s->lzma.literal[low + high]; +} + +/* Decode a literal (one 8-bit byte) */ +static void lzma_literal(struct xz_dec_lzma2 *s) +{ + uint16_t *probs; + uint32_t symbol; + uint32_t match_byte; + uint32_t match_bit; + uint32_t offset; + uint32_t i; + + probs =3D lzma_literal_probs(s); + + if (lzma_state_is_literal(s->lzma.state)) { + symbol =3D rc_bittree(&s->rc, probs, 0x100); + } else { + symbol =3D 1; + match_byte =3D dict_get(&s->dict, s->lzma.rep0) << 1; + offset =3D 0x100; + + do { + match_bit =3D match_byte & offset; + match_byte <<=3D 1; + i =3D offset + match_bit + symbol; + + if (rc_bit(&s->rc, &probs[i])) { + symbol =3D (symbol << 1) + 1; + offset &=3D match_bit; + } else { + symbol <<=3D 1; + offset &=3D ~match_bit; + } + } while (symbol < 0x100); + } + + dict_put(&s->dict, (uint8_t)symbol); + lzma_state_literal(&s->lzma.state); +} + +/* Decode the length of the match into s->lzma.len. */ +static void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l, + uint32_t pos_state) +{ + uint16_t *probs; + uint32_t limit; + + if (!rc_bit(&s->rc, &l->choice)) { + probs =3D l->low[pos_state]; + limit =3D LEN_LOW_SYMBOLS; + s->lzma.len =3D MATCH_LEN_MIN; + } else { + if (!rc_bit(&s->rc, &l->choice2)) { + probs =3D l->mid[pos_state]; + limit =3D LEN_MID_SYMBOLS; + s->lzma.len =3D MATCH_LEN_MIN + LEN_LOW_SYMBOLS; + } else { + probs =3D l->high; + limit =3D LEN_HIGH_SYMBOLS; + s->lzma.len =3D MATCH_LEN_MIN + LEN_LOW_SYMBOLS + + LEN_MID_SYMBOLS; + } + } + + s->lzma.len +=3D rc_bittree(&s->rc, probs, limit) - limit; +} + +/* Decode a match. The distance will be stored in s->lzma.rep0. */ +static void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +{ + uint16_t *probs; + uint32_t dist_slot; + uint32_t limit; + + lzma_state_match(&s->lzma.state); + + s->lzma.rep3 =3D s->lzma.rep2; + s->lzma.rep2 =3D s->lzma.rep1; + s->lzma.rep1 =3D s->lzma.rep0; + + lzma_len(s, &s->lzma.match_len_dec, pos_state); + + probs =3D s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)]; + dist_slot =3D rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS; + + if (dist_slot < DIST_MODEL_START) { + s->lzma.rep0 =3D dist_slot; + } else { + limit =3D (dist_slot >> 1) - 1; + s->lzma.rep0 =3D 2 + (dist_slot & 1); + + if (dist_slot < DIST_MODEL_END) { + s->lzma.rep0 <<=3D limit; + probs =3D s->lzma.dist_special + s->lzma.rep0 + - dist_slot - 1; + rc_bittree_reverse(&s->rc, probs, + &s->lzma.rep0, limit); + } else { + rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BIT= S); + s->lzma.rep0 <<=3D ALIGN_BITS; + rc_bittree_reverse(&s->rc, s->lzma.dist_align, + &s->lzma.rep0, ALIGN_BITS); + } + } +} + +/* + * Decode a repeated match. The distance is one of the four most recently + * seen matches. The distance will be stored in s->lzma.rep0. + */ +static void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state) +{ + uint32_t tmp; + + if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) { + if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[ + s->lzma.state][pos_state])) { + lzma_state_short_rep(&s->lzma.state); + s->lzma.len =3D 1; + return; + } + } else { + if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) { + tmp =3D s->lzma.rep1; + } else { + if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state= ])) { + tmp =3D s->lzma.rep2; + } else { + tmp =3D s->lzma.rep3; + s->lzma.rep3 =3D s->lzma.rep2; + } + + s->lzma.rep2 =3D s->lzma.rep1; + } + + s->lzma.rep1 =3D s->lzma.rep0; + s->lzma.rep0 =3D tmp; + } + + lzma_state_long_rep(&s->lzma.state); + lzma_len(s, &s->lzma.rep_len_dec, pos_state); +} + +/* LZMA decoder core */ +static bool lzma_main(struct xz_dec_lzma2 *s) +{ + uint32_t pos_state; + + /* + * If the dictionary was reached during the previous call, try to + * finish the possibly pending repeat in the dictionary. + */ + if (dict_has_space(&s->dict) && s->lzma.len > 0) + dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0); + + /* + * Decode more LZMA symbols. One iteration may consume up to + * LZMA_IN_REQUIRED - 1 bytes. + */ + while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) { + pos_state =3D s->dict.pos & s->lzma.pos_mask; + + if (!rc_bit(&s->rc, &s->lzma.is_match[ + s->lzma.state][pos_state])) { + lzma_literal(s); + } else { + if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state])) + lzma_rep_match(s, pos_state); + else + lzma_match(s, pos_state); + + if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.r= ep0)) + return false; + } + } + + /* + * Having the range decoder always normalized when we are outside + * this function makes it easier to correctly handle end of the ch= unk. + */ + rc_normalize(&s->rc); + + return true; +} + +/* + * Reset the LZMA decoder and range decoder state. Dictionary is nore reset + * here, because LZMA state may be reset without resetting the dictionary. + */ +static void lzma_reset(struct xz_dec_lzma2 *s) +{ + uint16_t *probs; + size_t i; + + s->lzma.state =3D STATE_LIT_LIT; + s->lzma.rep0 =3D 0; + s->lzma.rep1 =3D 0; + s->lzma.rep2 =3D 0; + s->lzma.rep3 =3D 0; + + /* + * All probabilities are initialized to the same value. This hack + * makes the code smaller by avoiding a separate loop for each + * probability array. + * + * This could be optimized so that only that part of literal + * probabilities that are actually required. In the common case + * we would write 12 KiB less. + */ + probs =3D s->lzma.is_match[0]; + for (i =3D 0; i < PROBS_TOTAL; ++i) + probs[i] =3D RC_BIT_MODEL_TOTAL / 2; + + rc_reset(&s->rc); +} + +/* + * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit ma= sks + * from the decoded lp and pb values. On success, the LZMA decoder state is + * reset and true is returned. + */ +static bool lzma_props(struct xz_dec_lzma2 *s, uint8_t props) +{ + if (props > (4 * 5 + 4) * 9 + 8) + return false; + + s->lzma.pos_mask =3D 0; + while (props >=3D 9 * 5) { + props -=3D 9 * 5; + ++s->lzma.pos_mask; + } + + s->lzma.pos_mask =3D (1 << s->lzma.pos_mask) - 1; + + s->lzma.literal_pos_mask =3D 0; + while (props >=3D 9) { + props -=3D 9; + ++s->lzma.literal_pos_mask; + } + + s->lzma.lc =3D props; + + if (s->lzma.lc + s->lzma.literal_pos_mask > 4) + return false; + + s->lzma.literal_pos_mask =3D (1 << s->lzma.literal_pos_mask) - 1; + + lzma_reset(s); + + return true; +} + +/********* + * LZMA2 * + *********/ + +/* + * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't + * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This + * wrapper function takes care of making the LZMA decoder's assumption saf= e. + * + * As long as there is plenty of input left to be decoded in the current L= ZMA + * chunk, we decode directly from the caller-supplied input buffer until + * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied i= nto + * s->temp.buf, which (hopefully) gets filled on the next call to this + * function. We decode a few bytes from the temporary buffer so that we can + * continue decoding from the caller-supplied input buffer again. + */ +static bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b) +{ + size_t in_avail; + uint32_t tmp; + + in_avail =3D b->in_size - b->in_pos; + if (s->temp.size > 0 || s->lzma2.compressed =3D=3D 0) { + tmp =3D 2 * LZMA_IN_REQUIRED - s->temp.size; + if (tmp > s->lzma2.compressed - s->temp.size) + tmp =3D s->lzma2.compressed - s->temp.size; + if (tmp > in_avail) + tmp =3D in_avail; + + memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp); + + if (s->temp.size + tmp =3D=3D s->lzma2.compressed) { + memzero(s->temp.buf + s->temp.size + tmp, + sizeof(s->temp.buf) + - s->temp.size - tmp); + s->rc.in_limit =3D s->temp.size + tmp; + } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) { + s->temp.size +=3D tmp; + b->in_pos +=3D tmp; + return true; + } else { + s->rc.in_limit =3D s->temp.size + tmp - LZMA_IN_RE= QUIRED; + } + + s->rc.in =3D s->temp.buf; + s->rc.in_pos =3D 0; + + if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp) + return false; + + s->lzma2.compressed -=3D s->rc.in_pos; + + if (s->rc.in_pos < s->temp.size) { + s->temp.size -=3D s->rc.in_pos; + memmove(s->temp.buf, s->temp.buf + s->rc.in_pos, + s->temp.size); + return true; + } + + b->in_pos +=3D s->rc.in_pos - s->temp.size; + s->temp.size =3D 0; + } + + in_avail =3D b->in_size - b->in_pos; + if (in_avail >=3D LZMA_IN_REQUIRED) { + s->rc.in =3D b->in; + s->rc.in_pos =3D b->in_pos; + + if (in_avail >=3D s->lzma2.compressed + LZMA_IN_REQUIRED) + s->rc.in_limit =3D b->in_pos + s->lzma2.compressed; + else + s->rc.in_limit =3D b->in_size - LZMA_IN_REQUIRED; + + if (!lzma_main(s)) + return false; + + in_avail =3D s->rc.in_pos - b->in_pos; + if (in_avail > s->lzma2.compressed) + return false; + + s->lzma2.compressed -=3D in_avail; + b->in_pos =3D s->rc.in_pos; + } + + in_avail =3D b->in_size - b->in_pos; + if (in_avail < LZMA_IN_REQUIRED) { + if (in_avail > s->lzma2.compressed) + in_avail =3D s->lzma2.compressed; + + memcpy(s->temp.buf, b->in + b->in_pos, in_avail); + s->temp.size =3D in_avail; + b->in_pos +=3D in_avail; + } + + return true; +} + +/* + * Take care of the LZMA2 control layer, and forward the job of actual LZMA + * decoding or copying of uncompressed chunks to other functions. + */ +XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s, + struct xz_buf *b) +{ + uint32_t tmp; + + while (b->in_pos < b->in_size || s->lzma2.sequence =3D=3D SEQ_LZMA= _RUN) { + switch (s->lzma2.sequence) { + case SEQ_CONTROL: + /* + * LZMA2 control byte + * + * Exact values: + * 0x00 End marker + * 0x01 Dictionary reset followed by + * an uncompressed chunk + * 0x02 Uncompressed chunk (no dictionary rese= t) + * + * Highest three bits (s->control & 0xE0): + * 0xE0 Dictionary reset, new properties and s= tate + * reset, followed by LZMA compressed chu= nk + * 0xC0 New properties and state reset, follow= ed + * by LZMA compressed chunk (no dictionary + * reset) + * 0xA0 State reset using old properties, + * followed by LZMA compressed chunk (no + * dictionary reset) + * 0x80 LZMA chunk (no dictionary or state res= et) + * + * For LZMA compressed chunks, the lowest five bits + * (s->control & 1F) are the highest bits of the + * uncompressed size (bits 16-20). + * + * A new LZMA2 stream must begin with a dictionary + * reset. The first LZMA chunk must set new + * properties and reset the LZMA state. + * + * Values that don't match anything described above + * are invalid and we return XZ_DATA_ERROR. + */ + tmp =3D b->in[b->in_pos++]; + + if (tmp =3D=3D 0x00) + return XZ_STREAM_END; + + if (tmp >=3D 0xE0 || tmp =3D=3D 0x01) { + s->lzma2.need_props =3D true; + s->lzma2.need_dict_reset =3D false; + dict_reset(&s->dict, b); + } else if (s->lzma2.need_dict_reset) { + return XZ_DATA_ERROR; + } + + if (tmp >=3D 0x80) { + s->lzma2.uncompressed =3D (tmp & 0x1F) << = 16; + s->lzma2.sequence =3D SEQ_UNCOMPRESSED_1; + + if (tmp >=3D 0xC0) { + /* + * When there are new properties, + * state reset is done at + * SEQ_PROPERTIES. + */ + s->lzma2.need_props =3D false; + s->lzma2.next_sequence + =3D SEQ_PROPERTIES; + + } else if (s->lzma2.need_props) { + return XZ_DATA_ERROR; + + } else { + s->lzma2.next_sequence + =3D SEQ_LZMA_PREPA= RE; + if (tmp >=3D 0xA0) + lzma_reset(s); + } + } else { + if (tmp > 0x02) + return XZ_DATA_ERROR; + + s->lzma2.sequence =3D SEQ_COMPRESSED_0; + s->lzma2.next_sequence =3D SEQ_COPY; + } + + break; + + case SEQ_UNCOMPRESSED_1: + s->lzma2.uncompressed + +=3D (uint32_t)b->in[b->in_pos++] = << 8; + s->lzma2.sequence =3D SEQ_UNCOMPRESSED_2; + break; + + case SEQ_UNCOMPRESSED_2: + s->lzma2.uncompressed + +=3D (uint32_t)b->in[b->in_pos++] = + 1; + s->lzma2.sequence =3D SEQ_COMPRESSED_0; + break; + + case SEQ_COMPRESSED_0: + s->lzma2.compressed + =3D (uint32_t)b->in[b->in_pos++] <= < 8; + s->lzma2.sequence =3D SEQ_COMPRESSED_1; + break; + + case SEQ_COMPRESSED_1: + s->lzma2.compressed + +=3D (uint32_t)b->in[b->in_pos++] = + 1; + s->lzma2.sequence =3D s->lzma2.next_sequence; + break; + + case SEQ_PROPERTIES: + if (!lzma_props(s, b->in[b->in_pos++])) + return XZ_DATA_ERROR; + + s->lzma2.sequence =3D SEQ_LZMA_PREPARE; + + case SEQ_LZMA_PREPARE: + if (s->lzma2.compressed < RC_INIT_BYTES) + return XZ_DATA_ERROR; + + if (!rc_read_init(&s->rc, b)) + return XZ_OK; + + s->lzma2.compressed -=3D RC_INIT_BYTES; + s->lzma2.sequence =3D SEQ_LZMA_RUN; + + case SEQ_LZMA_RUN: + /* + * Set dictionary limit to indicate how much we wa= nt + * to be encoded at maximum. Decode new data into = the + * dictionary. Flush the new data from dictionary = to + * b->out. Check if we finished decoding this chun= k. + * In case the dictionary got full but we didn't f= ill + * the output buffer yet, we may run this loop + * multiple times without changing s->lzma2.sequen= ce. + */ + dict_limit(&s->dict, min_t(size_t, + b->out_size - b->out_pos, + s->lzma2.uncompressed)); + if (!lzma2_lzma(s, b)) + return XZ_DATA_ERROR; + + s->lzma2.uncompressed -=3D dict_flush(&s->dict, b); + + if (s->lzma2.uncompressed =3D=3D 0) { + if (s->lzma2.compressed > 0 || s->lzma.len= > 0 + || !rc_is_finished(&s->rc)) + return XZ_DATA_ERROR; + + rc_reset(&s->rc); + s->lzma2.sequence =3D SEQ_CONTROL; + + } else if (b->out_pos =3D=3D b->out_size + || (b->in_pos =3D=3D b->in_size + && s->temp.size + < s->lzma2.compressed)) { + return XZ_OK; + } + + break; + + case SEQ_COPY: + dict_uncompressed(&s->dict, b, &s->lzma2.compresse= d); + if (s->lzma2.compressed > 0) + return XZ_OK; + + s->lzma2.sequence =3D SEQ_CONTROL; + break; + } + } + + return XZ_OK; +} + +XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode, + uint32_t dict_max) +{ + struct xz_dec_lzma2 *s =3D kmalloc(sizeof(*s), GFP_KERNEL); + if (s =3D=3D NULL) + return NULL; + + s->dict.mode =3D mode; + s->dict.size_max =3D dict_max; + + if (DEC_IS_PREALLOC(mode)) { + s->dict.buf =3D vmalloc(dict_max); + if (s->dict.buf =3D=3D NULL) { + kfree(s); + return NULL; + } + } else if (DEC_IS_DYNALLOC(mode)) { + s->dict.buf =3D NULL; + s->dict.allocated =3D 0; + } + + return s; +} + +XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t p= rops) +{ + /* This limits dictionary size to 3 GiB to keep parsing simpler. */ + if (props > 39) + return XZ_OPTIONS_ERROR; + + s->dict.size =3D 2 + (props & 1); + s->dict.size <<=3D (props >> 1) + 11; + + if (DEC_IS_MULTI(s->dict.mode)) { + if (s->dict.size > s->dict.size_max) + return XZ_MEMLIMIT_ERROR; + + s->dict.end =3D s->dict.size; + + if (DEC_IS_DYNALLOC(s->dict.mode)) { + if (s->dict.allocated < s->dict.size) { + vfree(s->dict.buf); + s->dict.buf =3D vmalloc(s->dict.size); + if (s->dict.buf =3D=3D NULL) { + s->dict.allocated =3D 0; + return XZ_MEM_ERROR; + } + } + } + } + + s->lzma.len =3D 0; + + s->lzma2.sequence =3D SEQ_CONTROL; + s->lzma2.need_dict_reset =3D true; + + s->temp.size =3D 0; + + return XZ_OK; +} + +XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s) +{ + if (DEC_IS_MULTI(s->dict.mode)) + vfree(s->dict.buf); + + kfree(s); +} diff --git a/libxz/xz_dec_stream.c b/libxz/xz_dec_stream.c new file mode 100644 index 000000000000..bb9517a985df --- /dev/null +++ b/libxz/xz_dec_stream.c @@ -0,0 +1,847 @@ +/* + * .xz Stream decoder + * + * Author: Lasse Collin + * + * This file has been put into the public domain. + * You can do whatever you want with this file. + */ + +#include "xz_private.h" +#include "xz_stream.h" + +#ifdef XZ_USE_CRC64 +# define IS_CRC64(check_type) ((check_type) =3D=3D XZ_CHECK_CRC64) +#else +# define IS_CRC64(check_type) false +#endif + +/* Hash used to validate the Index field */ +struct xz_dec_hash { + vli_type unpadded; + vli_type uncompressed; + uint32_t crc32; +}; + +struct xz_dec { + /* Position in dec_main() */ + enum { + SEQ_STREAM_HEADER, + SEQ_BLOCK_START, + SEQ_BLOCK_HEADER, + SEQ_BLOCK_UNCOMPRESS, + SEQ_BLOCK_PADDING, + SEQ_BLOCK_CHECK, + SEQ_INDEX, + SEQ_INDEX_PADDING, + SEQ_INDEX_CRC32, + SEQ_STREAM_FOOTER + } sequence; + + /* Position in variable-length integers and Check fields */ + uint32_t pos; + + /* Variable-length integer decoded by dec_vli() */ + vli_type vli; + + /* Saved in_pos and out_pos */ + size_t in_start; + size_t out_start; + +#ifdef XZ_USE_CRC64 + /* CRC32 or CRC64 value in Block or CRC32 value in Index */ + uint64_t crc; +#else + /* CRC32 value in Block or Index */ + uint32_t crc; +#endif + + /* Type of the integrity check calculated from uncompressed data */ + enum xz_check check_type; + + /* Operation mode */ + enum xz_mode mode; + + /* + * True if the next call to xz_dec_run() is allowed to return + * XZ_BUF_ERROR. + */ + bool allow_buf_error; + + /* Information stored in Block Header */ + struct { + /* + * Value stored in the Compressed Size field, or + * VLI_UNKNOWN if Compressed Size is not present. + */ + vli_type compressed; + + /* + * Value stored in the Uncompressed Size field, or + * VLI_UNKNOWN if Uncompressed Size is not present. + */ + vli_type uncompressed; + + /* Size of the Block Header field */ + uint32_t size; + } block_header; + + /* Information collected when decoding Blocks */ + struct { + /* Observed compressed size of the current Block */ + vli_type compressed; + + /* Observed uncompressed size of the current Block */ + vli_type uncompressed; + + /* Number of Blocks decoded so far */ + vli_type count; + + /* + * Hash calculated from the Block sizes. This is used to + * validate the Index field. + */ + struct xz_dec_hash hash; + } block; + + /* Variables needed when verifying the Index field */ + struct { + /* Position in dec_index() */ + enum { + SEQ_INDEX_COUNT, + SEQ_INDEX_UNPADDED, + SEQ_INDEX_UNCOMPRESSED + } sequence; + + /* Size of the Index in bytes */ + vli_type size; + + /* Number of Records (matches block.count in valid files) = */ + vli_type count; + + /* + * Hash calculated from the Records (matches block.hash in + * valid files). + */ + struct xz_dec_hash hash; + } index; + + /* + * Temporary buffer needed to hold Stream Header, Block Header, + * and Stream Footer. The Block Header is the biggest (1 KiB) + * so we reserve space according to that. buf[] has to be aligned + * to a multiple of four bytes; the size_t variables before it + * should guarantee this. + */ + struct { + size_t pos; + size_t size; + uint8_t buf[1024]; + } temp; + + struct xz_dec_lzma2 *lzma2; + +#ifdef XZ_DEC_BCJ + struct xz_dec_bcj *bcj; + bool bcj_active; +#endif +}; + +#ifdef XZ_DEC_ANY_CHECK +/* Sizes of the Check field with different Check IDs */ +static const uint8_t check_sizes[16] =3D { + 0, + 4, 4, 4, + 8, 8, 8, + 16, 16, 16, + 32, 32, 32, + 64, 64, 64 +}; +#endif + +/* + * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller + * must have set s->temp.pos to indicate how much data we are supposed + * to copy into s->temp.buf. Return true once s->temp.pos has reached + * s->temp.size. + */ +static bool fill_temp(struct xz_dec *s, struct xz_buf *b) +{ + size_t copy_size =3D min_t(size_t, + b->in_size - b->in_pos, s->temp.size - s->temp.pos= ); + + memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size); + b->in_pos +=3D copy_size; + s->temp.pos +=3D copy_size; + + if (s->temp.pos =3D=3D s->temp.size) { + s->temp.pos =3D 0; + return true; + } + + return false; +} + +/* Decode a variable-length integer (little-endian base-128 encoding) */ +static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in, + size_t *in_pos, size_t in_size) +{ + uint8_t byte; + + if (s->pos =3D=3D 0) + s->vli =3D 0; + + while (*in_pos < in_size) { + byte =3D in[*in_pos]; + ++*in_pos; + + s->vli |=3D (vli_type)(byte & 0x7F) << s->pos; + + if ((byte & 0x80) =3D=3D 0) { + /* Don't allow non-minimal encodings. */ + if (byte =3D=3D 0 && s->pos !=3D 0) + return XZ_DATA_ERROR; + + s->pos =3D 0; + return XZ_STREAM_END; + } + + s->pos +=3D 7; + if (s->pos =3D=3D 7 * VLI_BYTES_MAX) + return XZ_DATA_ERROR; + } + + return XZ_OK; +} + +/* + * Decode the Compressed Data field from a Block. Update and validate + * the observed compressed and uncompressed sizes of the Block so that + * they don't exceed the values possibly stored in the Block Header + * (validation assumes that no integer overflow occurs, since vli_type + * is normally uint64_t). Update the CRC32 or CRC64 value if presence of + * the CRC32 or CRC64 field was indicated in Stream Header. + * + * Once the decoding is finished, validate that the observed sizes match + * the sizes possibly stored in the Block Header. Update the hash and + * Block count, which are later used to validate the Index field. + */ +static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + s->in_start =3D b->in_pos; + s->out_start =3D b->out_pos; + +#ifdef XZ_DEC_BCJ + if (s->bcj_active) + ret =3D xz_dec_bcj_run(s->bcj, s->lzma2, b); + else +#endif + ret =3D xz_dec_lzma2_run(s->lzma2, b); + + s->block.compressed +=3D b->in_pos - s->in_start; + s->block.uncompressed +=3D b->out_pos - s->out_start; + + /* + * There is no need to separately check for VLI_UNKNOWN, since + * the observed sizes are always smaller than VLI_UNKNOWN. + */ + if (s->block.compressed > s->block_header.compressed + || s->block.uncompressed + > s->block_header.uncompressed) + return XZ_DATA_ERROR; + + if (s->check_type =3D=3D XZ_CHECK_CRC32) + s->crc =3D xz_crc32(b->out + s->out_start, + b->out_pos - s->out_start, s->crc); +#ifdef XZ_USE_CRC64 + else if (s->check_type =3D=3D XZ_CHECK_CRC64) + s->crc =3D xz_crc64(b->out + s->out_start, + b->out_pos - s->out_start, s->crc); +#endif + + if (ret =3D=3D XZ_STREAM_END) { + if (s->block_header.compressed !=3D VLI_UNKNOWN + && s->block_header.compressed + !=3D s->block.compressed) + return XZ_DATA_ERROR; + + if (s->block_header.uncompressed !=3D VLI_UNKNOWN + && s->block_header.uncompressed + !=3D s->block.uncompressed) + return XZ_DATA_ERROR; + + s->block.hash.unpadded +=3D s->block_header.size + + s->block.compressed; + +#ifdef XZ_DEC_ANY_CHECK + s->block.hash.unpadded +=3D check_sizes[s->check_type]; +#else + if (s->check_type =3D=3D XZ_CHECK_CRC32) + s->block.hash.unpadded +=3D 4; + else if (IS_CRC64(s->check_type)) + s->block.hash.unpadded +=3D 8; +#endif + + s->block.hash.uncompressed +=3D s->block.uncompressed; + s->block.hash.crc32 =3D xz_crc32( + (const uint8_t *)&s->block.hash, + sizeof(s->block.hash), s->block.hash.crc32= ); + + ++s->block.count; + } + + return ret; +} + +/* Update the Index size and the CRC32 value. */ +static void index_update(struct xz_dec *s, const struct xz_buf *b) +{ + size_t in_used =3D b->in_pos - s->in_start; + s->index.size +=3D in_used; + s->crc =3D xz_crc32(b->in + s->in_start, in_used, s->crc); +} + +/* + * Decode the Number of Records, Unpadded Size, and Uncompressed Size + * fields from the Index field. That is, Index Padding and CRC32 are not + * decoded by this function. + * + * This can return XZ_OK (more input needed), XZ_STREAM_END (everything + * successfully decoded), or XZ_DATA_ERROR (input is corrupt). + */ +static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + do { + ret =3D dec_vli(s, b->in, &b->in_pos, b->in_size); + if (ret !=3D XZ_STREAM_END) { + index_update(s, b); + return ret; + } + + switch (s->index.sequence) { + case SEQ_INDEX_COUNT: + s->index.count =3D s->vli; + + /* + * Validate that the Number of Records field + * indicates the same number of Records as + * there were Blocks in the Stream. + */ + if (s->index.count !=3D s->block.count) + return XZ_DATA_ERROR; + + s->index.sequence =3D SEQ_INDEX_UNPADDED; + break; + + case SEQ_INDEX_UNPADDED: + s->index.hash.unpadded +=3D s->vli; + s->index.sequence =3D SEQ_INDEX_UNCOMPRESSED; + break; + + case SEQ_INDEX_UNCOMPRESSED: + s->index.hash.uncompressed +=3D s->vli; + s->index.hash.crc32 =3D xz_crc32( + (const uint8_t *)&s->index.hash, + sizeof(s->index.hash), + s->index.hash.crc32); + --s->index.count; + s->index.sequence =3D SEQ_INDEX_UNPADDED; + break; + } + } while (s->index.count > 0); + + return XZ_STREAM_END; +} + +/* + * Validate that the next four or eight input bytes match the value + * of s->crc. s->pos must be zero when starting to validate the first byte. + * The "bits" argument allows using the same code for both CRC32 and CRC64. + */ +static enum xz_ret crc_validate(struct xz_dec *s, struct xz_buf *b, + uint32_t bits) +{ + do { + if (b->in_pos =3D=3D b->in_size) + return XZ_OK; + + if (((s->crc >> s->pos) & 0xFF) !=3D b->in[b->in_pos++]) + return XZ_DATA_ERROR; + + s->pos +=3D 8; + + } while (s->pos < bits); + + s->crc =3D 0; + s->pos =3D 0; + + return XZ_STREAM_END; +} + +#ifdef XZ_DEC_ANY_CHECK +/* + * Skip over the Check field when the Check ID is not supported. + * Returns true once the whole Check field has been skipped over. + */ +static bool check_skip(struct xz_dec *s, struct xz_buf *b) +{ + while (s->pos < check_sizes[s->check_type]) { + if (b->in_pos =3D=3D b->in_size) + return false; + + ++b->in_pos; + ++s->pos; + } + + s->pos =3D 0; + + return true; +} +#endif + +/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). = */ +static enum xz_ret dec_stream_header(struct xz_dec *s) +{ + if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE)) + return XZ_FORMAT_ERROR; + + if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0) + !=3D get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2)) + return XZ_DATA_ERROR; + + if (s->temp.buf[HEADER_MAGIC_SIZE] !=3D 0) + return XZ_OPTIONS_ERROR; + + /* + * Of integrity checks, we support none (Check ID =3D 0), + * CRC32 (Check ID =3D 1), and optionally CRC64 (Check ID =3D 4). + * However, if XZ_DEC_ANY_CHECK is defined, we will accept other + * check types too, but then the check won't be verified and + * a warning (XZ_UNSUPPORTED_CHECK) will be given. + */ + s->check_type =3D s->temp.buf[HEADER_MAGIC_SIZE + 1]; + +#ifdef XZ_DEC_ANY_CHECK + if (s->check_type > XZ_CHECK_MAX) + return XZ_OPTIONS_ERROR; + + if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type)) + return XZ_UNSUPPORTED_CHECK; +#else + if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type)) + return XZ_OPTIONS_ERROR; +#endif + + return XZ_OK; +} + +/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */ +static enum xz_ret dec_stream_footer(struct xz_dec *s) +{ + if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE)) + return XZ_DATA_ERROR; + + if (xz_crc32(s->temp.buf + 4, 6, 0) !=3D get_le32(s->temp.buf)) + return XZ_DATA_ERROR; + + /* + * Validate Backward Size. Note that we never added the size of the + * Index CRC32 field to s->index.size, thus we use s->index.size /= 4 + * instead of s->index.size / 4 - 1. + */ + if ((s->index.size >> 2) !=3D get_le32(s->temp.buf + 4)) + return XZ_DATA_ERROR; + + if (s->temp.buf[8] !=3D 0 || s->temp.buf[9] !=3D s->check_type) + return XZ_DATA_ERROR; + + /* + * Use XZ_STREAM_END instead of XZ_OK to be more convenient + * for the caller. + */ + return XZ_STREAM_END; +} + +/* Decode the Block Header and initialize the filter chain. */ +static enum xz_ret dec_block_header(struct xz_dec *s) +{ + enum xz_ret ret; + + /* + * Validate the CRC32. We know that the temp buffer is at least + * eight bytes so this is safe. + */ + s->temp.size -=3D 4; + if (xz_crc32(s->temp.buf, s->temp.size, 0) + !=3D get_le32(s->temp.buf + s->temp.size)) + return XZ_DATA_ERROR; + + s->temp.pos =3D 2; + + /* + * Catch unsupported Block Flags. We support only one or two filte= rs + * in the chain, so we catch that with the same test. + */ +#ifdef XZ_DEC_BCJ + if (s->temp.buf[1] & 0x3E) +#else + if (s->temp.buf[1] & 0x3F) +#endif + return XZ_OPTIONS_ERROR; + + /* Compressed Size */ + if (s->temp.buf[1] & 0x40) { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) + !=3D XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.compressed =3D s->vli; + } else { + s->block_header.compressed =3D VLI_UNKNOWN; + } + + /* Uncompressed Size */ + if (s->temp.buf[1] & 0x80) { + if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size) + !=3D XZ_STREAM_END) + return XZ_DATA_ERROR; + + s->block_header.uncompressed =3D s->vli; + } else { + s->block_header.uncompressed =3D VLI_UNKNOWN; + } + +#ifdef XZ_DEC_BCJ + /* If there are two filters, the first one must be a BCJ filter. */ + s->bcj_active =3D s->temp.buf[1] & 0x01; + if (s->bcj_active) { + if (s->temp.size - s->temp.pos < 2) + return XZ_OPTIONS_ERROR; + + ret =3D xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++= ]); + if (ret !=3D XZ_OK) + return ret; + + /* + * We don't support custom start offset, + * so Size of Properties must be zero. + */ + if (s->temp.buf[s->temp.pos++] !=3D 0x00) + return XZ_OPTIONS_ERROR; + } +#endif + + /* Valid Filter Flags always take at least two bytes. */ + if (s->temp.size - s->temp.pos < 2) + return XZ_DATA_ERROR; + + /* Filter ID =3D LZMA2 */ + if (s->temp.buf[s->temp.pos++] !=3D 0x21) + return XZ_OPTIONS_ERROR; + + /* Size of Properties =3D 1-byte Filter Properties */ + if (s->temp.buf[s->temp.pos++] !=3D 0x01) + return XZ_OPTIONS_ERROR; + + /* Filter Properties contains LZMA2 dictionary size. */ + if (s->temp.size - s->temp.pos < 1) + return XZ_DATA_ERROR; + + ret =3D xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]); + if (ret !=3D XZ_OK) + return ret; + + /* The rest must be Header Padding. */ + while (s->temp.pos < s->temp.size) + if (s->temp.buf[s->temp.pos++] !=3D 0x00) + return XZ_OPTIONS_ERROR; + + s->temp.pos =3D 0; + s->block.compressed =3D 0; + s->block.uncompressed =3D 0; + + return XZ_OK; +} + +static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b) +{ + enum xz_ret ret; + + /* + * Store the start position for the case when we are in the middle + * of the Index field. + */ + s->in_start =3D b->in_pos; + + while (true) { + switch (s->sequence) { + case SEQ_STREAM_HEADER: + /* + * Stream Header is copied to s->temp, and then + * decoded from there. This way if the caller + * gives us only little input at a time, we can + * still keep the Stream Header decoding code + * simple. Similar approach is used in many places + * in this file. + */ + if (!fill_temp(s, b)) + return XZ_OK; + + /* + * If dec_stream_header() returns + * XZ_UNSUPPORTED_CHECK, it is still possible + * to continue decoding if working in multi-call + * mode. Thus, update s->sequence before calling + * dec_stream_header(). + */ + s->sequence =3D SEQ_BLOCK_START; + + ret =3D dec_stream_header(s); + if (ret !=3D XZ_OK) + return ret; + + case SEQ_BLOCK_START: + /* We need one byte of input to continue. */ + if (b->in_pos =3D=3D b->in_size) + return XZ_OK; + + /* See if this is the beginning of the Index field= . */ + if (b->in[b->in_pos] =3D=3D 0) { + s->in_start =3D b->in_pos++; + s->sequence =3D SEQ_INDEX; + break; + } + + /* + * Calculate the size of the Block Header and + * prepare to decode it. + */ + s->block_header.size + =3D ((uint32_t)b->in[b->in_pos] + 1) * 4; + + s->temp.size =3D s->block_header.size; + s->temp.pos =3D 0; + s->sequence =3D SEQ_BLOCK_HEADER; + + case SEQ_BLOCK_HEADER: + if (!fill_temp(s, b)) + return XZ_OK; + + ret =3D dec_block_header(s); + if (ret !=3D XZ_OK) + return ret; + + s->sequence =3D SEQ_BLOCK_UNCOMPRESS; + + case SEQ_BLOCK_UNCOMPRESS: + ret =3D dec_block(s, b); + if (ret !=3D XZ_STREAM_END) + return ret; + + s->sequence =3D SEQ_BLOCK_PADDING; + + case SEQ_BLOCK_PADDING: + /* + * Size of Compressed Data + Block Padding + * must be a multiple of four. We don't need + * s->block.compressed for anything else + * anymore, so we use it here to test the size + * of the Block Padding field. + */ + while (s->block.compressed & 3) { + if (b->in_pos =3D=3D b->in_size) + return XZ_OK; + + if (b->in[b->in_pos++] !=3D 0) + return XZ_DATA_ERROR; + + ++s->block.compressed; + } + + s->sequence =3D SEQ_BLOCK_CHECK; + + case SEQ_BLOCK_CHECK: + if (s->check_type =3D=3D XZ_CHECK_CRC32) { + ret =3D crc_validate(s, b, 32); + if (ret !=3D XZ_STREAM_END) + return ret; + } + else if (IS_CRC64(s->check_type)) { + ret =3D crc_validate(s, b, 64); + if (ret !=3D XZ_STREAM_END) + return ret; + } +#ifdef XZ_DEC_ANY_CHECK + else if (!check_skip(s, b)) { + return XZ_OK; + } +#endif + + s->sequence =3D SEQ_BLOCK_START; + break; + + case SEQ_INDEX: + ret =3D dec_index(s, b); + if (ret !=3D XZ_STREAM_END) + return ret; + + s->sequence =3D SEQ_INDEX_PADDING; + + case SEQ_INDEX_PADDING: + while ((s->index.size + (b->in_pos - s->in_start)) + & 3) { + if (b->in_pos =3D=3D b->in_size) { + index_update(s, b); + return XZ_OK; + } + + if (b->in[b->in_pos++] !=3D 0) + return XZ_DATA_ERROR; + } + + /* Finish the CRC32 value and Index size. */ + index_update(s, b); + + /* Compare the hashes to validate the Index field.= */ + if (!memeq(&s->block.hash, &s->index.hash, + sizeof(s->block.hash))) + return XZ_DATA_ERROR; + + s->sequence =3D SEQ_INDEX_CRC32; + + case SEQ_INDEX_CRC32: + ret =3D crc_validate(s, b, 32); + if (ret !=3D XZ_STREAM_END) + return ret; + + s->temp.size =3D STREAM_HEADER_SIZE; + s->sequence =3D SEQ_STREAM_FOOTER; + + case SEQ_STREAM_FOOTER: + if (!fill_temp(s, b)) + return XZ_OK; + + return dec_stream_footer(s); + } + } + + /* Never reached */ +} + +/* + * xz_dec_run() is a wrapper for dec_main() to handle some special cases in + * multi-call and single-call decoding. + * + * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear tha= t we + * are not going to make any progress anymore. This is to prevent the call= er + * from calling us infinitely when the input file is truncated or otherwise + * corrupt. Since zlib-style API allows that the caller fills the input bu= ffer + * only when the decoder doesn't produce any new output, we have to be car= eful + * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned on= ly + * after the second consecutive call to xz_dec_run() that makes no progres= s. + * + * In single-call mode, if we couldn't decode everything and no error + * occurred, either the input is truncated or the output buffer is too sma= ll. + * Since we know that the last input byte never produces any output, we kn= ow + * that if all the input was consumed and decoding wasn't finished, the fi= le + * must be corrupt. Otherwise the output buffer has to be too small or the + * file is corrupt in a way that decoding it produces too big output. + * + * If single-call decoding fails, we reset b->in_pos and b->out_pos back to + * their original values. This is because with some filter chains there wo= n't + * be any valid uncompressed data in the output buffer unless the decoding + * actually succeeds (that's the price to pay of using the output buffer as + * the workspace). + */ +XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b) +{ + size_t in_start; + size_t out_start; + enum xz_ret ret; + + if (DEC_IS_SINGLE(s->mode)) + xz_dec_reset(s); + + in_start =3D b->in_pos; + out_start =3D b->out_pos; + ret =3D dec_main(s, b); + + if (DEC_IS_SINGLE(s->mode)) { + if (ret =3D=3D XZ_OK) + ret =3D b->in_pos =3D=3D b->in_size + ? XZ_DATA_ERROR : XZ_BUF_ERROR; + + if (ret !=3D XZ_STREAM_END) { + b->in_pos =3D in_start; + b->out_pos =3D out_start; + } + + } else if (ret =3D=3D XZ_OK && in_start =3D=3D b->in_pos + && out_start =3D=3D b->out_pos) { + if (s->allow_buf_error) + ret =3D XZ_BUF_ERROR; + + s->allow_buf_error =3D true; + } else { + s->allow_buf_error =3D false; + } + + return ret; +} + +XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max) +{ + struct xz_dec *s =3D kmalloc(sizeof(*s), GFP_KERNEL); + if (s =3D=3D NULL) + return NULL; + + s->mode =3D mode; + +#ifdef XZ_DEC_BCJ + s->bcj =3D xz_dec_bcj_create(DEC_IS_SINGLE(mode)); + if (s->bcj =3D=3D NULL) + goto error_bcj; +#endif + + s->lzma2 =3D xz_dec_lzma2_create(mode, dict_max); + if (s->lzma2 =3D=3D NULL) + goto error_lzma2; + + xz_dec_reset(s); + return s; + +error_lzma2: +#ifdef XZ_DEC_BCJ + xz_dec_bcj_end(s->bcj); +error_bcj: +#endif + kfree(s); + return NULL; +} + +XZ_EXTERN void xz_dec_reset(struct xz_dec *s) +{ + s->sequence =3D SEQ_STREAM_HEADER; + s->allow_buf_error =3D false; + s->pos =3D 0; + s->crc =3D 0; + memzero(&s->block, sizeof(s->block)); + memzero(&s->index, sizeof(s->index)); + s->temp.pos =3D 0; + s->temp.size =3D STREAM_HEADER_SIZE; +} + +XZ_EXTERN void xz_dec_end(struct xz_dec *s) +{ + if (s !=3D NULL) { + xz_dec_lzma2_end(s->lzma2); +#ifdef XZ_DEC_BCJ + xz_dec_bcj_end(s->bcj); +#endif + kfree(s); + } +} diff --git a/libxz/Makefile.objs b/libxz/Makefile.objs new file mode 100644 index 000000000000..0fdd53400437 --- /dev/null +++ b/libxz/Makefile.objs @@ -0,0 +1 @@ +obj-y +=3D xz_dec_stream.o xz_dec_lzma2.o xz_crc32.o diff --git a/target/ppc/Makefile.objs b/target/ppc/Makefile.objs index e8fa18ce13d1..ec2ad3e14dd5 100644 --- a/target/ppc/Makefile.objs +++ b/target/ppc/Makefile.objs @@ -18,3 +18,4 @@ obj-y +=3D mem_helper.o obj-y +=3D ../../libdecnumber/ obj-$(CONFIG_USER_ONLY) +=3D user_only_helper.o obj-y +=3D gdbstub.o +obj-$(TARGET_PPC64) +=3D ../../libxz/ --=20 2.21.0 From nobody Thu May 16 14:52:48 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1571664641; cv=none; d=zoho.com; s=zohoarc; b=XRY3lT5rXvBpbJJOh3goaeEvDNqjCWT5tbAGf3kyr6BQo4r/DwvKKVKcsTThTuk/ggFOkc9QtUP6cYBlvvhaaIZnmkGW44BAfdJHKM6sPtD7z1PLyDugYrn2tmM9WCID2aLzXhgJ/cSEgQm2eHefvKEvNq1brusBCUtvQU3Q12w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1571664641; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=9NCXfXkdZmuJscVoLNaI1jk3ySiPSsffN9sxV6fnvI8=; b=LR6ewIE5XTPNoTGYiEmu2weLSt+bjLdLs1xAiNsRFWcq4EPCK+nyZh2vQGTn/jY5IQau0ShQpJoJh32Bk7qUshJTjdAN74OdkUgq4/GtDHM8HzG30M5XSDuJxSHy5AeYaaqzMNWoPDy7+ucmE8tPHj+nY//6VUpHFwpeMouqW7g= ARC-Authentication-Results: i=1; mx.zoho.com; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1571664641405495.9634678045811; Mon, 21 Oct 2019 06:30:41 -0700 (PDT) Received: from localhost ([::1]:40874 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMXWA-0006kN-Sq for importer@patchew.org; Mon, 21 Oct 2019 09:14:38 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:34546) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1iMXUF-0005RI-9R for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:42 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1iMXUC-0003qn-ON for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:39 -0400 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:48620) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1iMXUC-0003pN-Ek for qemu-devel@nongnu.org; Mon, 21 Oct 2019 09:12:36 -0400 Received: from pps.filterd (m0098399.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.27/8.16.0.27) with SMTP id x9LD8WXW105059 for ; Mon, 21 Oct 2019 09:12:34 -0400 Received: from e06smtp02.uk.ibm.com (e06smtp02.uk.ibm.com [195.75.94.98]) by mx0a-001b2d01.pphosted.com with ESMTP id 2vsd2qgw16-1 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=NOT) for ; Mon, 21 Oct 2019 09:12:33 -0400 Received: from localhost by e06smtp02.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 21 Oct 2019 14:12:31 +0100 Received: from b06avi18626390.portsmouth.uk.ibm.com (9.149.26.192) by e06smtp02.uk.ibm.com (192.168.101.132) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; (version=TLSv1/SSLv3 cipher=AES256-GCM-SHA384 bits=256/256) Mon, 21 Oct 2019 14:12:27 +0100 Received: from d06av25.portsmouth.uk.ibm.com (d06av25.portsmouth.uk.ibm.com [9.149.105.61]) by b06avi18626390.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id x9LDBsMf13697520 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Mon, 21 Oct 2019 13:11:54 GMT Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 744D711C05C; Mon, 21 Oct 2019 13:12:26 +0000 (GMT) Received: from d06av25.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 5DE7011C050; Mon, 21 Oct 2019 13:12:26 +0000 (GMT) Received: from smtp.tls.ibm.com (unknown [9.101.4.1]) by d06av25.portsmouth.uk.ibm.com (Postfix) with ESMTP; Mon, 21 Oct 2019 13:12:26 +0000 (GMT) Received: from yukon.kaod.org.ibmuc.com (sig-9-145-86-58.uk.ibm.com [9.145.86.58]) by smtp.tls.ibm.com (Postfix) with ESMTP id A40C92201BC; Mon, 21 Oct 2019 15:12:25 +0200 (CEST) From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= To: David Gibson Subject: [PATCH 5/5] ppc/pnv: Read the PNOR partition table Date: Mon, 21 Oct 2019 15:12:15 +0200 X-Mailer: git-send-email 2.21.0 In-Reply-To: <20191021131215.3693-1-clg@kaod.org> References: <20191021131215.3693-1-clg@kaod.org> MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 19102113-0008-0000-0000-00000324ECD6 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 19102113-0009-0000-0000-00004A4414D0 Message-Id: <20191021131215.3693-6-clg@kaod.org> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-10-21_04:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=9 phishscore=0 bulkscore=0 spamscore=0 clxscore=1034 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1908290000 definitions=main-1910210128 Content-Transfer-Encoding: quoted-printable X-MIME-Autoconverted: from 8bit to quoted-printable by mx0a-001b2d01.pphosted.com id x9LD8WXW105059 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.156.1 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-devel@nongnu.org, Greg Kurz , =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= , qemu-ppc@nongnu.org, Joel Stanley , "Marty E . Plummer" , =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Type: text/plain; charset="utf-8" From: C=C3=A9dric Le Goater This patch adds the logic to read the partition table of a PNOR file and a routine to load the PAYLOAD (skiboot) partition in the machine memory. Code based on the libflash of skiboot: https://github.com/open-power/skiboot/blob/master/libflash/ Signed-off-by: C=C3=A9dric Le Goater --- include/hw/ppc/ffs.h | 150 ++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv_pnor.h | 6 ++ hw/ppc/pnv.c | 33 +++++--- hw/ppc/pnv_pnor.c | 156 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 334 insertions(+), 11 deletions(-) create mode 100644 include/hw/ppc/ffs.h diff --git a/include/hw/ppc/ffs.h b/include/hw/ppc/ffs.h new file mode 100644 index 000000000000..15216cf2389b --- /dev/null +++ b/include/hw/ppc/ffs.h @@ -0,0 +1,150 @@ +/* + * Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * FSP Flash Structure + * + * This header defines the layout for the FSP Flash Structure. + */ + +#ifndef FFS_H +#define FFS_H + +#include + +/* The version of this partition implementation */ +#define FFS_VERSION_1 1 + +/* Magic number for the partition header (ASCII 'PART') */ +#define FFS_MAGIC 0x50415254 + +/* The maximum length of the partition name */ +#define PART_NAME_MAX 15 + +/* + * Sizes of the data structures + */ +#define FFS_HDR_SIZE sizeof(struct ffs_hdr) +#define FFS_ENTRY_SIZE sizeof(struct ffs_entry) + +/* + * Sizes of the data structures w/o checksum + */ +#define FFS_HDR_SIZE_CSUM (FFS_HDR_SIZE - sizeof(uint32_t)) +#define FFS_ENTRY_SIZE_CSUM (FFS_ENTRY_SIZE - sizeof(uint32_t)) + +/* pid of logical partitions/containers */ +#define FFS_PID_TOPLEVEL 0xFFFFFFFF + +/* + * Type of image contained w/in partition + */ +enum type { + FFS_TYPE_DATA =3D 1, + FFS_TYPE_LOGICAL =3D 2, + FFS_TYPE_PARTITION =3D 3, +}; + +/* + * Flag bit definitions + */ +#define FFS_FLAGS_PROTECTED 0x0001 +#define FFS_FLAGS_U_BOOT_ENV 0x0002 + +/* Data integrity flags */ +#define FFS_ENRY_INTEG_ECC 0x8000 + +/** + * struct ffs_entry_user - User data enties + * + * @chip: Chip Select (0,1) + * @compressType: Compression Indication/alg (0=3Dnot compressed) + * @dataInteg: Indicates Data Integrity mechanism + * @verCheck: Indicates Version check type + * @miscFlags: Misc Partition related Flags + * @freeMisc[2]: Unused Miscellaneious Info + * @freeUser[14]: Unused User Data + */ +struct ffs_entry_user { + uint8_t chip; + uint8_t compresstype; + uint16_t datainteg; + uint8_t vercheck; + uint8_t miscflags; + uint8_t freemisc[2]; + uint32_t reserved[14]; +}; + +/** + * struct ffs_entry - Partition entry + * + * @name: Opaque null terminated string + * @base: Starting offset of partition in flash (in hdr.block_size) + * @size: Partition size (in hdr.block_size) + * @pid: Parent partition entry (FFS_PID_TOPLEVEL for toplevel) + * @id: Partition entry ID [1..65536] + * @type: Describe type of partition + * @flags: Partition attributes (optional) + * @actual: Actual partition size (in bytes) + * @resvd: Reserved words for future use + * @user: User data (optional) + * @checksum: Partition entry checksum (includes all above) + */ +struct ffs_entry { + char name[PART_NAME_MAX + 1]; + uint32_t base; + uint32_t size; + uint32_t pid; + uint32_t id; + uint32_t type; + uint32_t flags; + uint32_t actual; + uint32_t resvd[4]; + struct ffs_entry_user user; + uint32_t checksum; +} __attribute__ ((packed)); + +/** + * struct ffs_hdr - FSP Flash Structure header + * + * @magic: Eye catcher/corruption detector + * @version: Version of the structure + * @size: Size of partition table (in block_size) + * @entry_size: Size of struct ffs_entry element (in bytes) + * @entry_count: Number of struct ffs_entry elements in @entries ar= ray + * @block_size: Size of block on device (in bytes) + * @block_count: Number of blocks on device + * @resvd: Reserved words for future use + * @checksum: Header checksum + * @entries: Pointer to array of partition entries + */ +struct ffs_hdr { + uint32_t magic; + uint32_t version; + uint32_t size; + uint32_t entry_size; + uint32_t entry_count; + uint32_t block_size; + uint32_t block_count; + uint32_t resvd[4]; + uint32_t checksum; + struct ffs_entry entries[]; +} __attribute__ ((packed)); + + +#endif /* FFS_H */ diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h index c3dd28643cae..14ec06038f3e 100644 --- a/include/hw/ppc/pnv_pnor.h +++ b/include/hw/ppc/pnv_pnor.h @@ -25,6 +25,12 @@ typedef struct PnvPnor { uint8_t *storage; uint32_t size; MemoryRegion mmio; + + uint32_t skiboot_addr; + uint32_t skiboot_size; } PnvPnor; =20 +extern int pnv_pnor_load_skiboot(PnvPnor *pnor, hwaddr addr, size_t max_si= ze, + Error **errp); + #endif /* _PPC_PNV_PNOR_H */ diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 7019273f1cdd..8cc792dee25d 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -637,6 +637,7 @@ static void pnv_init(MachineState *machine) char *chip_typename; DriveInfo *pnor =3D drive_get(IF_MTD, 0, 0); DeviceState *dev; + bool load_skiboot_from_pnor =3D false; =20 /* allocate RAM */ if (machine->ram_size < (1 * GiB)) { @@ -659,23 +660,33 @@ static void pnv_init(MachineState *machine) qdev_init_nofail(dev); pnv->pnor =3D PNV_PNOR(dev); =20 - /* load skiboot firmware */ + /* + * Try to load skiboot from pnor if no 'bios' was provided on the + * command line. skiboot will load the kernel and initramfs from + * the PNOR. + */ if (bios_name =3D=3D NULL) { bios_name =3D FW_FILE_NAME; + load_skiboot_from_pnor =3D !!pnor; } =20 - fw_filename =3D qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (!fw_filename) { - error_report("Could not find OPAL firmware '%s'", bios_name); - exit(1); - } + if (load_skiboot_from_pnor) { + pnv_pnor_load_skiboot(pnv->pnor, FW_LOAD_ADDR, FW_MAX_SIZE, + &error_fatal); + } else { + fw_filename =3D qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!fw_filename) { + error_report("Could not find OPAL firmware '%s'", bios_name); + exit(1); + } =20 - fw_size =3D load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE= ); - if (fw_size < 0) { - error_report("Could not load OPAL firmware '%s'", fw_filename); - exit(1); + fw_size =3D load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_= SIZE); + if (fw_size < 0) { + error_report("Could not load OPAL firmware '%s'", fw_filename); + exit(1); + } + g_free(fw_filename); } - g_free(fw_filename); =20 /* load kernel */ if (machine->kernel_filename) { diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index bfb1e92b0392..c29bf7eea197 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -14,8 +14,161 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/loader.h" +#include "hw/ppc/ffs.h" #include "hw/ppc/pnv_pnor.h" #include "hw/qdev-properties.h" +#include "libxz/xz.h" + +static uint32_t ffs_checksum(void *data, size_t size) +{ + uint32_t i, csum =3D 0; + + for (i =3D csum =3D 0; i < (size / 4); i++) { + csum ^=3D ((uint32_t *)data)[i]; + } + return csum; +} + +static int ffs_check_convert_header(struct ffs_hdr *dst, struct ffs_hdr *s= rc) +{ + dst->magic =3D be32_to_cpu(src->magic); + if (dst->magic !=3D FFS_MAGIC) { + return -1; + } + dst->version =3D be32_to_cpu(src->version); + if (dst->version !=3D FFS_VERSION_1) { + return -1; + } + if (ffs_checksum(src, FFS_HDR_SIZE) !=3D 0) { + return -1; + } + dst->size =3D be32_to_cpu(src->size); + dst->entry_size =3D be32_to_cpu(src->entry_size); + dst->entry_count =3D be32_to_cpu(src->entry_count); + dst->block_size =3D be32_to_cpu(src->block_size); + dst->block_count =3D be32_to_cpu(src->block_count); + + return 0; +} + +static int ffs_check_convert_entry(struct ffs_entry *dst, struct ffs_entry= *src) +{ + if (ffs_checksum(src, FFS_ENTRY_SIZE) !=3D 0) { + return -1; + } + + memcpy(dst->name, src->name, sizeof(dst->name)); + dst->base =3D be32_to_cpu(src->base); + dst->size =3D be32_to_cpu(src->size); + dst->pid =3D be32_to_cpu(src->pid); + dst->id =3D be32_to_cpu(src->id); + dst->type =3D be32_to_cpu(src->type); + dst->flags =3D be32_to_cpu(src->flags); + dst->actual =3D be32_to_cpu(src->actual); + dst->user.datainteg =3D be16_to_cpu(src->user.datainteg); + + return 0; +} + +static int decompress(void *dst, size_t dst_size, void *src, size_t src_si= ze, + Error **errp) +{ + struct xz_dec *s; + struct xz_buf b; + int ret =3D 0; + + /* Initialize the xz library first */ + xz_crc32_init(); + s =3D xz_dec_init(XZ_SINGLE, 0); + if (!s) { + error_report("failed to initialize xz"); + return -1; + } + + b.in =3D src; + b.in_pos =3D 0; + b.in_size =3D src_size; + b.out =3D dst; + b.out_pos =3D 0; + b.out_size =3D dst_size; + + /* Start decompressing */ + ret =3D xz_dec_run(s, &b); + if (ret !=3D XZ_STREAM_END) { + error_setg(errp, "failed to decompress : %d", ret); + ret =3D -1; + } else { + ret =3D 0; + } + + /* Clean up memory */ + xz_dec_end(s); + return ret; +} + +int pnv_pnor_load_skiboot(PnvPnor *s, hwaddr addr, size_t max_size, + Error **errp) +{ + int rc; + void *buffer =3D g_malloc0(max_size); + + rc =3D decompress(buffer, max_size, &s->storage[s->skiboot_addr], + s->skiboot_size, errp); + if (rc =3D=3D 0) { + rom_add_blob_fixed("pnor.skiboot", buffer, max_size, addr); + } + g_free(buffer); + return rc; +} + +#define SECUREBOOT_HEADER_MAGIC 0x17082011 +#define SECUREBOOT_HEADER_SIZE 4096 + +static void pnv_pnor_find_skiboot(PnvPnor *s, Error **errp) +{ + uint8_t *storage =3D s->storage; + struct ffs_hdr hdr; + struct ffs_entry ent; + uint32_t magic; + uint32_t i; + int rc; + + rc =3D ffs_check_convert_header(&hdr, (struct ffs_hdr *) storage); + if (rc) { + error_setg(errp, "bad header"); + return; + } + + for (i =3D 0; i < hdr.entry_count; i++) { + uint32_t esize =3D hdr.entry_size; + uint32_t offset =3D FFS_HDR_SIZE + i * esize; + struct ffs_entry *src_ent =3D (struct ffs_entry *)(storage + offse= t); + + rc =3D ffs_check_convert_entry(&ent, src_ent); + if (rc) { + error_report("bad partition entry %d", i); + continue; + } + + if (strcmp("PAYLOAD", ent.name)) { + continue; + } + + s->skiboot_addr =3D ent.base * 0x1000, + s->skiboot_size =3D ent.size * 0x1000; + + /* Check for secure boot header */ + magic =3D be32_to_cpu(*(uint32_t *)&s->storage[s->skiboot_addr]); + if (magic =3D=3D SECUREBOOT_HEADER_MAGIC) { + s->skiboot_addr +=3D SECUREBOOT_HEADER_SIZE; + s->skiboot_size -=3D SECUREBOOT_HEADER_SIZE; + } + + return; + } + + error_setg(errp, "pnv_pnor: no skiboot partition !?"); +} =20 static uint64_t pnv_pnor_read(void *opaque, hwaddr addr, unsigned size) { @@ -97,6 +250,9 @@ static void pnv_pnor_realize(DeviceState *dev, Error **e= rrp) error_setg(errp, "failed to read the initial flash content"); return; } + + /* Read partitions to validate contents */ + pnv_pnor_find_skiboot(s, errp); } else { s->storage =3D blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); --=20 2.21.0