diff options
author | Karel Kočí <cynerd@email.cz> | 2022-09-27 17:09:49 +0200 |
---|---|---|
committer | Karel Kočí <cynerd@email.cz> | 2022-09-27 17:09:49 +0200 |
commit | 213042bba186b995bc2f25c8c2d06a9652177fa3 (patch) | |
tree | f8a12ada9ea2a95e400802e4a6451be7f1178ef7 /nixos/modules/kernel-patches | |
parent | ff1073d03d303d3b15e66d03dc4a5a479a387fa7 (diff) | |
download | nixturris-213042bba186b995bc2f25c8c2d06a9652177fa3.tar.gz nixturris-213042bba186b995bc2f25c8c2d06a9652177fa3.tar.bz2 nixturris-213042bba186b995bc2f25c8c2d06a9652177fa3.zip |
Import Turris OS kernel patches
Diffstat (limited to 'nixos/modules/kernel-patches')
99 files changed, 14108 insertions, 327 deletions
diff --git a/nixos/modules/kernel-patches/0001-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/nixos/modules/kernel-patches/0001-net-dsa-mv88e6xxx-disable-ATU-violation.patch new file mode 100644 index 0000000..916899c --- /dev/null +++ b/nixos/modules/kernel-patches/0001-net-dsa-mv88e6xxx-disable-ATU-violation.patch @@ -0,0 +1,26 @@ +From f93fc8ab929406310384688f9d86cf9c7f3ebcc6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= <cynerd@email.cz> +Date: Tue, 27 Sep 2022 15:57:12 +0200 +Subject: [PATCH 01/96] net/dsa/mv88e6xxx: disable ATU violation + +--- + drivers/net/dsa/mv88e6xxx/chip.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index 0b49d243e00b..74f6db6bb804 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -3425,6 +3425,9 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) + else + reg = 1 << port; + ++ /* Disable ATU member violation interrupt */ ++ reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG; ++ + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, + reg); + if (err) +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0002-dt-bindings-Add-slot-power-limit-milliwatt-PCIe-port.patch b/nixos/modules/kernel-patches/0002-dt-bindings-Add-slot-power-limit-milliwatt-PCIe-port.patch new file mode 100644 index 0000000..7bd4e76 --- /dev/null +++ b/nixos/modules/kernel-patches/0002-dt-bindings-Add-slot-power-limit-milliwatt-PCIe-port.patch @@ -0,0 +1,46 @@ +From 368886a6626e6884d029ba4fe0975e9dc6499750 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sun, 31 Oct 2021 16:07:05 +0100 +Subject: [PATCH 02/96] dt-bindings: Add 'slot-power-limit-milliwatt' PCIe port + property +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This property specifies slot power limit in mW unit. It is a form-factor +and board specific value and must be initialized by hardware. + +Some PCIe controllers delegate this work to software to allow hardware +flexibility and therefore this property basically specifies what should +host bridge program into PCIe Slot Capabilities registers. + +The property needs to be specified in mW unit instead of the special format +defined by Slot Capabilities (which encodes scaling factor or different +unit). Host drivers should convert the value from mW to needed format. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + Documentation/devicetree/bindings/pci/pci.txt | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/Documentation/devicetree/bindings/pci/pci.txt b/Documentation/devicetree/bindings/pci/pci.txt +index 6a8f2874a24d..b0cc133ed00d 100644 +--- a/Documentation/devicetree/bindings/pci/pci.txt ++++ b/Documentation/devicetree/bindings/pci/pci.txt +@@ -32,6 +32,12 @@ driver implementation may support the following properties: + root port to downstream device and host bridge drivers can do programming + which depends on CLKREQ signal existence. For example, programming root port + not to advertise ASPM L1 Sub-States support if there is no CLKREQ signal. ++- slot-power-limit-milliwatt: ++ If present, this property specifies slot power limit in milliwatts. Host ++ drivers can parse this property and use it for programming Root Port or host ++ bridge, or for composing and sending PCIe Set_Slot_Power_Limit messages ++ through the Root Port or host bridge when transitioning PCIe link from a ++ non-DL_Up Status to a DL_Up Status. + + PCI-PCI Bridge properties + ------------------------- +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0003-PCI-pci-bridge-emul-Set-position-of-PCI-capabilities.patch b/nixos/modules/kernel-patches/0003-PCI-pci-bridge-emul-Set-position-of-PCI-capabilities.patch new file mode 100644 index 0000000..314d47f --- /dev/null +++ b/nixos/modules/kernel-patches/0003-PCI-pci-bridge-emul-Set-position-of-PCI-capabilities.patch @@ -0,0 +1,164 @@ +From 5e75f896d16aa9f4fffa3c8747d948c0f0afa225 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sun, 3 Jul 2022 12:40:13 +0200 +Subject: [PATCH 03/96] PCI: pci-bridge-emul: Set position of PCI capabilities + to real HW value +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mvebu and aardvark HW have PCIe capabilities on different offset in PCI +config space. Extend pci-bridge-emul.c code to allow setting custom driver +custom value where PCIe capabilities starts. + +With this change PCIe capabilities of both drivers are reported at the same +location as where they are reported by U-Boot - in their real HW offset. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 1 + + drivers/pci/controller/pci-mvebu.c | 1 + + drivers/pci/pci-bridge-emul.c | 48 +++++++++++++++++---------- + drivers/pci/pci-bridge-emul.h | 2 ++ + 4 files changed, 35 insertions(+), 17 deletions(-) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 62db476a8651..3767b25769bc 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -999,6 +999,7 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + bridge->pcie_conf.rootcap = cpu_to_le16(PCI_EXP_RTCAP_CRSVIS); + + bridge->has_pcie = true; ++ bridge->pcie_start = PCIE_CORE_PCIEXP_CAP; + bridge->data = pcie; + bridge->ops = &advk_pci_bridge_emul_ops; + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index c1ffdb06c971..1589b0ba5038 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -946,6 +946,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge->subsystem_vendor_id = ssdev_id & 0xffff; + bridge->subsystem_id = ssdev_id >> 16; + bridge->has_pcie = true; ++ bridge->pcie_start = PCIE_CAP_PCIEXP; + bridge->data = port; + bridge->ops = &mvebu_pci_bridge_emul_ops; + +diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c +index 9c2ca28e3ecf..9334b2dd4764 100644 +--- a/drivers/pci/pci-bridge-emul.c ++++ b/drivers/pci/pci-bridge-emul.c +@@ -22,11 +22,7 @@ + + #define PCI_BRIDGE_CONF_END PCI_STD_HEADER_SIZEOF + #define PCI_CAP_SSID_SIZEOF (PCI_SSVID_DEVICE_ID + 2) +-#define PCI_CAP_SSID_START PCI_BRIDGE_CONF_END +-#define PCI_CAP_SSID_END (PCI_CAP_SSID_START + PCI_CAP_SSID_SIZEOF) + #define PCI_CAP_PCIE_SIZEOF (PCI_EXP_SLTSTA2 + 2) +-#define PCI_CAP_PCIE_START PCI_CAP_SSID_END +-#define PCI_CAP_PCIE_END (PCI_CAP_PCIE_START + PCI_CAP_PCIE_SIZEOF) + + /** + * struct pci_bridge_reg_behavior - register bits behaviors +@@ -324,7 +320,7 @@ pci_bridge_emul_read_ssid(struct pci_bridge_emul *bridge, int reg, u32 *value) + switch (reg) { + case PCI_CAP_LIST_ID: + *value = PCI_CAP_ID_SSVID | +- (bridge->has_pcie ? (PCI_CAP_PCIE_START << 8) : 0); ++ ((bridge->pcie_start > bridge->ssid_start) ? (bridge->pcie_start << 8) : 0); + return PCI_BRIDGE_EMUL_HANDLED; + + case PCI_SSVID_VENDOR_ID: +@@ -365,18 +361,33 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, + if (!bridge->pci_regs_behavior) + return -ENOMEM; + +- if (bridge->subsystem_vendor_id) +- bridge->conf.capabilities_pointer = PCI_CAP_SSID_START; +- else if (bridge->has_pcie) +- bridge->conf.capabilities_pointer = PCI_CAP_PCIE_START; +- else +- bridge->conf.capabilities_pointer = 0; ++ /* If ssid_start and pcie_start were not specified then choose the lowest possible value. */ ++ if (!bridge->ssid_start && !bridge->pcie_start) { ++ if (bridge->subsystem_vendor_id) ++ bridge->ssid_start = PCI_BRIDGE_CONF_END; ++ if (bridge->has_pcie) ++ bridge->pcie_start = bridge->ssid_start + PCI_CAP_SSID_SIZEOF; ++ } else if (!bridge->ssid_start && bridge->subsystem_vendor_id) { ++ if (bridge->pcie_start - PCI_BRIDGE_CONF_END >= PCI_CAP_SSID_SIZEOF) ++ bridge->ssid_start = PCI_BRIDGE_CONF_END; ++ else ++ bridge->ssid_start = bridge->pcie_start + PCI_CAP_PCIE_SIZEOF; ++ } else if (!bridge->pcie_start && bridge->has_pcie) { ++ if (bridge->ssid_start - PCI_BRIDGE_CONF_END >= PCI_CAP_PCIE_SIZEOF) ++ bridge->pcie_start = PCI_BRIDGE_CONF_END; ++ else ++ bridge->pcie_start = bridge->ssid_start + PCI_CAP_SSID_SIZEOF; ++ } ++ ++ bridge->conf.capabilities_pointer = min(bridge->ssid_start, bridge->pcie_start); + + if (bridge->conf.capabilities_pointer) + bridge->conf.status |= cpu_to_le16(PCI_STATUS_CAP_LIST); + + if (bridge->has_pcie) { + bridge->pcie_conf.cap_id = PCI_CAP_ID_EXP; ++ bridge->pcie_conf.next = (bridge->ssid_start > bridge->pcie_start) ? ++ bridge->ssid_start : 0; + bridge->pcie_conf.cap |= cpu_to_le16(PCI_EXP_TYPE_ROOT_PORT << 4); + bridge->pcie_cap_regs_behavior = + kmemdup(pcie_cap_regs_behavior, +@@ -459,15 +470,17 @@ int pci_bridge_emul_conf_read(struct pci_bridge_emul *bridge, int where, + read_op = bridge->ops->read_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; +- } else if (reg >= PCI_CAP_SSID_START && reg < PCI_CAP_SSID_END && bridge->subsystem_vendor_id) { ++ } else if (reg >= bridge->ssid_start && reg < bridge->ssid_start + PCI_CAP_SSID_SIZEOF && ++ bridge->subsystem_vendor_id) { + /* Emulated PCI Bridge Subsystem Vendor ID capability */ +- reg -= PCI_CAP_SSID_START; ++ reg -= bridge->ssid_start; + read_op = pci_bridge_emul_read_ssid; + cfgspace = NULL; + behavior = NULL; +- } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) { ++ } else if (reg >= bridge->pcie_start && reg < bridge->pcie_start + PCI_CAP_PCIE_SIZEOF && ++ bridge->has_pcie) { + /* Our emulated PCIe capability */ +- reg -= PCI_CAP_PCIE_START; ++ reg -= bridge->pcie_start; + read_op = bridge->ops->read_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; +@@ -538,9 +551,10 @@ int pci_bridge_emul_conf_write(struct pci_bridge_emul *bridge, int where, + write_op = bridge->ops->write_base; + cfgspace = (__le32 *) &bridge->conf; + behavior = bridge->pci_regs_behavior; +- } else if (reg >= PCI_CAP_PCIE_START && reg < PCI_CAP_PCIE_END && bridge->has_pcie) { ++ } else if (reg >= bridge->pcie_start && reg < bridge->pcie_start + PCI_CAP_PCIE_SIZEOF && ++ bridge->has_pcie) { + /* Our emulated PCIe capability */ +- reg -= PCI_CAP_PCIE_START; ++ reg -= bridge->pcie_start; + write_op = bridge->ops->write_pcie; + cfgspace = (__le32 *) &bridge->pcie_conf; + behavior = bridge->pcie_cap_regs_behavior; +diff --git a/drivers/pci/pci-bridge-emul.h b/drivers/pci/pci-bridge-emul.h +index 71392b67471d..2a0e59c7f0d9 100644 +--- a/drivers/pci/pci-bridge-emul.h ++++ b/drivers/pci/pci-bridge-emul.h +@@ -131,6 +131,8 @@ struct pci_bridge_emul { + struct pci_bridge_reg_behavior *pci_regs_behavior; + struct pci_bridge_reg_behavior *pcie_cap_regs_behavior; + void *data; ++ u8 pcie_start; ++ u8 ssid_start; + bool has_pcie; + u16 subsystem_vendor_id; + u16 subsystem_id; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0004-PCI-mvebu-Use-devm_request_irq-for-registering-inter.patch b/nixos/modules/kernel-patches/0004-PCI-mvebu-Use-devm_request_irq-for-registering-inter.patch new file mode 100644 index 0000000..b985bfe --- /dev/null +++ b/nixos/modules/kernel-patches/0004-PCI-mvebu-Use-devm_request_irq-for-registering-inter.patch @@ -0,0 +1,99 @@ +From fdafc999a4a33e028b52fba4c1378766b7722cab Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 24 May 2022 13:57:37 +0200 +Subject: [PATCH 04/96] PCI: mvebu: Use devm_request_irq() for registering + interrupt handler +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Same as in commit a3b69dd0ad62 ("Revert "PCI: aardvark: Rewrite IRQ code to +chained IRQ handler"") for pci-aardvark driver, use devm_request_irq() +instead of chained IRQ handler in pci-mvebu.c driver. + +This change fixes affinity support and allows to pin interrupts from +different PCIe controllers to different CPU cores. + +Fixes: ec075262648f ("PCI: mvebu: Implement support for legacy INTx interrupts") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 30 +++++++++++++++++------------- + 1 file changed, 17 insertions(+), 13 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 1589b0ba5038..73d8fb6952e6 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -1090,16 +1090,13 @@ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) + return 0; + } + +-static void mvebu_pcie_irq_handler(struct irq_desc *desc) ++static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg) + { +- struct mvebu_pcie_port *port = irq_desc_get_handler_data(desc); +- struct irq_chip *chip = irq_desc_get_chip(desc); ++ struct mvebu_pcie_port *port = arg; + struct device *dev = &port->pcie->pdev->dev; + u32 cause, unmask, status; + int i; + +- chained_irq_enter(chip, desc); +- + cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF); + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); + status = cause & unmask; +@@ -1113,7 +1110,7 @@ static void mvebu_pcie_irq_handler(struct irq_desc *desc) + dev_err_ratelimited(dev, "unexpected INT%c IRQ\n", (char)i+'A'); + } + +- chained_irq_exit(chip, desc); ++ return status ? IRQ_HANDLED : IRQ_NONE; + } + + static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +@@ -1573,9 +1570,20 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + mvebu_pcie_powerdown(port); + continue; + } +- irq_set_chained_handler_and_data(irq, +- mvebu_pcie_irq_handler, +- port); ++ ++ ret = devm_request_irq(dev, irq, mvebu_pcie_irq_handler, ++ IRQF_SHARED | IRQF_NO_THREAD, ++ port->name, port); ++ if (ret) { ++ dev_err(dev, "%s: cannot register interrupt handler: %d\n", ++ port->name, ret); ++ irq_domain_remove(port->intx_irq_domain); ++ pci_bridge_emul_cleanup(&port->bridge); ++ devm_iounmap(dev, port->base); ++ port->base = NULL; ++ mvebu_pcie_powerdown(port); ++ continue; ++ } + } + + /* +@@ -1682,7 +1690,6 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; +- int irq = port->intx_irq; + + if (!port->base) + continue; +@@ -1698,9 +1705,6 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + /* Clear all interrupt causes. */ + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF); + +- if (irq > 0) +- irq_set_chained_handler_and_data(irq, NULL, NULL); +- + /* Remove IRQ domains. */ + if (port->intx_irq_domain) + irq_domain_remove(port->intx_irq_domain); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0005-PCI-mvebu-Dispose-INTx-irqs-prior-to-removing-INTx-d.patch b/nixos/modules/kernel-patches/0005-PCI-mvebu-Dispose-INTx-irqs-prior-to-removing-INTx-d.patch new file mode 100644 index 0000000..b5d1651 --- /dev/null +++ b/nixos/modules/kernel-patches/0005-PCI-mvebu-Dispose-INTx-irqs-prior-to-removing-INTx-d.patch @@ -0,0 +1,47 @@ +From c9259b56b015aee9c5984cba54912e78d2abf74f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sat, 9 Jul 2022 16:12:40 +0200 +Subject: [PATCH 05/96] PCI: mvebu: Dispose INTx irqs prior to removing INTx + domain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Documentation for irq_domain_remove() says that all mapping within the +domain must be disposed prior to domain remove. + +Currently INTx irqs are not disposed in pci-mvebu.c device unbind callback +which cause that kernel crashes after unloading driver and trying to read +/sys/kernel/debug/irq/irqs/<num> or /proc/interrupts. + +Fixes: ec075262648f ("PCI: mvebu: Implement support for legacy INTx interrupts") +Reported-by: Hajo Noerenberg <hajo-linux-bugzilla@noerenberg.de> +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 73d8fb6952e6..159167ebb2de 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -1706,8 +1706,15 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF); + + /* Remove IRQ domains. */ +- if (port->intx_irq_domain) ++ if (port->intx_irq_domain) { ++ int virq, j; ++ for (j = 0; j < PCI_NUM_INTX; j++) { ++ virq = irq_find_mapping(port->intx_irq_domain, j); ++ if (virq > 0) ++ irq_dispose_mapping(virq); ++ } + irq_domain_remove(port->intx_irq_domain); ++ } + + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0006-PCI-Assign-PCI-domain-by-ida_alloc.patch b/nixos/modules/kernel-patches/0006-PCI-Assign-PCI-domain-by-ida_alloc.patch new file mode 100644 index 0000000..27911fa --- /dev/null +++ b/nixos/modules/kernel-patches/0006-PCI-Assign-PCI-domain-by-ida_alloc.patch @@ -0,0 +1,215 @@ +From 18001a289af20564c1f17fb5bcc261c02bbb70d2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sat, 2 Jul 2022 21:37:51 +0200 +Subject: [PATCH 06/96] PCI: Assign PCI domain by ida_alloc() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Replace assignment of PCI domain from atomic_inc_return() to ida_alloc(). + +Use two IDAs, one for static domain allocations (those which are defined in +device tree) and second for dynamic allocations (all other). + +During removal of root bus / host bridge release also allocated domain id. +So released id can be reused again, for example in situation when +dynamically loading and unloading native PCI host bridge drivers. + +This change also allows to mix static device tree assignment and dynamic by +kernel as all static allocations are reserved in dynamic pool. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/pci.c | 103 +++++++++++++++++++++++++------------------ + drivers/pci/probe.c | 5 +++ + drivers/pci/remove.c | 6 +++ + include/linux/pci.h | 1 + + 4 files changed, 72 insertions(+), 43 deletions(-) + +diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c +index cfaf40a540a8..34fdcee6634a 100644 +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -6762,60 +6762,70 @@ static void pci_no_domains(void) + } + + #ifdef CONFIG_PCI_DOMAINS_GENERIC +-static atomic_t __domain_nr = ATOMIC_INIT(-1); ++static DEFINE_IDA(pci_domain_nr_static_ida); ++static DEFINE_IDA(pci_domain_nr_dynamic_ida); + +-static int pci_get_new_domain_nr(void) ++static void of_pci_reserve_static_domain_nr(void) + { +- return atomic_inc_return(&__domain_nr); ++ struct device_node *np; ++ int domain_nr; ++ ++ for_each_node_by_type(np, "pci") { ++ domain_nr = of_get_pci_domain_nr(np); ++ if (domain_nr < 0) ++ continue; ++ /* ++ * Permanently allocate domain_nr in dynamic_ida ++ * to prevent it from dynamic allocation. ++ */ ++ ida_alloc_range(&pci_domain_nr_dynamic_ida, ++ domain_nr, domain_nr, GFP_KERNEL); ++ } + } + + static int of_pci_bus_find_domain_nr(struct device *parent) + { +- static int use_dt_domains = -1; +- int domain = -1; ++ static bool static_domains_reserved = false; ++ int domain_nr; + +- if (parent) +- domain = of_get_pci_domain_nr(parent->of_node); ++ /* On the first call scan device tree for static allocations. */ ++ if (!static_domains_reserved) { ++ of_pci_reserve_static_domain_nr(); ++ static_domains_reserved = true; ++ } ++ ++ if (parent) { ++ /* ++ * If domain is in DT then allocate it in static IDA. ++ * This prevent duplicate static allocations in case ++ * of errors in DT. ++ */ ++ domain_nr = of_get_pci_domain_nr(parent->of_node); ++ if (domain_nr >= 0) ++ return ida_alloc_range(&pci_domain_nr_static_ida, ++ domain_nr, domain_nr, ++ GFP_KERNEL); ++ } + + /* +- * Check DT domain and use_dt_domains values. +- * +- * If DT domain property is valid (domain >= 0) and +- * use_dt_domains != 0, the DT assignment is valid since this means +- * we have not previously allocated a domain number by using +- * pci_get_new_domain_nr(); we should also update use_dt_domains to +- * 1, to indicate that we have just assigned a domain number from +- * DT. +- * +- * If DT domain property value is not valid (ie domain < 0), and we +- * have not previously assigned a domain number from DT +- * (use_dt_domains != 1) we should assign a domain number by +- * using the: +- * +- * pci_get_new_domain_nr() +- * +- * API and update the use_dt_domains value to keep track of method we +- * are using to assign domain numbers (use_dt_domains = 0). +- * +- * All other combinations imply we have a platform that is trying +- * to mix domain numbers obtained from DT and pci_get_new_domain_nr(), +- * which is a recipe for domain mishandling and it is prevented by +- * invalidating the domain value (domain = -1) and printing a +- * corresponding error. ++ * If domain was not specified in DT then choose free id from dynamic ++ * allocations. All domain numbers from DT are permanently in dynamic ++ * allocations to prevent assigning them to other DT nodes without ++ * static domain. + */ +- if (domain >= 0 && use_dt_domains) { +- use_dt_domains = 1; +- } else if (domain < 0 && use_dt_domains != 1) { +- use_dt_domains = 0; +- domain = pci_get_new_domain_nr(); +- } else { +- if (parent) +- pr_err("Node %pOF has ", parent->of_node); +- pr_err("Inconsistent \"linux,pci-domain\" property in DT\n"); +- domain = -1; +- } ++ return ida_alloc(&pci_domain_nr_dynamic_ida, GFP_KERNEL); ++} + +- return domain; ++static void of_pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent) ++{ ++ if (bus->domain_nr < 0) ++ return; ++ ++ /* Release domain from ida in which was it allocated. */ ++ if (of_get_pci_domain_nr(parent->of_node) == bus->domain_nr) ++ ida_free(&pci_domain_nr_static_ida, bus->domain_nr); ++ else ++ ida_free(&pci_domain_nr_dynamic_ida, bus->domain_nr); + } + + int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) +@@ -6823,6 +6833,13 @@ int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent) + return acpi_disabled ? of_pci_bus_find_domain_nr(parent) : + acpi_pci_bus_find_domain_nr(bus); + } ++ ++void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent) ++{ ++ if (!acpi_disabled) ++ return; ++ of_pci_bus_release_domain_nr(bus, parent); ++} + #endif + + /** +diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c +index 17a969942d37..12092d238403 100644 +--- a/drivers/pci/probe.c ++++ b/drivers/pci/probe.c +@@ -906,6 +906,8 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) + bus->domain_nr = pci_bus_find_domain_nr(bus, parent); + else + bus->domain_nr = bridge->domain_nr; ++ if (bus->domain_nr < 0) ++ goto free; + #endif + + b = pci_find_bus(pci_domain_nr(bus), bridge->busnr); +@@ -1030,6 +1032,9 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) + device_del(&bridge->dev); + + free: ++#ifdef CONFIG_PCI_DOMAINS_GENERIC ++ pci_bus_release_domain_nr(bus, parent); ++#endif + kfree(bus); + return err; + } +diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c +index 4c54c75050dc..0145aef1b930 100644 +--- a/drivers/pci/remove.c ++++ b/drivers/pci/remove.c +@@ -160,6 +160,12 @@ void pci_remove_root_bus(struct pci_bus *bus) + pci_remove_bus(bus); + host_bridge->bus = NULL; + ++#ifdef CONFIG_PCI_DOMAINS_GENERIC ++ /* Release domain_nr if it was dynamically allocated */ ++ if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET) ++ pci_bus_release_domain_nr(bus, host_bridge->dev.parent); ++#endif ++ + /* remove the host bridge */ + device_del(&host_bridge->dev); + } +diff --git a/include/linux/pci.h b/include/linux/pci.h +index 81a57b498f22..6c7f27e62bcc 100644 +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1723,6 +1723,7 @@ static inline int acpi_pci_bus_find_domain_nr(struct pci_bus *bus) + { return 0; } + #endif + int pci_bus_find_domain_nr(struct pci_bus *bus, struct device *parent); ++void pci_bus_release_domain_nr(struct pci_bus *bus, struct device *parent); + #endif + + /* Some architectures require additional setup to direct VGA traffic */ +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0007-PCI-mvebu-Fix-endianity-when-accessing-pci-emul-brid.patch b/nixos/modules/kernel-patches/0007-PCI-mvebu-Fix-endianity-when-accessing-pci-emul-brid.patch new file mode 100644 index 0000000..566c793 --- /dev/null +++ b/nixos/modules/kernel-patches/0007-PCI-mvebu-Fix-endianity-when-accessing-pci-emul-brid.patch @@ -0,0 +1,68 @@ +From b788cf679463d8805bee225400edbd09b7d6bd0e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 12 Aug 2022 11:09:11 +0200 +Subject: [PATCH 07/96] PCI: mvebu: Fix endianity when accessing pci emul + bridge members +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PCI emul bridge members iolimitupper, iobaseupper, memlimit and membase are +of type __le16, so correctly access these members via le16_to_cpu() macros. + +Fixes: 4ded69473adb ("PCI: mvebu: Propagate errors when updating PCI_IO_BASE and PCI_MEM_BASE registers") +Reported-by: kernel test robot <lkp@intel.com> +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 159167ebb2de..cacc78863915 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -523,7 +523,7 @@ static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) + + /* Are the new iobase/iolimit values invalid? */ + if (conf->iolimit < conf->iobase || +- conf->iolimitupper < conf->iobaseupper) ++ le16_to_cpu(conf->iolimitupper) < le16_to_cpu(conf->iobaseupper)) + return mvebu_pcie_set_window(port, port->io_target, port->io_attr, + &desired, &port->iowin); + +@@ -535,10 +535,10 @@ static int mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port) + * is the CPU address. + */ + desired.remap = ((conf->iobase & 0xF0) << 8) | +- (conf->iobaseupper << 16); ++ (le16_to_cpu(conf->iobaseupper) << 16); + desired.base = port->pcie->io.start + desired.remap; + desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) | +- (conf->iolimitupper << 16)) - ++ (le16_to_cpu(conf->iolimitupper) << 16)) - + desired.remap) + + 1; + +@@ -552,7 +552,7 @@ static int mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) + struct pci_bridge_emul_conf *conf = &port->bridge.conf; + + /* Are the new membase/memlimit values invalid? */ +- if (conf->memlimit < conf->membase) ++ if (le16_to_cpu(conf->memlimit) < le16_to_cpu(conf->membase)) + return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, + &desired, &port->memwin); + +@@ -562,8 +562,8 @@ static int mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port) + * window to setup, according to the PCI-to-PCI bridge + * specifications. + */ +- desired.base = ((conf->membase & 0xFFF0) << 16); +- desired.size = (((conf->memlimit & 0xFFF0) << 16) | 0xFFFFF) - ++ desired.base = ((le16_to_cpu(conf->membase) & 0xFFF0) << 16); ++ desired.size = (((le16_to_cpu(conf->memlimit) & 0xFFF0) << 16) | 0xFFFFF) - + desired.base + 1; + + return mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired, +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0007-pci-mvebu-time-out-reset-on-link-up.patch b/nixos/modules/kernel-patches/0007-pci-mvebu-time-out-reset-on-link-up.patch deleted file mode 100644 index b163995..0000000 --- a/nixos/modules/kernel-patches/0007-pci-mvebu-time-out-reset-on-link-up.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 2793db3bb1f859bde77b40fd27da1452a06daf8d Mon Sep 17 00:00:00 2001 -From: Russell King <rmk+kernel@armlinux.org.uk> -Date: Sat, 9 Jul 2016 10:58:16 +0100 -Subject: [PATCH 07/10] pci: mvebu: time out reset on link up - -If the port reports that the link is up while we are resetting, there's -little point in waiting for the full duration. - -Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> ---- - drivers/pci/controller/pci-mvebu.c | 20 ++++++++++++++------ - 1 file changed, 14 insertions(+), 6 deletions(-) - -diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c -index c1ffdb06c971..b02f8120c24b 100644 ---- a/drivers/pci/controller/pci-mvebu.c -+++ b/drivers/pci/controller/pci-mvebu.c -@@ -1415,6 +1415,7 @@ static int mvebu_pcie_powerup(struct mvebu_pcie_port *port) - - if (port->reset_gpio) { - u32 reset_udelay = PCI_PM_D3COLD_WAIT * 1000; -+ unsigned int i; - - of_property_read_u32(port->dn, "reset-delay-us", - &reset_udelay); -@@ -1422,7 +1423,13 @@ static int mvebu_pcie_powerup(struct mvebu_pcie_port *port) - udelay(100); - - gpiod_set_value_cansleep(port->reset_gpio, 0); -- msleep(reset_udelay / 1000); -+ for (i = 0; i < reset_udelay; i += 1000) { -+ if (mvebu_pcie_link_up(port)) -+ break; -+ msleep(1); -+ } -+ -+ printk("%s: reset completed in %dus\n", port->name, i); - } - - return 0; -@@ -1539,15 +1546,16 @@ static int mvebu_pcie_probe(struct platform_device *pdev) - if (!child) - continue; - -- ret = mvebu_pcie_powerup(port); -- if (ret < 0) -- continue; -- - port->base = mvebu_pcie_map_registers(pdev, child, port); - if (IS_ERR(port->base)) { - dev_err(dev, "%s: cannot map registers\n", port->name); - port->base = NULL; -- mvebu_pcie_powerdown(port); -+ continue; -+ } -+ -+ ret = mvebu_pcie_powerup(port); -+ if (ret < 0) { -+ port->base = NULL; - continue; - } - --- -2.37.2 - diff --git a/nixos/modules/kernel-patches/0008-ARM-dts-armada-38x-Fix-assigned-addresses-for-every-.patch b/nixos/modules/kernel-patches/0008-ARM-dts-armada-38x-Fix-assigned-addresses-for-every-.patch new file mode 100644 index 0000000..cdab261 --- /dev/null +++ b/nixos/modules/kernel-patches/0008-ARM-dts-armada-38x-Fix-assigned-addresses-for-every-.patch @@ -0,0 +1,76 @@ +From f64617ce6467532a4a2ebba14b1655e5a06535bb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 18 Aug 2022 00:03:45 +0200 +Subject: [PATCH 08/96] ARM: dts: armada-38x: Fix assigned-addresses for every + PCIe Root Port +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +BDF of resource in DT assigned-addresses property of Marvell PCIe Root Port +(PCI-to-PCI bridge) should match BDF in address part in that DT node name +as specified resource belongs to Marvell PCIe Root Port itself. + +Fixes: 0d3d96ab0059 ("ARM: mvebu: add Device Tree description of the Armada 380/385 SoCs") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-380.dtsi | 4 ++-- + arch/arm/boot/dts/armada-385.dtsi | 6 +++--- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-380.dtsi b/arch/arm/boot/dts/armada-380.dtsi +index cff1269f3fbf..7146cc8f082a 100644 +--- a/arch/arm/boot/dts/armada-380.dtsi ++++ b/arch/arm/boot/dts/armada-380.dtsi +@@ -79,7 +79,7 @@ pcie@1,0 { + /* x1 port */ + pcie@2,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x40000 0 0x2000>; ++ assigned-addresses = <0x82001000 0 0x40000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -98,7 +98,7 @@ pcie@2,0 { + /* x1 port */ + pcie@3,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>; ++ assigned-addresses = <0x82001800 0 0x44000 0 0x2000>; + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +diff --git a/arch/arm/boot/dts/armada-385.dtsi b/arch/arm/boot/dts/armada-385.dtsi +index 83392b92dae2..be8d607c59b2 100644 +--- a/arch/arm/boot/dts/armada-385.dtsi ++++ b/arch/arm/boot/dts/armada-385.dtsi +@@ -93,7 +93,7 @@ pcie1_intc: interrupt-controller { + /* x1 port */ + pcie2: pcie@2,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x40000 0 0x2000>; ++ assigned-addresses = <0x82001000 0 0x40000 0 0x2000>; + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -121,7 +121,7 @@ pcie2_intc: interrupt-controller { + /* x1 port */ + pcie3: pcie@3,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x44000 0 0x2000>; ++ assigned-addresses = <0x82001800 0 0x44000 0 0x2000>; + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +@@ -152,7 +152,7 @@ pcie3_intc: interrupt-controller { + */ + pcie4: pcie@4,0 { + device_type = "pci"; +- assigned-addresses = <0x82000800 0 0x48000 0 0x2000>; ++ assigned-addresses = <0x82002000 0 0x48000 0 0x2000>; + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0008-ARM-dts-mvebu-armada-385-turris-omnia-separate-dts-f.patch b/nixos/modules/kernel-patches/0008-ARM-dts-mvebu-armada-385-turris-omnia-separate-dts-f.patch deleted file mode 100644 index a292738..0000000 --- a/nixos/modules/kernel-patches/0008-ARM-dts-mvebu-armada-385-turris-omnia-separate-dts-f.patch +++ /dev/null @@ -1,213 +0,0 @@ -From 03ba17e25dd41d414c00ddde855a36464698d2e1 Mon Sep 17 00:00:00 2001 -From: Tomas Hlavacek <tmshlvck@gmail.com> -Date: Tue, 5 May 2020 20:40:24 +0200 -Subject: [PATCH 08/10] ARM: dts: mvebu: armada-385-turris-omnia: separate dts - for SFP and PHY -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The Turris Omnia board contains dual-personality ethernet NIC eth2 with -two operation modes: 1) SFP cage and 2) metalic 1000BASE-X PHY. -The differential pair carrying SGMII/1000BASE-X of eth2 is wired through -a multiplexor driven by the module-detect signal from the SFP cage. -The pin status can be read through I2C GPIO expander chip in userspace -when the sfp driver module is unloaded and / or in U-Boot prior to the -start of the kernel and the proper DTS file can be selected for the -(floolowing) boot. - -Split DTS for Turris Omnia (that does not have any support for SFP cage) -into three files: - armada-385-turris-omnia.dtsi - common base - armada-385-turris-omnia-sfp.dts - DT with the SFP configuration and -PHY disabled - armada-385-turris-omnia-phy.dts - DT with the PHY configuration and -SFP disabled - -Current DSA driver does not allow multiple CPU ports and Turris Omnia -has two RGMII iterfaces wired between CPU and DSA switch. -Disable the second CPU port until there is a suitable driver to use it. - -Signed-off-by: Tomas Hlavacek <tmshlvck@gmail.com> -Signed-off-by: Marek Behún <marek.behun@nic.cz> ---- - arch/arm/boot/dts/Makefile | 3 +- - .../boot/dts/armada-385-turris-omnia-phy.dts | 22 ++++++++++++ - .../boot/dts/armada-385-turris-omnia-sfp.dts | 23 ++++++++++++ - ...omnia.dts => armada-385-turris-omnia.dtsi} | 35 +++++++++++-------- - 4 files changed, 68 insertions(+), 15 deletions(-) - create mode 100644 arch/arm/boot/dts/armada-385-turris-omnia-phy.dts - create mode 100644 arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts - rename arch/arm/boot/dts/{armada-385-turris-omnia.dts => armada-385-turris-omnia.dtsi} (94%) - -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index 27eec8e670ec..f320ee8e2a00 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -1495,7 +1495,8 @@ dtb-$(CONFIG_MACH_ARMADA_38X) += \ - armada-385-linksys-rango.dtb \ - armada-385-linksys-shelby.dtb \ - armada-385-synology-ds116.dtb \ -- armada-385-turris-omnia.dtb \ -+ armada-385-turris-omnia-phy.dtb \ -+ armada-385-turris-omnia-sfp.dtb \ - armada-388-clearfog.dtb \ - armada-388-clearfog-base.dtb \ - armada-388-clearfog-pro.dtb \ -diff --git a/arch/arm/boot/dts/armada-385-turris-omnia-phy.dts b/arch/arm/boot/dts/armada-385-turris-omnia-phy.dts -new file mode 100644 -index 000000000000..706f6a2f8065 ---- /dev/null -+++ b/arch/arm/boot/dts/armada-385-turris-omnia-phy.dts -@@ -0,0 +1,22 @@ -+// SPDX-License-Identifier: (GPL-2.0 OR MIT) -+/* -+ * Device Tree file for the Turris Omnia -+ * -+ * Copyright (C) 2016 Uwe Kleine-König <uwe@kleine-koenig.org> -+ * Copyright (C) 2016-2019 Tomas Hlavacek <tmshlvkc@gmail.com> -+ * -+ * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf -+ */ -+ -+/dts-v1/; -+ -+#include "armada-385-turris-omnia.dtsi" -+ -+&phy1 { -+ status = "okay"; -+}; -+ -+ð2 { -+ phy-mode = "sgmii"; -+ phy = <&phy1>; -+}; -diff --git a/arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts b/arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts -new file mode 100644 -index 000000000000..b9f2b88834be ---- /dev/null -+++ b/arch/arm/boot/dts/armada-385-turris-omnia-sfp.dts -@@ -0,0 +1,23 @@ -+// SPDX-License-Identifier: (GPL-2.0 OR MIT) -+/* -+ * Device Tree file for the Turris Omnia -+ * -+ * Copyright (C) 2016 Uwe Kleine-König <uwe@kleine-koenig.org> -+ * Copyright (C) 2016-2019 Tomas Hlavacek <tmshlvkc@gmail.com> -+ * -+ * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf -+ */ -+ -+/dts-v1/; -+ -+#include "armada-385-turris-omnia.dtsi" -+ -+&sfp { -+ status = "okay"; -+}; -+ -+ð2 { -+ phy-mode = "sgmii"; -+ managed = "in-band-status"; -+ sfp = <&sfp>; -+}; -diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dtsi -similarity index 94% -rename from arch/arm/boot/dts/armada-385-turris-omnia.dts -rename to arch/arm/boot/dts/armada-385-turris-omnia.dtsi -index bcced203c612..58e3d86771db 100644 ---- a/arch/arm/boot/dts/armada-385-turris-omnia.dts -+++ b/arch/arm/boot/dts/armada-385-turris-omnia.dtsi -@@ -8,8 +8,6 @@ - * Schematic available at https://www.turris.cz/doc/_media/rtrom01-schema.pdf - */ - --/dts-v1/; -- - #include <dt-bindings/gpio/gpio.h> - #include <dt-bindings/input/input.h> - #include <dt-bindings/leds/common.h> -@@ -91,11 +89,11 @@ pcie@3,0 { - sfp: sfp { - compatible = "sff,sfp"; - i2c-bus = <&sfp_i2c>; -- tx-fault-gpios = <&pcawan 0 GPIO_ACTIVE_HIGH>; -- tx-disable-gpios = <&pcawan 1 GPIO_ACTIVE_HIGH>; -- rate-select0-gpios = <&pcawan 2 GPIO_ACTIVE_HIGH>; -- los-gpios = <&pcawan 3 GPIO_ACTIVE_HIGH>; -- mod-def0-gpios = <&pcawan 4 GPIO_ACTIVE_LOW>; -+ tx-fault-gpios = <&sfpgpio 0 GPIO_ACTIVE_HIGH>; -+ tx-disable-gpios = <&sfpgpio 1 GPIO_ACTIVE_HIGH>; -+ rate-select0-gpios = <&sfpgpio 2 GPIO_ACTIVE_HIGH>; -+ los-gpios = <&sfpgpio 3 GPIO_ACTIVE_HIGH>; -+ mod-def0-gpios = <&sfpgpio 4 GPIO_ACTIVE_LOW>; - maximum-power-milliwatt = <3000>; - - /* -@@ -147,7 +145,7 @@ fixed-link { - }; - }; - --/* WAN port */ -+/* WAN dual-personality port */ - ð2 { - /* - * eth2 is connected via a multiplexor to both the SFP cage and to -@@ -158,9 +156,8 @@ ð2 { - * is present, U-Boot has to enable the sfp node above, remove phy - * handle and add managed = "in-band-status" property. - */ -+ phys = <&comphy5 2>; - status = "okay"; -- phy-mode = "sgmii"; -- phy-handle = <&phy1>; - phys = <&comphy5 2>; - sfp = <&sfp>; - buffer-manager = <&bm>; -@@ -365,7 +362,7 @@ i2c@7 { - #size-cells = <0>; - reg = <7>; - -- pcawan: gpio@71 { -+ sfpgpio: gpio@71 { - /* - * GPIO expander for SFP+ signals and - * and phy irq -@@ -374,7 +371,7 @@ pcawan: gpio@71 { - reg = <0x71>; - - pinctrl-names = "default"; -- pinctrl-0 = <&pcawan_pins>; -+ pinctrl-0 = <&wanint_pins>; - - interrupt-parent = <&gpio1>; - interrupts = <14 IRQ_TYPE_LEVEL_LOW>; -@@ -454,13 +451,23 @@ fixed-link { - }; - }; - -- /* port 6 is connected to eth0 */ -+ ports@6 { -+ reg = <6>; -+ label = "cpu"; -+ ethernet = <ð0>; -+ phy-mode = "rgmii-id"; -+ -+ fixed-link { -+ speed = <1000>; -+ full-duplex; -+ }; -+ }; - }; - }; - }; - - &pinctrl { -- pcawan_pins: pcawan-pins { -+ wanint_pins: wanint-pins { - marvell,pins = "mpp46"; - marvell,function = "gpio"; - }; --- -2.37.2 - diff --git a/nixos/modules/kernel-patches/0009-dt-bindings-PCI-mvebu-Update-information-about-error.patch b/nixos/modules/kernel-patches/0009-dt-bindings-PCI-mvebu-Update-information-about-error.patch new file mode 100644 index 0000000..fbb3c6a --- /dev/null +++ b/nixos/modules/kernel-patches/0009-dt-bindings-PCI-mvebu-Update-information-about-error.patch @@ -0,0 +1,32 @@ +From d3bdf92b9373c1d8ce917de30afd1ac07b083360 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 2 Nov 2021 11:06:18 +0100 +Subject: [PATCH 09/96] dt-bindings: PCI: mvebu: Update information about error + interrupt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mvebu error interrupt is triggered by any non-intx event, which is mainly +some pcie error. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + Documentation/devicetree/bindings/pci/mvebu-pci.txt | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt +index 6d022a9d36ee..8f0bca42113f 100644 +--- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt ++++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt +@@ -83,6 +83,7 @@ and the following optional properties: + specified will default to 100ms, as required by the PCIe specification. + - interrupt-names: list of interrupt names, supported are: + - "intx" - interrupt line triggered by one of the legacy interrupt ++ - "error" - interrupt line triggered by any other event (non-intx) + - interrupts or interrupts-extended: List of the interrupt sources which + corresponding to the "interrupt-names". If non-empty then also additional + 'interrupt-controller' subnode must be defined. +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0010-PCI-mvebu-Implement-support-for-interrupts-on-emulat.patch b/nixos/modules/kernel-patches/0010-PCI-mvebu-Implement-support-for-interrupts-on-emulat.patch new file mode 100644 index 0000000..73ead93 --- /dev/null +++ b/nixos/modules/kernel-patches/0010-PCI-mvebu-Implement-support-for-interrupts-on-emulat.patch @@ -0,0 +1,437 @@ +From 1f840ec09a1bd8925decbc4917d701f6b93483b8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:40:17 +0200 +Subject: [PATCH 10/96] PCI: mvebu: Implement support for interrupts on + emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This adds support for PME and ERR interrupts reported by emulated bridge +(for PME and AER kernel drivers) via new Root Port irq chip as these +interrupts from PCIe Root Ports are handled by mvebu hardware completely +separately from INTx and MSI interrupts send by real PCIe devices. + +With this change, kernel PME and AER drivers start working as they can +acquire required interrupt lines (provided by mvebu rp virtual irq chip). + +Note that for this support, device tree files has to be properly adjusted +to provide "interrupts" or "interrupts-extended" property with error +interrupt source and "interrupt-names" property with "error" string. + +If device tree files do not provide these properties then driver would work +as before and would not provide interrupts on emulated bridge, like before. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 256 ++++++++++++++++++++++++++--- + 1 file changed, 237 insertions(+), 19 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index cacc78863915..56924b0a1969 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -56,8 +56,16 @@ + #define PCIE_CONF_DATA_OFF 0x18fc + #define PCIE_INT_CAUSE_OFF 0x1900 + #define PCIE_INT_UNMASK_OFF 0x1910 ++#define PCIE_INT_DET_COR BIT(8) ++#define PCIE_INT_DET_NONFATAL BIT(9) ++#define PCIE_INT_DET_FATAL BIT(10) ++#define PCIE_INT_ERR_FATAL BIT(16) ++#define PCIE_INT_ERR_NONFATAL BIT(17) ++#define PCIE_INT_ERR_COR BIT(18) + #define PCIE_INT_INTX(i) BIT(24+i) + #define PCIE_INT_PM_PME BIT(28) ++#define PCIE_INT_DET_MASK (PCIE_INT_DET_COR | PCIE_INT_DET_NONFATAL | PCIE_INT_DET_FATAL) ++#define PCIE_INT_ERR_MASK (PCIE_INT_ERR_FATAL | PCIE_INT_ERR_NONFATAL | PCIE_INT_ERR_COR) + #define PCIE_INT_ALL_MASK GENMASK(31, 0) + #define PCIE_CTRL_OFF 0x1a00 + #define PCIE_CTRL_X1_MODE 0x0001 +@@ -120,9 +128,12 @@ struct mvebu_pcie_port { + struct resource regs; + u8 slot_power_limit_value; + u8 slot_power_limit_scale; ++ struct irq_domain *rp_irq_domain; + struct irq_domain *intx_irq_domain; + raw_spinlock_t irq_lock; ++ int error_irq; + int intx_irq; ++ bool pme_pending; + }; + + static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) +@@ -321,9 +332,19 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + /* Clear all interrupt causes. */ + mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_CAUSE_OFF); + +- /* Check if "intx" interrupt was specified in DT. */ +- if (port->intx_irq > 0) +- return; ++ /* ++ * Unmask all error interrupts which are internally generated. ++ * They cannot be disabled by SERR# Enable bit in PCI Command register, ++ * see Figure 6-3: Pseudo Logic Diagram for Error Message Controls in ++ * PCIe base specification. ++ * Internally generated mvebu interrupts are reported via mvebu summary ++ * interrupt which requires "error" interrupt to be specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ unmask |= PCIE_INT_DET_MASK; ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } + + /* + * Fallback code when "intx" interrupt was not specified in DT: +@@ -335,10 +356,12 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + * performance penalty as every PCIe interrupt handler needs to be + * called when some interrupt is triggered. + */ +- unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); +- unmask |= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) | +- PCIE_INT_INTX(2) | PCIE_INT_INTX(3); +- mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ if (port->intx_irq <= 0) { ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ unmask |= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) | ++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3); ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } + } + + static struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie, +@@ -598,11 +621,16 @@ mvebu_pci_bridge_emul_base_conf_read(struct pci_bridge_emul *bridge, + case PCI_INTERRUPT_LINE: { + /* + * From the whole 32bit register we support reading from HW only +- * one bit: PCI_BRIDGE_CTL_BUS_RESET. ++ * two bits: PCI_BRIDGE_CTL_BUS_RESET and PCI_BRIDGE_CTL_SERR. + * Other bits are retrieved only from emulated config buffer. + */ + __le32 *cfgspace = (__le32 *)&bridge->conf; + u32 val = le32_to_cpu(cfgspace[PCI_INTERRUPT_LINE / 4]); ++ if ((mvebu_readl(port, PCIE_INT_UNMASK_OFF) & ++ PCIE_INT_ERR_MASK) == PCIE_INT_ERR_MASK) ++ val |= PCI_BRIDGE_CTL_SERR << 16; ++ else ++ val &= ~(PCI_BRIDGE_CTL_SERR << 16); + if (mvebu_readl(port, PCIE_CTRL_OFF) & PCIE_CTRL_MASTER_HOT_RESET) + val |= PCI_BRIDGE_CTL_BUS_RESET << 16; + else +@@ -670,6 +698,11 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + break; + } + ++ case PCI_EXP_RTCTL: ++ *value = (mvebu_readl(port, PCIE_INT_UNMASK_OFF) & ++ PCIE_INT_PM_PME) ? PCI_EXP_RTCTL_PMEIE : 0; ++ break; ++ + case PCI_EXP_RTSTA: + *value = mvebu_readl(port, PCIE_RC_RTSTA); + break; +@@ -775,6 +808,14 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, + break; + + case PCI_INTERRUPT_LINE: ++ if ((mask & (PCI_BRIDGE_CTL_SERR << 16)) && port->error_irq > 0) { ++ u32 unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ if (new & (PCI_BRIDGE_CTL_SERR << 16)) ++ unmask |= PCIE_INT_ERR_MASK; ++ else ++ unmask &= ~PCIE_INT_ERR_MASK; ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } + if (mask & (PCI_BRIDGE_CTL_BUS_RESET << 16)) { + u32 ctrl = mvebu_readl(port, PCIE_CTRL_OFF); + if (new & (PCI_BRIDGE_CTL_BUS_RESET << 16)) +@@ -833,10 +874,25 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + * PME Status bit in Root Status Register (PCIE_RC_RTSTA) + * is read-only and can be cleared only by writing 0b to the + * Interrupt Cause RW0C register (PCIE_INT_CAUSE_OFF). So +- * clear PME via Interrupt Cause. ++ * clear PME via Interrupt Cause and also set port->pme_pending ++ * variable to false value to start processing PME interrupts ++ * in interrupt handler again. + */ +- if (new & PCI_EXP_RTSTA_PME) ++ if (new & PCI_EXP_RTSTA_PME) { + mvebu_writel(port, ~PCIE_INT_PM_PME, PCIE_INT_CAUSE_OFF); ++ port->pme_pending = false; ++ } ++ break; ++ ++ case PCI_EXP_RTCTL: ++ if ((mask & PCI_EXP_RTCTL_PMEIE) && port->error_irq > 0) { ++ u32 unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ if (new & PCI_EXP_RTCTL_PMEIE) ++ unmask |= PCIE_INT_PM_PME; ++ else ++ unmask &= ~PCIE_INT_PM_PME; ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } + break; + + case PCI_EXP_DEVCTL2: +@@ -919,6 +975,14 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge_flags |= PCI_BRIDGE_EMUL_NO_IO_FORWARD; + } + ++ /* ++ * Interrupts on emulated bridge are supported only when "error" ++ * interrupt was specified in DT. Without it emulated bridge cannot ++ * emulate interrupts. ++ */ ++ if (port->error_irq > 0) ++ bridge->conf.intpin = PCI_INTERRUPT_INTA; ++ + /* + * Older mvebu hardware provides PCIe Capability structure only in + * version 1. New hardware provides it in version 2. +@@ -1065,6 +1129,26 @@ static const struct irq_domain_ops mvebu_pcie_intx_irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + }; + ++static struct irq_chip rp_irq_chip = { ++ .name = "mvebu-rp", ++}; ++ ++static int mvebu_pcie_rp_irq_map(struct irq_domain *h, ++ unsigned int virq, irq_hw_number_t hwirq) ++{ ++ struct mvebu_pcie_port *port = h->host_data; ++ ++ irq_set_chip_and_handler(virq, &rp_irq_chip, handle_simple_irq); ++ irq_set_chip_data(virq, port); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops mvebu_pcie_rp_irq_domain_ops = { ++ .map = mvebu_pcie_rp_irq_map, ++ .xlate = irq_domain_xlate_onecell, ++}; ++ + static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) + { + struct device *dev = &port->pcie->pdev->dev; +@@ -1087,10 +1171,72 @@ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) + return -ENOMEM; + } + ++ /* ++ * When "error" interrupt was not specified in DT then there is no ++ * support for interrupts on emulated root bridge. So skip following ++ * initialization. ++ */ ++ if (port->error_irq <= 0) ++ return 0; ++ ++ port->rp_irq_domain = irq_domain_add_linear(NULL, 1, ++ &mvebu_pcie_rp_irq_domain_ops, ++ port); ++ if (!port->rp_irq_domain) { ++ irq_domain_remove(port->intx_irq_domain); ++ dev_err(dev, "Failed to add Root Port IRQ domain for %s\n", port->name); ++ return -ENOMEM; ++ } ++ + return 0; + } + +-static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg) ++static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg) ++{ ++ struct mvebu_pcie_port *port = arg; ++ struct device *dev = &port->pcie->pdev->dev; ++ u32 cause, unmask, status; ++ ++ cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF); ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ status = cause & unmask; ++ ++ /* "error" interrupt handler does not process INTX interrupts */ ++ status &= ~(PCIE_INT_INTX(0) | PCIE_INT_INTX(1) | ++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3)); ++ ++ /* Process PME interrupt */ ++ if ((status & PCIE_INT_PM_PME) && !port->pme_pending) { ++ /* ++ * Do not clear PME interrupt bit in Cause Register as it ++ * invalidates also content of Root Status Register. Instead ++ * set port->pme_pending variable to true to indicate that ++ * next time PME interrupt should be ignored until variable ++ * is back to the false value. ++ */ ++ port->pme_pending = true; ++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(dev, "unhandled PME IRQ\n"); ++ } ++ ++ /* Process ERR interrupt */ ++ if (status & PCIE_INT_ERR_MASK) { ++ mvebu_writel(port, ~PCIE_INT_ERR_MASK, PCIE_INT_CAUSE_OFF); ++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(dev, "unhandled ERR IRQ\n"); ++ } ++ ++ /* Process local ERR interrupt */ ++ if (status & PCIE_INT_DET_MASK) { ++ mvebu_writel(port, ~PCIE_INT_DET_MASK, PCIE_INT_CAUSE_OFF); ++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(dev, "unhandled ERR IRQ\n"); ++ } ++ ++ return status ? IRQ_HANDLED : IRQ_NONE; ++} ++ ++static irqreturn_t mvebu_pcie_intx_irq_handler(int irq, void *arg) + { + struct mvebu_pcie_port *port = arg; + struct device *dev = &port->pcie->pdev->dev; +@@ -1101,6 +1247,10 @@ static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg) + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); + status = cause & unmask; + ++ /* "intx" interrupt handler process only INTX interrupts */ ++ status &= PCIE_INT_INTX(0) | PCIE_INT_INTX(1) | ++ PCIE_INT_INTX(2) | PCIE_INT_INTX(3); ++ + /* Process legacy INTx interrupts */ + for (i = 0; i < PCI_NUM_INTX; i++) { + if (!(status & PCIE_INT_INTX(i))) +@@ -1115,9 +1265,29 @@ static irqreturn_t mvebu_pcie_irq_handler(int irq, void *arg) + + static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) + { +- /* Interrupt support on mvebu emulated bridges is not implemented yet */ +- if (dev->bus->number == 0) +- return 0; /* Proper return code 0 == NO_IRQ */ ++ struct mvebu_pcie_port *port; ++ struct mvebu_pcie *pcie; ++ ++ if (dev->bus->number == 0) { ++ /* ++ * Each emulated root bridge for every mvebu port has its own ++ * Root Port irq chip and irq domain. Argument pin is the INTx ++ * pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) and hwirq for function ++ * irq_create_mapping() is indexed from zero. ++ */ ++ pcie = dev->bus->sysdata; ++ port = mvebu_pcie_find_port(pcie, dev->bus, PCI_DEVFN(slot, 0)); ++ if (!port) ++ return 0; /* Proper return code 0 == NO_IRQ */ ++ /* ++ * port->rp_irq_domain is available only when "error" interrupt ++ * was specified in DT. When is not available then interrupts ++ * for emulated root bridge are not provided. ++ */ ++ if (port->error_irq <= 0) ++ return 0; /* Proper return code 0 == NO_IRQ */ ++ return irq_create_mapping(port->rp_irq_domain, pin - 1); ++ } + + return of_irq_parse_and_map_pci(dev, slot, pin); + } +@@ -1326,6 +1496,21 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie, + port->name, child); + } + ++ /* ++ * Old DT bindings do not contain "error" interrupt ++ * so do not fail probing driver when interrupt does not exist. ++ */ ++ port->error_irq = of_irq_get_byname(child, "error"); ++ if (port->error_irq == -EPROBE_DEFER) { ++ ret = port->error_irq; ++ goto err; ++ } ++ if (port->error_irq <= 0) { ++ dev_warn(dev, "%s: interrupts on Root Port are unsupported, " ++ "%pOF does not contain error interrupt\n", ++ port->name, child); ++ } ++ + reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags); + if (reset_gpio == -EPROBE_DEFER) { + ret = reset_gpio; +@@ -1531,7 +1716,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + + for (i = 0; i < pcie->nports; i++) { + struct mvebu_pcie_port *port = &pcie->ports[i]; +- int irq = port->intx_irq; + + child = port->dn; + if (!child) +@@ -1559,7 +1743,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + continue; + } + +- if (irq > 0) { ++ if (port->error_irq > 0 || port->intx_irq > 0) { + ret = mvebu_pcie_init_irq_domain(port); + if (ret) { + dev_err(dev, "%s: cannot init irq domain\n", +@@ -1570,14 +1754,42 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + mvebu_pcie_powerdown(port); + continue; + } ++ } ++ ++ if (port->error_irq > 0) { ++ ret = devm_request_irq(dev, port->error_irq, ++ mvebu_pcie_error_irq_handler, ++ IRQF_SHARED | IRQF_NO_THREAD, ++ port->name, port); ++ if (ret) { ++ dev_err(dev, "%s: cannot register error interrupt handler: %d\n", ++ port->name, ret); ++ if (port->intx_irq_domain) ++ irq_domain_remove(port->intx_irq_domain); ++ if (port->rp_irq_domain) ++ irq_domain_remove(port->rp_irq_domain); ++ pci_bridge_emul_cleanup(&port->bridge); ++ devm_iounmap(dev, port->base); ++ port->base = NULL; ++ mvebu_pcie_powerdown(port); ++ continue; ++ } ++ } + +- ret = devm_request_irq(dev, irq, mvebu_pcie_irq_handler, ++ if (port->intx_irq > 0) { ++ ret = devm_request_irq(dev, port->intx_irq, ++ mvebu_pcie_intx_irq_handler, + IRQF_SHARED | IRQF_NO_THREAD, + port->name, port); + if (ret) { +- dev_err(dev, "%s: cannot register interrupt handler: %d\n", ++ dev_err(dev, "%s: cannot register intx interrupt handler: %d\n", + port->name, ret); +- irq_domain_remove(port->intx_irq_domain); ++ if (port->error_irq > 0) ++ devm_free_irq(dev, port->error_irq, port); ++ if (port->intx_irq_domain) ++ irq_domain_remove(port->intx_irq_domain); ++ if (port->rp_irq_domain) ++ irq_domain_remove(port->rp_irq_domain); + pci_bridge_emul_cleanup(&port->bridge); + devm_iounmap(dev, port->base); + port->base = NULL; +@@ -1715,6 +1927,12 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + } + irq_domain_remove(port->intx_irq_domain); + } ++ if (port->rp_irq_domain) { ++ int virq = irq_find_mapping(port->rp_irq_domain, 0); ++ if (virq > 0) ++ irq_dispose_mapping(virq); ++ irq_domain_remove(port->rp_irq_domain); ++ } + + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0010-Rename-device-tree-for-Omnia-back-as-that-is-what-is.patch b/nixos/modules/kernel-patches/0010-Rename-device-tree-for-Omnia-back-as-that-is-what-is.patch deleted file mode 100644 index befe186..0000000 --- a/nixos/modules/kernel-patches/0010-Rename-device-tree-for-Omnia-back-as-that-is-what-is.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0aa8841f0a0580490003204fb3cacb5043de6876 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= <cynerd@email.cz> -Date: Tue, 27 Sep 2022 10:19:39 +0200 -Subject: [PATCH 10/10] Rename device tree for Omnia back as that is what is - expected by U-Boot - ---- - arch/arm/boot/dts/Makefile | 2 +- - ...ada-385-turris-omnia-phy.dts => armada-385-turris-omnia.dts} | 0 - 2 files changed, 1 insertion(+), 1 deletion(-) - rename arch/arm/boot/dts/{armada-385-turris-omnia-phy.dts => armada-385-turris-omnia.dts} (100%) - -diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile -index f320ee8e2a00..5d82687e3b88 100644 ---- a/arch/arm/boot/dts/Makefile -+++ b/arch/arm/boot/dts/Makefile -@@ -1495,7 +1495,7 @@ dtb-$(CONFIG_MACH_ARMADA_38X) += \ - armada-385-linksys-rango.dtb \ - armada-385-linksys-shelby.dtb \ - armada-385-synology-ds116.dtb \ -- armada-385-turris-omnia-phy.dtb \ -+ armada-385-turris-omnia.dtb \ - armada-385-turris-omnia-sfp.dtb \ - armada-388-clearfog.dtb \ - armada-388-clearfog-base.dtb \ -diff --git a/arch/arm/boot/dts/armada-385-turris-omnia-phy.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts -similarity index 100% -rename from arch/arm/boot/dts/armada-385-turris-omnia-phy.dts -rename to arch/arm/boot/dts/armada-385-turris-omnia.dts --- -2.37.2 - diff --git a/nixos/modules/kernel-patches/0011-ARM-dts-armada-38x.dtsi-Add-node-for-MPIC-SoC-Error-.patch b/nixos/modules/kernel-patches/0011-ARM-dts-armada-38x.dtsi-Add-node-for-MPIC-SoC-Error-.patch new file mode 100644 index 0000000..b7b3e7b --- /dev/null +++ b/nixos/modules/kernel-patches/0011-ARM-dts-armada-38x.dtsi-Add-node-for-MPIC-SoC-Error-.patch @@ -0,0 +1,35 @@ +From ae6b14b03c3d42a1eb5c2ba5ae3ab5600c069f60 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 18 Apr 2022 00:39:52 +0200 +Subject: [PATCH 11/96] ARM: dts: armada-38x.dtsi: Add node for MPIC SoC Error + IRQ controller +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It is child of the MPIC IRQ controller. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-38x.dtsi | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi +index df3c8d1d8f64..099f167b65aa 100644 +--- a/arch/arm/boot/dts/armada-38x.dtsi ++++ b/arch/arm/boot/dts/armada-38x.dtsi +@@ -398,6 +398,11 @@ mpic: interrupt-controller@20a00 { + interrupt-controller; + msi-controller; + interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>; ++ ++ soc_err: interrupt-controller@20 { ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; + }; + + timer: timer@20300 { +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0012-PCI-pciehp-Enable-DLLSC-interrupt-only-if-supported.patch b/nixos/modules/kernel-patches/0012-PCI-pciehp-Enable-DLLSC-interrupt-only-if-supported.patch new file mode 100644 index 0000000..c71098a --- /dev/null +++ b/nixos/modules/kernel-patches/0012-PCI-pciehp-Enable-DLLSC-interrupt-only-if-supported.patch @@ -0,0 +1,139 @@ +From 306447b8079037762c67aabd749ed22e6bb892a4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 31 Mar 2021 15:12:50 +0200 +Subject: [PATCH 12/96] PCI: pciehp: Enable DLLSC interrupt only if supported +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Don't enable Data Link Layer State Changed interrupt if it isn't +supported. + +Data Link Layer Link Active Reporting Capable bit in Link Capabilities +register indicates if Data Link Layer State Changed Enable is supported. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/hotplug/pciehp_hpc.c | 32 ++++++++++++++++++++++++-------- + drivers/pci/hotplug/pnv_php.c | 13 +++++++++---- + 2 files changed, 33 insertions(+), 12 deletions(-) + +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index 040ae076ec0e..ef807c79b1b1 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -788,6 +788,7 @@ static int pciehp_poll(void *data) + static void pcie_enable_notification(struct controller *ctrl) + { + u16 cmd, mask; ++ u32 link_cap; + + /* + * TBD: Power fault detected software notification support. +@@ -800,12 +801,17 @@ static void pcie_enable_notification(struct controller *ctrl) + * next power fault detected interrupt was notified again. + */ + ++ pcie_capability_read_dword(ctrl_dev(ctrl), PCI_EXP_LNKCAP, &link_cap); ++ + /* +- * Always enable link events: thus link-up and link-down shall +- * always be treated as hotplug and unplug respectively. Enable +- * presence detect only if Attention Button is not present. +- */ +- cmd = PCI_EXP_SLTCTL_DLLSCE; ++ * Enable link events if their support is indicated in Link Capability ++ * register: thus link-up and link-down shall always be treated as ++ * hotplug and unplug respectively. Enable presence detect only if ++ * Attention Button is not present. ++ */ ++ cmd = 0; ++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC) ++ cmd |= PCI_EXP_SLTCTL_DLLSCE; + if (ATTN_BUTTN(ctrl)) + cmd |= PCI_EXP_SLTCTL_ABPE; + else +@@ -845,8 +851,13 @@ void pcie_clear_hotplug_events(struct controller *ctrl) + void pcie_enable_interrupt(struct controller *ctrl) + { + u16 mask; ++ u32 link_cap; + +- mask = PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; ++ pcie_capability_read_dword(ctrl_dev(ctrl), PCI_EXP_LNKCAP, &link_cap); ++ ++ mask = PCI_EXP_SLTCTL_HPIE; ++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC) ++ mask |= PCI_EXP_SLTCTL_DLLSCE; + pcie_write_cmd(ctrl, mask, mask); + } + +@@ -904,19 +915,24 @@ int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, bool probe) + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 stat_mask = 0, ctrl_mask = 0; ++ u32 link_cap; + int rc; + + if (probe) + return 0; + ++ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); ++ + down_write_nested(&ctrl->reset_lock, ctrl->depth); + + if (!ATTN_BUTTN(ctrl)) { + ctrl_mask |= PCI_EXP_SLTCTL_PDCE; + stat_mask |= PCI_EXP_SLTSTA_PDC; + } +- ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE; +- stat_mask |= PCI_EXP_SLTSTA_DLLSC; ++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC) { ++ ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE; ++ stat_mask |= PCI_EXP_SLTSTA_DLLSC; ++ } + + pcie_write_cmd(ctrl, 0, ctrl_mask); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, +diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c +index 881d420637bf..5c700d3a9009 100644 +--- a/drivers/pci/hotplug/pnv_php.c ++++ b/drivers/pci/hotplug/pnv_php.c +@@ -840,6 +840,7 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) + { + struct pci_dev *pdev = php_slot->pdev; + u32 broken_pdc = 0; ++ u32 link_cap; + u16 sts, ctrl; + int ret; + +@@ -874,17 +875,21 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) + return; + } + ++ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); ++ + /* Enable the interrupts */ + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl); + if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) { + ctrl &= ~PCI_EXP_SLTCTL_PDCE; +- ctrl |= (PCI_EXP_SLTCTL_HPIE | +- PCI_EXP_SLTCTL_DLLSCE); ++ ctrl |= PCI_EXP_SLTCTL_HPIE; + } else { + ctrl |= (PCI_EXP_SLTCTL_HPIE | +- PCI_EXP_SLTCTL_PDCE | +- PCI_EXP_SLTCTL_DLLSCE); ++ PCI_EXP_SLTCTL_PDCE); + } ++ if (link_cap & PCI_EXP_LNKCAP_DLLLARC) ++ ctrl |= PCI_EXP_SLTCTL_DLLSCE; ++ else ++ ctrl &= ~PCI_EXP_SLTCTL_DLLSCE; + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl); + + /* The interrupt is initialized successfully when @irq is valid */ +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0013-PCI-pciehp-Enable-Command-Completed-Interrupt-only-i.patch b/nixos/modules/kernel-patches/0013-PCI-pciehp-Enable-Command-Completed-Interrupt-only-i.patch new file mode 100644 index 0000000..f34c666 --- /dev/null +++ b/nixos/modules/kernel-patches/0013-PCI-pciehp-Enable-Command-Completed-Interrupt-only-i.patch @@ -0,0 +1,38 @@ +From 7cc3ad181a36378397c4cdb68d034b7c719c17ab Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 31 Mar 2021 15:14:29 +0200 +Subject: [PATCH 13/96] PCI: pciehp: Enable Command Completed Interrupt only if + supported +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The No Command Completed Support bit in the Slot Capabilities register +indicates whether Command Completed Interrupt Enable is unsupported. + +Enable this interrupt only in the case it is supported. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/hotplug/pciehp_hpc.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c +index ef807c79b1b1..a5199b312e74 100644 +--- a/drivers/pci/hotplug/pciehp_hpc.c ++++ b/drivers/pci/hotplug/pciehp_hpc.c +@@ -817,7 +817,9 @@ static void pcie_enable_notification(struct controller *ctrl) + else + cmd |= PCI_EXP_SLTCTL_PDCE; + if (!pciehp_poll_mode) +- cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE; ++ cmd |= PCI_EXP_SLTCTL_HPIE; ++ if (!pciehp_poll_mode && !NO_CMD_CMPL(ctrl)) ++ cmd |= PCI_EXP_SLTCTL_CCIE; + + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_PFDE | +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0014-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch b/nixos/modules/kernel-patches/0014-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch new file mode 100644 index 0000000..febba42 --- /dev/null +++ b/nixos/modules/kernel-patches/0014-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch @@ -0,0 +1,297 @@ +From 1acf23b5be75ad9981eafb94044e507a53cb29c8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:53:11 +0200 +Subject: [PATCH 14/96] PCI: mvebu: Add support for PCI_EXP_SLTSTA_DLLSC via + hot plug interrupt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If link up/down state is changed in mvebu_pcie_link_up() then trigger +hot plug interrupt with DLLSC state change. + +Also triggers hot plug interrupt when mvebu triggers Link Failure interrupt +which indicates that link was changed from active state or when mvebu +triggers TxReq No Link interrupt which indicates that link is down while +trying to transmit PCIe transaction. + +And this hot plug interrupt also when explicit Link Disable or PCIe Host +Reset is issued as mvebu does not trigger Link Failure when dropping to +Detect via Hot Reset or Link Disable. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/Kconfig | 3 + + drivers/pci/controller/pci-mvebu.c | 147 ++++++++++++++++++++++++++++- + 2 files changed, 149 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig +index b8d96d38064d..e4635712dbce 100644 +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -10,6 +10,9 @@ config PCI_MVEBU + depends on ARM + depends on OF + select PCI_BRIDGE_EMUL ++ select PCIEPORTBUS ++ select HOTPLUG_PCI ++ select HOTPLUG_PCI_PCIE + help + Add support for Marvell EBU PCIe controller. This PCIe controller + is used on 32-bit Marvell ARM SoCs: Dove, Kirkwood, Armada 370, +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 56924b0a1969..d832c5135d5c 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -56,12 +56,14 @@ + #define PCIE_CONF_DATA_OFF 0x18fc + #define PCIE_INT_CAUSE_OFF 0x1900 + #define PCIE_INT_UNMASK_OFF 0x1910 ++#define PCIE_INT_TXREQ_NOLINK BIT(0) + #define PCIE_INT_DET_COR BIT(8) + #define PCIE_INT_DET_NONFATAL BIT(9) + #define PCIE_INT_DET_FATAL BIT(10) + #define PCIE_INT_ERR_FATAL BIT(16) + #define PCIE_INT_ERR_NONFATAL BIT(17) + #define PCIE_INT_ERR_COR BIT(18) ++#define PCIE_INT_LINK_FAIL BIT(23) + #define PCIE_INT_INTX(i) BIT(24+i) + #define PCIE_INT_PM_PME BIT(28) + #define PCIE_INT_DET_MASK (PCIE_INT_DET_COR | PCIE_INT_DET_NONFATAL | PCIE_INT_DET_FATAL) +@@ -134,6 +136,8 @@ struct mvebu_pcie_port { + int error_irq; + int intx_irq; + bool pme_pending; ++ struct timer_list link_irq_timer; ++ bool link_was_up; + }; + + static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) +@@ -153,7 +157,26 @@ static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port) + + static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) + { +- return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); ++ bool link_is_up; ++ u16 slotsta; ++ ++ link_is_up = !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); ++ ++ if (link_is_up != port->link_was_up) { ++ port->link_was_up = link_is_up; ++ /* ++ * Link IRQ timer/handler is available only when "error" ++ * interrupt was specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ mod_timer(&port->link_irq_timer, jiffies + 1); ++ } ++ } ++ ++ return link_is_up; + } + + static u8 mvebu_pcie_get_local_bus_nr(struct mvebu_pcie_port *port) +@@ -346,6 +369,19 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); + } + ++ /* ++ * Unmask No Link and Link Failure interrupts to process Link Down ++ * events. These events are reported as Data Link Layer State Changed ++ * notification via Hot Plug Interrupt. Other parts of Link change ++ * events are available only when "error" interrupt was specified in DT. ++ * So enable these interrupts under same conditions. ++ */ ++ if (port->error_irq > 0) { ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ unmask |= PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL; ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } ++ + /* + * Fallback code when "intx" interrupt was not specified in DT: + * Unmask all legacy INTx interrupts as driver does not provide a way +@@ -692,6 +728,14 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + val |= slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE; + else if (!(mvebu_readl(port, PCIE_SSPL_OFF) & PCIE_SSPL_ENABLE)) + val |= PCI_EXP_SLTCTL_ASPL_DISABLE; ++ /* ++ * HPIE and DLLSCE bits are stored only in emulated config ++ * space buffer and are supported only when or "error" interrupt ++ * was specified in DT. ++ */ ++ if (port->error_irq > 0) ++ val |= slotctl & (PCI_EXP_SLTCTL_HPIE | ++ PCI_EXP_SLTCTL_DLLSCE); + /* This callback is 32-bit and in high bits is slot status. */ + val |= slotsta << 16; + *value = val; +@@ -823,6 +867,25 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, + else + ctrl &= ~PCIE_CTRL_MASTER_HOT_RESET; + mvebu_writel(port, ctrl, PCIE_CTRL_OFF); ++ /* ++ * When dropping to Detect via Hot Reset, Disable Link ++ * or Loopback states, the Link Failure interrupt is not ++ * asserted. So when setting Secondary Bus Reset / Hot ++ * Reset bit, call link IRQ timer/handler manually. ++ */ ++ if ((ctrl & PCIE_CTRL_MASTER_HOT_RESET) && port->link_was_up) { ++ port->link_was_up = false; ++ /* ++ * Link IRQ timer/handler is available only when ++ * "error" interrupt was specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ u16 slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ mod_timer(&port->link_irq_timer, jiffies + 1); ++ } ++ } + } + break; + +@@ -851,6 +914,25 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + new &= ~PCI_EXP_LNKCTL_CLKREQ_EN; + + mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); ++ /* ++ * When dropping to Detect via Hot Reset, Disable Link ++ * or Loopback states, the Link Failure interrupt is not ++ * asserted. So when setting Link Disable bit, call link ++ * IRQ timer/handler manually. ++ */ ++ if ((new & PCI_EXP_LNKCTL_LD) && port->link_was_up) { ++ port->link_was_up = false; ++ /* ++ * Link IRQ timer/handler is available only when ++ * "error" interrupt was specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ u16 slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ mod_timer(&port->link_irq_timer, jiffies + 1); ++ } ++ } + break; + + case PCI_EXP_SLTCTL: +@@ -991,6 +1073,15 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver | PCI_EXP_FLAGS_SLOT); + + /* ++ * When "error" interrupt was specified in DT then driver is able to ++ * deliver Data Link Layer State Change interrupt. So in this case mark ++ * bridge as Hot Plug Capable as this is the way how to enable ++ * delivering of Data Link Layer State Change interrupts. ++ * ++ * No Command Completed Support is set because bridge does not support ++ * Command Completed Interrupt. Every command is executed immediately ++ * without any delay. ++ * + * Set Presence Detect State bit permanently as there is no support for + * unplugging PCIe card from the slot. Assume that PCIe card is always + * connected in slot. +@@ -1002,6 +1093,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + * Also set correct slot power limit. + */ + bridge->pcie_conf.slotcap = cpu_to_le32( ++ PCI_EXP_SLTCAP_NCCS | ++ (port->error_irq > 0 ? PCI_EXP_SLTCAP_HPC : 0) | + FIELD_PREP(PCI_EXP_SLTCAP_SPLV, port->slot_power_limit_value) | + FIELD_PREP(PCI_EXP_SLTCAP_SPLS, port->slot_power_limit_scale) | + FIELD_PREP(PCI_EXP_SLTCAP_PSN, port->port+1)); +@@ -1191,11 +1284,29 @@ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) + return 0; + } + ++static void mvebu_pcie_link_irq_handler(struct timer_list *timer) ++{ ++ struct mvebu_pcie_port *port = from_timer(port, timer, link_irq_timer); ++ struct device *dev = &port->pcie->pdev->dev; ++ u16 slotctl; ++ ++ dev_info(dev, "%s: link %s\n", port->name, port->link_was_up ? "up" : "down"); ++ ++ slotctl = le16_to_cpu(port->bridge.pcie_conf.slotctl); ++ if (!(slotctl & PCI_EXP_SLTCTL_DLLSCE) || ++ !(slotctl & PCI_EXP_SLTCTL_HPIE)) ++ return; ++ ++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(dev, "unhandled HP IRQ\n"); ++} ++ + static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg) + { + struct mvebu_pcie_port *port = arg; + struct device *dev = &port->pcie->pdev->dev; + u32 cause, unmask, status; ++ u16 slotsta; + + cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF); + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); +@@ -1233,6 +1344,25 @@ static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg) + dev_err_ratelimited(dev, "unhandled ERR IRQ\n"); + } + ++ /* Process No Link and Link Failure interrupts as HP IRQ */ ++ if (status & (PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL)) { ++ mvebu_writel(port, ++ ~(PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL), ++ PCIE_INT_CAUSE_OFF); ++ if (port->link_was_up) { ++ port->link_was_up = false; ++ slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ /* ++ * Deactivate timer and call mvebu_pcie_link_irq_handler() ++ * function directly as we are in the interrupt context. ++ */ ++ del_timer_sync(&port->link_irq_timer); ++ mvebu_pcie_link_irq_handler(&port->link_irq_timer); ++ } ++ } ++ + return status ? IRQ_HANDLED : IRQ_NONE; + } + +@@ -1798,6 +1928,18 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + } + } + ++ /* ++ * Function mvebu_pcie_link_irq_handler() calls function ++ * generic_handle_irq() and it expects local IRQs to be disabled ++ * as normally generic_handle_irq() is called from the interrupt ++ * context. So use TIMER_IRQSAFE flag for this link_irq_timer. ++ * Available only if "or "error" interrupt was specified. ++ */ ++ if (port->error_irq > 0) ++ timer_setup(&port->link_irq_timer, ++ mvebu_pcie_link_irq_handler, ++ TIMER_IRQSAFE); ++ + /* + * PCIe topology exported by mvebu hw is quite complicated. In + * reality has something like N fully independent host bridges +@@ -1934,6 +2076,9 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + irq_domain_remove(port->rp_irq_domain); + } + ++ if (port->error_irq > 0) ++ del_timer_sync(&port->link_irq_timer); ++ + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0015-PCI-mvebu-use-BIT-and-GENMASK-macros-instead-of-hard.patch b/nixos/modules/kernel-patches/0015-PCI-mvebu-use-BIT-and-GENMASK-macros-instead-of-hard.patch new file mode 100644 index 0000000..1e75168 --- /dev/null +++ b/nixos/modules/kernel-patches/0015-PCI-mvebu-use-BIT-and-GENMASK-macros-instead-of-hard.patch @@ -0,0 +1,47 @@ +From 4b442a1a2a4a00ae7f776bffe53e17421621c57b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:54:29 +0200 +Subject: [PATCH 15/96] PCI: mvebu: use BIT() and GENMASK() macros instead of + hardcoded hex values +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index d832c5135d5c..90a32db42a87 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -44,7 +44,7 @@ + #define PCIE_WIN5_BASE_OFF 0x1884 + #define PCIE_WIN5_REMAP_OFF 0x188c + #define PCIE_CONF_ADDR_OFF 0x18f8 +-#define PCIE_CONF_ADDR_EN 0x80000000 ++#define PCIE_CONF_ADDR_EN BIT(31) + #define PCIE_CONF_REG(r) ((((r) & 0xf00) << 16) | ((r) & 0xfc)) + #define PCIE_CONF_BUS(b) (((b) & 0xff) << 16) + #define PCIE_CONF_DEV(d) (((d) & 0x1f) << 11) +@@ -70,13 +70,13 @@ + #define PCIE_INT_ERR_MASK (PCIE_INT_ERR_FATAL | PCIE_INT_ERR_NONFATAL | PCIE_INT_ERR_COR) + #define PCIE_INT_ALL_MASK GENMASK(31, 0) + #define PCIE_CTRL_OFF 0x1a00 +-#define PCIE_CTRL_X1_MODE 0x0001 ++#define PCIE_CTRL_X1_MODE BIT(0) + #define PCIE_CTRL_RC_MODE BIT(1) + #define PCIE_CTRL_MASTER_HOT_RESET BIT(24) + #define PCIE_STAT_OFF 0x1a04 +-#define PCIE_STAT_BUS 0xff00 +-#define PCIE_STAT_DEV 0x1f0000 + #define PCIE_STAT_LINK_DOWN BIT(0) ++#define PCIE_STAT_BUS GENMASK(15, 8) ++#define PCIE_STAT_DEV GENMASK(20, 16) + #define PCIE_SSPL_OFF 0x1a0c + #define PCIE_SSPL_VALUE_SHIFT 0 + #define PCIE_SSPL_VALUE_MASK GENMASK(7, 0) +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0016-PCI-mvebu-For-consistency-add-_OFF-suffix-to-all-reg.patch b/nixos/modules/kernel-patches/0016-PCI-mvebu-For-consistency-add-_OFF-suffix-to-all-reg.patch new file mode 100644 index 0000000..e112cdd --- /dev/null +++ b/nixos/modules/kernel-patches/0016-PCI-mvebu-For-consistency-add-_OFF-suffix-to-all-reg.patch @@ -0,0 +1,167 @@ +From e8bd26a6ae1f60ed750b7210383f68f0dd091339 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Fri, 17 Sep 2021 14:55:03 +0200 +Subject: [PATCH 16/96] PCI: mvebu: For consistency add _OFF suffix to all + registers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-mvebu.c | 40 +++++++++++++++--------------- + 1 file changed, 20 insertions(+), 20 deletions(-) + +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 90a32db42a87..d5b630e27882 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -34,7 +34,7 @@ + #define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3)) + #define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3)) + #define PCIE_SSDEV_ID_OFF 0x002c +-#define PCIE_CAP_PCIEXP 0x0060 ++#define PCIE_CAP_PCIEXP_OFF 0x0060 + #define PCIE_CAP_PCIERR_OFF 0x0100 + #define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4)) + #define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4)) +@@ -83,8 +83,8 @@ + #define PCIE_SSPL_SCALE_SHIFT 8 + #define PCIE_SSPL_SCALE_MASK GENMASK(9, 8) + #define PCIE_SSPL_ENABLE BIT(16) +-#define PCIE_RC_RTSTA 0x1a14 +-#define PCIE_DEBUG_CTRL 0x1a60 ++#define PCIE_RC_RTSTA_OFF 0x1a14 ++#define PCIE_DEBUG_CTRL_OFF 0x1a60 + #define PCIE_DEBUG_SOFT_RESET BIT(20) + + struct mvebu_pcie_port; +@@ -296,10 +296,10 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + * be set to number of SerDes PCIe lanes (1 or 4). If this register is + * not set correctly then link with endpoint card is not established. + */ +- lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); ++ lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCAP); + lnkcap &= ~PCI_EXP_LNKCAP_MLW; + lnkcap |= (port->is_x4 ? 4 : 1) << 4; +- mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP); ++ mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCAP); + + /* Disable Root Bridge I/O space, memory space and bus mastering. */ + cmd = mvebu_readl(port, PCIE_CMD_OFF); +@@ -690,11 +690,11 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + + switch (reg) { + case PCI_EXP_DEVCAP: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP); ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCAP); + break; + + case PCI_EXP_DEVCTL: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL); ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL); + break; + + case PCI_EXP_LNKCAP: +@@ -704,13 +704,13 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + * Additionally enable Data Link Layer Link Active Reporting + * Capable bit as DL_Active indication is provided too. + */ +- *value = (mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) & ++ *value = (mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCAP) & + ~PCI_EXP_LNKCAP_CLKPM) | PCI_EXP_LNKCAP_DLLLARC; + break; + + case PCI_EXP_LNKCTL: + /* DL_Active indication is provided via PCIE_STAT_OFF */ +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL) | ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL) | + (mvebu_pcie_link_up(port) ? + (PCI_EXP_LNKSTA_DLLLA << 16) : 0); + break; +@@ -748,19 +748,19 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + break; + + case PCI_EXP_RTSTA: +- *value = mvebu_readl(port, PCIE_RC_RTSTA); ++ *value = mvebu_readl(port, PCIE_RC_RTSTA_OFF); + break; + + case PCI_EXP_DEVCAP2: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP2); ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCAP2); + break; + + case PCI_EXP_DEVCTL2: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL2); ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL2); + break; + + case PCI_EXP_LNKCTL2: +- *value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL2); ++ *value = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL2); + break; + + default: +@@ -902,7 +902,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + + switch (reg) { + case PCI_EXP_DEVCTL: +- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL); ++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL); + break; + + case PCI_EXP_LNKCTL: +@@ -913,7 +913,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + */ + new &= ~PCI_EXP_LNKCTL_CLKREQ_EN; + +- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); ++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL); + /* + * When dropping to Detect via Hot Reset, Disable Link + * or Loopback states, the Link Failure interrupt is not +@@ -953,7 +953,7 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + + case PCI_EXP_RTSTA: + /* +- * PME Status bit in Root Status Register (PCIE_RC_RTSTA) ++ * PME Status bit in Root Status Register (PCIE_RC_RTSTA_OFF) + * is read-only and can be cleared only by writing 0b to the + * Interrupt Cause RW0C register (PCIE_INT_CAUSE_OFF). So + * clear PME via Interrupt Cause and also set port->pme_pending +@@ -978,11 +978,11 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + break; + + case PCI_EXP_DEVCTL2: +- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL2); ++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_DEVCTL2); + break; + + case PCI_EXP_LNKCTL2: +- mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL2); ++ mvebu_writel(port, new, PCIE_CAP_PCIEXP_OFF + PCI_EXP_LNKCTL2); + break; + + default: +@@ -1042,7 +1042,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + u32 dev_id = mvebu_readl(port, PCIE_DEV_ID_OFF); + u32 dev_rev = mvebu_readl(port, PCIE_DEV_REV_OFF); + u32 ssdev_id = mvebu_readl(port, PCIE_SSDEV_ID_OFF); +- u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP); ++ u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP_OFF); + u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS); + + bridge->conf.vendor = cpu_to_le16(dev_id & 0xffff); +@@ -1103,7 +1103,7 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge->subsystem_vendor_id = ssdev_id & 0xffff; + bridge->subsystem_id = ssdev_id >> 16; + bridge->has_pcie = true; +- bridge->pcie_start = PCIE_CAP_PCIEXP; ++ bridge->pcie_start = PCIE_CAP_PCIEXP_OFF; + bridge->data = port; + bridge->ops = &mvebu_pci_bridge_emul_ops; + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0017-PCI-aardvark-Add-support-for-PCI-Bridge-Subsystem-Ve.patch b/nixos/modules/kernel-patches/0017-PCI-aardvark-Add-support-for-PCI-Bridge-Subsystem-Ve.patch new file mode 100644 index 0000000..083ed79 --- /dev/null +++ b/nixos/modules/kernel-patches/0017-PCI-aardvark-Add-support-for-PCI-Bridge-Subsystem-Ve.patch @@ -0,0 +1,45 @@ +From 3a116a12e6e1e76971315d233007941673adb8c8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 19 Oct 2021 13:54:19 +0200 +Subject: [PATCH 17/96] PCI: aardvark: Add support for PCI Bridge Subsystem + Vendor ID on emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Register with Subsystem Device/Vendor ID is at offset 0x2c. Export is via +emulated bridge. + +After this change Subsystem ID is visible in lspci output at line: + + Capabilities: [40] Subsystem + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 3767b25769bc..dd1b29af0470 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -33,6 +33,7 @@ + #define PCIE_CORE_DEV_ID_REG 0x0 + #define PCIE_CORE_CMD_STATUS_REG 0x4 + #define PCIE_CORE_DEV_REV_REG 0x8 ++#define PCIE_CORE_SSDEV_ID_REG 0x2c + #define PCIE_CORE_PCIEXP_CAP 0xc0 + #define PCIE_CORE_ERR_CAPCTL_REG 0x118 + #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) +@@ -998,6 +999,8 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + /* Indicates supports for Completion Retry Status */ + bridge->pcie_conf.rootcap = cpu_to_le16(PCI_EXP_RTCAP_CRSVIS); + ++ bridge->subsystem_vendor_id = advk_readl(pcie, PCIE_CORE_SSDEV_ID_REG) & 0xffff; ++ bridge->subsystem_id = advk_readl(pcie, PCIE_CORE_SSDEV_ID_REG) >> 16; + bridge->has_pcie = true; + bridge->pcie_start = PCIE_CORE_PCIEXP_CAP; + bridge->data = pcie; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0018-PCI-aardvark-Dispose-INTx-irqs-prior-to-removing-INT.patch b/nixos/modules/kernel-patches/0018-PCI-aardvark-Dispose-INTx-irqs-prior-to-removing-INT.patch new file mode 100644 index 0000000..4b4f856 --- /dev/null +++ b/nixos/modules/kernel-patches/0018-PCI-aardvark-Dispose-INTx-irqs-prior-to-removing-INT.patch @@ -0,0 +1,44 @@ +From 3c6148dc2653a7026a1e8295b1eccb98fdd1c3ac Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Sat, 9 Jul 2022 16:55:54 +0200 +Subject: [PATCH 18/96] PCI: aardvark: Dispose INTx irqs prior to removing INTx + domain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Documentation for irq_domain_remove() says that all mapping within the +domain must be disposed prior to domain remove. + +Currently INTx irqs are not disposed in pci-aardvark.c device unbind callback +which cause that kernel crashes after unloading driver and trying to read +/sys/kernel/debug/irq/irqs/<num> or /proc/interrupts. + +Fixes: 526a76991b7b ("PCI: aardvark: Implement driver 'remove' function and allow to build it as module") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index dd1b29af0470..a97502b2aef1 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1452,6 +1452,14 @@ static int advk_pcie_init_irq_domain(struct advk_pcie *pcie) + + static void advk_pcie_remove_irq_domain(struct advk_pcie *pcie) + { ++ int virq, i; ++ ++ for (i = 0; i < PCI_NUM_INTX; i++) { ++ virq = irq_find_mapping(pcie->irq_domain, i); ++ if (virq > 0) ++ irq_dispose_mapping(virq); ++ } ++ + irq_domain_remove(pcie->irq_domain); + } + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0019-PCI-aardvark-Add-support-for-AER-registers-on-emulat.patch b/nixos/modules/kernel-patches/0019-PCI-aardvark-Add-support-for-AER-registers-on-emulat.patch new file mode 100644 index 0000000..6a28e7f --- /dev/null +++ b/nixos/modules/kernel-patches/0019-PCI-aardvark-Add-support-for-AER-registers-on-emulat.patch @@ -0,0 +1,139 @@ +From cbe67d3f1b2565b4d94ec08cbc2b144165cb9117 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Tue, 24 May 2022 15:28:26 +0200 +Subject: [PATCH 19/96] PCI: aardvark: Add support for AER registers on + emulated bridge +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Aardvark controller supports Advanced Error Reporting configuration +registers. + +Export these registers on the emulated root bridge via new .read_ext() and +.write_ext() methods. + +Note that in the Advanced Error Reporting Capability header the offset to +the next Extended Capability header is set, but it is not documented in +Armada 3700 Functional Specification. Since this change adds support only +for Advanced Error Reporting, explicitly clear PCI_EXT_CAP_NEXT bits in AER +capability header. + +Now the pcieport driver correctly detects AER support and allows PCIe AER +driver to start receiving ERR interrupts. Kernel log now says: + + pcieport 0000:00:00.0: AER: enabled with IRQ 52 + +Link: https://lore.kernel.org/r/20220524132827.8837-2-kabel@kernel.org +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> +--- + drivers/pci/controller/pci-aardvark.c | 79 +++++++++++++++++++++++++++ + 1 file changed, 79 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index a97502b2aef1..4921d9cf80d2 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -35,6 +35,7 @@ + #define PCIE_CORE_DEV_REV_REG 0x8 + #define PCIE_CORE_SSDEV_ID_REG 0x2c + #define PCIE_CORE_PCIEXP_CAP 0xc0 ++#define PCIE_CORE_PCIERR_CAP 0x100 + #define PCIE_CORE_ERR_CAPCTL_REG 0x118 + #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) + #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6) +@@ -943,11 +944,89 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + } + } + ++static pci_bridge_emul_read_status_t ++advk_pci_bridge_emul_ext_conf_read(struct pci_bridge_emul *bridge, ++ int reg, u32 *value) ++{ ++ struct advk_pcie *pcie = bridge->data; ++ ++ switch (reg) { ++ case 0: ++ *value = advk_readl(pcie, PCIE_CORE_PCIERR_CAP + reg); ++ ++ /* ++ * PCI_EXT_CAP_NEXT bits are set to offset 0x150, but Armada ++ * 3700 Functional Specification does not document registers ++ * at those addresses. ++ * ++ * Thus we clear PCI_EXT_CAP_NEXT bits to make Advanced Error ++ * Reporting Capability header the last Extended Capability. ++ * If we obtain documentation for those registers in the ++ * future, this can be changed. ++ */ ++ *value &= 0x000fffff; ++ return PCI_BRIDGE_EMUL_HANDLED; ++ ++ case PCI_ERR_UNCOR_STATUS: ++ case PCI_ERR_UNCOR_MASK: ++ case PCI_ERR_UNCOR_SEVER: ++ case PCI_ERR_COR_STATUS: ++ case PCI_ERR_COR_MASK: ++ case PCI_ERR_CAP: ++ case PCI_ERR_HEADER_LOG + 0: ++ case PCI_ERR_HEADER_LOG + 4: ++ case PCI_ERR_HEADER_LOG + 8: ++ case PCI_ERR_HEADER_LOG + 12: ++ case PCI_ERR_ROOT_COMMAND: ++ case PCI_ERR_ROOT_STATUS: ++ case PCI_ERR_ROOT_ERR_SRC: ++ *value = advk_readl(pcie, PCIE_CORE_PCIERR_CAP + reg); ++ return PCI_BRIDGE_EMUL_HANDLED; ++ ++ default: ++ return PCI_BRIDGE_EMUL_NOT_HANDLED; ++ } ++} ++ ++static void ++advk_pci_bridge_emul_ext_conf_write(struct pci_bridge_emul *bridge, ++ int reg, u32 old, u32 new, u32 mask) ++{ ++ struct advk_pcie *pcie = bridge->data; ++ ++ switch (reg) { ++ /* These are W1C registers, so clear other bits */ ++ case PCI_ERR_UNCOR_STATUS: ++ case PCI_ERR_COR_STATUS: ++ case PCI_ERR_ROOT_STATUS: ++ new &= mask; ++ fallthrough; ++ ++ case PCI_ERR_UNCOR_MASK: ++ case PCI_ERR_UNCOR_SEVER: ++ case PCI_ERR_COR_MASK: ++ case PCI_ERR_CAP: ++ case PCI_ERR_HEADER_LOG + 0: ++ case PCI_ERR_HEADER_LOG + 4: ++ case PCI_ERR_HEADER_LOG + 8: ++ case PCI_ERR_HEADER_LOG + 12: ++ case PCI_ERR_ROOT_COMMAND: ++ case PCI_ERR_ROOT_ERR_SRC: ++ advk_writel(pcie, new, PCIE_CORE_PCIERR_CAP + reg); ++ break; ++ ++ default: ++ break; ++ } ++} ++ + static const struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { + .read_base = advk_pci_bridge_emul_base_conf_read, + .write_base = advk_pci_bridge_emul_base_conf_write, + .read_pcie = advk_pci_bridge_emul_pcie_conf_read, + .write_pcie = advk_pci_bridge_emul_pcie_conf_write, ++ .read_ext = advk_pci_bridge_emul_ext_conf_read, ++ .write_ext = advk_pci_bridge_emul_ext_conf_write, + }; + + /* +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0020-PCI-aardvark-Dispose-bridge-irq-prior-to-removing-br.patch b/nixos/modules/kernel-patches/0020-PCI-aardvark-Dispose-bridge-irq-prior-to-removing-br.patch new file mode 100644 index 0000000..3b65ce5 --- /dev/null +++ b/nixos/modules/kernel-patches/0020-PCI-aardvark-Dispose-bridge-irq-prior-to-removing-br.patch @@ -0,0 +1,41 @@ +From 36711e04b081f70286acb2911447550ac9f14d27 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 24 Aug 2022 15:59:49 +0200 +Subject: [PATCH 20/96] PCI: aardvark: Dispose bridge irq prior to removing + bridge domain +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Documentation for irq_domain_remove() says that all mapping within the +domain must be disposed prior to domain remove. + +Currently bridge irq is not disposed in pci-aardvark.c device unbind callback +which cause that kernel crashes after unloading driver and trying to read +/sys/kernel/debug/irq/irqs/<num> or /proc/interrupts. + +Fixes: 815bc3136867 ("PCI: aardvark: Use separate INTA interrupt for emulated root bridge") +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 4921d9cf80d2..668b052e3e2a 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1577,6 +1577,11 @@ static int advk_pcie_init_rp_irq_domain(struct advk_pcie *pcie) + + static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie) + { ++ int virq; ++ ++ virq = irq_find_mapping(pcie->rp_irq_domain, 0); ++ if (virq > 0) ++ irq_dispose_mapping(virq); + irq_domain_remove(pcie->rp_irq_domain); + } + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0021-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch b/nixos/modules/kernel-patches/0021-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch new file mode 100644 index 0000000..5debe96 --- /dev/null +++ b/nixos/modules/kernel-patches/0021-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch @@ -0,0 +1,268 @@ +From e9d12baaf2ca8f10303816dd3114f6fb54d9481f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 31 Aug 2022 15:55:46 +0200 +Subject: [PATCH 21/96] PCI: aardvark: Add support for DLLSC and hotplug + interrupt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for Data Link Layer State Change in the emulated slot +registers and hotplug interrupt via the emulated root bridge. + +This is mainly useful for when an error causes link down event. With +this change, drivers can try recovery. + +Link down state change can be implemented because Aardvark supports Link +Down event interrupt. Use it for signaling that Data Link Layer Link is +not active anymore via Hot-Plug Interrupt on emulated root bridge. + +Link up interrupt is not available on Aardvark, but we check for whether +link is up in the advk_pcie_link_up() function. By triggering Hot-Plug +Interrupt from this function we achieve Link up event, so long as the +function is called (which it is after probe and when rescanning). +Although it is not ideal, it is better than nothing. + +Since advk_pcie_link_up() is not called from interrupt handler, we +cannot call generic_handle_domain_irq() from it directly. Instead create +a TIMER_IRQSAFE timer and trigger it from advk_pcie_link_up(). + +(We haven't been able to find any documentation for a Link Up interrupt + on Aardvark, but it is possible there is one, in some undocumented + register. If we manage to find this information, this can be + rewritten.) + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/Kconfig | 3 + + drivers/pci/controller/pci-aardvark.c | 101 ++++++++++++++++++++++++-- + 2 files changed, 99 insertions(+), 5 deletions(-) + +diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig +index e4635712dbce..d4baecbcf574 100644 +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -24,6 +24,9 @@ config PCI_AARDVARK + depends on OF + depends on PCI_MSI_IRQ_DOMAIN + select PCI_BRIDGE_EMUL ++ select PCIEPORTBUS ++ select HOTPLUG_PCI ++ select HOTPLUG_PCI_PCIE + help + Add support for Aardvark 64bit PCIe Host Controller. This + controller is part of the South Bridge of the Marvel Armada +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 668b052e3e2a..b2f143191313 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -25,6 +25,7 @@ + #include <linux/of_address.h> + #include <linux/of_gpio.h> + #include <linux/of_pci.h> ++#include <linux/timer.h> + + #include "../pci.h" + #include "../pci-bridge-emul.h" +@@ -101,6 +102,7 @@ + #define PCIE_MSG_PM_PME_MASK BIT(7) + #define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44) + #define PCIE_ISR0_MSI_INT_PENDING BIT(24) ++#define PCIE_ISR0_LINK_DOWN BIT(1) + #define PCIE_ISR0_CORR_ERR BIT(11) + #define PCIE_ISR0_NFAT_ERR BIT(12) + #define PCIE_ISR0_FAT_ERR BIT(13) +@@ -285,6 +287,8 @@ struct advk_pcie { + DECLARE_BITMAP(msi_used, MSI_IRQ_NUM); + struct mutex msi_used_lock; + int link_gen; ++ bool link_was_up; ++ struct timer_list link_irq_timer; + struct pci_bridge_emul bridge; + struct gpio_desc *reset_gpio; + struct phy *phy; +@@ -314,7 +318,24 @@ static inline bool advk_pcie_link_up(struct advk_pcie *pcie) + { + /* check if LTSSM is in normal operation - some L* state */ + u8 ltssm_state = advk_pcie_ltssm_state(pcie); +- return ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED; ++ bool link_is_up; ++ u16 slotsta; ++ ++ link_is_up = ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED; ++ ++ if (link_is_up && !pcie->link_was_up) { ++ dev_info(&pcie->pdev->dev, "link up\n"); ++ ++ pcie->link_was_up = true; ++ ++ slotsta = le16_to_cpu(pcie->bridge.pcie_conf.slotsta); ++ slotsta |= PCI_EXP_SLTSTA_DLLSC; ++ pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta); ++ ++ mod_timer(&pcie->link_irq_timer, jiffies + 1); ++ } ++ ++ return link_is_up; + } + + static inline bool advk_pcie_link_active(struct advk_pcie *pcie) +@@ -443,8 +464,6 @@ static void advk_pcie_train_link(struct advk_pcie *pcie) + ret = advk_pcie_wait_for_link(pcie); + if (ret < 0) + dev_err(dev, "link never came up\n"); +- else +- dev_info(dev, "link up\n"); + } + + /* +@@ -593,6 +612,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) + reg &= ~PCIE_ISR0_MSI_INT_PENDING; + advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); + ++ /* Unmask Link Down interrupt */ ++ reg = advk_readl(pcie, PCIE_ISR0_MASK_REG); ++ reg &= ~PCIE_ISR0_LINK_DOWN; ++ advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); ++ + /* Unmask PME interrupt for processing of PME requester */ + reg = advk_readl(pcie, PCIE_ISR0_MASK_REG); + reg &= ~PCIE_MSG_PM_PME_MASK; +@@ -919,6 +943,14 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + advk_pcie_wait_for_retrain(pcie); + break; + ++ case PCI_EXP_SLTCTL: { ++ u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl); ++ /* Only emulation of HPIE and DLLSCE bits is provided */ ++ slotctl &= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; ++ bridge->pcie_conf.slotctl = cpu_to_le16(slotctl); ++ break; ++ } ++ + case PCI_EXP_RTCTL: { + u16 rootctl = le16_to_cpu(bridge->pcie_conf.rootctl); + /* Only emulation of PMEIE and CRSSVE bits is provided */ +@@ -1036,6 +1068,7 @@ static const struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { + static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + { + struct pci_bridge_emul *bridge = &pcie->bridge; ++ u32 slotcap; + + bridge->conf.vendor = + cpu_to_le16(advk_readl(pcie, PCIE_CORE_DEV_ID_REG) & 0xffff); +@@ -1062,6 +1095,13 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + bridge->pcie_conf.cap = cpu_to_le16(2 | PCI_EXP_FLAGS_SLOT); + + /* ++ * Mark bridge as Hot Plug Capable since this is the way how to enable ++ * delivering of Data Link Layer State Change interrupts. ++ * ++ * Set No Command Completed Support because bridge does not support ++ * Command Completed Interrupt. Every command is executed immediately ++ * without any delay. ++ * + * Set Presence Detect State bit permanently since there is no support + * for unplugging the card nor detecting whether it is plugged. (If a + * platform exists in the future that supports it, via a GPIO for +@@ -1071,8 +1111,9 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + * value is reserved for ports within the same silicon as Root Port + * which is not our case. + */ +- bridge->pcie_conf.slotcap = cpu_to_le32(FIELD_PREP(PCI_EXP_SLTCAP_PSN, +- 1)); ++ slotcap = PCI_EXP_SLTCAP_NCCS | PCI_EXP_SLTCAP_HPC | ++ FIELD_PREP(PCI_EXP_SLTCAP_PSN, 1); ++ bridge->pcie_conf.slotcap = cpu_to_le32(slotcap); + bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS); + + /* Indicates supports for Completion Retry Status */ +@@ -1585,6 +1626,24 @@ static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie) + irq_domain_remove(pcie->rp_irq_domain); + } + ++static void advk_pcie_link_irq_handler(struct timer_list *timer) ++{ ++ struct advk_pcie *pcie = from_timer(pcie, timer, link_irq_timer); ++ u16 slotctl; ++ ++ slotctl = le16_to_cpu(pcie->bridge.pcie_conf.slotctl); ++ if (!(slotctl & PCI_EXP_SLTCTL_DLLSCE) || ++ !(slotctl & PCI_EXP_SLTCTL_HPIE)) ++ return; ++ ++ /* ++ * Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ, so use PCIe ++ * interrupt 0 ++ */ ++ if (generic_handle_domain_irq(pcie->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(&pcie->pdev->dev, "unhandled HP IRQ\n"); ++} ++ + static void advk_pcie_handle_pme(struct advk_pcie *pcie) + { + u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16; +@@ -1636,6 +1695,7 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie) + { + u32 isr0_val, isr0_mask, isr0_status; + u32 isr1_val, isr1_mask, isr1_status; ++ u16 slotsta; + int i; + + isr0_val = advk_readl(pcie, PCIE_ISR0_REG); +@@ -1662,6 +1722,26 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie) + dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n"); + } + ++ /* Process Link Down interrupt as HP IRQ */ ++ if (isr0_status & PCIE_ISR0_LINK_DOWN) { ++ advk_writel(pcie, PCIE_ISR0_LINK_DOWN, PCIE_ISR0_REG); ++ ++ dev_info(&pcie->pdev->dev, "link down\n"); ++ ++ pcie->link_was_up = false; ++ ++ slotsta = le16_to_cpu(pcie->bridge.pcie_conf.slotsta); ++ slotsta |= PCI_EXP_SLTSTA_DLLSC; ++ pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta); ++ ++ /* ++ * Deactivate timer and call advk_pcie_link_irq_handler() ++ * function directly as we are in the interrupt context. ++ */ ++ del_timer_sync(&pcie->link_irq_timer); ++ advk_pcie_link_irq_handler(&pcie->link_irq_timer); ++ } ++ + /* Process MSI interrupts */ + if (isr0_status & PCIE_ISR0_MSI_INT_PENDING) + advk_pcie_handle_msi(pcie); +@@ -1898,6 +1978,14 @@ static int advk_pcie_probe(struct platform_device *pdev) + if (ret) + return ret; + ++ /* ++ * generic_handle_domain_irq() expects local IRQs to be disabled since ++ * normally it is called from interrupt context, so use TIMER_IRQSAFE ++ * flag for this link_irq_timer. ++ */ ++ timer_setup(&pcie->link_irq_timer, advk_pcie_link_irq_handler, ++ TIMER_IRQSAFE); ++ + advk_pcie_setup_hw(pcie); + + ret = advk_sw_pci_bridge_init(pcie); +@@ -1986,6 +2074,9 @@ static int advk_pcie_remove(struct platform_device *pdev) + advk_pcie_remove_msi_irq_domain(pcie); + advk_pcie_remove_irq_domain(pcie); + ++ /* Deactivate link event timer */ ++ del_timer_sync(&pcie->link_irq_timer); ++ + /* Free config space for emulated root bridge */ + pci_bridge_emul_cleanup(&pcie->bridge); + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0022-PCI-aardvark-Send-Set_Slot_Power_Limit-message.patch b/nixos/modules/kernel-patches/0022-PCI-aardvark-Send-Set_Slot_Power_Limit-message.patch new file mode 100644 index 0000000..2c64e5b --- /dev/null +++ b/nixos/modules/kernel-patches/0022-PCI-aardvark-Send-Set_Slot_Power_Limit-message.patch @@ -0,0 +1,148 @@ +From 6f717ca06ec6ba55ec97851763dbd4e26fe7ea0f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 31 Aug 2022 15:57:01 +0200 +Subject: [PATCH 22/96] PCI: aardvark: Send Set_Slot_Power_Limit message +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Emulate Slot PowerLimit Scale and Value bits in the Slot Capabilities +register of the emulated bridge and if slot power limit value is +defined, send that Set_Slot_Power_Limit message via Message Generation +Control Register in Link Up handler on link up event. + +Slot power limit value is read from device-tree property +'slot-power-limit-milliwatt'. If this property is not specified, we +treat it as "Slot Capabilities register has not yet been initialized". + +According to PCIe Base specification 3.0, when transitioning from a +non-DL_Up Status to a DL_Up Status, the Port must initiate the +transmission of a Set_Slot_Power_Limit Message to the other component +on the Link to convey the value programmed in the Slot Power Limit +Scale and Value fields of the Slot Capabilities register. This +transmission is optional if the Slot Capabilities register has not +yet been initialized. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 51 ++++++++++++++++++++++++--- + 1 file changed, 47 insertions(+), 4 deletions(-) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index b2f143191313..656e4ea95e2e 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -214,6 +214,11 @@ enum { + }; + + #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44) ++#define PME_MSG_GEN_CTRL (LMI_BASE_ADDR + 0x220) ++#define SEND_SET_SLOT_POWER_LIMIT BIT(13) ++#define SEND_PME_TURN_OFF BIT(14) ++#define SLOT_POWER_LIMIT_DATA_SHIFT 16 ++#define SLOT_POWER_LIMIT_DATA_MASK GENMASK(25, 16) + + /* PCIe core controller registers */ + #define CTRL_CORE_BASE_ADDR 0x18000 +@@ -286,6 +291,8 @@ struct advk_pcie { + raw_spinlock_t msi_irq_lock; + DECLARE_BITMAP(msi_used, MSI_IRQ_NUM); + struct mutex msi_used_lock; ++ u8 slot_power_limit_value; ++ u8 slot_power_limit_scale; + int link_gen; + bool link_was_up; + struct timer_list link_irq_timer; +@@ -318,8 +325,9 @@ static inline bool advk_pcie_link_up(struct advk_pcie *pcie) + { + /* check if LTSSM is in normal operation - some L* state */ + u8 ltssm_state = advk_pcie_ltssm_state(pcie); ++ u16 slotsta, slotctl; ++ u32 slotpwr, val; + bool link_is_up; +- u16 slotsta; + + link_is_up = ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED; + +@@ -333,6 +341,27 @@ static inline bool advk_pcie_link_up(struct advk_pcie *pcie) + pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta); + + mod_timer(&pcie->link_irq_timer, jiffies + 1); ++ ++ /* ++ * According to PCIe Base specification 3.0, when transitioning ++ * from a non-DL_Up Status to a DL_Up Status, the Port must ++ * initiate the transmission of a Set_Slot_Power_Limit Message ++ * to the other component on the Link to convey the value ++ * programmed in the Slot Power Limit Scale and Value fields of ++ * the Slot Capabilities register. This transmission is optional ++ * if the Slot Capabilities register has not yet been ++ * initialized. ++ */ ++ slotctl = le16_to_cpu(pcie->bridge.pcie_conf.slotctl); ++ slotpwr = FIELD_GET(PCI_EXP_SLTCAP_SPLV | PCI_EXP_SLTCAP_SPLS, ++ le32_to_cpu(pcie->bridge.pcie_conf.slotcap)); ++ if (!(slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE) && slotpwr) { ++ val = advk_readl(pcie, PME_MSG_GEN_CTRL); ++ val &= ~SLOT_POWER_LIMIT_DATA_MASK; ++ val |= slotpwr << SLOT_POWER_LIMIT_DATA_SHIFT; ++ val |= SEND_SET_SLOT_POWER_LIMIT; ++ advk_writel(pcie, val, PME_MSG_GEN_CTRL); ++ } + } + + return link_is_up; +@@ -945,8 +974,9 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + + case PCI_EXP_SLTCTL: { + u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl); +- /* Only emulation of HPIE and DLLSCE bits is provided */ +- slotctl &= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; ++ /* Only emulation of HPIE, DLLSCE and ASPLD bits is provided */ ++ slotctl &= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE | ++ PCI_EXP_SLTCTL_ASPL_DISABLE; + bridge->pcie_conf.slotctl = cpu_to_le16(slotctl); + break; + } +@@ -1110,9 +1140,13 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + * Set physical slot number to 1 since there is only one port and zero + * value is reserved for ports within the same silicon as Root Port + * which is not our case. ++ * ++ * Set slot power limit. + */ + slotcap = PCI_EXP_SLTCAP_NCCS | PCI_EXP_SLTCAP_HPC | +- FIELD_PREP(PCI_EXP_SLTCAP_PSN, 1); ++ FIELD_PREP(PCI_EXP_SLTCAP_PSN, 1) | ++ FIELD_PREP(PCI_EXP_SLTCAP_SPLV, pcie->slot_power_limit_value) | ++ FIELD_PREP(PCI_EXP_SLTCAP_SPLS, pcie->slot_power_limit_scale); + bridge->pcie_conf.slotcap = cpu_to_le32(slotcap); + bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS); + +@@ -1854,6 +1888,7 @@ static int advk_pcie_probe(struct platform_device *pdev) + struct advk_pcie *pcie; + struct pci_host_bridge *bridge; + struct resource_entry *entry; ++ u32 slot_power_limit; + int ret, irq; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct advk_pcie)); +@@ -1974,6 +2009,14 @@ static int advk_pcie_probe(struct platform_device *pdev) + else + pcie->link_gen = ret; + ++ slot_power_limit = of_pci_get_slot_power_limit(dev->of_node, ++ &pcie->slot_power_limit_value, ++ &pcie->slot_power_limit_scale); ++ if (slot_power_limit) ++ dev_info(dev, "Slot Power Limit: %u.%uW\n", ++ slot_power_limit / 1000, ++ (slot_power_limit / 100) % 10); ++ + ret = advk_pcie_setup_phy(pcie); + if (ret) + return ret; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0023-PCI-aardvark-Add-clock-support.patch b/nixos/modules/kernel-patches/0023-PCI-aardvark-Add-clock-support.patch new file mode 100644 index 0000000..779d803 --- /dev/null +++ b/nixos/modules/kernel-patches/0023-PCI-aardvark-Add-clock-support.patch @@ -0,0 +1,93 @@ +From 0d829d8007898c85cecdb750ca6892cadbab21e5 Mon Sep 17 00:00:00 2001 +From: Miquel Raynal <miquel.raynal@bootlin.com> +Date: Wed, 31 Aug 2022 15:59:39 +0200 +Subject: [PATCH 23/96] PCI: aardvark: Add clock support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The IP relies on a gated clock. When we will add S2RAM support, this +clock will need to be resumed before any PCIe registers are +accessed. Add support for this clock. + +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 32 +++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 656e4ea95e2e..84c1f3165472 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -9,6 +9,7 @@ + */ + + #include <linux/bitfield.h> ++#include <linux/clk.h> + #include <linux/delay.h> + #include <linux/gpio/consumer.h> + #include <linux/interrupt.h> +@@ -298,6 +299,7 @@ struct advk_pcie { + struct timer_list link_irq_timer; + struct pci_bridge_emul bridge; + struct gpio_desc *reset_gpio; ++ struct clk *clk; + struct phy *phy; + }; + +@@ -1826,6 +1828,29 @@ static int advk_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) + return of_irq_parse_and_map_pci(dev, slot, pin); + } + ++static int advk_pcie_setup_clk(struct advk_pcie *pcie) ++{ ++ struct device *dev = &pcie->pdev->dev; ++ int ret; ++ ++ pcie->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(pcie->clk) && (PTR_ERR(pcie->clk) == -EPROBE_DEFER)) ++ return PTR_ERR(pcie->clk); ++ ++ /* Old bindings miss the clock handle */ ++ if (IS_ERR(pcie->clk)) { ++ dev_warn(dev, "Clock unavailable (%ld)\n", PTR_ERR(pcie->clk)); ++ pcie->clk = NULL; ++ return 0; ++ } ++ ++ ret = clk_prepare_enable(pcie->clk); ++ if (ret) ++ dev_err(dev, "Clock initialization failed (%d)\n", ret); ++ ++ return ret; ++} ++ + static void advk_pcie_disable_phy(struct advk_pcie *pcie) + { + phy_power_off(pcie->phy); +@@ -2017,6 +2042,10 @@ static int advk_pcie_probe(struct platform_device *pdev) + slot_power_limit / 1000, + (slot_power_limit / 100) % 10); + ++ ret = advk_pcie_setup_clk(pcie); ++ if (ret) ++ return ret; ++ + ret = advk_pcie_setup_phy(pcie); + if (ret) + return ret; +@@ -2139,6 +2168,9 @@ static int advk_pcie_remove(struct platform_device *pdev) + /* Disable phy */ + advk_pcie_disable_phy(pcie); + ++ /* Disable clock */ ++ clk_disable_unprepare(pcie->clk); ++ + return 0; + } + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0024-PCI-aardvark-Add-suspend-to-RAM-support.patch b/nixos/modules/kernel-patches/0024-PCI-aardvark-Add-suspend-to-RAM-support.patch new file mode 100644 index 0000000..d03de3c --- /dev/null +++ b/nixos/modules/kernel-patches/0024-PCI-aardvark-Add-suspend-to-RAM-support.patch @@ -0,0 +1,74 @@ +From ff0d91bd6cfdd3c27d75e631a6aaeb5b4eee4f1f Mon Sep 17 00:00:00 2001 +From: Miquel Raynal <miquel.raynal@bootlin.com> +Date: Wed, 31 Aug 2022 16:07:27 +0200 +Subject: [PATCH 24/96] PCI: aardvark: Add suspend to RAM support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add suspend and resume callbacks. We need to use the NOIRQ variants to +ensure the controller's IRQ handlers are not run during suspend() / +resume(), which could cause races. + +Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 34 +++++++++++++++++++++++++++ + 1 file changed, 34 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 84c1f3165472..a042bfe70d69 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -1907,6 +1907,39 @@ static int advk_pcie_setup_phy(struct advk_pcie *pcie) + return ret; + } + ++static int advk_pcie_suspend(struct device *dev) ++{ ++ struct advk_pcie *pcie = dev_get_drvdata(dev); ++ ++ advk_pcie_disable_phy(pcie); ++ ++ clk_disable_unprepare(pcie->clk); ++ ++ return 0; ++} ++ ++static int advk_pcie_resume(struct device *dev) ++{ ++ struct advk_pcie *pcie = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_prepare_enable(pcie->clk); ++ if (ret) ++ return ret; ++ ++ ret = advk_pcie_enable_phy(pcie); ++ if (ret) ++ return ret; ++ ++ advk_pcie_setup_hw(pcie); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops advk_pcie_dev_pm_ops = { ++ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(advk_pcie_suspend, advk_pcie_resume) ++}; ++ + static int advk_pcie_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -2184,6 +2217,7 @@ static struct platform_driver advk_pcie_driver = { + .driver = { + .name = "advk-pcie", + .of_match_table = advk_pcie_of_match_table, ++ .pm = &advk_pcie_dev_pm_ops, + }, + .probe = advk_pcie_probe, + .remove = advk_pcie_remove, +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0025-PCI-aardvark-Replace-custom-PCIE_CORE_ERR_CAPCTL_-ma.patch b/nixos/modules/kernel-patches/0025-PCI-aardvark-Replace-custom-PCIE_CORE_ERR_CAPCTL_-ma.patch new file mode 100644 index 0000000..8fee40f --- /dev/null +++ b/nixos/modules/kernel-patches/0025-PCI-aardvark-Replace-custom-PCIE_CORE_ERR_CAPCTL_-ma.patch @@ -0,0 +1,51 @@ +From a293a3635908a9af07f9ec5e3e01ceb936058710 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Thu, 1 Sep 2022 11:32:28 +0200 +Subject: [PATCH 25/96] PCI: aardvark: Replace custom PCIE_CORE_ERR_CAPCTL_* + macros by linux/pci_regs.h macros +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Kernel already has these macros defined under different names. + +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 13 +++---------- + 1 file changed, 3 insertions(+), 10 deletions(-) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index a042bfe70d69..635f236232bb 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -38,11 +38,6 @@ + #define PCIE_CORE_SSDEV_ID_REG 0x2c + #define PCIE_CORE_PCIEXP_CAP 0xc0 + #define PCIE_CORE_PCIERR_CAP 0x100 +-#define PCIE_CORE_ERR_CAPCTL_REG 0x118 +-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) +-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6) +-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK BIT(7) +-#define PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV BIT(8) + /* PIO registers base address and register offsets */ + #define PIO_BASE_ADDR 0x4000 + #define PIO_CTRL (PIO_BASE_ADDR + 0x0) +@@ -590,11 +585,9 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) + advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); + + /* Set Advanced Error Capabilities and Control PF0 register */ +- reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX | +- PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN | +- PCIE_CORE_ERR_CAPCTL_ECRC_CHCK | +- PCIE_CORE_ERR_CAPCTL_ECRC_CHCK_RCV; +- advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG); ++ reg = PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_GENE | ++ PCI_ERR_CAP_ECRC_CHKC | PCI_ERR_CAP_ECRC_CHKE; ++ advk_writel(pcie, reg, PCIE_CORE_PCIERR_CAP + PCI_ERR_CAP); + + /* Set PCIe Device Control register */ + reg = advk_readl(pcie, PCIE_CORE_PCIEXP_CAP + PCI_EXP_DEVCTL); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0026-PCI-aardvark-Don-t-write-read-only-bits-explicitly-i.patch b/nixos/modules/kernel-patches/0026-PCI-aardvark-Don-t-write-read-only-bits-explicitly-i.patch new file mode 100644 index 0000000..0a248e4 --- /dev/null +++ b/nixos/modules/kernel-patches/0026-PCI-aardvark-Don-t-write-read-only-bits-explicitly-i.patch @@ -0,0 +1,39 @@ +From 3d3bb1ce67148f05ae3f42d9642bae8011919800 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Thu, 18 Aug 2022 15:51:38 +0200 +Subject: [PATCH 26/96] PCI: aardvark: Don't write read-only bits explicitly in + PCI_ERR_CAP register +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The bits PCI_ERR_CAP_ECRC_GENC and PCI_ERR_CAP_ECRC_CHKC are read only, +reporting the capability of ECRC. Don't write them explicitly, instead +read the register (where they are set), and add the bits that enable +these features. + +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 635f236232bb..93e2e0234df6 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -584,9 +584,9 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) + reg &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); + +- /* Set Advanced Error Capabilities and Control PF0 register */ +- reg = PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_GENE | +- PCI_ERR_CAP_ECRC_CHKC | PCI_ERR_CAP_ECRC_CHKE; ++ /* Enable generation and checking of ECRC on Root Bridge */ ++ reg = advk_readl(pcie, PCIE_CORE_PCIERR_CAP + PCI_ERR_CAP); ++ reg |= PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE; + advk_writel(pcie, reg, PCIE_CORE_PCIERR_CAP + PCI_ERR_CAP); + + /* Set PCIe Device Control register */ +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0027-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch b/nixos/modules/kernel-patches/0027-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch new file mode 100644 index 0000000..75579b1 --- /dev/null +++ b/nixos/modules/kernel-patches/0027-compiler.h-only-include-asm-rwonce.h-for-kernel-code.patch @@ -0,0 +1,37 @@ +From fffecea36f26cf21d4a7cf38d0697fe841d6a84f Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Thu, 22 Oct 2020 22:00:03 +0200 +Subject: [PATCH 27/96] compiler.h: only include asm/rwonce.h for kernel code + +This header file is not in uapi, which makes any user space code that includes +linux/compiler.h to fail with the error 'asm/rwonce.h: No such file or directory' + +Fixes: e506ea451254 ("compiler.h: Split {READ,WRITE}_ONCE definitions out into rwonce.h") +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/linux/compiler.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/include/linux/compiler.h b/include/linux/compiler.h +index 01ce94b58b42..a93693c632af 100644 +--- a/include/linux/compiler.h ++++ b/include/linux/compiler.h +@@ -213,6 +213,8 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, + #define function_nocfi(x) (x) + #endif + ++#include <asm/rwonce.h> ++ + #endif /* __KERNEL__ */ + + /* +@@ -245,6 +247,4 @@ static inline void *offset_to_ptr(const int *off) + */ + #define prevent_tail_call_optimization() mb() + +-#include <asm/rwonce.h> +- + #endif /* __LINUX_COMPILER_H */ +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0028-swab-use-stddefs.h-instead-of-compiler.h.patch b/nixos/modules/kernel-patches/0028-swab-use-stddefs.h-instead-of-compiler.h.patch new file mode 100644 index 0000000..2b49bb8 --- /dev/null +++ b/nixos/modules/kernel-patches/0028-swab-use-stddefs.h-instead-of-compiler.h.patch @@ -0,0 +1,26 @@ +From 36da7f38eabfcc0802786eb691cf8231cd4e496a Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 11:36:00 +0200 +Subject: [PATCH 28/96] swab: use stddefs.h instead of compiler.h + +Fix an issue with kernel headers that broke perf. +--- + include/uapi/linux/swab.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/uapi/linux/swab.h b/include/uapi/linux/swab.h +index 7272f85d6d6a..3736f2fe1541 100644 +--- a/include/uapi/linux/swab.h ++++ b/include/uapi/linux/swab.h +@@ -3,7 +3,7 @@ + #define _UAPI_LINUX_SWAB_H + + #include <linux/types.h> +-#include <linux/compiler.h> ++#include <linux/stddef.h> + #include <asm/bitsperlong.h> + #include <asm/swab.h> + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0029-net-sfp-move-quirk-handling-into-sfp.c.patch b/nixos/modules/kernel-patches/0029-net-sfp-move-quirk-handling-into-sfp.c.patch new file mode 100644 index 0000000..2c3d27e --- /dev/null +++ b/nixos/modules/kernel-patches/0029-net-sfp-move-quirk-handling-into-sfp.c.patch @@ -0,0 +1,299 @@ +From 36f747eca3edd3260b4f2fd2cd899af3cc2d9892 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Fri, 26 Aug 2022 08:43:30 +0100 +Subject: [PATCH 29/96] net: sfp: move quirk handling into sfp.c + +We need to handle more quirks than just those which affect the link +modes of the module. Move the quirk lookup into sfp.c, and pass the +quirk to sfp-bus.c + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +--- + drivers/net/phy/sfp-bus.c | 98 ++------------------------------------- + drivers/net/phy/sfp.c | 94 ++++++++++++++++++++++++++++++++++++- + drivers/net/phy/sfp.h | 9 +++- + 3 files changed, 104 insertions(+), 97 deletions(-) + +diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c +index 15aa5ac1ff49..82216c7bb470 100644 +--- a/drivers/net/phy/sfp-bus.c ++++ b/drivers/net/phy/sfp-bus.c +@@ -10,12 +10,6 @@ + + #include "sfp.h" + +-struct sfp_quirk { +- const char *vendor; +- const char *part; +- void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); +-}; +- + /** + * struct sfp_bus - internal representation of a sfp bus + */ +@@ -38,93 +32,6 @@ struct sfp_bus { + bool started; + }; + +-static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, +- unsigned long *modes) +-{ +- phylink_set(modes, 2500baseX_Full); +-} +- +-static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, +- unsigned long *modes) +-{ +- /* Ubiquiti U-Fiber Instant module claims that support all transceiver +- * types including 10G Ethernet which is not truth. So clear all claimed +- * modes and set only one mode which module supports: 1000baseX_Full. +- */ +- phylink_zero(modes); +- phylink_set(modes, 1000baseX_Full); +-} +- +-static const struct sfp_quirk sfp_quirks[] = { +- { +- // Alcatel Lucent G-010S-P can operate at 2500base-X, but +- // incorrectly report 2500MBd NRZ in their EEPROM +- .vendor = "ALCATELLUCENT", +- .part = "G010SP", +- .modes = sfp_quirk_2500basex, +- }, { +- // Alcatel Lucent G-010S-A can operate at 2500base-X, but +- // report 3.2GBd NRZ in their EEPROM +- .vendor = "ALCATELLUCENT", +- .part = "3FE46541AA", +- .modes = sfp_quirk_2500basex, +- }, { +- // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd +- // NRZ in their EEPROM +- .vendor = "HUAWEI", +- .part = "MA5671A", +- .modes = sfp_quirk_2500basex, +- }, { +- // Lantech 8330-262D-E can operate at 2500base-X, but +- // incorrectly report 2500MBd NRZ in their EEPROM +- .vendor = "Lantech", +- .part = "8330-262D-E", +- .modes = sfp_quirk_2500basex, +- }, { +- .vendor = "UBNT", +- .part = "UF-INSTANT", +- .modes = sfp_quirk_ubnt_uf_instant, +- }, +-}; +- +-static size_t sfp_strlen(const char *str, size_t maxlen) +-{ +- size_t size, i; +- +- /* Trailing characters should be filled with space chars */ +- for (i = 0, size = 0; i < maxlen; i++) +- if (str[i] != ' ') +- size = i + 1; +- +- return size; +-} +- +-static bool sfp_match(const char *qs, const char *str, size_t len) +-{ +- if (!qs) +- return true; +- if (strlen(qs) != len) +- return false; +- return !strncmp(qs, str, len); +-} +- +-static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) +-{ +- const struct sfp_quirk *q; +- unsigned int i; +- size_t vs, ps; +- +- vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); +- ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); +- +- for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) +- if (sfp_match(q->vendor, id->base.vendor_name, vs) && +- sfp_match(q->part, id->base.vendor_pn, ps)) +- return q; +- +- return NULL; +-} +- + /** + * sfp_parse_port() - Parse the EEPROM base ID, setting the port type + * @bus: a pointer to the &struct sfp_bus structure for the sfp module +@@ -786,12 +693,13 @@ void sfp_link_down(struct sfp_bus *bus) + } + EXPORT_SYMBOL_GPL(sfp_link_down); + +-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id) ++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, ++ const struct sfp_quirk *quirk) + { + const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus); + int ret = 0; + +- bus->sfp_quirk = sfp_lookup_quirk(id); ++ bus->sfp_quirk = quirk; + + if (ops && ops->module_insert) + ret = ops->module_insert(bus->upstream, id); +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index e7b0e12cc75b..8515685d4581 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -252,6 +252,8 @@ struct sfp { + unsigned int module_t_start_up; + bool tx_fault_ignore; + ++ const struct sfp_quirk *quirk; ++ + #if IS_ENABLED(CONFIG_HWMON) + struct sfp_diag diag; + struct delayed_work hwmon_probe; +@@ -308,6 +310,93 @@ static const struct of_device_id sfp_of_match[] = { + }; + MODULE_DEVICE_TABLE(of, sfp_of_match); + ++static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, ++ unsigned long *modes) ++{ ++ linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseX_Full_BIT, modes); ++} ++ ++static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id, ++ unsigned long *modes) ++{ ++ /* Ubiquiti U-Fiber Instant module claims that support all transceiver ++ * types including 10G Ethernet which is not truth. So clear all claimed ++ * modes and set only one mode which module supports: 1000baseX_Full. ++ */ ++ linkmode_zero(modes); ++ linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT, modes); ++} ++ ++static const struct sfp_quirk sfp_quirks[] = { ++ { ++ // Alcatel Lucent G-010S-P can operate at 2500base-X, but ++ // incorrectly report 2500MBd NRZ in their EEPROM ++ .vendor = "ALCATELLUCENT", ++ .part = "G010SP", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ // Alcatel Lucent G-010S-A can operate at 2500base-X, but ++ // report 3.2GBd NRZ in their EEPROM ++ .vendor = "ALCATELLUCENT", ++ .part = "3FE46541AA", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd ++ // NRZ in their EEPROM ++ .vendor = "HUAWEI", ++ .part = "MA5671A", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ // Lantech 8330-262D-E can operate at 2500base-X, but ++ // incorrectly report 2500MBd NRZ in their EEPROM ++ .vendor = "Lantech", ++ .part = "8330-262D-E", ++ .modes = sfp_quirk_2500basex, ++ }, { ++ .vendor = "UBNT", ++ .part = "UF-INSTANT", ++ .modes = sfp_quirk_ubnt_uf_instant, ++ }, ++}; ++ ++static size_t sfp_strlen(const char *str, size_t maxlen) ++{ ++ size_t size, i; ++ ++ /* Trailing characters should be filled with space chars */ ++ for (i = 0, size = 0; i < maxlen; i++) ++ if (str[i] != ' ') ++ size = i + 1; ++ ++ return size; ++} ++ ++static bool sfp_match(const char *qs, const char *str, size_t len) ++{ ++ if (!qs) ++ return true; ++ if (strlen(qs) != len) ++ return false; ++ return !strncmp(qs, str, len); ++} ++ ++static const struct sfp_quirk *sfp_lookup_quirk(const struct sfp_eeprom_id *id) ++{ ++ const struct sfp_quirk *q; ++ unsigned int i; ++ size_t vs, ps; ++ ++ vs = sfp_strlen(id->base.vendor_name, ARRAY_SIZE(id->base.vendor_name)); ++ ps = sfp_strlen(id->base.vendor_pn, ARRAY_SIZE(id->base.vendor_pn)); ++ ++ for (i = 0, q = sfp_quirks; i < ARRAY_SIZE(sfp_quirks); i++, q++) ++ if (sfp_match(q->vendor, id->base.vendor_name, vs) && ++ sfp_match(q->part, id->base.vendor_pn, ps)) ++ return q; ++ ++ return NULL; ++} ++ + static unsigned long poll_jiffies; + + static unsigned int sfp_gpio_get_state(struct sfp *sfp) +@@ -1963,6 +2052,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + else + sfp->tx_fault_ignore = false; + ++ sfp->quirk = sfp_lookup_quirk(&id); ++ + return 0; + } + +@@ -2075,7 +2166,8 @@ static void sfp_sm_module(struct sfp *sfp, unsigned int event) + break; + + /* Report the module insertion to the upstream device */ +- err = sfp_module_insert(sfp->sfp_bus, &sfp->id); ++ err = sfp_module_insert(sfp->sfp_bus, &sfp->id, ++ sfp->quirk); + if (err < 0) { + sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0); + break; +diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h +index 27226535c72b..03f1d47fe6ca 100644 +--- a/drivers/net/phy/sfp.h ++++ b/drivers/net/phy/sfp.h +@@ -6,6 +6,12 @@ + + struct sfp; + ++struct sfp_quirk { ++ const char *vendor; ++ const char *part; ++ void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); ++}; ++ + struct sfp_socket_ops { + void (*attach)(struct sfp *sfp); + void (*detach)(struct sfp *sfp); +@@ -23,7 +29,8 @@ int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev); + void sfp_remove_phy(struct sfp_bus *bus); + void sfp_link_up(struct sfp_bus *bus); + void sfp_link_down(struct sfp_bus *bus); +-int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id); ++int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id, ++ const struct sfp_quirk *quirk); + void sfp_module_remove(struct sfp_bus *bus); + int sfp_module_start(struct sfp_bus *bus); + void sfp_module_stop(struct sfp_bus *bus); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0030-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch b/nixos/modules/kernel-patches/0030-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch new file mode 100644 index 0000000..846e443 --- /dev/null +++ b/nixos/modules/kernel-patches/0030-net-sfp-move-Alcatel-Lucent-3FE46541AA-fixup.patch @@ -0,0 +1,75 @@ +From dc751e277ce69c792de8627a24833c9668828e21 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Fri, 26 Aug 2022 08:43:35 +0100 +Subject: [PATCH 30/96] net: sfp: move Alcatel Lucent 3FE46541AA fixup + +Add a new fixup mechanism to the SFP quirks, and use it for this +module. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +--- + drivers/net/phy/sfp.c | 14 +++++++++----- + drivers/net/phy/sfp.h | 1 + + 2 files changed, 10 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 8515685d4581..37b29e1463bf 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -310,6 +310,11 @@ static const struct of_device_id sfp_of_match[] = { + }; + MODULE_DEVICE_TABLE(of, sfp_of_match); + ++static void sfp_fixup_long_startup(struct sfp *sfp) ++{ ++ sfp->module_t_start_up = T_START_UP_BAD_GPON; ++} ++ + static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, + unsigned long *modes) + { +@@ -340,6 +345,7 @@ static const struct sfp_quirk sfp_quirks[] = { + .vendor = "ALCATELLUCENT", + .part = "3FE46541AA", + .modes = sfp_quirk_2500basex, ++ .fixup = sfp_fixup_long_startup, + }, { + // Huawei MA5671A can operate at 2500base-X, but report 1.2GBd + // NRZ in their EEPROM +@@ -2040,11 +2046,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + if (ret < 0) + return ret; + +- if (!memcmp(id.base.vendor_name, "ALCATELLUCENT ", 16) && +- !memcmp(id.base.vendor_pn, "3FE46541AA ", 16)) +- sfp->module_t_start_up = T_START_UP_BAD_GPON; +- else +- sfp->module_t_start_up = T_START_UP; ++ sfp->module_t_start_up = T_START_UP; + + if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) && + !memcmp(id.base.vendor_pn, "MA5671A ", 16)) +@@ -2053,6 +2055,8 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + sfp->tx_fault_ignore = false; + + sfp->quirk = sfp_lookup_quirk(&id); ++ if (sfp->quirk && sfp->quirk->fixup) ++ sfp->quirk->fixup(sfp); + + return 0; + } +diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h +index 03f1d47fe6ca..7ad06deae76c 100644 +--- a/drivers/net/phy/sfp.h ++++ b/drivers/net/phy/sfp.h +@@ -10,6 +10,7 @@ struct sfp_quirk { + const char *vendor; + const char *part; + void (*modes)(const struct sfp_eeprom_id *id, unsigned long *modes); ++ void (*fixup)(struct sfp *sfp); + }; + + struct sfp_socket_ops { +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0031-net-sfp-move-Huawei-MA5671A-fixup.patch b/nixos/modules/kernel-patches/0031-net-sfp-move-Huawei-MA5671A-fixup.patch new file mode 100644 index 0000000..3d54ea0 --- /dev/null +++ b/nixos/modules/kernel-patches/0031-net-sfp-move-Huawei-MA5671A-fixup.patch @@ -0,0 +1,52 @@ +From cdd0eac0bbbb07f5df4813d47ba7871d8836d750 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Fri, 26 Aug 2022 08:43:40 +0100 +Subject: [PATCH 31/96] net: sfp: move Huawei MA5671A fixup + +Move this module over to the new fixup mechanism. + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +--- + drivers/net/phy/sfp.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 37b29e1463bf..404ea1ba457a 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -315,6 +315,11 @@ static void sfp_fixup_long_startup(struct sfp *sfp) + sfp->module_t_start_up = T_START_UP_BAD_GPON; + } + ++static void sfp_fixup_ignore_tx_fault(struct sfp *sfp) ++{ ++ sfp->tx_fault_ignore = true; ++} ++ + static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id, + unsigned long *modes) + { +@@ -352,6 +357,7 @@ static const struct sfp_quirk sfp_quirks[] = { + .vendor = "HUAWEI", + .part = "MA5671A", + .modes = sfp_quirk_2500basex, ++ .fixup = sfp_fixup_ignore_tx_fault, + }, { + // Lantech 8330-262D-E can operate at 2500base-X, but + // incorrectly report 2500MBd NRZ in their EEPROM +@@ -2048,11 +2054,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + + sfp->module_t_start_up = T_START_UP; + +- if (!memcmp(id.base.vendor_name, "HUAWEI ", 16) && +- !memcmp(id.base.vendor_pn, "MA5671A ", 16)) +- sfp->tx_fault_ignore = true; +- else +- sfp->tx_fault_ignore = false; ++ sfp->tx_fault_ignore = false; + + sfp->quirk = sfp_lookup_quirk(&id); + if (sfp->quirk && sfp->quirk->fixup) +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0032-net-sfp-redo-soft-state-polling.patch b/nixos/modules/kernel-patches/0032-net-sfp-redo-soft-state-polling.patch new file mode 100644 index 0000000..0c7a4b1 --- /dev/null +++ b/nixos/modules/kernel-patches/0032-net-sfp-redo-soft-state-polling.patch @@ -0,0 +1,84 @@ +From 5bec210c1c4677ecef5962138c75cb514a079661 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" <rmk+kernel@armlinux.org.uk> +Date: Fri, 26 Aug 2022 08:48:20 +0100 +Subject: [PATCH 32/96] net: sfp: redo soft state polling + +Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk> +--- + drivers/net/phy/sfp.c | 35 ++++++++++++++++++++++++----------- + 1 file changed, 24 insertions(+), 11 deletions(-) + +diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c +index 404ea1ba457a..b213e9c2e456 100644 +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -234,6 +234,7 @@ struct sfp { + bool need_poll; + + struct mutex st_mutex; /* Protects state */ ++ unsigned int state_ignore_hw_mask; + unsigned int state_soft_mask; + unsigned int state; + struct delayed_work poll; +@@ -600,17 +601,18 @@ static void sfp_soft_set_state(struct sfp *sfp, unsigned int state) + static void sfp_soft_start_poll(struct sfp *sfp) + { + const struct sfp_eeprom_id *id = &sfp->id; ++ unsigned int mask = 0; + + sfp->state_soft_mask = 0; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE && +- !sfp->gpio[GPIO_TX_DISABLE]) +- sfp->state_soft_mask |= SFP_F_TX_DISABLE; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT && +- !sfp->gpio[GPIO_TX_FAULT]) +- sfp->state_soft_mask |= SFP_F_TX_FAULT; +- if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS && +- !sfp->gpio[GPIO_LOS]) +- sfp->state_soft_mask |= SFP_F_LOS; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE) ++ mask |= SFP_F_TX_DISABLE; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT) ++ mask |= SFP_F_TX_FAULT; ++ if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS) ++ mask |= SFP_F_LOS; ++ ++ // Poll the soft state for hardware pins we want to ignore ++ sfp->state_soft_mask = sfp->state_ignore_hw_mask & mask; + + if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) && + !sfp->need_poll) +@@ -624,10 +626,12 @@ static void sfp_soft_stop_poll(struct sfp *sfp) + + static unsigned int sfp_get_state(struct sfp *sfp) + { ++ unsigned int soft = sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT); + unsigned int state = sfp->get_state(sfp); + +- if (state & SFP_F_PRESENT && +- sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT)) ++ state &= ~sfp->state_ignore_hw_mask; ++ ++ if (state & SFP_F_PRESENT && soft) + state |= sfp_soft_get_state(sfp); + + return state; +@@ -2052,6 +2056,15 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report) + if (ret < 0) + return ret; + ++ /* Initialise state bits to ignore from hardware */ ++ sfp->state_ignore_hw_mask = 0; ++ if (!sfp->gpio[GPIO_TX_DISABLE]) ++ sfp->state_ignore_hw_mask |= SFP_F_TX_DISABLE; ++ if (!sfp->gpio[GPIO_TX_FAULT]) ++ sfp->state_ignore_hw_mask |= SFP_F_TX_FAULT; ++ if (!sfp->gpio[GPIO_LOS]) ++ sfp->state_ignore_hw_mask |= SFP_F_LOS; ++ + sfp->module_t_start_up = T_START_UP; + + sfp->tx_fault_ignore = false; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0033-mm-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch b/nixos/modules/kernel-patches/0033-mm-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch new file mode 100644 index 0000000..55aa1d6 --- /dev/null +++ b/nixos/modules/kernel-patches/0033-mm-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch @@ -0,0 +1,92 @@ +From 4818a6d22f2ca39d148608e365209978a2be8c24 Mon Sep 17 00:00:00 2001 +From: Tobias Wolf <dev-NTEO@vplace.de> +Date: Tue, 27 Sep 2022 16:21:21 +0200 +Subject: [PATCH 33/96] mm: Fix alloc_node_mem_map with ARCH_PFN_OFFSET + calculation + +An rt288x (ralink) based router (Belkin F5D8235 v1) does not boot with any +kernel beyond version 4.3 resulting in: + +BUG: Bad page state in process swapper pfn:086ac + +bisect resulted in: + +a1c34a3bf00af2cede839879502e12dc68491ad5 is the first bad commit +commit a1c34a3bf00af2cede839879502e12dc68491ad5 +Author: Laura Abbott <laura@labbott.name> +Date: Thu Nov 5 18:48:46 2015 -0800 + + mm: Don't offset memmap for flatmem + + Srinivas Kandagatla reported bad page messages when trying to remove the + bottom 2MB on an ARM based IFC6410 board + + BUG: Bad page state in process swapper pfn:fffa8 + page:ef7fb500 count:0 mapcount:0 mapping: (null) index:0x0 + flags: 0x96640253(locked|error|dirty|active|arch_1|reclaim|mlocked) + page dumped because: PAGE_FLAGS_CHECK_AT_FREE flag(s) set + bad because of flags: + flags: 0x200041(locked|active|mlocked) + Modules linked in: + CPU: 0 PID: 0 Comm: swapper Not tainted 3.19.0-rc3-00007-g412f9ba-dirty +#816 + Hardware name: Qualcomm (Flattened Device Tree) + unwind_backtrace + show_stack + dump_stack + bad_page + free_pages_prepare + free_hot_cold_page + __free_pages + free_highmem_page + mem_init + start_kernel + Disabling lock debugging due to kernel taint + [...] +:040000 040000 2de013c372345fd471cd58f0553c9b38b0ef1cc4 +0a8156f848733dfa21e16c196dfb6c0a76290709 M mm + +This fix for ARM does not account ARCH_PFN_OFFSET for mem_map as later used by +page_to_pfn anymore. + +The following output was generated with two hacked in printk statements: + +printk("before %p vs. %p or %p\n", mem_map, mem_map - offset, mem_map - +(pgdat->node_start_pfn - ARCH_PFN_OFFSET)); + if (page_to_pfn(mem_map) != pgdat->node_start_pfn) + mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET); +printk("after %p\n", mem_map); + +Output: + +[ 0.000000] before 8861b280 vs. 8861b280 or 8851b280 +[ 0.000000] after 8851b280 + +As seen in the first line mem_map with subtraction of offset does not equal the +mem_map after subtraction of ARCH_PFN_OFFSET. + +After adding the offset of ARCH_PFN_OFFSET as well to mem_map as the +previously calculated offset is zero for the named platform it is able to boot +4.4 and 4.9-rc7 again. + +Signed-off-by: Tobias Wolf <dev-NTEO@vplace.de> +--- + mm/page_alloc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/mm/page_alloc.c b/mm/page_alloc.c +index cdf0e7d707c3..f3a69976b0b2 100644 +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -7698,7 +7698,7 @@ static void __init alloc_node_mem_map(struct pglist_data *pgdat) + if (pgdat == NODE_DATA(0)) { + mem_map = NODE_DATA(0)->node_mem_map; + if (page_to_pfn(mem_map) != pgdat->node_start_pfn) +- mem_map -= offset; ++ mem_map -= offset + (pgdat->node_start_pfn - ARCH_PFN_OFFSET); + } + #endif + } +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0034-rtc-rs5c372-support-alarms-up-to-1-week.patch b/nixos/modules/kernel-patches/0034-rtc-rs5c372-support-alarms-up-to-1-week.patch new file mode 100644 index 0000000..7ad61a4 --- /dev/null +++ b/nixos/modules/kernel-patches/0034-rtc-rs5c372-support-alarms-up-to-1-week.patch @@ -0,0 +1,104 @@ +From debf651897e47681bb72bfcefd6b308264ae4985 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20Cabanelas?= <dgcbueu@gmail.com> +Date: Tue, 27 Sep 2022 16:21:26 +0200 +Subject: [PATCH 34/96] rtc: rs5c372: support alarms up to 1 week +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The Ricoh R2221x, R2223x, RS5C372, RV5C387A chips can handle 1 week +alarms. + +Read the "wday" alarm register and convert it to a date to support up 1 +week in our driver. + +Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com> +--- + drivers/rtc/rtc-rs5c372.c | 48 ++++++++++++++++++++++++++++++++++----- + 1 file changed, 42 insertions(+), 6 deletions(-) + +diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c +index cb15983383f5..ab4b5209870d 100644 +--- a/drivers/rtc/rtc-rs5c372.c ++++ b/drivers/rtc/rtc-rs5c372.c +@@ -399,7 +399,9 @@ static int rs5c_read_alarm(struct device *dev, struct rtc_wkalrm *t) + { + struct i2c_client *client = to_i2c_client(dev); + struct rs5c372 *rs5c = i2c_get_clientdata(client); +- int status; ++ int status, wday_offs; ++ struct rtc_time rtc; ++ unsigned long alarm_secs; + + status = rs5c_get_regs(rs5c); + if (status < 0) +@@ -409,6 +411,30 @@ static int rs5c_read_alarm(struct device *dev, struct rtc_wkalrm *t) + t->time.tm_sec = 0; + t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f); + t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]); ++ t->time.tm_wday = ffs(rs5c->regs[RS5C_REG_ALARM_A_WDAY] & 0x7f) - 1; ++ ++ /* determine the day, month and year based on alarm wday, taking as a ++ * reference the current time from the rtc ++ */ ++ status = rs5c372_rtc_read_time(dev, &rtc); ++ if (status < 0) ++ return status; ++ ++ wday_offs = t->time.tm_wday - rtc.tm_wday; ++ alarm_secs = mktime64(rtc.tm_year + 1900, ++ rtc.tm_mon + 1, ++ rtc.tm_mday + wday_offs, ++ t->time.tm_hour, ++ t->time.tm_min, ++ t->time.tm_sec); ++ ++ if (wday_offs < 0 || (wday_offs == 0 && ++ (t->time.tm_hour < rtc.tm_hour || ++ (t->time.tm_hour == rtc.tm_hour && ++ t->time.tm_min <= rtc.tm_min)))) ++ alarm_secs += 7 * 86400; ++ ++ rtc_time64_to_tm(alarm_secs, &t->time); + + /* ... and status */ + t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE); +@@ -423,12 +449,20 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t) + struct rs5c372 *rs5c = i2c_get_clientdata(client); + int status, addr, i; + unsigned char buf[3]; ++ struct rtc_time rtc_tm; ++ unsigned long rtc_secs, alarm_secs; + +- /* only handle up to 24 hours in the future, like RTC_ALM_SET */ +- if (t->time.tm_mday != -1 +- || t->time.tm_mon != -1 +- || t->time.tm_year != -1) ++ /* chip only can handle alarms up to one week in the future*/ ++ status = rs5c372_rtc_read_time(dev, &rtc_tm); ++ if (status) ++ return status; ++ rtc_secs = rtc_tm_to_time64(&rtc_tm); ++ alarm_secs = rtc_tm_to_time64(&t->time); ++ if (alarm_secs >= rtc_secs + 7 * 86400) { ++ dev_err(dev, "%s: alarm maximum is one week in the future (%d)\n", ++ __func__, status); + return -EINVAL; ++ } + + /* REVISIT: round up tm_sec */ + +@@ -449,7 +483,9 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t) + /* set alarm */ + buf[0] = bin2bcd(t->time.tm_min); + buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour); +- buf[2] = 0x7f; /* any/all days */ ++ /* each bit is the day of the week, 0x7f means all days */ ++ buf[2] = (t->time.tm_wday >= 0 && t->time.tm_wday < 7) ? ++ BIT(t->time.tm_wday) : 0x7f; + + for (i = 0; i < sizeof(buf); i++) { + addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0035-rtc-rs5c372-let-the-alarm-to-be-used-as-wakeup-sourc.patch b/nixos/modules/kernel-patches/0035-rtc-rs5c372-let-the-alarm-to-be-used-as-wakeup-sourc.patch new file mode 100644 index 0000000..ec3186a --- /dev/null +++ b/nixos/modules/kernel-patches/0035-rtc-rs5c372-let-the-alarm-to-be-used-as-wakeup-sourc.patch @@ -0,0 +1,80 @@ +From 406867ec540eff2b484512063e8f9ade22daac07 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20Cabanelas?= <dgcbueu@gmail.com> +Date: Tue, 27 Sep 2022 16:21:26 +0200 +Subject: [PATCH 35/96] rtc: rs5c372: let the alarm to be used as wakeup source +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently there is no use for the interrupts on the rs5c372 RTC and the +wakealarm isn't enabled. There are some devices like NASes which use this +RTC to wake up from the power off state when the INTR pin is activated by +the alarm clock. + +Enable the alarm and let to be used as a wakeup source. + +Tested on a Buffalo LS421DE NAS. + +Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com> +--- + drivers/rtc/rtc-rs5c372.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c +index ab4b5209870d..5a1db45998c2 100644 +--- a/drivers/rtc/rtc-rs5c372.c ++++ b/drivers/rtc/rtc-rs5c372.c +@@ -833,6 +833,7 @@ static int rs5c372_probe(struct i2c_client *client, + int err = 0; + int smbus_mode = 0; + struct rs5c372 *rs5c372; ++ bool rs5c372_can_wakeup_device = false; + + dev_dbg(&client->dev, "%s\n", __func__); + +@@ -868,6 +869,12 @@ static int rs5c372_probe(struct i2c_client *client, + else + rs5c372->type = id->driver_data; + ++#ifdef CONFIG_OF ++ if(of_property_read_bool(client->dev.of_node, ++ "wakeup-source")) ++ rs5c372_can_wakeup_device = true; ++#endif ++ + /* we read registers 0x0f then 0x00-0x0f; skip the first one */ + rs5c372->regs = &rs5c372->buf[1]; + rs5c372->smbus = smbus_mode; +@@ -901,6 +908,8 @@ static int rs5c372_probe(struct i2c_client *client, + goto exit; + } + ++ rs5c372->has_irq = 1; ++ + /* if the oscillator lost power and no other software (like + * the bootloader) set it up, do it here. + * +@@ -927,6 +936,10 @@ static int rs5c372_probe(struct i2c_client *client, + ); + + /* REVISIT use client->irq to register alarm irq ... */ ++ if (rs5c372_can_wakeup_device) { ++ device_init_wakeup(&client->dev, true); ++ } ++ + rs5c372->rtc = devm_rtc_device_register(&client->dev, + rs5c372_driver.driver.name, + &rs5c372_rtc_ops, THIS_MODULE); +@@ -940,6 +953,9 @@ static int rs5c372_probe(struct i2c_client *client, + if (err) + goto exit; + ++ /* the rs5c372 alarm only supports a minute accuracy */ ++ rs5c372->rtc->uie_unsupported = 1; ++ + return 0; + + exit: +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0036-kernel-add-a-config-option-for-keeping-the-kallsyms-.patch b/nixos/modules/kernel-patches/0036-kernel-add-a-config-option-for-keeping-the-kallsyms-.patch new file mode 100644 index 0000000..4597baa --- /dev/null +++ b/nixos/modules/kernel-patches/0036-kernel-add-a-config-option-for-keeping-the-kallsyms-.patch @@ -0,0 +1,133 @@ +From 0ef054c3e7d7423d0f18edd7a29f62a9c6a811b4 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:21:26 +0200 +Subject: [PATCH 36/96] kernel: add a config option for keeping the kallsyms + table uncompressed, saving ~9kb kernel size after lzma on ar71xx + +[john@phrozen.org: added to my upstream queue 30.12.2016] +lede-commit: e0e3509b5ce2ccf93d4d67ea907613f5f7ec2eed +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + init/Kconfig | 11 +++++++++++ + kernel/kallsyms.c | 8 ++++++++ + scripts/kallsyms.c | 12 ++++++++++++ + scripts/link-vmlinux.sh | 4 ++++ + 4 files changed, 35 insertions(+) + +diff --git a/init/Kconfig b/init/Kconfig +index c7900e8975f1..4d5f1ee66139 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1475,6 +1475,17 @@ config SYSCTL_ARCH_UNALIGN_ALLOW + the unaligned access emulation. + see arch/parisc/kernel/unaligned.c for reference + ++config KALLSYMS_UNCOMPRESSED ++ bool "Keep kallsyms uncompressed" ++ depends on KALLSYMS ++ help ++ Normally kallsyms contains compressed symbols (using a token table), ++ reducing the uncompressed kernel image size. Keeping the symbol table ++ uncompressed significantly improves the size of this part in compressed ++ kernel images. ++ ++ Say N unless you need compressed kernel images to be small. ++ + config HAVE_PCSPKR_PLATFORM + bool + +diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c +index fbdf8d3279ac..f30ecfedd293 100644 +--- a/kernel/kallsyms.c ++++ b/kernel/kallsyms.c +@@ -81,6 +81,11 @@ static unsigned int kallsyms_expand_symbol(unsigned int off, + * For every byte on the compressed symbol data, copy the table + * entry for that byte. + */ ++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED ++ memcpy(result, data + 1, len - 1); ++ result += len - 1; ++ len = 0; ++#endif + while (len) { + tptr = &kallsyms_token_table[kallsyms_token_index[*data]]; + data++; +@@ -113,6 +118,9 @@ static unsigned int kallsyms_expand_symbol(unsigned int off, + */ + static char kallsyms_get_symbol_type(unsigned int off) + { ++#ifdef CONFIG_KALLSYMS_UNCOMPRESSED ++ return kallsyms_names[off + 1]; ++#endif + /* + * Get just the first code, look it up in the token table, + * and return the first char from this token. +diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c +index f18e6dfc68c5..262a3799bae1 100644 +--- a/scripts/kallsyms.c ++++ b/scripts/kallsyms.c +@@ -58,6 +58,7 @@ static struct addr_range percpu_range = { + static struct sym_entry **table; + static unsigned int table_size, table_cnt; + static int all_symbols; ++static int uncompressed; + static int absolute_percpu; + static int base_relative; + +@@ -487,6 +488,9 @@ static void write_src(void) + + free(markers); + ++ if (uncompressed) ++ return; ++ + output_label("kallsyms_token_table"); + off = 0; + for (i = 0; i < 256; i++) { +@@ -538,6 +542,9 @@ static unsigned char *find_token(unsigned char *str, int len, + { + int i; + ++ if (uncompressed) ++ return NULL; ++ + for (i = 0; i < len - 1; i++) { + if (str[i] == token[0] && str[i+1] == token[1]) + return &str[i]; +@@ -610,6 +617,9 @@ static void optimize_result(void) + { + int i, best; + ++ if (uncompressed) ++ return; ++ + /* using the '\0' symbol last allows compress_symbols to use standard + * fast string functions */ + for (i = 255; i >= 0; i--) { +@@ -774,6 +784,8 @@ int main(int argc, char **argv) + absolute_percpu = 1; + else if (strcmp(argv[i], "--base-relative") == 0) + base_relative = 1; ++ else if (strcmp(argv[i], "--uncompressed") == 0) ++ uncompressed = 1; + else + usage(); + } +diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh +index eecc1863e556..8b8c9000892a 100755 +--- a/scripts/link-vmlinux.sh ++++ b/scripts/link-vmlinux.sh +@@ -156,6 +156,10 @@ kallsyms() + kallsymopt="${kallsymopt} --base-relative" + fi + ++ if [ -n "${CONFIG_KALLSYMS_UNCOMPRESSED}" ]; then ++ kallsymopt="${kallsymopt} --uncompressed" ++ fi ++ + info KSYMS ${2} + ${NM} -n ${1} | scripts/kallsyms ${kallsymopt} > ${2} + } +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0037-kernel-when-KALLSYMS-is-disabled-print-module-addres.patch b/nixos/modules/kernel-patches/0037-kernel-when-KALLSYMS-is-disabled-print-module-addres.patch new file mode 100644 index 0000000..27cebc9 --- /dev/null +++ b/nixos/modules/kernel-patches/0037-kernel-when-KALLSYMS-is-disabled-print-module-addres.patch @@ -0,0 +1,49 @@ +From 045761a60c8beb28e82b68e284bf8e395036688f Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:21:26 +0200 +Subject: [PATCH 37/96] kernel: when KALLSYMS is disabled, print module address + + size for matching backtrace entries + +[john@phrozen.org: felix will add this to his upstream queue] + +lede-commit 53827cdc824556cda910b23ce5030c363b8f1461 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + lib/vsprintf.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/lib/vsprintf.c b/lib/vsprintf.c +index 3c1853a9d1c0..9803f88d7d21 100644 +--- a/lib/vsprintf.c ++++ b/lib/vsprintf.c +@@ -980,8 +980,10 @@ char *symbol_string(char *buf, char *end, void *ptr, + struct printf_spec spec, const char *fmt) + { + unsigned long value; +-#ifdef CONFIG_KALLSYMS + char sym[KSYM_SYMBOL_LEN]; ++#ifndef CONFIG_KALLSYMS ++ struct module *mod; ++ int len; + #endif + + if (fmt[1] == 'R') +@@ -1002,8 +1004,14 @@ char *symbol_string(char *buf, char *end, void *ptr, + + return string_nocheck(buf, end, sym, spec); + #else +- return special_hex_number(buf, end, value, sizeof(void *)); ++ len = snprintf(sym, sizeof(sym), "0x%lx", value); ++ mod = __module_address(value); ++ if (mod) ++ snprintf(sym + len, sizeof(sym) - len, " [%s@%p+0x%x]", ++ mod->name, mod->core_layout.base, ++ mod->core_layout.size); + #endif ++ return string(buf, end, sym, spec); + } + + static const struct printf_spec default_str_spec = { +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0038-usr-sanitize-deps_initramfs-list.patch b/nixos/modules/kernel-patches/0038-usr-sanitize-deps_initramfs-list.patch new file mode 100644 index 0000000..684905c --- /dev/null +++ b/nixos/modules/kernel-patches/0038-usr-sanitize-deps_initramfs-list.patch @@ -0,0 +1,37 @@ +From 994a9ab083bc730f0057131d8fde7cb0dc7b490f Mon Sep 17 00:00:00 2001 +From: Gabor Juhos <juhosg@openwrt.org> +Date: Tue, 27 Sep 2022 16:21:26 +0200 +Subject: [PATCH 38/96] usr: sanitize deps_initramfs list + +If any filename in the intramfs dependency +list contains a colon, that causes a kernel +build error like this: + +/devel/openwrt/build_dir/linux-ar71xx_generic/linux-3.6.6/usr/Makefile:58: *** multiple target patterns. Stop. +make[5]: *** [usr] Error 2 + +Fix it by removing such filenames from the +deps_initramfs list. + +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + usr/Makefile | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/usr/Makefile b/usr/Makefile +index 59d9e8b07a01..5a8a00cf8580 100644 +--- a/usr/Makefile ++++ b/usr/Makefile +@@ -56,6 +56,8 @@ hostprogs := gen_init_cpio + # The dependency list is generated by gen_initramfs.sh -l + -include $(obj)/.initramfs_data.cpio.d + ++deps_initramfs := $(foreach v,$(deps_initramfs),$(if $(findstring :,$(v)),,$(v))) ++ + # do not try to update files included in initramfs + $(deps_initramfs): ; + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0039-hack-net-wireless-make-the-wl12xx-glue-code-availabl.patch b/nixos/modules/kernel-patches/0039-hack-net-wireless-make-the-wl12xx-glue-code-availabl.patch new file mode 100644 index 0000000..38ce38d --- /dev/null +++ b/nixos/modules/kernel-patches/0039-hack-net-wireless-make-the-wl12xx-glue-code-availabl.patch @@ -0,0 +1,27 @@ +From 7484653f41a82ac2dae41e686645f423e24f19a8 Mon Sep 17 00:00:00 2001 +From: Imre Kaloz <kaloz@openwrt.org> +Date: Tue, 27 Sep 2022 16:21:26 +0200 +Subject: [PATCH 39/96] hack: net: wireless: make the wl12xx glue code + available with compat-wireless, too + +Signed-off-by: Imre Kaloz <kaloz@openwrt.org> +--- + drivers/net/wireless/ti/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ti/Kconfig b/drivers/net/wireless/ti/Kconfig +index 7c0b17a76fe2..9f0edc5aca27 100644 +--- a/drivers/net/wireless/ti/Kconfig ++++ b/drivers/net/wireless/ti/Kconfig +@@ -20,7 +20,7 @@ source "drivers/net/wireless/ti/wlcore/Kconfig" + + config WILINK_PLATFORM_DATA + bool "TI WiLink platform data" +- depends on WLCORE_SDIO || WL1251_SDIO ++ depends on WLCORE_SDIO || WL1251_SDIO || ARCH_OMAP2PLUS + default y + help + Small platform data bit needed to pass data to the sdio modules. +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0040-generic-platform-mikrotik-build-bits-5.4.patch b/nixos/modules/kernel-patches/0040-generic-platform-mikrotik-build-bits-5.4.patch new file mode 100644 index 0000000..15f615e --- /dev/null +++ b/nixos/modules/kernel-patches/0040-generic-platform-mikrotik-build-bits-5.4.patch @@ -0,0 +1,38 @@ +From 6abb32c1bb4fdf810faba9b732582406578ca104 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= <hacks@slashdirt.org> +Date: Sat, 28 Mar 2020 12:11:50 +0100 +Subject: [PATCH 40/96] generic: platform/mikrotik build bits (5.4) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch adds platform/mikrotik kernel build bits + +Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org> +--- + drivers/platform/Kconfig | 2 ++ + drivers/platform/Makefile | 1 + + 2 files changed, 3 insertions(+) + +diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig +index 18fc6a08569e..ff069e4a2625 100644 +--- a/drivers/platform/Kconfig ++++ b/drivers/platform/Kconfig +@@ -15,3 +15,5 @@ source "drivers/platform/mellanox/Kconfig" + source "drivers/platform/olpc/Kconfig" + + source "drivers/platform/surface/Kconfig" ++ ++source "drivers/platform/mikrotik/Kconfig" +diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile +index 4de08ef4ec9d..9cf599863f71 100644 +--- a/drivers/platform/Makefile ++++ b/drivers/platform/Makefile +@@ -10,3 +10,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/ + obj-$(CONFIG_GOLDFISH) += goldfish/ + obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ + obj-$(CONFIG_SURFACE_PLATFORMS) += surface/ ++obj-$(CONFIG_MIKROTIK) += mikrotik/ +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0041-fix-errors-in-unresolved-weak-symbols-on-arm.patch b/nixos/modules/kernel-patches/0041-fix-errors-in-unresolved-weak-symbols-on-arm.patch new file mode 100644 index 0000000..3e86460 --- /dev/null +++ b/nixos/modules/kernel-patches/0041-fix-errors-in-unresolved-weak-symbols-on-arm.patch @@ -0,0 +1,29 @@ +From 5e60b036c4ea403bd96eb4eac0f711cd38c9e766 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:21:35 +0200 +Subject: [PATCH 41/96] fix errors in unresolved weak symbols on arm + +lede-commit: 570699d4838a907c3ef9f2819bf19eb72997b32f +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + arch/arm/kernel/module.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c +index d59c36dc0494..200a31b0acad 100644 +--- a/arch/arm/kernel/module.c ++++ b/arch/arm/kernel/module.c +@@ -146,6 +146,10 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, + return -ENOEXEC; + } + ++ if ((IS_ERR_VALUE(sym->st_value) || !sym->st_value) && ++ ELF_ST_BIND(sym->st_info) == STB_WEAK) ++ continue; ++ + loc = dstsec->sh_addr + rel->r_offset; + + switch (ELF32_R_TYPE(rel->r_info)) { +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0042-arc-add-OWRTDTB-section.patch b/nixos/modules/kernel-patches/0042-arc-add-OWRTDTB-section.patch new file mode 100644 index 0000000..402ea47 --- /dev/null +++ b/nixos/modules/kernel-patches/0042-arc-add-OWRTDTB-section.patch @@ -0,0 +1,93 @@ +From ec54842bfce8240815a9ce7a1db0935851fe9ea8 Mon Sep 17 00:00:00 2001 +From: Evgeniy Didin <Evgeniy.Didin@synopsys.com> +Date: Fri, 15 Mar 2019 18:53:38 +0300 +Subject: [PATCH 42/96] arc add OWRTDTB section + +This change allows OpenWRT to patch resulting kernel binary with +external .dtb. + +That allows us to re-use exactky the same vmlinux on different boards +given its ARC core configurations match (at least cache line sizes etc). + +""patch-dtb" searches for ASCII "OWRTDTB:" strign and copies external +.dtb right after it, keeping the string in place. + +Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> +Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com> +Signed-off-by: Evgeniy Didin <Evgeniy.Didin@synopsys.com> +--- + arch/arc/kernel/head.S | 10 ++++++++++ + arch/arc/kernel/setup.c | 4 +++- + arch/arc/kernel/vmlinux.lds.S | 13 +++++++++++++ + 3 files changed, 26 insertions(+), 1 deletion(-) + +diff --git a/arch/arc/kernel/head.S b/arch/arc/kernel/head.S +index 9152782444b5..167d81ce6ab7 100644 +--- a/arch/arc/kernel/head.S ++++ b/arch/arc/kernel/head.S +@@ -88,6 +88,16 @@ + DSP_EARLY_INIT + .endm + ++ ; Here "patch-dtb" will embed external .dtb ++ ; Note "patch-dtb" searches for ASCII "OWRTDTB:" string ++ ; and pastes .dtb right after it, hense the string precedes ++ ; __image_dtb symbol. ++ .section .owrt, "aw",@progbits ++ .ascii "OWRTDTB:" ++ENTRY(__image_dtb) ++ .fill 0x4000 ++END(__image_dtb) ++ + .section .init.text, "ax",@progbits + + ;---------------------------------------------------------------- +diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c +index 41f07b3e594e..d0c3e7817022 100644 +--- a/arch/arc/kernel/setup.c ++++ b/arch/arc/kernel/setup.c +@@ -495,6 +495,8 @@ static inline bool uboot_arg_invalid(unsigned long addr) + /* We always pass 0 as magic from U-boot */ + #define UBOOT_MAGIC_VALUE 0 + ++extern struct boot_param_header __image_dtb; ++ + void __init handle_uboot_args(void) + { + bool use_embedded_dtb = true; +@@ -533,7 +535,7 @@ void __init handle_uboot_args(void) + ignore_uboot_args: + + if (use_embedded_dtb) { +- machine_desc = setup_machine_fdt(__dtb_start); ++ machine_desc = setup_machine_fdt(&__image_dtb); + if (!machine_desc) + panic("Embedded DT invalid\n"); + } +diff --git a/arch/arc/kernel/vmlinux.lds.S b/arch/arc/kernel/vmlinux.lds.S +index 529ae50f9fe2..ecdcd6842c0d 100644 +--- a/arch/arc/kernel/vmlinux.lds.S ++++ b/arch/arc/kernel/vmlinux.lds.S +@@ -27,6 +27,19 @@ SECTIONS + + . = CONFIG_LINUX_LINK_BASE; + ++ /* ++ * In OpenWRT we want to patch built binary embedding .dtb of choice. ++ * This is implemented with "patch-dtb" utility which searches for ++ * "OWRTDTB:" string in first 16k of image and if it is found ++ * copies .dtb right after mentioned string. ++ * ++ * Note: "OWRTDTB:" won't be overwritten with .dtb, .dtb will follow it. ++ */ ++ .owrt : { ++ *(.owrt) ++ . = ALIGN(PAGE_SIZE); ++ } ++ + _int_vec_base_lds = .; + .vector : { + *(.vector) +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0043-arc-enable-unaligned-access-in-kernel-mode.patch b/nixos/modules/kernel-patches/0043-arc-enable-unaligned-access-in-kernel-mode.patch new file mode 100644 index 0000000..95d86d4 --- /dev/null +++ b/nixos/modules/kernel-patches/0043-arc-enable-unaligned-access-in-kernel-mode.patch @@ -0,0 +1,31 @@ +From e4a3cb7256682f9f2e0fd2b3c5e06441052a9822 Mon Sep 17 00:00:00 2001 +From: Alexey Brodkin <abrodkin@synopsys.com> +Date: Tue, 27 Sep 2022 16:21:35 +0200 +Subject: [PATCH 43/96] arc: enable unaligned access in kernel mode + +This enables misaligned access handling even in kernel mode. +Some wireless drivers (ath9k-htc and mt7601u) use misaligned accesses +here and there and to cope with that without fixing stuff in the drivers +we're just gracefully handling it on ARC. + +Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com> +--- + arch/arc/kernel/unaligned.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c +index 99a9b92ed98d..a4da2cbcac97 100644 +--- a/arch/arc/kernel/unaligned.c ++++ b/arch/arc/kernel/unaligned.c +@@ -202,7 +202,7 @@ int misaligned_fixup(unsigned long address, struct pt_regs *regs, + char buf[TASK_COMM_LEN]; + + /* handle user mode only and only if enabled by sysadmin */ +- if (!user_mode(regs) || !unaligned_enabled) ++ if (!unaligned_enabled) + return 1; + + if (no_unaligned_warning) { +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0044-mtd-mtdsplit-support.patch b/nixos/modules/kernel-patches/0044-mtd-mtdsplit-support.patch new file mode 100644 index 0000000..cf98c20 --- /dev/null +++ b/nixos/modules/kernel-patches/0044-mtd-mtdsplit-support.patch @@ -0,0 +1,340 @@ +From 3048cf485cbb35e59865e1d841bfbb06a093987a Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 11:47:35 +0200 +Subject: [PATCH 44/96] mtd: mtdsplit support + +--- + drivers/mtd/Kconfig | 19 ++++ + drivers/mtd/Makefile | 2 + + drivers/mtd/mtdpart.c | 169 ++++++++++++++++++++++++++++----- + include/linux/mtd/mtd.h | 25 +++++ + include/linux/mtd/partitions.h | 7 ++ + 5 files changed, 197 insertions(+), 25 deletions(-) + +diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig +index 796a2eccbef0..f9ed93c4cf0f 100644 +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -12,6 +12,25 @@ menuconfig MTD + + if MTD + ++menu "OpenWrt specific MTD options" ++ ++config MTD_ROOTFS_ROOT_DEV ++ bool "Automatically set 'rootfs' partition to be root filesystem" ++ default y ++ ++config MTD_SPLIT_FIRMWARE ++ bool "Automatically split firmware partition for kernel+rootfs" ++ default y ++ ++config MTD_SPLIT_FIRMWARE_NAME ++ string "Firmware partition name" ++ depends on MTD_SPLIT_FIRMWARE ++ default "firmware" ++ ++source "drivers/mtd/mtdsplit/Kconfig" ++ ++endmenu ++ + config MTD_TESTS + tristate "MTD tests support (DANGEROUS)" + depends on m +diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile +index 593d0593a038..b14b7fe0f597 100644 +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -9,6 +9,8 @@ mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o + + obj-y += parsers/ + ++obj-$(CONFIG_MTD_SPLIT) += mtdsplit/ ++ + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o + obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c +index d442fa94c872..f1ed12aae1fe 100644 +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -15,11 +15,13 @@ + #include <linux/kmod.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> ++#include <linux/magic.h> + #include <linux/err.h> + #include <linux/of.h> + #include <linux/of_platform.h> + + #include "mtdcore.h" ++#include "mtdsplit/mtdsplit.h" + + /* + * MTD methods which simply translate the effective address and pass through +@@ -236,6 +238,146 @@ static int mtd_add_partition_attrs(struct mtd_info *new) + return ret; + } + ++static DEFINE_SPINLOCK(part_parser_lock); ++static LIST_HEAD(part_parsers); ++ ++static struct mtd_part_parser *mtd_part_parser_get(const char *name) ++{ ++ struct mtd_part_parser *p, *ret = NULL; ++ ++ spin_lock(&part_parser_lock); ++ ++ list_for_each_entry(p, &part_parsers, list) ++ if (!strcmp(p->name, name) && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++static inline void mtd_part_parser_put(const struct mtd_part_parser *p) ++{ ++ module_put(p->owner); ++} ++ ++static struct mtd_part_parser * ++get_partition_parser_by_type(enum mtd_parser_type type, ++ struct mtd_part_parser *start) ++{ ++ struct mtd_part_parser *p, *ret = NULL; ++ ++ spin_lock(&part_parser_lock); ++ ++ p = list_prepare_entry(start, &part_parsers, list); ++ if (start) ++ mtd_part_parser_put(start); ++ ++ list_for_each_entry_continue(p, &part_parsers, list) { ++ if (p->type == type && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ } ++ ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++static int parse_mtd_partitions_by_type(struct mtd_info *master, ++ enum mtd_parser_type type, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct mtd_part_parser *prev = NULL; ++ int ret = 0; ++ ++ while (1) { ++ struct mtd_part_parser *parser; ++ ++ parser = get_partition_parser_by_type(type, prev); ++ if (!parser) ++ break; ++ ++ ret = (*parser->parse_fn)(master, pparts, data); ++ ++ if (ret > 0) { ++ mtd_part_parser_put(parser); ++ printk(KERN_NOTICE ++ "%d %s partitions found on MTD device %s\n", ++ ret, parser->name, master->name); ++ break; ++ } ++ ++ prev = parser; ++ } ++ ++ return ret; ++} ++ ++static int ++run_parsers_by_type(struct mtd_info *child, enum mtd_parser_type type) ++{ ++ struct mtd_partition *parts; ++ int nr_parts; ++ int i; ++ ++ nr_parts = parse_mtd_partitions_by_type(child, type, (const struct mtd_partition **)&parts, ++ NULL); ++ if (nr_parts <= 0) ++ return nr_parts; ++ ++ if (WARN_ON(!parts)) ++ return 0; ++ ++ for (i = 0; i < nr_parts; i++) { ++ /* adjust partition offsets */ ++ parts[i].offset += child->part.offset; ++ ++ mtd_add_partition(child->parent, ++ parts[i].name, ++ parts[i].offset, ++ parts[i].size); ++ } ++ ++ kfree(parts); ++ ++ return nr_parts; ++} ++ ++#ifdef CONFIG_MTD_SPLIT_FIRMWARE_NAME ++#define SPLIT_FIRMWARE_NAME CONFIG_MTD_SPLIT_FIRMWARE_NAME ++#else ++#define SPLIT_FIRMWARE_NAME "unused" ++#endif ++ ++static void split_firmware(struct mtd_info *master, struct mtd_info *part) ++{ ++ run_parsers_by_type(part, MTD_PARSER_TYPE_FIRMWARE); ++} ++ ++static void mtd_partition_split(struct mtd_info *master, struct mtd_info *part) ++{ ++ static int rootfs_found = 0; ++ ++ if (rootfs_found) ++ return; ++ ++ if (!strcmp(part->name, "rootfs")) { ++ run_parsers_by_type(part, MTD_PARSER_TYPE_ROOTFS); ++ ++ rootfs_found = 1; ++ } ++ ++ if (IS_ENABLED(CONFIG_MTD_SPLIT_FIRMWARE) && ++ !strcmp(part->name, SPLIT_FIRMWARE_NAME) && ++ !of_find_property(mtd_get_of_node(part), "compatible", NULL)) ++ split_firmware(master, part); ++} ++ + int mtd_add_partition(struct mtd_info *parent, const char *name, + long long offset, long long length) + { +@@ -274,6 +416,7 @@ int mtd_add_partition(struct mtd_info *parent, const char *name, + if (ret) + goto err_remove_part; + ++ mtd_partition_split(parent, child); + mtd_add_partition_attrs(child); + + return 0; +@@ -422,6 +565,7 @@ int add_mtd_partitions(struct mtd_info *parent, + goto err_del_partitions; + } + ++ mtd_partition_split(master, child); + mtd_add_partition_attrs(child); + + /* Look for subpartitions */ +@@ -438,31 +582,6 @@ int add_mtd_partitions(struct mtd_info *parent, + return ret; + } + +-static DEFINE_SPINLOCK(part_parser_lock); +-static LIST_HEAD(part_parsers); +- +-static struct mtd_part_parser *mtd_part_parser_get(const char *name) +-{ +- struct mtd_part_parser *p, *ret = NULL; +- +- spin_lock(&part_parser_lock); +- +- list_for_each_entry(p, &part_parsers, list) +- if (!strcmp(p->name, name) && try_module_get(p->owner)) { +- ret = p; +- break; +- } +- +- spin_unlock(&part_parser_lock); +- +- return ret; +-} +- +-static inline void mtd_part_parser_put(const struct mtd_part_parser *p) +-{ +- module_put(p->owner); +-} +- + /* + * Many partition parsers just expected the core to kfree() all their data in + * one chunk. Do that by default. +diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h +index 955aee14b0f7..e2359a3569f0 100644 +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -608,6 +608,24 @@ static inline void mtd_align_erase_req(struct mtd_info *mtd, + req->len += mtd->erasesize - mod; + } + ++static inline uint64_t mtd_roundup_to_eb(uint64_t sz, struct mtd_info *mtd) ++{ ++ if (mtd_mod_by_eb(sz, mtd) == 0) ++ return sz; ++ ++ /* Round up to next erase block */ ++ return (mtd_div_by_eb(sz, mtd) + 1) * mtd->erasesize; ++} ++ ++static inline uint64_t mtd_rounddown_to_eb(uint64_t sz, struct mtd_info *mtd) ++{ ++ if (mtd_mod_by_eb(sz, mtd) == 0) ++ return sz; ++ ++ /* Round down to the start of the current erase block */ ++ return (mtd_div_by_eb(sz, mtd)) * mtd->erasesize; ++} ++ + static inline uint32_t mtd_div_by_ws(uint64_t sz, struct mtd_info *mtd) + { + if (mtd->writesize_shift) +@@ -680,6 +698,13 @@ extern void __put_mtd_device(struct mtd_info *mtd); + extern struct mtd_info *get_mtd_device_nm(const char *name); + extern void put_mtd_device(struct mtd_info *mtd); + ++static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd) ++{ ++ if (!mtd_is_partition(mtd)) ++ return 0; ++ ++ return mtd->part.offset; ++} + + struct mtd_notifier { + void (*add)(struct mtd_info *mtd); +diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h +index b74a539ec581..65ba0dbf961d 100644 +--- a/include/linux/mtd/partitions.h ++++ b/include/linux/mtd/partitions.h +@@ -75,6 +75,12 @@ struct mtd_part_parser_data { + * Functions dealing with the various ways of partitioning the space + */ + ++enum mtd_parser_type { ++ MTD_PARSER_TYPE_DEVICE = 0, ++ MTD_PARSER_TYPE_ROOTFS, ++ MTD_PARSER_TYPE_FIRMWARE, ++}; ++ + struct mtd_part_parser { + struct list_head list; + struct module *owner; +@@ -83,6 +89,7 @@ struct mtd_part_parser { + int (*parse_fn)(struct mtd_info *, const struct mtd_partition **, + struct mtd_part_parser_data *); + void (*cleanup)(const struct mtd_partition *pparts, int nr_parts); ++ enum mtd_parser_type type; + }; + + /* Container for passing around a set of parsed partitions */ +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0045-mtd-spi-nor-write-support-for-minor-aligned-partitio.patch b/nixos/modules/kernel-patches/0045-mtd-spi-nor-write-support-for-minor-aligned-partitio.patch new file mode 100644 index 0000000..1374147 --- /dev/null +++ b/nixos/modules/kernel-patches/0045-mtd-spi-nor-write-support-for-minor-aligned-partitio.patch @@ -0,0 +1,238 @@ +From cb9e7995c29b8c25bd84b3f47797d67dbf6dfd0e Mon Sep 17 00:00:00 2001 +From: John Thomson <git@johnthomson.fastmail.com.au> +Date: Fri, 25 Dec 2020 18:50:08 +1000 +Subject: [PATCH 45/96] mtd: spi-nor: write support for minor aligned + partitions +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Do not prevent writing to mtd partitions where a partition boundary sits +on a minor erasesize boundary. +This addresses a FIXME that has been present since the start of the +linux git history: +/* Doesn't start on a boundary of major erase size */ +/* FIXME: Let it be writable if it is on a boundary of + * _minor_ erase size though */ + +Allow a uniform erase region spi-nor device to be configured +to use the non-uniform erase regions code path for an erase with: +CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y + +On supporting hardware (SECT_4K: majority of current SPI-NOR device) +provide the facility for an erase to use the least number +of SPI-NOR operations, as well as access to 4K erase without +requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS + +Introduce erasesize_minor to the mtd struct, +the smallest erasesize supported by the device + +On existing devices, this is useful where write support is wanted +for data on a 4K partition, such as some u-boot-env partitions, +or RouterBoot soft_config, while still netting the performance +benefits of using 64K sectors + +Performance: +time mtd erase firmware +OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length + +Without this patch +MTD_SPI_NOR_USE_4K_SECTORS=y |n +real 2m 11.66s |0m 50.86s +user 0m 0.00s |0m 0.00s +sys 1m 56.20s |0m 50.80s + +With this patch +MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y +real 0m 51.68s |0m 50.85s |2m 12.89s +user 0m 0.00s |0m 0.00s |0m 0.01s +sys 0m 46.94s |0m 50.38s |2m 12.46s + +Signed-off-by: John Thomson <git@johnthomson.fastmail.com.au> +Signed-off-by: Thibaut VARÈNE <hacks+kernel@slashdirt.org> +--- + drivers/mtd/mtdcore.c | 10 ++++++++++ + drivers/mtd/mtdpart.c | 35 +++++++++++++++++++++++++---------- + drivers/mtd/spi-nor/Kconfig | 10 ++++++++++ + drivers/mtd/spi-nor/core.c | 11 +++++++++-- + include/linux/mtd/mtd.h | 2 ++ + 5 files changed, 56 insertions(+), 12 deletions(-) + +diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c +index 9eb0680db312..ccb80197d960 100644 +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -168,6 +168,15 @@ static ssize_t mtd_erasesize_show(struct device *dev, + } + MTD_DEVICE_ATTR_RO(erasesize); + ++static ssize_t mtd_erasesize_minor_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct mtd_info *mtd = dev_get_drvdata(dev); ++ ++ return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize_minor); ++} ++MTD_DEVICE_ATTR_RO(erasesize_minor); ++ + static ssize_t mtd_writesize_show(struct device *dev, + struct device_attribute *attr, char *buf) + { +@@ -313,6 +322,7 @@ static struct attribute *mtd_attrs[] = { + &dev_attr_flags.attr, + &dev_attr_size.attr, + &dev_attr_erasesize.attr, ++ &dev_attr_erasesize_minor.attr, + &dev_attr_writesize.attr, + &dev_attr_subpagesize.attr, + &dev_attr_oobsize.attr, +diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c +index f1ed12aae1fe..0b7be183a1f6 100644 +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -41,6 +41,7 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent, + struct mtd_info *master = mtd_get_master(parent); + int wr_alignment = (parent->flags & MTD_NO_ERASE) ? + master->writesize : master->erasesize; ++ int wr_alignment_minor = 0; + u64 parent_size = mtd_is_partition(parent) ? + parent->part.size : parent->size; + struct mtd_info *child; +@@ -165,6 +166,7 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent, + } else { + /* Single erase size */ + child->erasesize = master->erasesize; ++ child->erasesize_minor = master->erasesize_minor; + } + + /* +@@ -172,26 +174,39 @@ static struct mtd_info *allocate_partition(struct mtd_info *parent, + * exposes several regions with different erasesize. Adjust + * wr_alignment accordingly. + */ +- if (!(child->flags & MTD_NO_ERASE)) ++ if (!(child->flags & MTD_NO_ERASE)) { + wr_alignment = child->erasesize; ++ wr_alignment_minor = child->erasesize_minor; ++ } + + tmp = mtd_get_master_ofs(child, 0); + remainder = do_div(tmp, wr_alignment); + if ((child->flags & MTD_WRITEABLE) && remainder) { +- /* Doesn't start on a boundary of major erase size */ +- /* FIXME: Let it be writable if it is on a boundary of +- * _minor_ erase size though */ +- child->flags &= ~MTD_WRITEABLE; +- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", +- part->name); ++ if (wr_alignment_minor) { ++ /* rely on minor being a factor of major erasesize */ ++ tmp = remainder; ++ remainder = do_div(tmp, wr_alignment_minor); ++ } ++ if (remainder) { ++ child->flags &= ~MTD_WRITEABLE; ++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", ++ part->name); ++ } + } + + tmp = mtd_get_master_ofs(child, 0) + child->part.size; + remainder = do_div(tmp, wr_alignment); + if ((child->flags & MTD_WRITEABLE) && remainder) { +- child->flags &= ~MTD_WRITEABLE; +- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", +- part->name); ++ if (wr_alignment_minor) { ++ tmp = remainder; ++ remainder = do_div(tmp, wr_alignment_minor); ++ } ++ ++ if (remainder) { ++ child->flags &= ~MTD_WRITEABLE; ++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", ++ part->name); ++ } + } + + child->size = child->part.size; +diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig +index 24cd25de2b8b..09df9f1a8127 100644 +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR + + if MTD_SPI_NOR + ++config MTD_SPI_NOR_USE_VARIABLE_ERASE ++ bool "Disable uniform_erase to allow use of all hardware supported erasesizes" ++ depends on !MTD_SPI_NOR_USE_4K_SECTORS ++ default n ++ help ++ Allow mixed use of all hardware supported erasesizes, ++ by forcing spi_nor to use the multiple eraseregions code path. ++ For example: A 68K erase will use one 64K erase, and one 4K erase ++ on supporting hardware. ++ + config MTD_SPI_NOR_USE_4K_SECTORS + bool "Use small 4096 B erase sectors" + default y +diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c +index e758ebfe1a9f..f237f050b43c 100644 +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -1048,6 +1048,8 @@ static u8 spi_nor_convert_3to4_erase(u8 opcode) + + static bool spi_nor_has_uniform_erase(const struct spi_nor *nor) + { ++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE)) ++ return false; + return !!nor->params->erase_map.uniform_erase_type; + } + +@@ -2144,6 +2146,7 @@ static int spi_nor_select_erase(struct spi_nor *nor) + { + struct spi_nor_erase_map *map = &nor->params->erase_map; + const struct spi_nor_erase_type *erase = NULL; ++ const struct spi_nor_erase_type *erase_minor = NULL; + struct mtd_info *mtd = &nor->mtd; + u32 wanted_size = nor->info->sector_size; + int i; +@@ -2176,8 +2179,9 @@ static int spi_nor_select_erase(struct spi_nor *nor) + */ + for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) { + if (map->erase_type[i].size) { +- erase = &map->erase_type[i]; +- break; ++ if (!erase) ++ erase = &map->erase_type[i]; ++ erase_minor = &map->erase_type[i]; + } + } + +@@ -2185,6 +2189,9 @@ static int spi_nor_select_erase(struct spi_nor *nor) + return -EINVAL; + + mtd->erasesize = erase->size; ++ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) && ++ erase_minor && erase_minor->size < erase->size) ++ mtd->erasesize_minor = erase_minor->size; + return 0; + } + +diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h +index e2359a3569f0..cd7807a23123 100644 +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -238,6 +238,8 @@ struct mtd_info { + * information below if they desire + */ + uint32_t erasesize; ++ /* "Minor" (smallest) erase size supported by the whole device */ ++ uint32_t erasesize_minor; + /* Minimal writable flash unit size. In case of NOR flash it is 1 (even + * though individual bits can be cleared), in case of NAND flash it is + * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0046-add-patch-for-including-unpartitioned-space-in-the-r.patch b/nixos/modules/kernel-patches/0046-add-patch-for-including-unpartitioned-space-in-the-r.patch new file mode 100644 index 0000000..a4a0576 --- /dev/null +++ b/nixos/modules/kernel-patches/0046-add-patch-for-including-unpartitioned-space-in-the-r.patch @@ -0,0 +1,49 @@ +From e9a2fef0e8f45928beb729eefde10d8e38dc75c1 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:21:36 +0200 +Subject: [PATCH 46/96] add patch for including unpartitioned space in the + rootfs partition for redboot devices (if applicable) + +[john@phrozen.org: used by ixp and others] + +lede-commit: 394918851f84e4d00fa16eb900e7700e95091f00 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/mtd/parsers/redboot.c | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) + +diff --git a/drivers/mtd/parsers/redboot.c b/drivers/mtd/parsers/redboot.c +index a16b42a88581..5d07ed6e1365 100644 +--- a/drivers/mtd/parsers/redboot.c ++++ b/drivers/mtd/parsers/redboot.c +@@ -278,14 +278,21 @@ static int parse_redboot_partitions(struct mtd_info *master, + #endif + names += strlen(names) + 1; + +-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED + if (fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { +- i++; +- parts[i].offset = parts[i - 1].size + parts[i - 1].offset; +- parts[i].size = fl->next->img->flash_base - parts[i].offset; +- parts[i].name = nullname; +- } ++ if (!strcmp(parts[i].name, "rootfs")) { ++ parts[i].size = fl->next->img->flash_base; ++ parts[i].size &= ~(master->erasesize - 1); ++ parts[i].size -= parts[i].offset; ++#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED ++ nrparts--; ++ } else { ++ i++; ++ parts[i].offset = parts[i-1].size + parts[i-1].offset; ++ parts[i].size = fl->next->img->flash_base - parts[i].offset; ++ parts[i].name = nullname; + #endif ++ } ++ } + tmp_fl = fl; + fl = fl->next; + kfree(tmp_fl); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0047-Add-myloader-partition-table-parser.patch b/nixos/modules/kernel-patches/0047-Add-myloader-partition-table-parser.patch new file mode 100644 index 0000000..6e9426b --- /dev/null +++ b/nixos/modules/kernel-patches/0047-Add-myloader-partition-table-parser.patch @@ -0,0 +1,247 @@ +From 03cf122501d9458a0eae29e3504c5e55aeac1ca6 Mon Sep 17 00:00:00 2001 +From: Florian Fainelli <f.fainelli@gmail.com> +Date: Tue, 27 Sep 2022 16:21:36 +0200 +Subject: [PATCH 47/96] Add myloader partition table parser + +[john@phozen.org: shoud be upstreamable] + +lede-commit: d8bf22859b51faa09d22c056fe221a45d2f7a3b8 +Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> +[adjust for kernel 5.4, add myloader.c to patch] +Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de> +--- + drivers/mtd/parsers/Kconfig | 16 +++ + drivers/mtd/parsers/Makefile | 1 + + drivers/mtd/parsers/myloader.c | 181 +++++++++++++++++++++++++++++++++ + 3 files changed, 198 insertions(+) + create mode 100644 drivers/mtd/parsers/myloader.c + +diff --git a/drivers/mtd/parsers/Kconfig b/drivers/mtd/parsers/Kconfig +index 23763d16e4f9..a3f344c27454 100644 +--- a/drivers/mtd/parsers/Kconfig ++++ b/drivers/mtd/parsers/Kconfig +@@ -57,6 +57,22 @@ config MTD_CMDLINE_PARTS + + If unsure, say 'N'. + ++config MTD_MYLOADER_PARTS ++ tristate "MyLoader partition parsing" ++ depends on ADM5120 || ATH25 || ATH79 ++ help ++ MyLoader is a bootloader which allows the user to define partitions ++ in flash devices, by putting a table in the second erase block ++ on the device, similar to a partition table. This table gives the ++ offsets and lengths of the user defined partitions. ++ ++ If you need code which can detect and parse these tables, and ++ register MTD 'partitions' corresponding to each image detected, ++ enable this option. ++ ++ You will still need the parsing functions to be called by the driver ++ for your particular device. It won't happen automatically. ++ + config MTD_OF_PARTS + tristate "OpenFirmware (device tree) partitioning parser" + default y +diff --git a/drivers/mtd/parsers/Makefile b/drivers/mtd/parsers/Makefile +index 2e98aa048278..fbce2f3206df 100644 +--- a/drivers/mtd/parsers/Makefile ++++ b/drivers/mtd/parsers/Makefile +@@ -3,6 +3,7 @@ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o + obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o + obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o + obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o ++obj-$(CONFIG_MTD_MYLOADER_PARTS) += myloader.o + obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o + ofpart-y += ofpart_core.o + ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) += ofpart_bcm4908.o +diff --git a/drivers/mtd/parsers/myloader.c b/drivers/mtd/parsers/myloader.c +new file mode 100644 +index 000000000000..50ed8b197ed2 +--- /dev/null ++++ b/drivers/mtd/parsers/myloader.c +@@ -0,0 +1,181 @@ ++/* ++ * Parse MyLoader-style flash partition tables and produce a Linux partition ++ * array to match. ++ * ++ * Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org> ++ * ++ * This file was based on drivers/mtd/redboot.c ++ * Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/version.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/vmalloc.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/byteorder/generic.h> ++#include <linux/myloader.h> ++ ++#define BLOCK_LEN_MIN 0x10000 ++#define PART_NAME_LEN 32 ++ ++struct part_data { ++ struct mylo_partition_table tab; ++ char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN]; ++}; ++ ++static int myloader_parse_partitions(struct mtd_info *master, ++ const struct mtd_partition **pparts, ++ struct mtd_part_parser_data *data) ++{ ++ struct part_data *buf; ++ struct mylo_partition_table *tab; ++ struct mylo_partition *part; ++ struct mtd_partition *mtd_parts; ++ struct mtd_partition *mtd_part; ++ int num_parts; ++ int ret, i; ++ size_t retlen; ++ char *names; ++ unsigned long offset; ++ unsigned long blocklen; ++ ++ buf = vmalloc(sizeof(*buf)); ++ if (!buf) { ++ return -ENOMEM; ++ goto out; ++ } ++ tab = &buf->tab; ++ ++ blocklen = master->erasesize; ++ if (blocklen < BLOCK_LEN_MIN) ++ blocklen = BLOCK_LEN_MIN; ++ ++ offset = blocklen; ++ ++ /* Find the partition table */ ++ for (i = 0; i < 4; i++, offset += blocklen) { ++ printk(KERN_DEBUG "%s: searching for MyLoader partition table" ++ " at offset 0x%lx\n", master->name, offset); ++ ++ ret = mtd_read(master, offset, sizeof(*buf), &retlen, ++ (void *)buf); ++ if (ret) ++ goto out_free_buf; ++ ++ if (retlen != sizeof(*buf)) { ++ ret = -EIO; ++ goto out_free_buf; ++ } ++ ++ /* Check for Partition Table magic number */ ++ if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS)) ++ break; ++ ++ } ++ ++ if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) { ++ printk(KERN_DEBUG "%s: no MyLoader partition table found\n", ++ master->name); ++ ret = 0; ++ goto out_free_buf; ++ } ++ ++ /* The MyLoader and the Partition Table is always present */ ++ num_parts = 2; ++ ++ /* Detect number of used partitions */ ++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { ++ part = &tab->partitions[i]; ++ ++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) ++ continue; ++ ++ num_parts++; ++ } ++ ++ mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) + ++ num_parts * PART_NAME_LEN), GFP_KERNEL); ++ ++ if (!mtd_parts) { ++ ret = -ENOMEM; ++ goto out_free_buf; ++ } ++ ++ mtd_part = mtd_parts; ++ names = (char *)&mtd_parts[num_parts]; ++ ++ strncpy(names, "myloader", PART_NAME_LEN); ++ mtd_part->name = names; ++ mtd_part->offset = 0; ++ mtd_part->size = offset; ++ mtd_part->mask_flags = MTD_WRITEABLE; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ ++ strncpy(names, "partition_table", PART_NAME_LEN); ++ mtd_part->name = names; ++ mtd_part->offset = offset; ++ mtd_part->size = blocklen; ++ mtd_part->mask_flags = MTD_WRITEABLE; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ ++ for (i = 0; i < MYLO_MAX_PARTITIONS; i++) { ++ part = &tab->partitions[i]; ++ ++ if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE) ++ continue; ++ ++ if ((buf->names[i][0]) && (buf->names[i][0] != '\xff')) ++ strncpy(names, buf->names[i], PART_NAME_LEN); ++ else ++ snprintf(names, PART_NAME_LEN, "partition%d", i); ++ ++ mtd_part->offset = le32_to_cpu(part->addr); ++ mtd_part->size = le32_to_cpu(part->size); ++ mtd_part->name = names; ++ mtd_part++; ++ names += PART_NAME_LEN; ++ } ++ ++ *pparts = mtd_parts; ++ ret = num_parts; ++ ++ out_free_buf: ++ vfree(buf); ++ out: ++ return ret; ++} ++ ++static struct mtd_part_parser myloader_mtd_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = myloader_parse_partitions, ++ .name = "MyLoader", ++}; ++ ++static int __init myloader_mtd_parser_init(void) ++{ ++ register_mtd_parser(&myloader_mtd_parser); ++ ++ return 0; ++} ++ ++static void __exit myloader_mtd_parser_exit(void) ++{ ++ deregister_mtd_parser(&myloader_mtd_parser); ++} ++ ++module_init(myloader_mtd_parser_init); ++module_exit(myloader_mtd_parser_exit); ++ ++MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); ++MODULE_DESCRIPTION("Parsing code for MyLoader partition tables"); ++MODULE_LICENSE("GPL v2"); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0048-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch b/nixos/modules/kernel-patches/0048-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch new file mode 100644 index 0000000..1bb5ae6 --- /dev/null +++ b/nixos/modules/kernel-patches/0048-mtd-bcm47xxpart-check-for-bad-blocks-when-calculatin.patch @@ -0,0 +1,81 @@ +From 73f6ab674b17ed3dc907c2ff04f0a934c90519ee Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> +Date: Tue, 27 Sep 2022 16:21:36 +0200 +Subject: [PATCH 48/96] mtd: bcm47xxpart: check for bad blocks when calculating + offsets +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Rafał Miłecki <zajec5@gmail.com> +--- + drivers/mtd/parsers/parser_trx.c | 35 ++++++++++++++++++++++++++++---- + 1 file changed, 31 insertions(+), 4 deletions(-) + +diff --git a/drivers/mtd/parsers/parser_trx.c b/drivers/mtd/parsers/parser_trx.c +index 4814cf218e17..a1aed25d433f 100644 +--- a/drivers/mtd/parsers/parser_trx.c ++++ b/drivers/mtd/parsers/parser_trx.c +@@ -25,6 +25,33 @@ struct trx_header { + uint32_t offset[3]; + } __packed; + ++/* ++ * Calculate real end offset (address) for a given amount of data. It checks ++ * all blocks skipping bad ones. ++ */ ++static size_t parser_trx_real_offset(struct mtd_info *mtd, size_t bytes) ++{ ++ size_t real_offset = 0; ++ ++ if (mtd_block_isbad(mtd, real_offset)) ++ pr_warn("Base offset shouldn't be at bad block"); ++ ++ while (bytes >= mtd->erasesize) { ++ bytes -= mtd->erasesize; ++ real_offset += mtd->erasesize; ++ while (mtd_block_isbad(mtd, real_offset)) { ++ real_offset += mtd->erasesize; ++ ++ if (real_offset >= mtd->size) ++ return real_offset - mtd->erasesize; ++ } ++ } ++ ++ real_offset += bytes; ++ ++ return real_offset; ++} ++ + static const char *parser_trx_data_part_name(struct mtd_info *master, + size_t offset) + { +@@ -86,21 +113,21 @@ static int parser_trx_parse(struct mtd_info *mtd, + if (trx.offset[2]) { + part = &parts[curr_part++]; + part->name = "loader"; +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); + i++; + } + + if (trx.offset[i]) { + part = &parts[curr_part++]; + part->name = "linux"; +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); + i++; + } + + if (trx.offset[i]) { + part = &parts[curr_part++]; +- part->name = parser_trx_data_part_name(mtd, trx.offset[i]); +- part->offset = trx.offset[i]; ++ part->offset = parser_trx_real_offset(mtd, trx.offset[i]); ++ part->name = parser_trx_data_part_name(mtd, part->offset); + i++; + } + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0049-mtd-bcm47xxpart-detect-T_Meter-partition.patch b/nixos/modules/kernel-patches/0049-mtd-bcm47xxpart-detect-T_Meter-partition.patch new file mode 100644 index 0000000..eda2b10 --- /dev/null +++ b/nixos/modules/kernel-patches/0049-mtd-bcm47xxpart-detect-T_Meter-partition.patch @@ -0,0 +1,47 @@ +From 80a6d73ee374e5c0034434881b0bc9b118ad381f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com> +Date: Tue, 27 Sep 2022 16:21:36 +0200 +Subject: [PATCH 49/96] mtd: bcm47xxpart: detect T_Meter partition +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +It can be found on many Netgear devices. It consists of many 0x30 blocks +starting with 4D 54. + +Signed-off-by: Rafał Miłecki <zajec5@gmail.com> +--- + drivers/mtd/parsers/bcm47xxpart.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/mtd/parsers/bcm47xxpart.c b/drivers/mtd/parsers/bcm47xxpart.c +index 50fcf4c2174b..045954f7ddc2 100644 +--- a/drivers/mtd/parsers/bcm47xxpart.c ++++ b/drivers/mtd/parsers/bcm47xxpart.c +@@ -35,6 +35,7 @@ + #define NVRAM_HEADER 0x48534C46 /* FLSH */ + #define POT_MAGIC1 0x54544f50 /* POTT */ + #define POT_MAGIC2 0x504f /* OP */ ++#define T_METER_MAGIC 0x4D540000 /* MT */ + #define ML_MAGIC1 0x39685a42 + #define ML_MAGIC2 0x26594131 + #define TRX_MAGIC 0x30524448 +@@ -179,6 +180,15 @@ static int bcm47xxpart_parse(struct mtd_info *master, + continue; + } + ++ /* T_Meter */ ++ if ((le32_to_cpu(buf[0x000 / 4]) & 0xFFFF0000) == T_METER_MAGIC && ++ (le32_to_cpu(buf[0x030 / 4]) & 0xFFFF0000) == T_METER_MAGIC && ++ (le32_to_cpu(buf[0x060 / 4]) & 0xFFFF0000) == T_METER_MAGIC) { ++ bcm47xxpart_add_part(&parts[curr_part++], "T_Meter", offset, ++ MTD_WRITEABLE); ++ continue; ++ } ++ + /* TRX */ + if (buf[0x000 / 4] == TRX_MAGIC) { + struct trx_header *trx; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0050-kernel-disable-cfi-cmdset-0002-erase-suspend.patch b/nixos/modules/kernel-patches/0050-kernel-disable-cfi-cmdset-0002-erase-suspend.patch new file mode 100644 index 0000000..f285a4d --- /dev/null +++ b/nixos/modules/kernel-patches/0050-kernel-disable-cfi-cmdset-0002-erase-suspend.patch @@ -0,0 +1,32 @@ +From 0b665a6c06dc23540d79f36704335d1063351f65 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:21:39 +0200 +Subject: [PATCH 50/96] kernel: disable cfi cmdset 0002 erase suspend + +on some platforms, erase suspend leads to data corruption and lockups when write +ops collide with erase ops. this has been observed on the buffalo wzr-hp-g300nh. +rather than play whack-a-mole with a hard to reproduce issue on a variety of devices, +simply disable erase suspend, as it will usually not produce any useful gain on +the small filesystems used on embedded hardware. + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c +index 67453f59c69c..a6692d72f24b 100644 +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -908,7 +908,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr + return 0; + + case FL_ERASING: +- if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) || ++ if (1 /* no suspend */ || !cfip || !(cfip->EraseSuspend & (0x1|0x2)) || + !(mode == FL_READY || mode == FL_POINT || + (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)))) + goto sleep; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0051-Issue-map-read-after-Write-Buffer-Load-command-to-en.patch b/nixos/modules/kernel-patches/0051-Issue-map-read-after-Write-Buffer-Load-command-to-en.patch new file mode 100644 index 0000000..c0faf03 --- /dev/null +++ b/nixos/modules/kernel-patches/0051-Issue-map-read-after-Write-Buffer-Load-command-to-en.patch @@ -0,0 +1,26 @@ +From 4575feb6cb9d44a15acdb66dba1d0e7f2b6f2c9e Mon Sep 17 00:00:00 2001 +From: George Kashperko <george@znau.edu.ua> +Date: Tue, 27 Sep 2022 16:21:39 +0200 +Subject: [PATCH 51/96] Issue map read after Write Buffer Load command to + ensure chip is ready to receive data. + +Signed-off-by: George Kashperko <george@znau.edu.ua> +--- + drivers/mtd/chips/cfi_cmdset_0002.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c +index a6692d72f24b..13694138a8b8 100644 +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -2052,6 +2052,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, + + /* Write Buffer Load */ + map_write(map, CMD(0x25), cmd_adr); ++ (void) map_read(map, cmd_adr); + + chip->state = FL_WRITING_TO_BUFFER; + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0052-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch b/nixos/modules/kernel-patches/0052-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch new file mode 100644 index 0000000..aa8d4ff --- /dev/null +++ b/nixos/modules/kernel-patches/0052-mtd-spinand-add-support-for-ESMT-F50x1G41LB.patch @@ -0,0 +1,155 @@ +From 212a7751ee6e76db714bb1dc50c80aa21c0ce871 Mon Sep 17 00:00:00 2001 +From: Chuanhong Guo <gch981213@gmail.com> +Date: Wed, 13 Apr 2022 11:58:17 +0800 +Subject: [PATCH 52/96] mtd: spinand: add support for ESMT F50x1G41LB + +This patch adds support for ESMT F50L1G41LB and F50D1G41LB. +It seems that ESMT likes to use random JEDEC ID from other vendors. +Their 1G chips uses 0xc8 from GigaDevice and 2G/4G chips uses 0x2c from +Micron. For this reason, the ESMT entry is named esmt_c8 with explicit +JEDEC ID in variable name. + +Datasheets: +https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F50L1G41LB(2M).pdf +https://www.esmt.com.tw/upload/pdf/ESMT/datasheets/F50D1G41LB(2M).pdf + +Signed-off-by: Chuanhong Guo <gch981213@gmail.com> +--- + drivers/mtd/nand/spi/Makefile | 2 +- + drivers/mtd/nand/spi/core.c | 1 + + drivers/mtd/nand/spi/esmt.c | 89 +++++++++++++++++++++++++++++++++++ + include/linux/mtd/spinand.h | 1 + + 4 files changed, 92 insertions(+), 1 deletion(-) + create mode 100644 drivers/mtd/nand/spi/esmt.c + +diff --git a/drivers/mtd/nand/spi/Makefile b/drivers/mtd/nand/spi/Makefile +index 80dabe6ff0f3..9c64d9fcb189 100644 +--- a/drivers/mtd/nand/spi/Makefile ++++ b/drivers/mtd/nand/spi/Makefile +@@ -1,3 +1,3 @@ + # SPDX-License-Identifier: GPL-2.0 +-spinand-objs := core.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o ++spinand-objs := core.o esmt.o gigadevice.o macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o + obj-$(CONFIG_MTD_SPI_NAND) += spinand.o +diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c +index d5b685d1605e..f9fd111f3364 100644 +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -927,6 +927,7 @@ static const struct nand_ops spinand_ops = { + }; + + static const struct spinand_manufacturer *spinand_manufacturers[] = { ++ &esmt_c8_spinand_manufacturer, + &gigadevice_spinand_manufacturer, + ¯onix_spinand_manufacturer, + µn_spinand_manufacturer, +diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c +new file mode 100644 +index 000000000000..dad0ba60bb79 +--- /dev/null ++++ b/drivers/mtd/nand/spi/esmt.c +@@ -0,0 +1,89 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Author: ++ * Chuanhong Guo <gch981213@gmail.com> ++ */ ++ ++#include <linux/device.h> ++#include <linux/kernel.h> ++#include <linux/mtd/spinand.h> ++ ++/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */ ++#define SPINAND_MFR_ESMT_C8 0xc8 ++ ++static SPINAND_OP_VARIANTS(read_cache_variants, ++ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0), ++ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(write_cache_variants, ++ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0), ++ SPINAND_PROG_LOAD(true, 0, NULL, 0)); ++ ++static SPINAND_OP_VARIANTS(update_cache_variants, ++ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0), ++ SPINAND_PROG_LOAD(false, 0, NULL, 0)); ++ ++static int f50l1g41lb_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = 16 * section + 8; ++ region->length = 8; ++ ++ return 0; ++} ++ ++static int f50l1g41lb_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = 16 * section + 2; ++ region->length = 6; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = { ++ .ecc = f50l1g41lb_ooblayout_ecc, ++ .free = f50l1g41lb_ooblayout_free, ++}; ++ ++static const struct spinand_info esmt_c8_spinand_table[] = { ++ SPINAND_INFO("F50L1G41LB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(1, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), ++ SPINAND_INFO("F50D1G41LB", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11), ++ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), ++ NAND_ECCREQ(1, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)), ++}; ++ ++static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = { ++}; ++ ++const struct spinand_manufacturer esmt_c8_spinand_manufacturer = { ++ .id = SPINAND_MFR_ESMT_C8, ++ .name = "ESMT", ++ .chips = esmt_c8_spinand_table, ++ .nchips = ARRAY_SIZE(esmt_c8_spinand_table), ++ .ops = &esmt_spinand_manuf_ops, ++}; +diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h +index 5584d3bb6556..3b42e7392226 100644 +--- a/include/linux/mtd/spinand.h ++++ b/include/linux/mtd/spinand.h +@@ -260,6 +260,7 @@ struct spinand_manufacturer { + }; + + /* SPI NAND manufacturers */ ++extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; + extern const struct spinand_manufacturer gigadevice_spinand_manufacturer; + extern const struct spinand_manufacturer macronix_spinand_manufacturer; + extern const struct spinand_manufacturer micron_spinand_manufacturer; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0053-mtd-add-EOF-marker-support-to-the-UBI-layer.patch b/nixos/modules/kernel-patches/0053-mtd-add-EOF-marker-support-to-the-UBI-layer.patch new file mode 100644 index 0000000..165106b --- /dev/null +++ b/nixos/modules/kernel-patches/0053-mtd-add-EOF-marker-support-to-the-UBI-layer.patch @@ -0,0 +1,69 @@ +From 799bf4a0592b64708a5cb61b092f4b6a27cae435 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos <juhosg@openwrt.org> +Date: Tue, 27 Sep 2022 16:22:01 +0200 +Subject: [PATCH 53/96] mtd: add EOF marker support to the UBI layer + +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +--- + drivers/mtd/ubi/attach.c | 25 ++++++++++++++++++++++--- + drivers/mtd/ubi/ubi.h | 1 + + 2 files changed, 23 insertions(+), 3 deletions(-) + +diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c +index ae5abe492b52..7a16e0e252a7 100644 +--- a/drivers/mtd/ubi/attach.c ++++ b/drivers/mtd/ubi/attach.c +@@ -926,6 +926,13 @@ static bool vol_ignored(int vol_id) + #endif + } + ++static bool ec_hdr_has_eof(struct ubi_ec_hdr *ech) ++{ ++ return ech->padding1[0] == 'E' && ++ ech->padding1[1] == 'O' && ++ ech->padding1[2] == 'F'; ++} ++ + /** + * scan_peb - scan and process UBI headers of a PEB. + * @ubi: UBI device description object +@@ -958,9 +965,21 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, + return 0; + } + +- err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); +- if (err < 0) +- return err; ++ if (!ai->eof_found) { ++ err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); ++ if (err < 0) ++ return err; ++ ++ if (ec_hdr_has_eof(ech)) { ++ pr_notice("UBI: EOF marker found, PEBs from %d will be erased\n", ++ pnum); ++ ai->eof_found = true; ++ } ++ } ++ ++ if (ai->eof_found) ++ err = UBI_IO_FF_BITFLIPS; ++ + switch (err) { + case 0: + break; +diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h +index 078112e23dfd..6f39e47b4869 100644 +--- a/drivers/mtd/ubi/ubi.h ++++ b/drivers/mtd/ubi/ubi.h +@@ -778,6 +778,7 @@ struct ubi_attach_info { + int mean_ec; + uint64_t ec_sum; + int ec_count; ++ bool eof_found; + struct kmem_cache *aeb_slab_cache; + struct ubi_ec_hdr *ech; + struct ubi_vid_io_buf *vidb; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0054-mtd-core-add-get_mtd_device_by_node.patch b/nixos/modules/kernel-patches/0054-mtd-core-add-get_mtd_device_by_node.patch new file mode 100644 index 0000000..6a57d72 --- /dev/null +++ b/nixos/modules/kernel-patches/0054-mtd-core-add-get_mtd_device_by_node.patch @@ -0,0 +1,82 @@ +From 1065fc1f12c66020a9e49a9951a7ba39112bf405 Mon Sep 17 00:00:00 2001 +From: Bernhard Frauendienst <kernel@nospam.obeliks.de> +Date: Sat, 1 Sep 2018 00:30:11 +0200 +Subject: [PATCH 54/96] mtd: core: add get_mtd_device_by_node + +Add function to retrieve a mtd device by its OF node. Since drivers can +assign arbitrary names to mtd devices in the absence of a label +property, there is no other reliable way to retrieve a mtd device for a +given OF node. + +Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de> +Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com> +--- + drivers/mtd/mtdcore.c | 38 ++++++++++++++++++++++++++++++++++++++ + include/linux/mtd/mtd.h | 2 ++ + 2 files changed, 40 insertions(+) + +diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c +index ccb80197d960..c8f8da3f3736 100644 +--- a/drivers/mtd/mtdcore.c ++++ b/drivers/mtd/mtdcore.c +@@ -1202,6 +1202,44 @@ struct mtd_info *get_mtd_device_nm(const char *name) + } + EXPORT_SYMBOL_GPL(get_mtd_device_nm); + ++/** ++ * get_mtd_device_by_node - obtain a validated handle for an MTD device ++ * by of_node ++ * @of_node: OF node of MTD device to open ++ * ++ * This function returns MTD device description structure in case of ++ * success and an error code in case of failure. ++ */ ++struct mtd_info *get_mtd_device_by_node(const struct device_node *of_node) ++{ ++ int err = -ENODEV; ++ struct mtd_info *mtd = NULL, *other; ++ ++ mutex_lock(&mtd_table_mutex); ++ ++ mtd_for_each_device(other) { ++ if (of_node == other->dev.of_node) { ++ mtd = other; ++ break; ++ } ++ } ++ ++ if (!mtd) ++ goto out_unlock; ++ ++ err = __get_mtd_device(mtd); ++ if (err) ++ goto out_unlock; ++ ++ mutex_unlock(&mtd_table_mutex); ++ return mtd; ++ ++out_unlock: ++ mutex_unlock(&mtd_table_mutex); ++ return ERR_PTR(err); ++} ++EXPORT_SYMBOL_GPL(get_mtd_device_by_node); ++ + void put_mtd_device(struct mtd_info *mtd) + { + mutex_lock(&mtd_table_mutex); +diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h +index cd7807a23123..3ec4f745f1b4 100644 +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -698,6 +698,8 @@ extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); + extern int __get_mtd_device(struct mtd_info *mtd); + extern void __put_mtd_device(struct mtd_info *mtd); + extern struct mtd_info *get_mtd_device_nm(const char *name); ++extern struct mtd_info *get_mtd_device_by_node( ++ const struct device_node *of_node); + extern void put_mtd_device(struct mtd_info *mtd); + + static inline uint64_t mtdpart_get_offset(const struct mtd_info *mtd) +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0055-dt-bindings-add-bindings-for-mtd-concat-devices.patch b/nixos/modules/kernel-patches/0055-dt-bindings-add-bindings-for-mtd-concat-devices.patch new file mode 100644 index 0000000..b327f77 --- /dev/null +++ b/nixos/modules/kernel-patches/0055-dt-bindings-add-bindings-for-mtd-concat-devices.patch @@ -0,0 +1,58 @@ +From b69bbb97eeabeaa1aa6fafcffcf331edd2ec2693 Mon Sep 17 00:00:00 2001 +From: Bernhard Frauendienst <kernel@nospam.obeliks.de> +Date: Wed, 5 Sep 2018 01:32:51 +0200 +Subject: [PATCH 55/96] dt-bindings: add bindings for mtd-concat devices + +Document virtual mtd-concat device bindings. + +Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de> +--- + .../devicetree/bindings/mtd/mtd-concat.txt | 36 +++++++++++++++++++ + 1 file changed, 36 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/mtd-concat.txt + +diff --git a/Documentation/devicetree/bindings/mtd/mtd-concat.txt b/Documentation/devicetree/bindings/mtd/mtd-concat.txt +new file mode 100644 +index 000000000000..2daf3157b163 +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/mtd-concat.txt +@@ -0,0 +1,36 @@ ++Virtual MTD concat device ++ ++Requires properties: ++- devices: list of phandles to mtd nodes that should be concatenated ++ ++Example: ++ ++&spi { ++ flash0: flash@0 { ++ ... ++ }; ++ flash1: flash@1 { ++ ... ++ }; ++}; ++ ++flash { ++ compatible = "mtd-concat"; ++ ++ devices = <&flash0 &flash1>; ++ ++ partitions { ++ compatible = "fixed-partitions"; ++ ++ partition@0 { ++ label = "boot"; ++ reg = <0x0000000 0x0040000>; ++ read-only; ++ }; ++ ++ partition@40000 { ++ label = "firmware"; ++ reg = <0x0040000 0x1fc0000>; ++ }; ++ } ++} +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0056-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch b/nixos/modules/kernel-patches/0056-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch new file mode 100644 index 0000000..92f5603 --- /dev/null +++ b/nixos/modules/kernel-patches/0056-mtd-mtdconcat-add-dt-driver-for-concat-devices.patch @@ -0,0 +1,232 @@ +From 0a58b4681f061bf6a49c7102b08359c3ea19d84f Mon Sep 17 00:00:00 2001 +From: Bernhard Frauendienst <kernel@nospam.obeliks.de> +Date: Sat, 25 Aug 2018 12:35:22 +0200 +Subject: [PATCH 56/96] mtd: mtdconcat: add dt driver for concat devices + +Some mtd drivers like physmap variants have support for concatenating +multiple mtd devices, but there is no generic way to define such a +concat device from within the device tree. + +This is useful for some SoC boards that use multiple flash chips as +memory banks of a single mtd device, with partitions spanning chip +borders. + +This commit adds a driver for creating virtual mtd-concat devices. They +must have a compatible = "mtd-concat" line, and define a list of devices +to concat in the 'devices' property, for example: + +flash { + compatible = "mtd-concat"; + + devices = <&flash0 &flash1>; + + partitions { + ... + }; +}; + +The driver is added to the very end of the mtd Makefile to increase the +likelyhood of all child devices already being loaded at the time of +probing, preventing unnecessary deferred probes. + +Signed-off-by: Bernhard Frauendienst <kernel@nospam.obeliks.de> +--- + drivers/mtd/Kconfig | 2 + + drivers/mtd/Makefile | 3 + + drivers/mtd/composite/Kconfig | 12 +++ + drivers/mtd/composite/Makefile | 6 ++ + drivers/mtd/composite/virt_concat.c | 128 ++++++++++++++++++++++++++++ + 5 files changed, 151 insertions(+) + create mode 100644 drivers/mtd/composite/Kconfig + create mode 100644 drivers/mtd/composite/Makefile + create mode 100644 drivers/mtd/composite/virt_concat.c + +diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig +index f9ed93c4cf0f..7e4a3fa1dfd0 100644 +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -241,4 +241,6 @@ source "drivers/mtd/ubi/Kconfig" + + source "drivers/mtd/hyperbus/Kconfig" + ++source "drivers/mtd/composite/Kconfig" ++ + endif # MTD +diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile +index b14b7fe0f597..fbea3736b488 100644 +--- a/drivers/mtd/Makefile ++++ b/drivers/mtd/Makefile +@@ -33,3 +33,6 @@ obj-y += chips/ lpddr/ maps/ devices/ nand/ tests/ + obj-$(CONFIG_MTD_SPI_NOR) += spi-nor/ + obj-$(CONFIG_MTD_UBI) += ubi/ + obj-$(CONFIG_MTD_HYPERBUS) += hyperbus/ ++ ++# Composite drivers must be loaded last ++obj-y += composite/ +diff --git a/drivers/mtd/composite/Kconfig b/drivers/mtd/composite/Kconfig +new file mode 100644 +index 000000000000..0490fc0284bb +--- /dev/null ++++ b/drivers/mtd/composite/Kconfig +@@ -0,0 +1,12 @@ ++menu "Composite MTD device drivers" ++ depends on MTD!=n ++ ++config MTD_VIRT_CONCAT ++ tristate "Virtual concat MTD device" ++ help ++ This driver allows creation of a virtual MTD concat device, which ++ concatenates multiple underlying MTD devices to a single device. ++ This is required by some SoC boards where multiple memory banks are ++ used as one device with partitions spanning across device boundaries. ++ ++endmenu +diff --git a/drivers/mtd/composite/Makefile b/drivers/mtd/composite/Makefile +new file mode 100644 +index 000000000000..8421a0a30606 +--- /dev/null ++++ b/drivers/mtd/composite/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# linux/drivers/mtd/composite/Makefile ++# ++ ++obj-$(CONFIG_MTD_VIRT_CONCAT) += virt_concat.o +diff --git a/drivers/mtd/composite/virt_concat.c b/drivers/mtd/composite/virt_concat.c +new file mode 100644 +index 000000000000..bfd432188c35 +--- /dev/null ++++ b/drivers/mtd/composite/virt_concat.c +@@ -0,0 +1,128 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Virtual concat MTD device driver ++ * ++ * Copyright (C) 2018 Bernhard Frauendienst ++ * Author: Bernhard Frauendienst, kernel@nospam.obeliks.de ++ */ ++ ++#include <linux/module.h> ++#include <linux/device.h> ++#include <linux/mtd/concat.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/of.h> ++#include <linux/of_platform.h> ++#include <linux/slab.h> ++ ++/* ++ * struct of_virt_concat - platform device driver data. ++ * @cmtd the final mtd_concat device ++ * @num_devices the number of devices in @devices ++ * @devices points to an array of devices already loaded ++ */ ++struct of_virt_concat { ++ struct mtd_info *cmtd; ++ int num_devices; ++ struct mtd_info **devices; ++}; ++ ++static int virt_concat_remove(struct platform_device *pdev) ++{ ++ struct of_virt_concat *info; ++ int i; ++ ++ info = platform_get_drvdata(pdev); ++ if (!info) ++ return 0; ++ ++ // unset data for when this is called after a probe error ++ platform_set_drvdata(pdev, NULL); ++ ++ if (info->cmtd) { ++ mtd_device_unregister(info->cmtd); ++ mtd_concat_destroy(info->cmtd); ++ } ++ ++ if (info->devices) { ++ for (i = 0; i < info->num_devices; i++) ++ put_mtd_device(info->devices[i]); ++ } ++ ++ return 0; ++} ++ ++static int virt_concat_probe(struct platform_device *pdev) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct of_phandle_iterator it; ++ struct of_virt_concat *info; ++ struct mtd_info *mtd; ++ int err = 0, count; ++ ++ count = of_count_phandle_with_args(node, "devices", NULL); ++ if (count <= 0) ++ return -EINVAL; ++ ++ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ info->devices = devm_kcalloc(&pdev->dev, count, ++ sizeof(*(info->devices)), GFP_KERNEL); ++ if (!info->devices) { ++ err = -ENOMEM; ++ goto err_remove; ++ } ++ ++ platform_set_drvdata(pdev, info); ++ ++ of_for_each_phandle(&it, err, node, "devices", NULL, 0) { ++ mtd = get_mtd_device_by_node(it.node); ++ if (IS_ERR(mtd)) { ++ of_node_put(it.node); ++ err = -EPROBE_DEFER; ++ goto err_remove; ++ } ++ ++ info->devices[info->num_devices++] = mtd; ++ } ++ ++ info->cmtd = mtd_concat_create(info->devices, info->num_devices, ++ dev_name(&pdev->dev)); ++ if (!info->cmtd) { ++ err = -ENXIO; ++ goto err_remove; ++ } ++ ++ info->cmtd->dev.parent = &pdev->dev; ++ mtd_set_of_node(info->cmtd, node); ++ mtd_device_register(info->cmtd, NULL, 0); ++ ++ return 0; ++ ++err_remove: ++ virt_concat_remove(pdev); ++ ++ return err; ++} ++ ++static const struct of_device_id virt_concat_of_match[] = { ++ { .compatible = "mtd-concat", }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, virt_concat_of_match); ++ ++static struct platform_driver virt_concat_driver = { ++ .probe = virt_concat_probe, ++ .remove = virt_concat_remove, ++ .driver = { ++ .name = "virt-mtdconcat", ++ .of_match_table = virt_concat_of_match, ++ }, ++}; ++ ++module_platform_driver(virt_concat_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Bernhard Frauendienst <kernel@nospam.obeliks.de>"); ++MODULE_DESCRIPTION("Virtual concat MTD device driver"); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0057-fs-add-cdrom-dependency.patch b/nixos/modules/kernel-patches/0057-fs-add-cdrom-dependency.patch new file mode 100644 index 0000000..1a2db45 --- /dev/null +++ b/nixos/modules/kernel-patches/0057-fs-add-cdrom-dependency.patch @@ -0,0 +1,63 @@ +From 9284aa002832679a506de26a13378d4364524ca4 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 12:21:15 +0200 +Subject: [PATCH 57/96] fs: add cdrom dependency + +--- + fs/hfs/Kconfig | 1 + + fs/hfsplus/Kconfig | 1 + + fs/isofs/Kconfig | 1 + + fs/udf/Kconfig | 1 + + 4 files changed, 4 insertions(+) + +diff --git a/fs/hfs/Kconfig b/fs/hfs/Kconfig +index 129926b5142d..e0d2c647aa07 100644 +--- a/fs/hfs/Kconfig ++++ b/fs/hfs/Kconfig +@@ -2,6 +2,7 @@ + config HFS_FS + tristate "Apple Macintosh file system support" + depends on BLOCK ++ select CDROM + select NLS + help + If you say Y here, you will be able to mount Macintosh-formatted +diff --git a/fs/hfsplus/Kconfig b/fs/hfsplus/Kconfig +index 7d4229aecec0..648d91d1881f 100644 +--- a/fs/hfsplus/Kconfig ++++ b/fs/hfsplus/Kconfig +@@ -2,6 +2,7 @@ + config HFSPLUS_FS + tristate "Apple Extended HFS file system support" + depends on BLOCK ++ select CDROM + select NLS + select NLS_UTF8 + help +diff --git a/fs/isofs/Kconfig b/fs/isofs/Kconfig +index 08ffd37b9bb8..f74680379207 100644 +--- a/fs/isofs/Kconfig ++++ b/fs/isofs/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + config ISO9660_FS + tristate "ISO 9660 CDROM file system support" ++ select CDROM + help + This is the standard file system used on CD-ROMs. It was previously + known as "High Sierra File System" and is called "hsfs" on other +diff --git a/fs/udf/Kconfig b/fs/udf/Kconfig +index 26e1a49f3ba7..3f85a084d2b5 100644 +--- a/fs/udf/Kconfig ++++ b/fs/udf/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + config UDF_FS + tristate "UDF file system support" ++ select CDROM + select CRC_ITU_T + select NLS + help +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0058-fs-add-jffs2-lzma-support-not-activated-by-default-y.patch b/nixos/modules/kernel-patches/0058-fs-add-jffs2-lzma-support-not-activated-by-default-y.patch new file mode 100644 index 0000000..380eb21 --- /dev/null +++ b/nixos/modules/kernel-patches/0058-fs-add-jffs2-lzma-support-not-activated-by-default-y.patch @@ -0,0 +1,5235 @@ +From 8bb3779dc3924250dc68185f37038bb0954f268a Mon Sep 17 00:00:00 2001 +From: "Alexandros C. Couloumbis" <alex@ozo.com> +Date: Tue, 27 Sep 2022 16:22:04 +0200 +Subject: [PATCH 58/96] fs: add jffs2/lzma support (not activated by default + yet) + +lede-commit: c2c88d315fa0e881f8b19da07b62859b915b11b2 +Signed-off-by: Alexandros C. Couloumbis <alex@ozo.com> +--- + fs/jffs2/Kconfig | 9 + + fs/jffs2/Makefile | 3 + + fs/jffs2/compr.c | 6 + + fs/jffs2/compr.h | 10 +- + fs/jffs2/compr_lzma.c | 128 ++ + fs/jffs2/super.c | 33 +- + include/linux/lzma.h | 62 + + include/linux/lzma/LzFind.h | 115 ++ + include/linux/lzma/LzHash.h | 54 + + include/linux/lzma/LzmaDec.h | 231 ++++ + include/linux/lzma/LzmaEnc.h | 80 ++ + include/linux/lzma/Types.h | 226 ++++ + include/uapi/linux/jffs2.h | 1 + + lib/Kconfig | 6 + + lib/Makefile | 12 + + lib/lzma/LzFind.c | 761 ++++++++++++ + lib/lzma/LzmaDec.c | 999 +++++++++++++++ + lib/lzma/LzmaEnc.c | 2271 ++++++++++++++++++++++++++++++++++ + lib/lzma/Makefile | 7 + + 19 files changed, 5008 insertions(+), 6 deletions(-) + create mode 100644 fs/jffs2/compr_lzma.c + create mode 100644 include/linux/lzma.h + create mode 100644 include/linux/lzma/LzFind.h + create mode 100644 include/linux/lzma/LzHash.h + create mode 100644 include/linux/lzma/LzmaDec.h + create mode 100644 include/linux/lzma/LzmaEnc.h + create mode 100644 include/linux/lzma/Types.h + create mode 100644 lib/lzma/LzFind.c + create mode 100644 lib/lzma/LzmaDec.c + create mode 100644 lib/lzma/LzmaEnc.c + create mode 100644 lib/lzma/Makefile + +diff --git a/fs/jffs2/Kconfig b/fs/jffs2/Kconfig +index 7c96bc107218..2944e0a912b3 100644 +--- a/fs/jffs2/Kconfig ++++ b/fs/jffs2/Kconfig +@@ -136,6 +136,15 @@ config JFFS2_LZO + This feature was added in July, 2007. Say 'N' if you need + compatibility with older bootloaders or kernels. + ++config JFFS2_LZMA ++ bool "JFFS2 LZMA compression support" if JFFS2_COMPRESSION_OPTIONS ++ select LZMA_COMPRESS ++ select LZMA_DECOMPRESS ++ depends on JFFS2_FS ++ default n ++ help ++ JFFS2 wrapper to the LZMA C SDK ++ + config JFFS2_RTIME + bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS +diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile +index 5294969d5bf9..11a89b96e5f3 100644 +--- a/fs/jffs2/Makefile ++++ b/fs/jffs2/Makefile +@@ -19,4 +19,7 @@ jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o + jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o + jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o + jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o ++jffs2-$(CONFIG_JFFS2_LZMA) += compr_lzma.o + jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o ++ ++CFLAGS_compr_lzma.o += -Iinclude/linux -Ilib/lzma +diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c +index 4849a4c9a0e2..6ece471685d4 100644 +--- a/fs/jffs2/compr.c ++++ b/fs/jffs2/compr.c +@@ -378,6 +378,9 @@ int __init jffs2_compressors_init(void) + #ifdef CONFIG_JFFS2_LZO + jffs2_lzo_init(); + #endif ++#ifdef CONFIG_JFFS2_LZMA ++ jffs2_lzma_init(); ++#endif + /* Setting default compression mode */ + #ifdef CONFIG_JFFS2_CMODE_NONE + jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; +@@ -401,6 +404,9 @@ int __init jffs2_compressors_init(void) + int jffs2_compressors_exit(void) + { + /* Unregistering compressors */ ++#ifdef CONFIG_JFFS2_LZMA ++ jffs2_lzma_exit(); ++#endif + #ifdef CONFIG_JFFS2_LZO + jffs2_lzo_exit(); + #endif +diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h +index 5e91d578f4ed..32db2e1ec610 100644 +--- a/fs/jffs2/compr.h ++++ b/fs/jffs2/compr.h +@@ -29,9 +29,9 @@ + #define JFFS2_DYNRUBIN_PRIORITY 20 + #define JFFS2_LZARI_PRIORITY 30 + #define JFFS2_RTIME_PRIORITY 50 +-#define JFFS2_ZLIB_PRIORITY 60 +-#define JFFS2_LZO_PRIORITY 80 +- ++#define JFFS2_LZMA_PRIORITY 70 ++#define JFFS2_ZLIB_PRIORITY 80 ++#define JFFS2_LZO_PRIORITY 90 + + #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ + #define JFFS2_DYNRUBIN_DISABLED /* for decompression */ +@@ -101,5 +101,9 @@ void jffs2_zlib_exit(void); + int jffs2_lzo_init(void); + void jffs2_lzo_exit(void); + #endif ++#ifdef CONFIG_JFFS2_LZMA ++int jffs2_lzma_init(void); ++void jffs2_lzma_exit(void); ++#endif + + #endif /* __JFFS2_COMPR_H__ */ +diff --git a/fs/jffs2/compr_lzma.c b/fs/jffs2/compr_lzma.c +new file mode 100644 +index 000000000000..ec0d9a6020de +--- /dev/null ++++ b/fs/jffs2/compr_lzma.c +@@ -0,0 +1,128 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * JFFS2 wrapper to the LZMA C SDK ++ * ++ */ ++ ++#include <linux/lzma.h> ++#include "compr.h" ++ ++#ifdef __KERNEL__ ++ static DEFINE_MUTEX(deflate_mutex); ++#endif ++ ++CLzmaEncHandle *p; ++Byte propsEncoded[LZMA_PROPS_SIZE]; ++SizeT propsSize = sizeof(propsEncoded); ++ ++STATIC void lzma_free_workspace(void) ++{ ++ LzmaEnc_Destroy(p, &lzma_alloc, &lzma_alloc); ++} ++ ++STATIC int INIT lzma_alloc_workspace(CLzmaEncProps *props) ++{ ++ if ((p = (CLzmaEncHandle *)LzmaEnc_Create(&lzma_alloc)) == NULL) ++ { ++ PRINT_ERROR("Failed to allocate lzma deflate workspace\n"); ++ return -ENOMEM; ++ } ++ ++ if (LzmaEnc_SetProps(p, props) != SZ_OK) ++ { ++ lzma_free_workspace(); ++ return -1; ++ } ++ ++ if (LzmaEnc_WriteProperties(p, propsEncoded, &propsSize) != SZ_OK) ++ { ++ lzma_free_workspace(); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++STATIC int jffs2_lzma_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) ++{ ++ SizeT compress_size = (SizeT)(*dstlen); ++ int ret; ++ ++ #ifdef __KERNEL__ ++ mutex_lock(&deflate_mutex); ++ #endif ++ ++ ret = LzmaEnc_MemEncode(p, cpage_out, &compress_size, data_in, *sourcelen, ++ 0, NULL, &lzma_alloc, &lzma_alloc); ++ ++ #ifdef __KERNEL__ ++ mutex_unlock(&deflate_mutex); ++ #endif ++ ++ if (ret != SZ_OK) ++ return -1; ++ ++ *dstlen = (uint32_t)compress_size; ++ ++ return 0; ++} ++ ++STATIC int jffs2_lzma_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen) ++{ ++ int ret; ++ SizeT dl = (SizeT)destlen; ++ SizeT sl = (SizeT)srclen; ++ ELzmaStatus status; ++ ++ ret = LzmaDecode(cpage_out, &dl, data_in, &sl, propsEncoded, ++ propsSize, LZMA_FINISH_ANY, &status, &lzma_alloc); ++ ++ if (ret != SZ_OK || status == LZMA_STATUS_NOT_FINISHED || dl != (SizeT)destlen) ++ return -1; ++ ++ return 0; ++} ++ ++static struct jffs2_compressor jffs2_lzma_comp = { ++ .priority = JFFS2_LZMA_PRIORITY, ++ .name = "lzma", ++ .compr = JFFS2_COMPR_LZMA, ++ .compress = &jffs2_lzma_compress, ++ .decompress = &jffs2_lzma_decompress, ++ .disabled = 0, ++}; ++ ++int INIT jffs2_lzma_init(void) ++{ ++ int ret; ++ CLzmaEncProps props; ++ LzmaEncProps_Init(&props); ++ ++ props.dictSize = LZMA_BEST_DICT(0x2000); ++ props.level = LZMA_BEST_LEVEL; ++ props.lc = LZMA_BEST_LC; ++ props.lp = LZMA_BEST_LP; ++ props.pb = LZMA_BEST_PB; ++ props.fb = LZMA_BEST_FB; ++ ++ ret = lzma_alloc_workspace(&props); ++ if (ret < 0) ++ return ret; ++ ++ ret = jffs2_register_compressor(&jffs2_lzma_comp); ++ if (ret) ++ lzma_free_workspace(); ++ ++ return ret; ++} ++ ++void jffs2_lzma_exit(void) ++{ ++ jffs2_unregister_compressor(&jffs2_lzma_comp); ++ lzma_free_workspace(); ++} +diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c +index 7ea37f49f1e1..9b93358d4586 100644 +--- a/fs/jffs2/super.c ++++ b/fs/jffs2/super.c +@@ -374,14 +374,41 @@ static int __init init_jffs2_fs(void) + BUILD_BUG_ON(sizeof(struct jffs2_raw_inode) != 68); + BUILD_BUG_ON(sizeof(struct jffs2_raw_summary) != 32); + +- pr_info("version 2.2." ++ pr_info("version 2.2" + #ifdef CONFIG_JFFS2_FS_WRITEBUFFER + " (NAND)" + #endif + #ifdef CONFIG_JFFS2_SUMMARY +- " (SUMMARY) " ++ " (SUMMARY)" + #endif +- " © 2001-2006 Red Hat, Inc.\n"); ++#ifdef CONFIG_JFFS2_ZLIB ++ " (ZLIB)" ++#endif ++#ifdef CONFIG_JFFS2_LZO ++ " (LZO)" ++#endif ++#ifdef CONFIG_JFFS2_LZMA ++ " (LZMA)" ++#endif ++#ifdef CONFIG_JFFS2_RTIME ++ " (RTIME)" ++#endif ++#ifdef CONFIG_JFFS2_RUBIN ++ " (RUBIN)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_NONE ++ " (CMODE_NONE)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_PRIORITY ++ " (CMODE_PRIORITY)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_SIZE ++ " (CMODE_SIZE)" ++#endif ++#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO ++ " (CMODE_FAVOURLZO)" ++#endif ++ " (c) 2001-2006 Red Hat, Inc.\n"); + + jffs2_inode_cachep = kmem_cache_create("jffs2_i", + sizeof(struct jffs2_inode_info), +diff --git a/include/linux/lzma.h b/include/linux/lzma.h +new file mode 100644 +index 000000000000..4299d19edf59 +--- /dev/null ++++ b/include/linux/lzma.h +@@ -0,0 +1,62 @@ ++#ifndef __LZMA_H__ ++#define __LZMA_H__ ++ ++#ifdef __KERNEL__ ++ #include <linux/kernel.h> ++ #include <linux/sched.h> ++ #include <linux/slab.h> ++ #include <linux/vmalloc.h> ++ #include <linux/init.h> ++ #define LZMA_MALLOC vmalloc ++ #define LZMA_FREE vfree ++ #define PRINT_ERROR(msg) printk(KERN_WARNING #msg) ++ #define INIT __init ++ #define STATIC static ++#else ++ #include <stdint.h> ++ #include <stdlib.h> ++ #include <stdio.h> ++ #include <unistd.h> ++ #include <string.h> ++ #include <asm/types.h> ++ #include <errno.h> ++ #include <linux/jffs2.h> ++ #ifndef PAGE_SIZE ++ extern int page_size; ++ #define PAGE_SIZE page_size ++ #endif ++ #define LZMA_MALLOC malloc ++ #define LZMA_FREE free ++ #define PRINT_ERROR(msg) fprintf(stderr, msg) ++ #define INIT ++ #define STATIC ++#endif ++ ++#include "lzma/LzmaDec.h" ++#include "lzma/LzmaEnc.h" ++ ++#define LZMA_BEST_LEVEL (9) ++#define LZMA_BEST_LC (0) ++#define LZMA_BEST_LP (0) ++#define LZMA_BEST_PB (0) ++#define LZMA_BEST_FB (273) ++ ++#define LZMA_BEST_DICT(n) (((int)((n) / 2)) * 2) ++ ++static void *p_lzma_malloc(void *p, size_t size) ++{ ++ if (size == 0) ++ return NULL; ++ ++ return LZMA_MALLOC(size); ++} ++ ++static void p_lzma_free(void *p, void *address) ++{ ++ if (address != NULL) ++ LZMA_FREE(address); ++} ++ ++static ISzAlloc lzma_alloc = {p_lzma_malloc, p_lzma_free}; ++ ++#endif +diff --git a/include/linux/lzma/LzFind.h b/include/linux/lzma/LzFind.h +new file mode 100644 +index 000000000000..010c4b92ba33 +--- /dev/null ++++ b/include/linux/lzma/LzFind.h +@@ -0,0 +1,115 @@ ++/* LzFind.h -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZ_FIND_H ++#define __LZ_FIND_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++typedef UInt32 CLzRef; ++ ++typedef struct _CMatchFinder ++{ ++ Byte *buffer; ++ UInt32 pos; ++ UInt32 posLimit; ++ UInt32 streamPos; ++ UInt32 lenLimit; ++ ++ UInt32 cyclicBufferPos; ++ UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */ ++ ++ UInt32 matchMaxLen; ++ CLzRef *hash; ++ CLzRef *son; ++ UInt32 hashMask; ++ UInt32 cutValue; ++ ++ Byte *bufferBase; ++ ISeqInStream *stream; ++ int streamEndWasReached; ++ ++ UInt32 blockSize; ++ UInt32 keepSizeBefore; ++ UInt32 keepSizeAfter; ++ ++ UInt32 numHashBytes; ++ int directInput; ++ size_t directInputRem; ++ int btMode; ++ int bigHash; ++ UInt32 historySize; ++ UInt32 fixedHashSize; ++ UInt32 hashSizeSum; ++ UInt32 numSons; ++ SRes result; ++ UInt32 crc[256]; ++} CMatchFinder; ++ ++#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) ++#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) ++ ++#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) ++ ++int MatchFinder_NeedMove(CMatchFinder *p); ++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); ++void MatchFinder_MoveBlock(CMatchFinder *p); ++void MatchFinder_ReadIfRequired(CMatchFinder *p); ++ ++void MatchFinder_Construct(CMatchFinder *p); ++ ++/* Conditions: ++ historySize <= 3 GB ++ keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB ++*/ ++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ++ ISzAlloc *alloc); ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); ++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems); ++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); ++ ++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue, ++ UInt32 *distances, UInt32 maxLen); ++ ++/* ++Conditions: ++ Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. ++ Mf_GetPointerToCurrentPos_Func's result must be used only before any other function ++*/ ++ ++typedef void (*Mf_Init_Func)(void *object); ++typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index); ++typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object); ++typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object); ++typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances); ++typedef void (*Mf_Skip_Func)(void *object, UInt32); ++ ++typedef struct _IMatchFinder ++{ ++ Mf_Init_Func Init; ++ Mf_GetIndexByte_Func GetIndexByte; ++ Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; ++ Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; ++ Mf_GetMatches_Func GetMatches; ++ Mf_Skip_Func Skip; ++} IMatchFinder; ++ ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); ++ ++void MatchFinder_Init(CMatchFinder *p); ++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); ++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); ++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); ++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/include/linux/lzma/LzHash.h b/include/linux/lzma/LzHash.h +new file mode 100644 +index 000000000000..f3e89966cc70 +--- /dev/null ++++ b/include/linux/lzma/LzHash.h +@@ -0,0 +1,54 @@ ++/* LzHash.h -- HASH functions for LZ algorithms ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZ_HASH_H ++#define __LZ_HASH_H ++ ++#define kHash2Size (1 << 10) ++#define kHash3Size (1 << 16) ++#define kHash4Size (1 << 20) ++ ++#define kFix3HashSize (kHash2Size) ++#define kFix4HashSize (kHash2Size + kHash3Size) ++#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) ++ ++#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8); ++ ++#define HASH3_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; } ++ ++#define HASH4_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; } ++ ++#define HASH5_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ ++ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ ++ hash4Value &= (kHash4Size - 1); } ++ ++/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */ ++#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; ++ ++ ++#define MT_HASH2_CALC \ ++ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); ++ ++#define MT_HASH3_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); } ++ ++#define MT_HASH4_CALC { \ ++ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \ ++ hash2Value = temp & (kHash2Size - 1); \ ++ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \ ++ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); } ++ ++#endif +diff --git a/include/linux/lzma/LzmaDec.h b/include/linux/lzma/LzmaDec.h +new file mode 100644 +index 000000000000..7927acd0d4eb +--- /dev/null ++++ b/include/linux/lzma/LzmaDec.h +@@ -0,0 +1,231 @@ ++/* LzmaDec.h -- LZMA Decoder ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_DEC_H ++#define __LZMA_DEC_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* #define _LZMA_PROB32 */ ++/* _LZMA_PROB32 can increase the speed on some CPUs, ++ but memory usage for CLzmaDec::probs will be doubled in that case */ ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++ ++/* ---------- LZMA Properties ---------- */ ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaProps ++{ ++ unsigned lc, lp, pb; ++ UInt32 dicSize; ++} CLzmaProps; ++ ++/* LzmaProps_Decode - decodes properties ++Returns: ++ SZ_OK ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); ++ ++ ++/* ---------- LZMA Decoder state ---------- */ ++ ++/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. ++ Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ ++ ++#define LZMA_REQUIRED_INPUT_MAX 20 ++ ++typedef struct ++{ ++ CLzmaProps prop; ++ CLzmaProb *probs; ++ Byte *dic; ++ const Byte *buf; ++ UInt32 range, code; ++ SizeT dicPos; ++ SizeT dicBufSize; ++ UInt32 processedPos; ++ UInt32 checkDicSize; ++ unsigned state; ++ UInt32 reps[4]; ++ unsigned remainLen; ++ int needFlush; ++ int needInitState; ++ UInt32 numProbs; ++ unsigned tempBufSize; ++ Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; ++} CLzmaDec; ++ ++#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } ++ ++void LzmaDec_Init(CLzmaDec *p); ++ ++/* There are two types of LZMA streams: ++ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. ++ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ ++ ++typedef enum ++{ ++ LZMA_FINISH_ANY, /* finish at any point */ ++ LZMA_FINISH_END /* block must be finished at the end */ ++} ELzmaFinishMode; ++ ++/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! ++ ++ You must use LZMA_FINISH_END, when you know that current output buffer ++ covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. ++ ++ If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, ++ and output value of destLen will be less than output buffer size limit. ++ You can check status result also. ++ ++ You can use multiple checks to test data integrity after full decompression: ++ 1) Check Result and "status" variable. ++ 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. ++ 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. ++ You must use correct finish mode in that case. */ ++ ++typedef enum ++{ ++ LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ ++ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ ++ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ ++ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ ++} ELzmaStatus; ++ ++/* ELzmaStatus is used only as output value for function call */ ++ ++ ++/* ---------- Interfaces ---------- */ ++ ++/* There are 3 levels of interfaces: ++ 1) Dictionary Interface ++ 2) Buffer Interface ++ 3) One Call Interface ++ You can select any of these interfaces, but don't mix functions from different ++ groups for same object. */ ++ ++ ++/* There are two variants to allocate state for Dictionary Interface: ++ 1) LzmaDec_Allocate / LzmaDec_Free ++ 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs ++ You can use variant 2, if you set dictionary buffer manually. ++ For Buffer Interface you must always use variant 1. ++ ++LzmaDec_Allocate* can return: ++ SZ_OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++*/ ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); ++ ++SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); ++void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); ++ ++/* ---------- Dictionary Interface ---------- */ ++ ++/* You can use it, if you want to eliminate the overhead for data copying from ++ dictionary to some other external buffer. ++ You must work with CLzmaDec variables directly in this interface. ++ ++ STEPS: ++ LzmaDec_Constr() ++ LzmaDec_Allocate() ++ for (each new stream) ++ { ++ LzmaDec_Init() ++ while (it needs more decompression) ++ { ++ LzmaDec_DecodeToDic() ++ use data from CLzmaDec::dic and update CLzmaDec::dicPos ++ } ++ } ++ LzmaDec_Free() ++*/ ++ ++/* LzmaDec_DecodeToDic ++ ++ The decoding to internal dictionary buffer (CLzmaDec::dic). ++ You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (dicLimit). ++ LZMA_FINISH_ANY - Decode just dicLimit bytes. ++ LZMA_FINISH_END - Stream must be finished after dicLimit. ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_NEEDS_MORE_INPUT ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++*/ ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- Buffer Interface ---------- */ ++ ++/* It's zlib-like interface. ++ See LzmaDec_DecodeToDic description for information about STEPS and return results, ++ but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need ++ to work with CLzmaDec variables manually. ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++*/ ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, ++ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); ++ ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaDecode ++ ++finishMode: ++ It has meaning only if the decoding reaches output limit (*destLen). ++ LZMA_FINISH_ANY - Decode just destLen bytes. ++ LZMA_FINISH_END - Stream must be finished after (*destLen). ++ ++Returns: ++ SZ_OK ++ status: ++ LZMA_STATUS_FINISHED_WITH_MARK ++ LZMA_STATUS_NOT_FINISHED ++ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK ++ SZ_ERROR_DATA - Data error ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_UNSUPPORTED - Unsupported properties ++ SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). ++*/ ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/include/linux/lzma/LzmaEnc.h b/include/linux/lzma/LzmaEnc.h +new file mode 100644 +index 000000000000..200d60eb83cd +--- /dev/null ++++ b/include/linux/lzma/LzmaEnc.h +@@ -0,0 +1,80 @@ ++/* LzmaEnc.h -- LZMA Encoder ++2009-02-07 : Igor Pavlov : Public domain */ ++ ++#ifndef __LZMA_ENC_H ++#define __LZMA_ENC_H ++ ++#include "Types.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define LZMA_PROPS_SIZE 5 ++ ++typedef struct _CLzmaEncProps ++{ ++ int level; /* 0 <= level <= 9 */ ++ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version ++ (1 << 12) <= dictSize <= (1 << 30) for 64-bit version ++ default = (1 << 24) */ ++ int lc; /* 0 <= lc <= 8, default = 3 */ ++ int lp; /* 0 <= lp <= 4, default = 0 */ ++ int pb; /* 0 <= pb <= 4, default = 2 */ ++ int algo; /* 0 - fast, 1 - normal, default = 1 */ ++ int fb; /* 5 <= fb <= 273, default = 32 */ ++ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ ++ int numHashBytes; /* 2, 3 or 4, default = 4 */ ++ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ ++ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ ++ int numThreads; /* 1 or 2, default = 2 */ ++} CLzmaEncProps; ++ ++void LzmaEncProps_Init(CLzmaEncProps *p); ++void LzmaEncProps_Normalize(CLzmaEncProps *p); ++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); ++ ++ ++/* ---------- CLzmaEncHandle Interface ---------- */ ++ ++/* LzmaEnc_* functions can return the following exit codes: ++Returns: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater in props ++ SZ_ERROR_WRITE - Write callback error. ++ SZ_ERROR_PROGRESS - some break from progress callback ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++typedef void * CLzmaEncHandle; ++ ++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); ++SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); ++SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++ ++/* ---------- One Call Interface ---------- */ ++ ++/* LzmaEncode ++Return code: ++ SZ_OK - OK ++ SZ_ERROR_MEM - Memory allocation error ++ SZ_ERROR_PARAM - Incorrect paramater ++ SZ_ERROR_OUTPUT_EOF - output buffer overflow ++ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) ++*/ ++ ++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +diff --git a/include/linux/lzma/Types.h b/include/linux/lzma/Types.h +new file mode 100644 +index 000000000000..4751acde0722 +--- /dev/null ++++ b/include/linux/lzma/Types.h +@@ -0,0 +1,226 @@ ++/* Types.h -- Basic types ++2009-11-23 : Igor Pavlov : Public domain */ ++ ++#ifndef __7Z_TYPES_H ++#define __7Z_TYPES_H ++ ++#include <stddef.h> ++ ++#ifdef _WIN32 ++#include <windows.h> ++#endif ++ ++#ifndef EXTERN_C_BEGIN ++#ifdef __cplusplus ++#define EXTERN_C_BEGIN extern "C" { ++#define EXTERN_C_END } ++#else ++#define EXTERN_C_BEGIN ++#define EXTERN_C_END ++#endif ++#endif ++ ++EXTERN_C_BEGIN ++ ++#define SZ_OK 0 ++ ++#define SZ_ERROR_DATA 1 ++#define SZ_ERROR_MEM 2 ++#define SZ_ERROR_CRC 3 ++#define SZ_ERROR_UNSUPPORTED 4 ++#define SZ_ERROR_PARAM 5 ++#define SZ_ERROR_INPUT_EOF 6 ++#define SZ_ERROR_OUTPUT_EOF 7 ++#define SZ_ERROR_READ 8 ++#define SZ_ERROR_WRITE 9 ++#define SZ_ERROR_PROGRESS 10 ++#define SZ_ERROR_FAIL 11 ++#define SZ_ERROR_THREAD 12 ++ ++#define SZ_ERROR_ARCHIVE 16 ++#define SZ_ERROR_NO_ARCHIVE 17 ++ ++typedef int SRes; ++ ++#ifdef _WIN32 ++typedef DWORD WRes; ++#else ++typedef int WRes; ++#endif ++ ++#ifndef RINOK ++#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } ++#endif ++ ++typedef unsigned char Byte; ++typedef short Int16; ++typedef unsigned short UInt16; ++ ++#ifdef _LZMA_UINT32_IS_ULONG ++typedef long Int32; ++typedef unsigned long UInt32; ++#else ++typedef int Int32; ++typedef unsigned int UInt32; ++#endif ++ ++#ifdef _SZ_NO_INT_64 ++ ++/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. ++ NOTES: Some code will work incorrectly in that case! */ ++ ++typedef long Int64; ++typedef unsigned long UInt64; ++ ++#else ++ ++#if defined(_MSC_VER) || defined(__BORLANDC__) ++typedef __int64 Int64; ++typedef unsigned __int64 UInt64; ++#else ++typedef long long int Int64; ++typedef unsigned long long int UInt64; ++#endif ++ ++#endif ++ ++#ifdef _LZMA_NO_SYSTEM_SIZE_T ++typedef UInt32 SizeT; ++#else ++typedef size_t SizeT; ++#endif ++ ++typedef int Bool; ++#define True 1 ++#define False 0 ++ ++ ++#ifdef _WIN32 ++#define MY_STD_CALL __stdcall ++#else ++#define MY_STD_CALL ++#endif ++ ++#ifdef _MSC_VER ++ ++#if _MSC_VER >= 1300 ++#define MY_NO_INLINE __declspec(noinline) ++#else ++#define MY_NO_INLINE ++#endif ++ ++#define MY_CDECL __cdecl ++#define MY_FAST_CALL __fastcall ++ ++#else ++ ++#define MY_CDECL ++#define MY_FAST_CALL ++ ++#endif ++ ++ ++/* The following interfaces use first parameter as pointer to structure */ ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) < input(*size)) is allowed */ ++} ISeqInStream; ++ ++/* it can return SZ_ERROR_INPUT_EOF */ ++SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); ++SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); ++SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); ++ ++typedef struct ++{ ++ size_t (*Write)(void *p, const void *buf, size_t size); ++ /* Returns: result - the number of actually written bytes. ++ (result < size) means error */ ++} ISeqOutStream; ++ ++typedef enum ++{ ++ SZ_SEEK_SET = 0, ++ SZ_SEEK_CUR = 1, ++ SZ_SEEK_END = 2 ++} ESzSeek; ++ ++typedef struct ++{ ++ SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ISeekInStream; ++ ++typedef struct ++{ ++ SRes (*Look)(void *p, void **buf, size_t *size); ++ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. ++ (output(*size) > input(*size)) is not allowed ++ (output(*size) < input(*size)) is allowed */ ++ SRes (*Skip)(void *p, size_t offset); ++ /* offset must be <= output(*size) of Look */ ++ ++ SRes (*Read)(void *p, void *buf, size_t *size); ++ /* reads directly (without buffer). It's same as ISeqInStream::Read */ ++ SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); ++} ILookInStream; ++ ++SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); ++SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); ++ ++/* reads via ILookInStream::Read */ ++SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); ++SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); ++ ++#define LookToRead_BUF_SIZE (1 << 14) ++ ++typedef struct ++{ ++ ILookInStream s; ++ ISeekInStream *realStream; ++ size_t pos; ++ size_t size; ++ Byte buf[LookToRead_BUF_SIZE]; ++} CLookToRead; ++ ++void LookToRead_CreateVTable(CLookToRead *p, int lookahead); ++void LookToRead_Init(CLookToRead *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToLook; ++ ++void SecToLook_CreateVTable(CSecToLook *p); ++ ++typedef struct ++{ ++ ISeqInStream s; ++ ILookInStream *realStream; ++} CSecToRead; ++ ++void SecToRead_CreateVTable(CSecToRead *p); ++ ++typedef struct ++{ ++ SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); ++ /* Returns: result. (result != SZ_OK) means break. ++ Value (UInt64)(Int64)-1 for size means unknown value. */ ++} ICompressProgress; ++ ++typedef struct ++{ ++ void *(*Alloc)(void *p, size_t size); ++ void (*Free)(void *p, void *address); /* address can be 0 */ ++} ISzAlloc; ++ ++#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) ++#define IAlloc_Free(p, a) (p)->Free((p), a) ++ ++EXTERN_C_END ++ ++#endif +diff --git a/include/uapi/linux/jffs2.h b/include/uapi/linux/jffs2.h +index 784ba0b9690a..cae7fa82c1a0 100644 +--- a/include/uapi/linux/jffs2.h ++++ b/include/uapi/linux/jffs2.h +@@ -46,6 +46,7 @@ + #define JFFS2_COMPR_DYNRUBIN 0x05 + #define JFFS2_COMPR_ZLIB 0x06 + #define JFFS2_COMPR_LZO 0x07 ++#define JFFS2_COMPR_LZMA 0x08 + /* Compatibility flags. */ + #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ + #define JFFS2_NODE_ACCURATE 0x2000 +diff --git a/lib/Kconfig b/lib/Kconfig +index eaaad4d85bf2..2f2da2fc2416 100644 +--- a/lib/Kconfig ++++ b/lib/Kconfig +@@ -346,6 +346,12 @@ config ZSTD_DECOMPRESS + + source "lib/xz/Kconfig" + ++config LZMA_COMPRESS ++ tristate ++ ++config LZMA_DECOMPRESS ++ tristate ++ + # + # These all provide a common interface (hence the apparent duplication with + # ZLIB_INFLATE; DECOMPRESS_GZIP is just a wrapper.) +diff --git a/lib/Makefile b/lib/Makefile +index f99bf61f8bbc..884fb0a205dd 100644 +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -136,6 +136,16 @@ CFLAGS_kobject.o += -DDEBUG + CFLAGS_kobject_uevent.o += -DDEBUG + endif + ++ifdef CONFIG_JFFS2_ZLIB ++ CONFIG_ZLIB_INFLATE:=y ++ CONFIG_ZLIB_DEFLATE:=y ++endif ++ ++ifdef CONFIG_JFFS2_LZMA ++ CONFIG_LZMA_DECOMPRESS:=y ++ CONFIG_LZMA_COMPRESS:=y ++endif ++ + obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o + CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any) + +@@ -194,6 +204,8 @@ obj-$(CONFIG_ZSTD_COMPRESS) += zstd/ + obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/ + obj-$(CONFIG_XZ_DEC) += xz/ + obj-$(CONFIG_RAID6_PQ) += raid6/ ++obj-$(CONFIG_LZMA_COMPRESS) += lzma/ ++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma/ + + lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o + lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o +diff --git a/lib/lzma/LzFind.c b/lib/lzma/LzFind.c +new file mode 100644 +index 000000000000..e753d04c692f +--- /dev/null ++++ b/lib/lzma/LzFind.c +@@ -0,0 +1,761 @@ ++/* LzFind.c -- Match finder for LZ algorithms ++2009-04-22 : Igor Pavlov : Public domain */ ++ ++#include <string.h> ++ ++#include "LzFind.h" ++#include "LzHash.h" ++ ++#define kEmptyHashValue 0 ++#define kMaxValForNormalize ((UInt32)0xFFFFFFFF) ++#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ ++#define kNormalizeMask (~(kNormalizeStepMin - 1)) ++#define kMaxHistorySize ((UInt32)3 << 30) ++ ++#define kStartMaxLen 3 ++ ++static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ if (!p->directInput) ++ { ++ alloc->Free(alloc, p->bufferBase); ++ p->bufferBase = 0; ++ } ++} ++ ++/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ ++ ++static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) ++{ ++ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; ++ if (p->directInput) ++ { ++ p->blockSize = blockSize; ++ return 1; ++ } ++ if (p->bufferBase == 0 || p->blockSize != blockSize) ++ { ++ LzInWindow_Free(p, alloc); ++ p->blockSize = blockSize; ++ p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); ++ } ++ return (p->bufferBase != 0); ++} ++ ++Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } ++Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } ++ ++UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } ++ ++void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) ++{ ++ p->posLimit -= subValue; ++ p->pos -= subValue; ++ p->streamPos -= subValue; ++} ++ ++static void MatchFinder_ReadBlock(CMatchFinder *p) ++{ ++ if (p->streamEndWasReached || p->result != SZ_OK) ++ return; ++ if (p->directInput) ++ { ++ UInt32 curSize = 0xFFFFFFFF - p->streamPos; ++ if (curSize > p->directInputRem) ++ curSize = (UInt32)p->directInputRem; ++ p->directInputRem -= curSize; ++ p->streamPos += curSize; ++ if (p->directInputRem == 0) ++ p->streamEndWasReached = 1; ++ return; ++ } ++ for (;;) ++ { ++ Byte *dest = p->buffer + (p->streamPos - p->pos); ++ size_t size = (p->bufferBase + p->blockSize - dest); ++ if (size == 0) ++ return; ++ p->result = p->stream->Read(p->stream, dest, &size); ++ if (p->result != SZ_OK) ++ return; ++ if (size == 0) ++ { ++ p->streamEndWasReached = 1; ++ return; ++ } ++ p->streamPos += (UInt32)size; ++ if (p->streamPos - p->pos > p->keepSizeAfter) ++ return; ++ } ++} ++ ++void MatchFinder_MoveBlock(CMatchFinder *p) ++{ ++ memmove(p->bufferBase, ++ p->buffer - p->keepSizeBefore, ++ (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); ++ p->buffer = p->bufferBase + p->keepSizeBefore; ++} ++ ++int MatchFinder_NeedMove(CMatchFinder *p) ++{ ++ if (p->directInput) ++ return 0; ++ /* if (p->streamEndWasReached) return 0; */ ++ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); ++} ++ ++void MatchFinder_ReadIfRequired(CMatchFinder *p) ++{ ++ if (p->streamEndWasReached) ++ return; ++ if (p->keepSizeAfter >= p->streamPos - p->pos) ++ MatchFinder_ReadBlock(p); ++} ++ ++static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) ++{ ++ if (MatchFinder_NeedMove(p)) ++ MatchFinder_MoveBlock(p); ++ MatchFinder_ReadBlock(p); ++} ++ ++static void MatchFinder_SetDefaultSettings(CMatchFinder *p) ++{ ++ p->cutValue = 32; ++ p->btMode = 1; ++ p->numHashBytes = 4; ++ p->bigHash = 0; ++} ++ ++#define kCrcPoly 0xEDB88320 ++ ++void MatchFinder_Construct(CMatchFinder *p) ++{ ++ UInt32 i; ++ p->bufferBase = 0; ++ p->directInput = 0; ++ p->hash = 0; ++ MatchFinder_SetDefaultSettings(p); ++ ++ for (i = 0; i < 256; i++) ++ { ++ UInt32 r = i; ++ int j; ++ for (j = 0; j < 8; j++) ++ r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); ++ p->crc[i] = r; ++ } ++} ++ ++static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->hash); ++ p->hash = 0; ++} ++ ++void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) ++{ ++ MatchFinder_FreeThisClassMemory(p, alloc); ++ LzInWindow_Free(p, alloc); ++} ++ ++static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc) ++{ ++ size_t sizeInBytes = (size_t)num * sizeof(CLzRef); ++ if (sizeInBytes / sizeof(CLzRef) != num) ++ return 0; ++ return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); ++} ++ ++int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, ++ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ++ ISzAlloc *alloc) ++{ ++ UInt32 sizeReserv; ++ if (historySize > kMaxHistorySize) ++ { ++ MatchFinder_Free(p, alloc); ++ return 0; ++ } ++ sizeReserv = historySize >> 1; ++ if (historySize > ((UInt32)2 << 30)) ++ sizeReserv = historySize >> 2; ++ sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); ++ ++ p->keepSizeBefore = historySize + keepAddBufferBefore + 1; ++ p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; ++ /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ ++ if (LzInWindow_Create(p, sizeReserv, alloc)) ++ { ++ UInt32 newCyclicBufferSize = historySize + 1; ++ UInt32 hs; ++ p->matchMaxLen = matchMaxLen; ++ { ++ p->fixedHashSize = 0; ++ if (p->numHashBytes == 2) ++ hs = (1 << 16) - 1; ++ else ++ { ++ hs = historySize - 1; ++ hs |= (hs >> 1); ++ hs |= (hs >> 2); ++ hs |= (hs >> 4); ++ hs |= (hs >> 8); ++ hs >>= 1; ++ hs |= 0xFFFF; /* don't change it! It's required for Deflate */ ++ if (hs > (1 << 24)) ++ { ++ if (p->numHashBytes == 3) ++ hs = (1 << 24) - 1; ++ else ++ hs >>= 1; ++ } ++ } ++ p->hashMask = hs; ++ hs++; ++ if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size; ++ if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size; ++ if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size; ++ hs += p->fixedHashSize; ++ } ++ ++ { ++ UInt32 prevSize = p->hashSizeSum + p->numSons; ++ UInt32 newSize; ++ p->historySize = historySize; ++ p->hashSizeSum = hs; ++ p->cyclicBufferSize = newCyclicBufferSize; ++ p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); ++ newSize = p->hashSizeSum + p->numSons; ++ if (p->hash != 0 && prevSize == newSize) ++ return 1; ++ MatchFinder_FreeThisClassMemory(p, alloc); ++ p->hash = AllocRefs(newSize, alloc); ++ if (p->hash != 0) ++ { ++ p->son = p->hash + p->hashSizeSum; ++ return 1; ++ } ++ } ++ } ++ MatchFinder_Free(p, alloc); ++ return 0; ++} ++ ++static void MatchFinder_SetLimits(CMatchFinder *p) ++{ ++ UInt32 limit = kMaxValForNormalize - p->pos; ++ UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos; ++ if (limit2 < limit) ++ limit = limit2; ++ limit2 = p->streamPos - p->pos; ++ if (limit2 <= p->keepSizeAfter) ++ { ++ if (limit2 > 0) ++ limit2 = 1; ++ } ++ else ++ limit2 -= p->keepSizeAfter; ++ if (limit2 < limit) ++ limit = limit2; ++ { ++ UInt32 lenLimit = p->streamPos - p->pos; ++ if (lenLimit > p->matchMaxLen) ++ lenLimit = p->matchMaxLen; ++ p->lenLimit = lenLimit; ++ } ++ p->posLimit = p->pos + limit; ++} ++ ++void MatchFinder_Init(CMatchFinder *p) ++{ ++ UInt32 i; ++ for (i = 0; i < p->hashSizeSum; i++) ++ p->hash[i] = kEmptyHashValue; ++ p->cyclicBufferPos = 0; ++ p->buffer = p->bufferBase; ++ p->pos = p->streamPos = p->cyclicBufferSize; ++ p->result = SZ_OK; ++ p->streamEndWasReached = 0; ++ MatchFinder_ReadBlock(p); ++ MatchFinder_SetLimits(p); ++} ++ ++static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) ++{ ++ return (p->pos - p->historySize - 1) & kNormalizeMask; ++} ++ ++void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems) ++{ ++ UInt32 i; ++ for (i = 0; i < numItems; i++) ++ { ++ UInt32 value = items[i]; ++ if (value <= subValue) ++ value = kEmptyHashValue; ++ else ++ value -= subValue; ++ items[i] = value; ++ } ++} ++ ++static void MatchFinder_Normalize(CMatchFinder *p) ++{ ++ UInt32 subValue = MatchFinder_GetSubValue(p); ++ MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); ++ MatchFinder_ReduceOffsets(p, subValue); ++} ++ ++static void MatchFinder_CheckLimits(CMatchFinder *p) ++{ ++ if (p->pos == kMaxValForNormalize) ++ MatchFinder_Normalize(p); ++ if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) ++ MatchFinder_CheckAndMoveAndRead(p); ++ if (p->cyclicBufferPos == p->cyclicBufferSize) ++ p->cyclicBufferPos = 0; ++ MatchFinder_SetLimits(p); ++} ++ ++static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, ++ UInt32 *distances, UInt32 maxLen) ++{ ++ son[_cyclicBufferPos] = curMatch; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ return distances; ++ { ++ const Byte *pb = cur - delta; ++ curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; ++ if (pb[maxLen] == cur[maxLen] && *pb == *cur) ++ { ++ UInt32 len = 0; ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ if (maxLen < len) ++ { ++ *distances++ = maxLen = len; ++ *distances++ = delta - 1; ++ if (len == lenLimit) ++ return distances; ++ } ++ } ++ } ++ } ++} ++ ++UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue, ++ UInt32 *distances, UInt32 maxLen) ++{ ++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; ++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1); ++ UInt32 len0 = 0, len1 = 0; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ { ++ *ptr0 = *ptr1 = kEmptyHashValue; ++ return distances; ++ } ++ { ++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); ++ const Byte *pb = cur - delta; ++ UInt32 len = (len0 < len1 ? len0 : len1); ++ if (pb[len] == cur[len]) ++ { ++ if (++len != lenLimit && pb[len] == cur[len]) ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ if (maxLen < len) ++ { ++ *distances++ = maxLen = len; ++ *distances++ = delta - 1; ++ if (len == lenLimit) ++ { ++ *ptr1 = pair[0]; ++ *ptr0 = pair[1]; ++ return distances; ++ } ++ } ++ } ++ if (pb[len] < cur[len]) ++ { ++ *ptr1 = curMatch; ++ ptr1 = pair + 1; ++ curMatch = *ptr1; ++ len1 = len; ++ } ++ else ++ { ++ *ptr0 = curMatch; ++ ptr0 = pair; ++ curMatch = *ptr0; ++ len0 = len; ++ } ++ } ++ } ++} ++ ++static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son, ++ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue) ++{ ++ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; ++ CLzRef *ptr1 = son + (_cyclicBufferPos << 1); ++ UInt32 len0 = 0, len1 = 0; ++ for (;;) ++ { ++ UInt32 delta = pos - curMatch; ++ if (cutValue-- == 0 || delta >= _cyclicBufferSize) ++ { ++ *ptr0 = *ptr1 = kEmptyHashValue; ++ return; ++ } ++ { ++ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); ++ const Byte *pb = cur - delta; ++ UInt32 len = (len0 < len1 ? len0 : len1); ++ if (pb[len] == cur[len]) ++ { ++ while (++len != lenLimit) ++ if (pb[len] != cur[len]) ++ break; ++ { ++ if (len == lenLimit) ++ { ++ *ptr1 = pair[0]; ++ *ptr0 = pair[1]; ++ return; ++ } ++ } ++ } ++ if (pb[len] < cur[len]) ++ { ++ *ptr1 = curMatch; ++ ptr1 = pair + 1; ++ curMatch = *ptr1; ++ len1 = len; ++ } ++ else ++ { ++ *ptr0 = curMatch; ++ ptr0 = pair; ++ curMatch = *ptr0; ++ len0 = len; ++ } ++ } ++ } ++} ++ ++#define MOVE_POS \ ++ ++p->cyclicBufferPos; \ ++ p->buffer++; \ ++ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p); ++ ++#define MOVE_POS_RET MOVE_POS return offset; ++ ++static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; } ++ ++#define GET_MATCHES_HEADER2(minLen, ret_op) \ ++ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \ ++ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \ ++ cur = p->buffer; ++ ++#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) ++#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) ++ ++#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue ++ ++#define GET_MATCHES_FOOTER(offset, maxLen) \ ++ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ ++ distances + offset, maxLen) - distances); MOVE_POS_RET; ++ ++#define SKIP_FOOTER \ ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; ++ ++static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(2) ++ HASH2_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = 0; ++ GET_MATCHES_FOOTER(offset, 1) ++} ++ ++UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = 0; ++ GET_MATCHES_FOOTER(offset, 2) ++} ++ ++static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, delta2, maxLen, offset; ++ GET_MATCHES_HEADER(3) ++ ++ HASH3_CALC; ++ ++ delta2 = p->pos - p->hash[hash2Value]; ++ curMatch = p->hash[kFix3HashSize + hashValue]; ++ ++ p->hash[hash2Value] = ++ p->hash[kFix3HashSize + hashValue] = p->pos; ++ ++ ++ maxLen = 2; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[0] = maxLen; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ if (maxLen == lenLimit) ++ { ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); ++ MOVE_POS_RET; ++ } ++ } ++ GET_MATCHES_FOOTER(offset, maxLen) ++} ++ ++static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; ++ GET_MATCHES_HEADER(4) ++ ++ HASH4_CALC; ++ ++ delta2 = p->pos - p->hash[ hash2Value]; ++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ ++ maxLen = 1; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ distances[0] = maxLen = 2; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ } ++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) ++ { ++ maxLen = 3; ++ distances[offset + 1] = delta3 - 1; ++ offset += 2; ++ delta2 = delta3; ++ } ++ if (offset != 0) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[offset - 2] = maxLen; ++ if (maxLen == lenLimit) ++ { ++ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); ++ MOVE_POS_RET; ++ } ++ } ++ if (maxLen < 3) ++ maxLen = 3; ++ GET_MATCHES_FOOTER(offset, maxLen) ++} ++ ++static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset; ++ GET_MATCHES_HEADER(4) ++ ++ HASH4_CALC; ++ ++ delta2 = p->pos - p->hash[ hash2Value]; ++ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ ++ maxLen = 1; ++ offset = 0; ++ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) ++ { ++ distances[0] = maxLen = 2; ++ distances[1] = delta2 - 1; ++ offset = 2; ++ } ++ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) ++ { ++ maxLen = 3; ++ distances[offset + 1] = delta3 - 1; ++ offset += 2; ++ delta2 = delta3; ++ } ++ if (offset != 0) ++ { ++ for (; maxLen != lenLimit; maxLen++) ++ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) ++ break; ++ distances[offset - 2] = maxLen; ++ if (maxLen == lenLimit) ++ { ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS_RET; ++ } ++ } ++ if (maxLen < 3) ++ maxLen = 3; ++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), ++ distances + offset, maxLen) - (distances)); ++ MOVE_POS_RET ++} ++ ++UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances) ++{ ++ UInt32 offset; ++ GET_MATCHES_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), ++ distances, 2) - (distances)); ++ MOVE_POS_RET ++} ++ ++static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(2) ++ HASH2_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value; ++ SKIP_HEADER(3) ++ HASH3_CALC; ++ curMatch = p->hash[kFix3HashSize + hashValue]; ++ p->hash[hash2Value] = ++ p->hash[kFix3HashSize + hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value, hash3Value; ++ SKIP_HEADER(4) ++ HASH4_CALC; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = p->pos; ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ SKIP_FOOTER ++ } ++ while (--num != 0); ++} ++ ++static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ UInt32 hash2Value, hash3Value; ++ SKIP_HEADER(4) ++ HASH4_CALC; ++ curMatch = p->hash[kFix4HashSize + hashValue]; ++ p->hash[ hash2Value] = ++ p->hash[kFix3HashSize + hash3Value] = ++ p->hash[kFix4HashSize + hashValue] = p->pos; ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS ++ } ++ while (--num != 0); ++} ++ ++void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num) ++{ ++ do ++ { ++ SKIP_HEADER(3) ++ HASH_ZIP_CALC; ++ curMatch = p->hash[hashValue]; ++ p->hash[hashValue] = p->pos; ++ p->son[p->cyclicBufferPos] = curMatch; ++ MOVE_POS ++ } ++ while (--num != 0); ++} ++ ++void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) ++{ ++ vTable->Init = (Mf_Init_Func)MatchFinder_Init; ++ vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; ++ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; ++ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; ++ if (!p->btMode) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; ++ } ++ else if (p->numHashBytes == 2) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; ++ } ++ else if (p->numHashBytes == 3) ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; ++ } ++ else ++ { ++ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; ++ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; ++ } ++} +diff --git a/lib/lzma/LzmaDec.c b/lib/lzma/LzmaDec.c +new file mode 100644 +index 000000000000..3fab58344239 +--- /dev/null ++++ b/lib/lzma/LzmaDec.c +@@ -0,0 +1,999 @@ ++/* LzmaDec.c -- LZMA Decoder ++2009-09-20 : Igor Pavlov : Public domain */ ++ ++#include "LzmaDec.h" ++ ++#include <string.h> ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++ ++#define RC_INIT_SIZE 5 ++ ++#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); ++#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); ++#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ ++ { UPDATE_0(p); i = (i + i); A0; } else \ ++ { UPDATE_1(p); i = (i + i) + 1; A1; } ++#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) ++ ++#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } ++#define TREE_DECODE(probs, limit, i) \ ++ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } ++ ++/* #define _LZMA_SIZE_OPT */ ++ ++#ifdef _LZMA_SIZE_OPT ++#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) ++#else ++#define TREE_6_DECODE(probs, i) \ ++ { i = 1; \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ TREE_GET_BIT(probs, i); \ ++ i -= 0x40; } ++#endif ++ ++#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } ++ ++#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) ++#define UPDATE_0_CHECK range = bound; ++#define UPDATE_1_CHECK range -= bound; code -= bound; ++#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ ++ { UPDATE_0_CHECK; i = (i + i); A0; } else \ ++ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } ++#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) ++#define TREE_DECODE_CHECK(probs, limit, i) \ ++ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } ++ ++ ++#define kNumPosBitsMax 4 ++#define kNumPosStatesMax (1 << kNumPosBitsMax) ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define LenChoice 0 ++#define LenChoice2 (LenChoice + 1) ++#define LenLow (LenChoice2 + 1) ++#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) ++#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) ++#define kNumLenProbs (LenHigh + kLenNumHighSymbols) ++ ++ ++#define kNumStates 12 ++#define kNumLitStates 7 ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#define kNumPosSlotBits 6 ++#define kNumLenToPosStates 4 ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++ ++#define kMatchMinLen 2 ++#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define IsMatch 0 ++#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) ++#define IsRepG0 (IsRep + kNumStates) ++#define IsRepG1 (IsRepG0 + kNumStates) ++#define IsRepG2 (IsRepG1 + kNumStates) ++#define IsRep0Long (IsRepG2 + kNumStates) ++#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) ++#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) ++#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) ++#define LenCoder (Align + kAlignTableSize) ++#define RepLenCoder (LenCoder + kNumLenProbs) ++#define Literal (RepLenCoder + kNumLenProbs) ++ ++#define LZMA_BASE_SIZE 1846 ++#define LZMA_LIT_SIZE 768 ++ ++#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) ++ ++#if Literal != LZMA_BASE_SIZE ++StopCompilingDueBUG ++#endif ++ ++#define LZMA_DIC_MIN (1 << 12) ++ ++/* First LZMA-symbol is always decoded. ++And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization ++Out: ++ Result: ++ SZ_OK - OK ++ SZ_ERROR_DATA - Error ++ p->remainLen: ++ < kMatchSpecLenStart : normal remain ++ = kMatchSpecLenStart : finished ++ = kMatchSpecLenStart + 1 : Flush marker ++ = kMatchSpecLenStart + 2 : State Init Marker ++*/ ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ CLzmaProb *probs = p->probs; ++ ++ unsigned state = p->state; ++ UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; ++ unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; ++ unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; ++ unsigned lc = p->prop.lc; ++ ++ Byte *dic = p->dic; ++ SizeT dicBufSize = p->dicBufSize; ++ SizeT dicPos = p->dicPos; ++ ++ UInt32 processedPos = p->processedPos; ++ UInt32 checkDicSize = p->checkDicSize; ++ unsigned len = 0; ++ ++ const Byte *buf = p->buf; ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ ++ do ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = processedPos & pbMask; ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ unsigned symbol; ++ UPDATE_0(prob); ++ prob = probs + Literal; ++ if (checkDicSize != 0 || processedPos != 0) ++ prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + ++ (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ state -= (state < 4) ? state : 3; ++ symbol = 1; ++ do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ state -= (state < 10) ? 3 : 6; ++ symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ dic[dicPos++] = (Byte)symbol; ++ processedPos++; ++ continue; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRep + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ state += kNumStates; ++ prob = probs + LenCoder; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ if (checkDicSize == 0 && processedPos == 0) ++ return SZ_ERROR_DATA; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ processedPos++; ++ state = state < kNumLitStates ? 9 : 11; ++ continue; ++ } ++ UPDATE_1(prob); ++ } ++ else ++ { ++ UInt32 distance; ++ UPDATE_1(prob); ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep1; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0(prob) ++ { ++ UPDATE_0(prob); ++ distance = rep2; ++ } ++ else ++ { ++ UPDATE_1(prob); ++ distance = rep3; ++ rep3 = rep2; ++ } ++ rep2 = rep1; ++ } ++ rep1 = rep0; ++ rep0 = distance; ++ } ++ state = state < kNumLitStates ? 8 : 11; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = (1 << kLenNumLowBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenChoice2; ++ IF_BIT_0(probLen) ++ { ++ UPDATE_0(probLen); ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = (1 << kLenNumMidBits); ++ } ++ else ++ { ++ UPDATE_1(probLen); ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = (1 << kLenNumHighBits); ++ } ++ } ++ TREE_DECODE(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state >= kNumStates) ++ { ++ UInt32 distance; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); ++ TREE_6_DECODE(prob, distance); ++ if (distance >= kStartPosModelIndex) ++ { ++ unsigned posSlot = (unsigned)distance; ++ int numDirectBits = (int)(((distance >> 1) - 1)); ++ distance = (2 | (distance & 1)); ++ if (posSlot < kEndPosModelIndex) ++ { ++ distance <<= numDirectBits; ++ prob = probs + SpecPos + distance - posSlot - 1; ++ { ++ UInt32 mask = 1; ++ unsigned i = 1; ++ do ++ { ++ GET_BIT2(prob + i, i, ; , distance |= mask); ++ mask <<= 1; ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE ++ range >>= 1; ++ ++ { ++ UInt32 t; ++ code -= range; ++ t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ ++ distance = (distance << 1) + (t + 1); ++ code += range & t; ++ } ++ /* ++ distance <<= 1; ++ if (code >= range) ++ { ++ code -= range; ++ distance |= 1; ++ } ++ */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ distance <<= kNumAlignBits; ++ { ++ unsigned i = 1; ++ GET_BIT2(prob + i, i, ; , distance |= 1); ++ GET_BIT2(prob + i, i, ; , distance |= 2); ++ GET_BIT2(prob + i, i, ; , distance |= 4); ++ GET_BIT2(prob + i, i, ; , distance |= 8); ++ } ++ if (distance == (UInt32)0xFFFFFFFF) ++ { ++ len += kMatchSpecLenStart; ++ state -= kNumStates; ++ break; ++ } ++ } ++ } ++ rep3 = rep2; ++ rep2 = rep1; ++ rep1 = rep0; ++ rep0 = distance + 1; ++ if (checkDicSize == 0) ++ { ++ if (distance >= processedPos) ++ return SZ_ERROR_DATA; ++ } ++ else if (distance >= checkDicSize) ++ return SZ_ERROR_DATA; ++ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; ++ } ++ ++ len += kMatchMinLen; ++ ++ if (limit == dicPos) ++ return SZ_ERROR_DATA; ++ { ++ SizeT rem = limit - dicPos; ++ unsigned curLen = ((rem < len) ? (unsigned)rem : len); ++ SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); ++ ++ processedPos += curLen; ++ ++ len -= curLen; ++ if (pos + curLen <= dicBufSize) ++ { ++ Byte *dest = dic + dicPos; ++ ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; ++ const Byte *lim = dest + curLen; ++ dicPos += curLen; ++ do ++ *(dest) = (Byte)*(dest + src); ++ while (++dest != lim); ++ } ++ else ++ { ++ do ++ { ++ dic[dicPos++] = dic[pos]; ++ if (++pos == dicBufSize) ++ pos = 0; ++ } ++ while (--curLen != 0); ++ } ++ } ++ } ++ } ++ while (dicPos < limit && buf < bufLimit); ++ NORMALIZE; ++ p->buf = buf; ++ p->range = range; ++ p->code = code; ++ p->remainLen = len; ++ p->dicPos = dicPos; ++ p->processedPos = processedPos; ++ p->reps[0] = rep0; ++ p->reps[1] = rep1; ++ p->reps[2] = rep2; ++ p->reps[3] = rep3; ++ p->state = state; ++ ++ return SZ_OK; ++} ++ ++static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) ++{ ++ if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) ++ { ++ Byte *dic = p->dic; ++ SizeT dicPos = p->dicPos; ++ SizeT dicBufSize = p->dicBufSize; ++ unsigned len = p->remainLen; ++ UInt32 rep0 = p->reps[0]; ++ if (limit - dicPos < len) ++ len = (unsigned)(limit - dicPos); ++ ++ if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) ++ p->checkDicSize = p->prop.dicSize; ++ ++ p->processedPos += len; ++ p->remainLen -= len; ++ while (len-- != 0) ++ { ++ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; ++ dicPos++; ++ } ++ p->dicPos = dicPos; ++ } ++} ++ ++static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) ++{ ++ do ++ { ++ SizeT limit2 = limit; ++ if (p->checkDicSize == 0) ++ { ++ UInt32 rem = p->prop.dicSize - p->processedPos; ++ if (limit - p->dicPos > rem) ++ limit2 = p->dicPos + rem; ++ } ++ RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); ++ if (p->processedPos >= p->prop.dicSize) ++ p->checkDicSize = p->prop.dicSize; ++ LzmaDec_WriteRem(p, limit); ++ } ++ while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); ++ ++ if (p->remainLen > kMatchSpecLenStart) ++ { ++ p->remainLen = kMatchSpecLenStart; ++ } ++ return 0; ++} ++ ++typedef enum ++{ ++ DUMMY_ERROR, /* unexpected end of input stream */ ++ DUMMY_LIT, ++ DUMMY_MATCH, ++ DUMMY_REP ++} ELzmaDummy; ++ ++static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) ++{ ++ UInt32 range = p->range; ++ UInt32 code = p->code; ++ const Byte *bufLimit = buf + inSize; ++ CLzmaProb *probs = p->probs; ++ unsigned state = p->state; ++ ELzmaDummy res; ++ ++ { ++ CLzmaProb *prob; ++ UInt32 bound; ++ unsigned ttt; ++ unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); ++ ++ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK ++ ++ /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ ++ ++ prob = probs + Literal; ++ if (p->checkDicSize != 0 || p->processedPos != 0) ++ prob += (LZMA_LIT_SIZE * ++ ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + ++ (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); ++ ++ if (state < kNumLitStates) ++ { ++ unsigned symbol = 1; ++ do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); ++ } ++ else ++ { ++ unsigned matchByte = p->dic[p->dicPos - p->reps[0] + ++ ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; ++ unsigned offs = 0x100; ++ unsigned symbol = 1; ++ do ++ { ++ unsigned bit; ++ CLzmaProb *probLit; ++ matchByte <<= 1; ++ bit = (matchByte & offs); ++ probLit = prob + offs + bit + symbol; ++ GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) ++ } ++ while (symbol < 0x100); ++ } ++ res = DUMMY_LIT; ++ } ++ else ++ { ++ unsigned len; ++ UPDATE_1_CHECK; ++ ++ prob = probs + IsRep + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ state = 0; ++ prob = probs + LenCoder; ++ res = DUMMY_MATCH; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ res = DUMMY_REP; ++ prob = probs + IsRepG0 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ NORMALIZE_CHECK; ++ return DUMMY_REP; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG1 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ prob = probs + IsRepG2 + state; ++ IF_BIT_0_CHECK(prob) ++ { ++ UPDATE_0_CHECK; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ } ++ } ++ } ++ state = kNumStates; ++ prob = probs + RepLenCoder; ++ } ++ { ++ unsigned limit, offset; ++ CLzmaProb *probLen = prob + LenChoice; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenLow + (posState << kLenNumLowBits); ++ offset = 0; ++ limit = 1 << kLenNumLowBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenChoice2; ++ IF_BIT_0_CHECK(probLen) ++ { ++ UPDATE_0_CHECK; ++ probLen = prob + LenMid + (posState << kLenNumMidBits); ++ offset = kLenNumLowSymbols; ++ limit = 1 << kLenNumMidBits; ++ } ++ else ++ { ++ UPDATE_1_CHECK; ++ probLen = prob + LenHigh; ++ offset = kLenNumLowSymbols + kLenNumMidSymbols; ++ limit = 1 << kLenNumHighBits; ++ } ++ } ++ TREE_DECODE_CHECK(probLen, limit, len); ++ len += offset; ++ } ++ ++ if (state < 4) ++ { ++ unsigned posSlot; ++ prob = probs + PosSlot + ++ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << ++ kNumPosSlotBits); ++ TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); ++ if (posSlot >= kStartPosModelIndex) ++ { ++ int numDirectBits = ((posSlot >> 1) - 1); ++ ++ /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ ++ ++ if (posSlot < kEndPosModelIndex) ++ { ++ prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; ++ } ++ else ++ { ++ numDirectBits -= kNumAlignBits; ++ do ++ { ++ NORMALIZE_CHECK ++ range >>= 1; ++ code -= range & (((code - range) >> 31) - 1); ++ /* if (code >= range) code -= range; */ ++ } ++ while (--numDirectBits != 0); ++ prob = probs + Align; ++ numDirectBits = kNumAlignBits; ++ } ++ { ++ unsigned i = 1; ++ do ++ { ++ GET_BIT_CHECK(prob + i, i); ++ } ++ while (--numDirectBits != 0); ++ } ++ } ++ } ++ } ++ } ++ NORMALIZE_CHECK; ++ return res; ++} ++ ++ ++static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) ++{ ++ p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]); ++ p->range = 0xFFFFFFFF; ++ p->needFlush = 0; ++} ++ ++void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) ++{ ++ p->needFlush = 1; ++ p->remainLen = 0; ++ p->tempBufSize = 0; ++ ++ if (initDic) ++ { ++ p->processedPos = 0; ++ p->checkDicSize = 0; ++ p->needInitState = 1; ++ } ++ if (initState) ++ p->needInitState = 1; ++} ++ ++void LzmaDec_Init(CLzmaDec *p) ++{ ++ p->dicPos = 0; ++ LzmaDec_InitDicAndState(p, True, True); ++} ++ ++static void LzmaDec_InitStateReal(CLzmaDec *p) ++{ ++ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); ++ UInt32 i; ++ CLzmaProb *probs = p->probs; ++ for (i = 0; i < numProbs; i++) ++ probs[i] = kBitModelTotal >> 1; ++ p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; ++ p->state = 0; ++ p->needInitState = 0; ++} ++ ++SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ++ ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT inSize = *srcLen; ++ (*srcLen) = 0; ++ LzmaDec_WriteRem(p, dicLimit); ++ ++ *status = LZMA_STATUS_NOT_SPECIFIED; ++ ++ while (p->remainLen != kMatchSpecLenStart) ++ { ++ int checkEndMarkNow; ++ ++ if (p->needFlush != 0) ++ { ++ for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) ++ p->tempBuf[p->tempBufSize++] = *src++; ++ if (p->tempBufSize < RC_INIT_SIZE) ++ { ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (p->tempBuf[0] != 0) ++ return SZ_ERROR_DATA; ++ ++ LzmaDec_InitRc(p, p->tempBuf); ++ p->tempBufSize = 0; ++ } ++ ++ checkEndMarkNow = 0; ++ if (p->dicPos >= dicLimit) ++ { ++ if (p->remainLen == 0 && p->code == 0) ++ { ++ *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; ++ return SZ_OK; ++ } ++ if (finishMode == LZMA_FINISH_ANY) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_OK; ++ } ++ if (p->remainLen != 0) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ checkEndMarkNow = 1; ++ } ++ ++ if (p->needInitState) ++ LzmaDec_InitStateReal(p); ++ ++ if (p->tempBufSize == 0) ++ { ++ SizeT processed; ++ const Byte *bufLimit; ++ if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, src, inSize); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ memcpy(p->tempBuf, src, inSize); ++ p->tempBufSize = (unsigned)inSize; ++ (*srcLen) += inSize; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ bufLimit = src; ++ } ++ else ++ bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; ++ p->buf = src; ++ if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) ++ return SZ_ERROR_DATA; ++ processed = (SizeT)(p->buf - src); ++ (*srcLen) += processed; ++ src += processed; ++ inSize -= processed; ++ } ++ else ++ { ++ unsigned rem = p->tempBufSize, lookAhead = 0; ++ while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) ++ p->tempBuf[rem++] = src[lookAhead++]; ++ p->tempBufSize = rem; ++ if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) ++ { ++ int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); ++ if (dummyRes == DUMMY_ERROR) ++ { ++ (*srcLen) += lookAhead; ++ *status = LZMA_STATUS_NEEDS_MORE_INPUT; ++ return SZ_OK; ++ } ++ if (checkEndMarkNow && dummyRes != DUMMY_MATCH) ++ { ++ *status = LZMA_STATUS_NOT_FINISHED; ++ return SZ_ERROR_DATA; ++ } ++ } ++ p->buf = p->tempBuf; ++ if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) ++ return SZ_ERROR_DATA; ++ lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); ++ (*srcLen) += lookAhead; ++ src += lookAhead; ++ inSize -= lookAhead; ++ p->tempBufSize = 0; ++ } ++ } ++ if (p->code == 0) ++ *status = LZMA_STATUS_FINISHED_WITH_MARK; ++ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; ++} ++ ++SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) ++{ ++ SizeT outSize = *destLen; ++ SizeT inSize = *srcLen; ++ *srcLen = *destLen = 0; ++ for (;;) ++ { ++ SizeT inSizeCur = inSize, outSizeCur, dicPos; ++ ELzmaFinishMode curFinishMode; ++ SRes res; ++ if (p->dicPos == p->dicBufSize) ++ p->dicPos = 0; ++ dicPos = p->dicPos; ++ if (outSize > p->dicBufSize - dicPos) ++ { ++ outSizeCur = p->dicBufSize; ++ curFinishMode = LZMA_FINISH_ANY; ++ } ++ else ++ { ++ outSizeCur = dicPos + outSize; ++ curFinishMode = finishMode; ++ } ++ ++ res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); ++ src += inSizeCur; ++ inSize -= inSizeCur; ++ *srcLen += inSizeCur; ++ outSizeCur = p->dicPos - dicPos; ++ memcpy(dest, p->dic + dicPos, outSizeCur); ++ dest += outSizeCur; ++ outSize -= outSizeCur; ++ *destLen += outSizeCur; ++ if (res != 0) ++ return res; ++ if (outSizeCur == 0 || outSize == 0) ++ return SZ_OK; ++ } ++} ++ ++void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->probs); ++ p->probs = 0; ++} ++ ++static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->dic); ++ p->dic = 0; ++} ++ ++void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) ++{ ++ LzmaDec_FreeProbs(p, alloc); ++ LzmaDec_FreeDict(p, alloc); ++} ++ ++SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) ++{ ++ UInt32 dicSize; ++ Byte d; ++ ++ if (size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_UNSUPPORTED; ++ else ++ dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); ++ ++ if (dicSize < LZMA_DIC_MIN) ++ dicSize = LZMA_DIC_MIN; ++ p->dicSize = dicSize; ++ ++ d = data[0]; ++ if (d >= (9 * 5 * 5)) ++ return SZ_ERROR_UNSUPPORTED; ++ ++ p->lc = d % 9; ++ d /= 9; ++ p->pb = d / 5; ++ p->lp = d % 5; ++ ++ return SZ_OK; ++} ++ ++static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) ++{ ++ UInt32 numProbs = LzmaProps_GetNumProbs(propNew); ++ if (p->probs == 0 || numProbs != p->numProbs) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); ++ p->numProbs = numProbs; ++ if (p->probs == 0) ++ return SZ_ERROR_MEM; ++ } ++ return SZ_OK; ++} ++ ++SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) ++{ ++ CLzmaProps propNew; ++ SizeT dicBufSize; ++ RINOK(LzmaProps_Decode(&propNew, props, propsSize)); ++ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); ++ dicBufSize = propNew.dicSize; ++ if (p->dic == 0 || dicBufSize != p->dicBufSize) ++ { ++ LzmaDec_FreeDict(p, alloc); ++ p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); ++ if (p->dic == 0) ++ { ++ LzmaDec_FreeProbs(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ } ++ p->dicBufSize = dicBufSize; ++ p->prop = propNew; ++ return SZ_OK; ++} ++ ++SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ++ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, ++ ELzmaStatus *status, ISzAlloc *alloc) ++{ ++ CLzmaDec p; ++ SRes res; ++ SizeT inSize = *srcLen; ++ SizeT outSize = *destLen; ++ *srcLen = *destLen = 0; ++ if (inSize < RC_INIT_SIZE) ++ return SZ_ERROR_INPUT_EOF; ++ ++ LzmaDec_Construct(&p); ++ res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc); ++ if (res != 0) ++ return res; ++ p.dic = dest; ++ p.dicBufSize = outSize; ++ ++ LzmaDec_Init(&p); ++ ++ *srcLen = inSize; ++ res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); ++ ++ if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) ++ res = SZ_ERROR_INPUT_EOF; ++ ++ (*destLen) = p.dicPos; ++ LzmaDec_FreeProbs(&p, alloc); ++ return res; ++} +diff --git a/lib/lzma/LzmaEnc.c b/lib/lzma/LzmaEnc.c +new file mode 100644 +index 000000000000..ee47d23054de +--- /dev/null ++++ b/lib/lzma/LzmaEnc.c +@@ -0,0 +1,2271 @@ ++/* LzmaEnc.c -- LZMA Encoder ++2009-11-24 : Igor Pavlov : Public domain */ ++ ++#include <string.h> ++ ++/* #define SHOW_STAT */ ++/* #define SHOW_STAT2 */ ++ ++#if defined(SHOW_STAT) || defined(SHOW_STAT2) ++#include <stdio.h> ++#endif ++ ++#include "LzmaEnc.h" ++ ++/* disable MT */ ++#define _7ZIP_ST ++ ++#include "LzFind.h" ++#ifndef _7ZIP_ST ++#include "LzFindMt.h" ++#endif ++ ++#ifdef SHOW_STAT ++static int ttt = 0; ++#endif ++ ++#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) ++ ++#define kBlockSize (9 << 10) ++#define kUnpackBlockSize (1 << 18) ++#define kMatchArraySize (1 << 21) ++#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) ++ ++#define kNumMaxDirectBits (31) ++ ++#define kNumTopBits 24 ++#define kTopValue ((UInt32)1 << kNumTopBits) ++ ++#define kNumBitModelTotalBits 11 ++#define kBitModelTotal (1 << kNumBitModelTotalBits) ++#define kNumMoveBits 5 ++#define kProbInitValue (kBitModelTotal >> 1) ++ ++#define kNumMoveReducingBits 4 ++#define kNumBitPriceShiftBits 4 ++#define kBitPrice (1 << kNumBitPriceShiftBits) ++ ++void LzmaEncProps_Init(CLzmaEncProps *p) ++{ ++ p->level = 5; ++ p->dictSize = p->mc = 0; ++ p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; ++ p->writeEndMark = 0; ++} ++ ++void LzmaEncProps_Normalize(CLzmaEncProps *p) ++{ ++ int level = p->level; ++ if (level < 0) level = 5; ++ p->level = level; ++ if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); ++ if (p->lc < 0) p->lc = 3; ++ if (p->lp < 0) p->lp = 0; ++ if (p->pb < 0) p->pb = 2; ++ if (p->algo < 0) p->algo = (level < 5 ? 0 : 1); ++ if (p->fb < 0) p->fb = (level < 7 ? 32 : 64); ++ if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1); ++ if (p->numHashBytes < 0) p->numHashBytes = 4; ++ if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); ++ if (p->numThreads < 0) ++ p->numThreads = ++ #ifndef _7ZIP_ST ++ ((p->btMode && p->algo) ? 2 : 1); ++ #else ++ 1; ++ #endif ++} ++ ++UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) ++{ ++ CLzmaEncProps props = *props2; ++ LzmaEncProps_Normalize(&props); ++ return props.dictSize; ++} ++ ++/* #define LZMA_LOG_BSR */ ++/* Define it for Intel's CPU */ ++ ++ ++#ifdef LZMA_LOG_BSR ++ ++#define kDicLogSizeMaxCompress 30 ++ ++#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } ++ ++UInt32 GetPosSlot1(UInt32 pos) ++{ ++ UInt32 res; ++ BSR2_RET(pos, res); ++ return res; ++} ++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } ++#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); } ++ ++#else ++ ++#define kNumLogBits (9 + (int)sizeof(size_t) / 2) ++#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) ++ ++void LzmaEnc_FastPosInit(Byte *g_FastPos) ++{ ++ int c = 2, slotFast; ++ g_FastPos[0] = 0; ++ g_FastPos[1] = 1; ++ ++ for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) ++ { ++ UInt32 k = (1 << ((slotFast >> 1) - 1)); ++ UInt32 j; ++ for (j = 0; j < k; j++, c++) ++ g_FastPos[c] = (Byte)slotFast; ++ } ++} ++ ++#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ ++ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ ++ res = p->g_FastPos[pos >> i] + (i * 2); } ++/* ++#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ ++ p->g_FastPos[pos >> 6] + 12 : \ ++ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } ++*/ ++ ++#define GetPosSlot1(pos) p->g_FastPos[pos] ++#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } ++#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } ++ ++#endif ++ ++ ++#define LZMA_NUM_REPS 4 ++ ++typedef unsigned CState; ++ ++typedef struct ++{ ++ UInt32 price; ++ ++ CState state; ++ int prev1IsChar; ++ int prev2; ++ ++ UInt32 posPrev2; ++ UInt32 backPrev2; ++ ++ UInt32 posPrev; ++ UInt32 backPrev; ++ UInt32 backs[LZMA_NUM_REPS]; ++} COptimal; ++ ++#define kNumOpts (1 << 12) ++ ++#define kNumLenToPosStates 4 ++#define kNumPosSlotBits 6 ++#define kDicLogSizeMin 0 ++#define kDicLogSizeMax 32 ++#define kDistTableSizeMax (kDicLogSizeMax * 2) ++ ++ ++#define kNumAlignBits 4 ++#define kAlignTableSize (1 << kNumAlignBits) ++#define kAlignMask (kAlignTableSize - 1) ++ ++#define kStartPosModelIndex 4 ++#define kEndPosModelIndex 14 ++#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) ++ ++#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) ++ ++#ifdef _LZMA_PROB32 ++#define CLzmaProb UInt32 ++#else ++#define CLzmaProb UInt16 ++#endif ++ ++#define LZMA_PB_MAX 4 ++#define LZMA_LC_MAX 8 ++#define LZMA_LP_MAX 4 ++ ++#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) ++ ++ ++#define kLenNumLowBits 3 ++#define kLenNumLowSymbols (1 << kLenNumLowBits) ++#define kLenNumMidBits 3 ++#define kLenNumMidSymbols (1 << kLenNumMidBits) ++#define kLenNumHighBits 8 ++#define kLenNumHighSymbols (1 << kLenNumHighBits) ++ ++#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) ++ ++#define LZMA_MATCH_LEN_MIN 2 ++#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) ++ ++#define kNumStates 12 ++ ++typedef struct ++{ ++ CLzmaProb choice; ++ CLzmaProb choice2; ++ CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; ++ CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; ++ CLzmaProb high[kLenNumHighSymbols]; ++} CLenEnc; ++ ++typedef struct ++{ ++ CLenEnc p; ++ UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; ++ UInt32 tableSize; ++ UInt32 counters[LZMA_NUM_PB_STATES_MAX]; ++} CLenPriceEnc; ++ ++typedef struct ++{ ++ UInt32 range; ++ Byte cache; ++ UInt64 low; ++ UInt64 cacheSize; ++ Byte *buf; ++ Byte *bufLim; ++ Byte *bufBase; ++ ISeqOutStream *outStream; ++ UInt64 processed; ++ SRes res; ++} CRangeEnc; ++ ++typedef struct ++{ ++ CLzmaProb *litProbs; ++ ++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ CLzmaProb isRep[kNumStates]; ++ CLzmaProb isRepG0[kNumStates]; ++ CLzmaProb isRepG1[kNumStates]; ++ CLzmaProb isRepG2[kNumStates]; ++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ ++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; ++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; ++ CLzmaProb posAlignEncoder[1 << kNumAlignBits]; ++ ++ CLenPriceEnc lenEnc; ++ CLenPriceEnc repLenEnc; ++ ++ UInt32 reps[LZMA_NUM_REPS]; ++ UInt32 state; ++} CSaveState; ++ ++typedef struct ++{ ++ IMatchFinder matchFinder; ++ void *matchFinderObj; ++ ++ #ifndef _7ZIP_ST ++ Bool mtMode; ++ CMatchFinderMt matchFinderMt; ++ #endif ++ ++ CMatchFinder matchFinderBase; ++ ++ #ifndef _7ZIP_ST ++ Byte pad[128]; ++ #endif ++ ++ UInt32 optimumEndIndex; ++ UInt32 optimumCurrentIndex; ++ ++ UInt32 longestMatchLength; ++ UInt32 numPairs; ++ UInt32 numAvail; ++ COptimal opt[kNumOpts]; ++ ++ #ifndef LZMA_LOG_BSR ++ Byte g_FastPos[1 << kNumLogBits]; ++ #endif ++ ++ UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; ++ UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; ++ UInt32 numFastBytes; ++ UInt32 additionalOffset; ++ UInt32 reps[LZMA_NUM_REPS]; ++ UInt32 state; ++ ++ UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; ++ UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; ++ UInt32 alignPrices[kAlignTableSize]; ++ UInt32 alignPriceCount; ++ ++ UInt32 distTableSize; ++ ++ unsigned lc, lp, pb; ++ unsigned lpMask, pbMask; ++ ++ CLzmaProb *litProbs; ++ ++ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ CLzmaProb isRep[kNumStates]; ++ CLzmaProb isRepG0[kNumStates]; ++ CLzmaProb isRepG1[kNumStates]; ++ CLzmaProb isRepG2[kNumStates]; ++ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; ++ ++ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; ++ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; ++ CLzmaProb posAlignEncoder[1 << kNumAlignBits]; ++ ++ CLenPriceEnc lenEnc; ++ CLenPriceEnc repLenEnc; ++ ++ unsigned lclp; ++ ++ Bool fastMode; ++ ++ CRangeEnc rc; ++ ++ Bool writeEndMark; ++ UInt64 nowPos64; ++ UInt32 matchPriceCount; ++ Bool finished; ++ Bool multiThread; ++ ++ SRes result; ++ UInt32 dictSize; ++ UInt32 matchFinderCycles; ++ ++ int needInit; ++ ++ CSaveState saveState; ++} CLzmaEnc; ++ ++void LzmaEnc_SaveState(CLzmaEncHandle pp) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ CSaveState *dest = &p->saveState; ++ int i; ++ dest->lenEnc = p->lenEnc; ++ dest->repLenEnc = p->repLenEnc; ++ dest->state = p->state; ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); ++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); ++ } ++ for (i = 0; i < kNumLenToPosStates; i++) ++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); ++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); ++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); ++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); ++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); ++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); ++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); ++ memcpy(dest->reps, p->reps, sizeof(p->reps)); ++ memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); ++} ++ ++void LzmaEnc_RestoreState(CLzmaEncHandle pp) ++{ ++ CLzmaEnc *dest = (CLzmaEnc *)pp; ++ const CSaveState *p = &dest->saveState; ++ int i; ++ dest->lenEnc = p->lenEnc; ++ dest->repLenEnc = p->repLenEnc; ++ dest->state = p->state; ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); ++ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); ++ } ++ for (i = 0; i < kNumLenToPosStates; i++) ++ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); ++ memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); ++ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); ++ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); ++ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); ++ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); ++ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); ++ memcpy(dest->reps, p->reps, sizeof(p->reps)); ++ memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); ++} ++ ++SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ CLzmaEncProps props = *props2; ++ LzmaEncProps_Normalize(&props); ++ ++ if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || ++ props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30)) ++ return SZ_ERROR_PARAM; ++ p->dictSize = props.dictSize; ++ p->matchFinderCycles = props.mc; ++ { ++ unsigned fb = props.fb; ++ if (fb < 5) ++ fb = 5; ++ if (fb > LZMA_MATCH_LEN_MAX) ++ fb = LZMA_MATCH_LEN_MAX; ++ p->numFastBytes = fb; ++ } ++ p->lc = props.lc; ++ p->lp = props.lp; ++ p->pb = props.pb; ++ p->fastMode = (props.algo == 0); ++ p->matchFinderBase.btMode = props.btMode; ++ { ++ UInt32 numHashBytes = 4; ++ if (props.btMode) ++ { ++ if (props.numHashBytes < 2) ++ numHashBytes = 2; ++ else if (props.numHashBytes < 4) ++ numHashBytes = props.numHashBytes; ++ } ++ p->matchFinderBase.numHashBytes = numHashBytes; ++ } ++ ++ p->matchFinderBase.cutValue = props.mc; ++ ++ p->writeEndMark = props.writeEndMark; ++ ++ #ifndef _7ZIP_ST ++ /* ++ if (newMultiThread != _multiThread) ++ { ++ ReleaseMatchFinder(); ++ _multiThread = newMultiThread; ++ } ++ */ ++ p->multiThread = (props.numThreads > 1); ++ #endif ++ ++ return SZ_OK; ++} ++ ++static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; ++static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; ++static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; ++static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; ++ ++#define IsCharState(s) ((s) < 7) ++ ++#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) ++ ++#define kInfinityPrice (1 << 30) ++ ++static void RangeEnc_Construct(CRangeEnc *p) ++{ ++ p->outStream = 0; ++ p->bufBase = 0; ++} ++ ++#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) ++ ++#define RC_BUF_SIZE (1 << 16) ++static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) ++{ ++ if (p->bufBase == 0) ++ { ++ p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); ++ if (p->bufBase == 0) ++ return 0; ++ p->bufLim = p->bufBase + RC_BUF_SIZE; ++ } ++ return 1; ++} ++ ++static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->bufBase); ++ p->bufBase = 0; ++} ++ ++static void RangeEnc_Init(CRangeEnc *p) ++{ ++ /* Stream.Init(); */ ++ p->low = 0; ++ p->range = 0xFFFFFFFF; ++ p->cacheSize = 1; ++ p->cache = 0; ++ ++ p->buf = p->bufBase; ++ ++ p->processed = 0; ++ p->res = SZ_OK; ++} ++ ++static void RangeEnc_FlushStream(CRangeEnc *p) ++{ ++ size_t num; ++ if (p->res != SZ_OK) ++ return; ++ num = p->buf - p->bufBase; ++ if (num != p->outStream->Write(p->outStream, p->bufBase, num)) ++ p->res = SZ_ERROR_WRITE; ++ p->processed += num; ++ p->buf = p->bufBase; ++} ++ ++static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) ++{ ++ if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0) ++ { ++ Byte temp = p->cache; ++ do ++ { ++ Byte *buf = p->buf; ++ *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); ++ p->buf = buf; ++ if (buf == p->bufLim) ++ RangeEnc_FlushStream(p); ++ temp = 0xFF; ++ } ++ while (--p->cacheSize != 0); ++ p->cache = (Byte)((UInt32)p->low >> 24); ++ } ++ p->cacheSize++; ++ p->low = (UInt32)p->low << 8; ++} ++ ++static void RangeEnc_FlushData(CRangeEnc *p) ++{ ++ int i; ++ for (i = 0; i < 5; i++) ++ RangeEnc_ShiftLow(p); ++} ++ ++static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits) ++{ ++ do ++ { ++ p->range >>= 1; ++ p->low += p->range & (0 - ((value >> --numBits) & 1)); ++ if (p->range < kTopValue) ++ { ++ p->range <<= 8; ++ RangeEnc_ShiftLow(p); ++ } ++ } ++ while (numBits != 0); ++} ++ ++static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) ++{ ++ UInt32 ttt = *prob; ++ UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; ++ if (symbol == 0) ++ { ++ p->range = newBound; ++ ttt += (kBitModelTotal - ttt) >> kNumMoveBits; ++ } ++ else ++ { ++ p->low += newBound; ++ p->range -= newBound; ++ ttt -= ttt >> kNumMoveBits; ++ } ++ *prob = (CLzmaProb)ttt; ++ if (p->range < kTopValue) ++ { ++ p->range <<= 8; ++ RangeEnc_ShiftLow(p); ++ } ++} ++ ++static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) ++{ ++ symbol |= 0x100; ++ do ++ { ++ RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); ++ symbol <<= 1; ++ } ++ while (symbol < 0x10000); ++} ++ ++static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) ++{ ++ UInt32 offs = 0x100; ++ symbol |= 0x100; ++ do ++ { ++ matchByte <<= 1; ++ RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); ++ symbol <<= 1; ++ offs &= ~(matchByte ^ symbol); ++ } ++ while (symbol < 0x10000); ++} ++ ++void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) ++{ ++ UInt32 i; ++ for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) ++ { ++ const int kCyclesBits = kNumBitPriceShiftBits; ++ UInt32 w = i; ++ UInt32 bitCount = 0; ++ int j; ++ for (j = 0; j < kCyclesBits; j++) ++ { ++ w = w * w; ++ bitCount <<= 1; ++ while (w >= ((UInt32)1 << 16)) ++ { ++ w >>= 1; ++ bitCount++; ++ } ++ } ++ ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); ++ } ++} ++ ++ ++#define GET_PRICE(prob, symbol) \ ++ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; ++ ++#define GET_PRICEa(prob, symbol) \ ++ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; ++ ++#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] ++#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] ++ ++#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] ++#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] ++ ++static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ symbol |= 0x100; ++ do ++ { ++ price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); ++ symbol <<= 1; ++ } ++ while (symbol < 0x10000); ++ return price; ++} ++ ++static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ UInt32 offs = 0x100; ++ symbol |= 0x100; ++ do ++ { ++ matchByte <<= 1; ++ price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); ++ symbol <<= 1; ++ offs &= ~(matchByte ^ symbol); ++ } ++ while (symbol < 0x10000); ++ return price; ++} ++ ++ ++static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) ++{ ++ UInt32 m = 1; ++ int i; ++ for (i = numBitLevels; i != 0;) ++ { ++ UInt32 bit; ++ i--; ++ bit = (symbol >> i) & 1; ++ RangeEnc_EncodeBit(rc, probs + m, bit); ++ m = (m << 1) | bit; ++ } ++} ++ ++static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) ++{ ++ UInt32 m = 1; ++ int i; ++ for (i = 0; i < numBitLevels; i++) ++ { ++ UInt32 bit = symbol & 1; ++ RangeEnc_EncodeBit(rc, probs + m, bit); ++ m = (m << 1) | bit; ++ symbol >>= 1; ++ } ++} ++ ++static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ symbol |= (1 << numBitLevels); ++ while (symbol != 1) ++ { ++ price += GET_PRICEa(probs[symbol >> 1], symbol & 1); ++ symbol >>= 1; ++ } ++ return price; ++} ++ ++static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices) ++{ ++ UInt32 price = 0; ++ UInt32 m = 1; ++ int i; ++ for (i = numBitLevels; i != 0; i--) ++ { ++ UInt32 bit = symbol & 1; ++ symbol >>= 1; ++ price += GET_PRICEa(probs[m], bit); ++ m = (m << 1) | bit; ++ } ++ return price; ++} ++ ++ ++static void LenEnc_Init(CLenEnc *p) ++{ ++ unsigned i; ++ p->choice = p->choice2 = kProbInitValue; ++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) ++ p->low[i] = kProbInitValue; ++ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) ++ p->mid[i] = kProbInitValue; ++ for (i = 0; i < kLenNumHighSymbols; i++) ++ p->high[i] = kProbInitValue; ++} ++ ++static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) ++{ ++ if (symbol < kLenNumLowSymbols) ++ { ++ RangeEnc_EncodeBit(rc, &p->choice, 0); ++ RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); ++ } ++ else ++ { ++ RangeEnc_EncodeBit(rc, &p->choice, 1); ++ if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) ++ { ++ RangeEnc_EncodeBit(rc, &p->choice2, 0); ++ RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); ++ } ++ else ++ { ++ RangeEnc_EncodeBit(rc, &p->choice2, 1); ++ RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); ++ } ++ } ++} ++ ++static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices) ++{ ++ UInt32 a0 = GET_PRICE_0a(p->choice); ++ UInt32 a1 = GET_PRICE_1a(p->choice); ++ UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); ++ UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); ++ UInt32 i = 0; ++ for (i = 0; i < kLenNumLowSymbols; i++) ++ { ++ if (i >= numSymbols) ++ return; ++ prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); ++ } ++ for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) ++ { ++ if (i >= numSymbols) ++ return; ++ prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); ++ } ++ for (; i < numSymbols; i++) ++ prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); ++} ++ ++static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices) ++{ ++ LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); ++ p->counters[posState] = p->tableSize; ++} ++ ++static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices) ++{ ++ UInt32 posState; ++ for (posState = 0; posState < numPosStates; posState++) ++ LenPriceEnc_UpdateTable(p, posState, ProbPrices); ++} ++ ++static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices) ++{ ++ LenEnc_Encode(&p->p, rc, symbol, posState); ++ if (updatePrice) ++ if (--p->counters[posState] == 0) ++ LenPriceEnc_UpdateTable(p, posState, ProbPrices); ++} ++ ++ ++ ++ ++static void MovePos(CLzmaEnc *p, UInt32 num) ++{ ++ #ifdef SHOW_STAT ++ ttt += num; ++ printf("\n MovePos %d", num); ++ #endif ++ if (num != 0) ++ { ++ p->additionalOffset += num; ++ p->matchFinder.Skip(p->matchFinderObj, num); ++ } ++} ++ ++static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) ++{ ++ UInt32 lenRes = 0, numPairs; ++ p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); ++ numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); ++ #ifdef SHOW_STAT ++ printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); ++ ttt++; ++ { ++ UInt32 i; ++ for (i = 0; i < numPairs; i += 2) ++ printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); ++ } ++ #endif ++ if (numPairs > 0) ++ { ++ lenRes = p->matches[numPairs - 2]; ++ if (lenRes == p->numFastBytes) ++ { ++ const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ UInt32 distance = p->matches[numPairs - 1] + 1; ++ UInt32 numAvail = p->numAvail; ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ { ++ const Byte *pby2 = pby - distance; ++ for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++); ++ } ++ } ++ } ++ p->additionalOffset++; ++ *numDistancePairsRes = numPairs; ++ return lenRes; ++} ++ ++ ++#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; ++#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; ++#define IsShortRep(p) ((p)->backPrev == 0) ++ ++static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) ++{ ++ return ++ GET_PRICE_0(p->isRepG0[state]) + ++ GET_PRICE_0(p->isRep0Long[state][posState]); ++} ++ ++static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) ++{ ++ UInt32 price; ++ if (repIndex == 0) ++ { ++ price = GET_PRICE_0(p->isRepG0[state]); ++ price += GET_PRICE_1(p->isRep0Long[state][posState]); ++ } ++ else ++ { ++ price = GET_PRICE_1(p->isRepG0[state]); ++ if (repIndex == 1) ++ price += GET_PRICE_0(p->isRepG1[state]); ++ else ++ { ++ price += GET_PRICE_1(p->isRepG1[state]); ++ price += GET_PRICE(p->isRepG2[state], repIndex - 2); ++ } ++ } ++ return price; ++} ++ ++static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) ++{ ++ return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + ++ GetPureRepPrice(p, repIndex, state, posState); ++} ++ ++static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) ++{ ++ UInt32 posMem = p->opt[cur].posPrev; ++ UInt32 backMem = p->opt[cur].backPrev; ++ p->optimumEndIndex = cur; ++ do ++ { ++ if (p->opt[cur].prev1IsChar) ++ { ++ MakeAsChar(&p->opt[posMem]) ++ p->opt[posMem].posPrev = posMem - 1; ++ if (p->opt[cur].prev2) ++ { ++ p->opt[posMem - 1].prev1IsChar = False; ++ p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; ++ p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; ++ } ++ } ++ { ++ UInt32 posPrev = posMem; ++ UInt32 backCur = backMem; ++ ++ backMem = p->opt[posPrev].backPrev; ++ posMem = p->opt[posPrev].posPrev; ++ ++ p->opt[posPrev].backPrev = backCur; ++ p->opt[posPrev].posPrev = cur; ++ cur = posPrev; ++ } ++ } ++ while (cur != 0); ++ *backRes = p->opt[0].backPrev; ++ p->optimumCurrentIndex = p->opt[0].posPrev; ++ return p->optimumCurrentIndex; ++} ++ ++#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) ++ ++static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) ++{ ++ UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; ++ UInt32 matchPrice, repMatchPrice, normalMatchPrice; ++ UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; ++ UInt32 *matches; ++ const Byte *data; ++ Byte curByte, matchByte; ++ if (p->optimumEndIndex != p->optimumCurrentIndex) ++ { ++ const COptimal *opt = &p->opt[p->optimumCurrentIndex]; ++ UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; ++ *backRes = opt->backPrev; ++ p->optimumCurrentIndex = opt->posPrev; ++ return lenRes; ++ } ++ p->optimumCurrentIndex = p->optimumEndIndex = 0; ++ ++ if (p->additionalOffset == 0) ++ mainLen = ReadMatchDistances(p, &numPairs); ++ else ++ { ++ mainLen = p->longestMatchLength; ++ numPairs = p->numPairs; ++ } ++ ++ numAvail = p->numAvail; ++ if (numAvail < 2) ++ { ++ *backRes = (UInt32)(-1); ++ return 1; ++ } ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ repMaxIndex = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 lenTest; ++ const Byte *data2; ++ reps[i] = p->reps[i]; ++ data2 = data - (reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ { ++ repLens[i] = 0; ++ continue; ++ } ++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); ++ repLens[i] = lenTest; ++ if (lenTest > repLens[repMaxIndex]) ++ repMaxIndex = i; ++ } ++ if (repLens[repMaxIndex] >= p->numFastBytes) ++ { ++ UInt32 lenRes; ++ *backRes = repMaxIndex; ++ lenRes = repLens[repMaxIndex]; ++ MovePos(p, lenRes - 1); ++ return lenRes; ++ } ++ ++ matches = p->matches; ++ if (mainLen >= p->numFastBytes) ++ { ++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 1); ++ return mainLen; ++ } ++ curByte = *data; ++ matchByte = *(data - (reps[0] + 1)); ++ ++ if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) ++ { ++ *backRes = (UInt32)-1; ++ return 1; ++ } ++ ++ p->opt[0].state = (CState)p->state; ++ ++ posState = (position & p->pbMask); ++ ++ { ++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); ++ p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + ++ (!IsCharState(p->state) ? ++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : ++ LitEnc_GetPrice(probs, curByte, p->ProbPrices)); ++ } ++ ++ MakeAsChar(&p->opt[1]); ++ ++ matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); ++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); ++ ++ if (matchByte == curByte) ++ { ++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); ++ if (shortRepPrice < p->opt[1].price) ++ { ++ p->opt[1].price = shortRepPrice; ++ MakeAsShortRep(&p->opt[1]); ++ } ++ } ++ lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); ++ ++ if (lenEnd < 2) ++ { ++ *backRes = p->opt[1].backPrev; ++ return 1; ++ } ++ ++ p->opt[1].posPrev = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ p->opt[0].backs[i] = reps[i]; ++ ++ len = lenEnd; ++ do ++ p->opt[len--].price = kInfinityPrice; ++ while (len >= 2); ++ ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 repLen = repLens[i]; ++ UInt32 price; ++ if (repLen < 2) ++ continue; ++ price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); ++ do ++ { ++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; ++ COptimal *opt = &p->opt[repLen]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = 0; ++ opt->backPrev = i; ++ opt->prev1IsChar = False; ++ } ++ } ++ while (--repLen >= 2); ++ } ++ ++ normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); ++ ++ len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); ++ if (len <= mainLen) ++ { ++ UInt32 offs = 0; ++ while (len > matches[offs]) ++ offs += 2; ++ for (; ; len++) ++ { ++ COptimal *opt; ++ UInt32 distance = matches[offs + 1]; ++ ++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; ++ UInt32 lenToPosState = GetLenToPosState(len); ++ if (distance < kNumFullDistances) ++ curAndLenPrice += p->distancesPrices[lenToPosState][distance]; ++ else ++ { ++ UInt32 slot; ++ GetPosSlot2(distance, slot); ++ curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; ++ } ++ opt = &p->opt[len]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = 0; ++ opt->backPrev = distance + LZMA_NUM_REPS; ++ opt->prev1IsChar = False; ++ } ++ if (len == matches[offs]) ++ { ++ offs += 2; ++ if (offs == numPairs) ++ break; ++ } ++ } ++ } ++ ++ cur = 0; ++ ++ #ifdef SHOW_STAT2 ++ if (position >= 0) ++ { ++ unsigned i; ++ printf("\n pos = %4X", position); ++ for (i = cur; i <= lenEnd; i++) ++ printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); ++ } ++ #endif ++ ++ for (;;) ++ { ++ UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; ++ UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; ++ Bool nextIsChar; ++ Byte curByte, matchByte; ++ const Byte *data; ++ COptimal *curOpt; ++ COptimal *nextOpt; ++ ++ cur++; ++ if (cur == lenEnd) ++ return Backward(p, backRes, cur); ++ ++ newLen = ReadMatchDistances(p, &numPairs); ++ if (newLen >= p->numFastBytes) ++ { ++ p->numPairs = numPairs; ++ p->longestMatchLength = newLen; ++ return Backward(p, backRes, cur); ++ } ++ position++; ++ curOpt = &p->opt[cur]; ++ posPrev = curOpt->posPrev; ++ if (curOpt->prev1IsChar) ++ { ++ posPrev--; ++ if (curOpt->prev2) ++ { ++ state = p->opt[curOpt->posPrev2].state; ++ if (curOpt->backPrev2 < LZMA_NUM_REPS) ++ state = kRepNextStates[state]; ++ else ++ state = kMatchNextStates[state]; ++ } ++ else ++ state = p->opt[posPrev].state; ++ state = kLiteralNextStates[state]; ++ } ++ else ++ state = p->opt[posPrev].state; ++ if (posPrev == cur - 1) ++ { ++ if (IsShortRep(curOpt)) ++ state = kShortRepNextStates[state]; ++ else ++ state = kLiteralNextStates[state]; ++ } ++ else ++ { ++ UInt32 pos; ++ const COptimal *prevOpt; ++ if (curOpt->prev1IsChar && curOpt->prev2) ++ { ++ posPrev = curOpt->posPrev2; ++ pos = curOpt->backPrev2; ++ state = kRepNextStates[state]; ++ } ++ else ++ { ++ pos = curOpt->backPrev; ++ if (pos < LZMA_NUM_REPS) ++ state = kRepNextStates[state]; ++ else ++ state = kMatchNextStates[state]; ++ } ++ prevOpt = &p->opt[posPrev]; ++ if (pos < LZMA_NUM_REPS) ++ { ++ UInt32 i; ++ reps[0] = prevOpt->backs[pos]; ++ for (i = 1; i <= pos; i++) ++ reps[i] = prevOpt->backs[i - 1]; ++ for (; i < LZMA_NUM_REPS; i++) ++ reps[i] = prevOpt->backs[i]; ++ } ++ else ++ { ++ UInt32 i; ++ reps[0] = (pos - LZMA_NUM_REPS); ++ for (i = 1; i < LZMA_NUM_REPS; i++) ++ reps[i] = prevOpt->backs[i - 1]; ++ } ++ } ++ curOpt->state = (CState)state; ++ ++ curOpt->backs[0] = reps[0]; ++ curOpt->backs[1] = reps[1]; ++ curOpt->backs[2] = reps[2]; ++ curOpt->backs[3] = reps[3]; ++ ++ curPrice = curOpt->price; ++ nextIsChar = False; ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ curByte = *data; ++ matchByte = *(data - (reps[0] + 1)); ++ ++ posState = (position & p->pbMask); ++ ++ curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); ++ { ++ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); ++ curAnd1Price += ++ (!IsCharState(state) ? ++ LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : ++ LitEnc_GetPrice(probs, curByte, p->ProbPrices)); ++ } ++ ++ nextOpt = &p->opt[cur + 1]; ++ ++ if (curAnd1Price < nextOpt->price) ++ { ++ nextOpt->price = curAnd1Price; ++ nextOpt->posPrev = cur; ++ MakeAsChar(nextOpt); ++ nextIsChar = True; ++ } ++ ++ matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); ++ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); ++ ++ if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) ++ { ++ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); ++ if (shortRepPrice <= nextOpt->price) ++ { ++ nextOpt->price = shortRepPrice; ++ nextOpt->posPrev = cur; ++ MakeAsShortRep(nextOpt); ++ nextIsChar = True; ++ } ++ } ++ numAvailFull = p->numAvail; ++ { ++ UInt32 temp = kNumOpts - 1 - cur; ++ if (temp < numAvailFull) ++ numAvailFull = temp; ++ } ++ ++ if (numAvailFull < 2) ++ continue; ++ numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); ++ ++ if (!nextIsChar && matchByte != curByte) /* speed optimization */ ++ { ++ /* try Literal + rep0 */ ++ UInt32 temp; ++ UInt32 lenTest2; ++ const Byte *data2 = data - (reps[0] + 1); ++ UInt32 limit = p->numFastBytes + 1; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ ++ for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); ++ lenTest2 = temp - 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kLiteralNextStates[state]; ++ UInt32 posStateNext = (position + 1) & p->pbMask; ++ UInt32 nextRepMatchPrice = curAnd1Price + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ UInt32 offset = cur + 1 + lenTest2; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = False; ++ } ++ } ++ } ++ } ++ ++ startLen = 2; /* speed optimization */ ++ { ++ UInt32 repIndex; ++ for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) ++ { ++ UInt32 lenTest; ++ UInt32 lenTestTemp; ++ UInt32 price; ++ const Byte *data2 = data - (reps[repIndex] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); ++ while (lenEnd < cur + lenTest) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ lenTestTemp = lenTest; ++ price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); ++ do ++ { ++ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; ++ COptimal *opt = &p->opt[cur + lenTest]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur; ++ opt->backPrev = repIndex; ++ opt->prev1IsChar = False; ++ } ++ } ++ while (--lenTest >= 2); ++ lenTest = lenTestTemp; ++ ++ if (repIndex == 0) ++ startLen = lenTest + 1; ++ ++ /* if (_maxMode) */ ++ { ++ UInt32 lenTest2 = lenTest + 1; ++ UInt32 limit = lenTest2 + p->numFastBytes; ++ UInt32 nextRepMatchPrice; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); ++ lenTest2 -= lenTest + 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kRepNextStates[state]; ++ UInt32 posStateNext = (position + lenTest) & p->pbMask; ++ UInt32 curAndLenCharPrice = ++ price + p->repLenEnc.prices[posState][lenTest - 2] + ++ GET_PRICE_0(p->isMatch[state2][posStateNext]) + ++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), ++ data[lenTest], data2[lenTest], p->ProbPrices); ++ state2 = kLiteralNextStates[state2]; ++ posStateNext = (position + lenTest + 1) & p->pbMask; ++ nextRepMatchPrice = curAndLenCharPrice + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ UInt32 offset = cur + lenTest + 1 + lenTest2; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + lenTest + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = True; ++ opt->posPrev2 = cur; ++ opt->backPrev2 = repIndex; ++ } ++ } ++ } ++ } ++ } ++ } ++ /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ ++ if (newLen > numAvail) ++ { ++ newLen = numAvail; ++ for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2); ++ matches[numPairs] = newLen; ++ numPairs += 2; ++ } ++ if (newLen >= startLen) ++ { ++ UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); ++ UInt32 offs, curBack, posSlot; ++ UInt32 lenTest; ++ while (lenEnd < cur + newLen) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ ++ offs = 0; ++ while (startLen > matches[offs]) ++ offs += 2; ++ curBack = matches[offs + 1]; ++ GetPosSlot2(curBack, posSlot); ++ for (lenTest = /*2*/ startLen; ; lenTest++) ++ { ++ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; ++ UInt32 lenToPosState = GetLenToPosState(lenTest); ++ COptimal *opt; ++ if (curBack < kNumFullDistances) ++ curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; ++ else ++ curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; ++ ++ opt = &p->opt[cur + lenTest]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur; ++ opt->backPrev = curBack + LZMA_NUM_REPS; ++ opt->prev1IsChar = False; ++ } ++ ++ if (/*_maxMode && */lenTest == matches[offs]) ++ { ++ /* Try Match + Literal + Rep0 */ ++ const Byte *data2 = data - (curBack + 1); ++ UInt32 lenTest2 = lenTest + 1; ++ UInt32 limit = lenTest2 + p->numFastBytes; ++ UInt32 nextRepMatchPrice; ++ if (limit > numAvailFull) ++ limit = numAvailFull; ++ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); ++ lenTest2 -= lenTest + 1; ++ if (lenTest2 >= 2) ++ { ++ UInt32 state2 = kMatchNextStates[state]; ++ UInt32 posStateNext = (position + lenTest) & p->pbMask; ++ UInt32 curAndLenCharPrice = curAndLenPrice + ++ GET_PRICE_0(p->isMatch[state2][posStateNext]) + ++ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), ++ data[lenTest], data2[lenTest], p->ProbPrices); ++ state2 = kLiteralNextStates[state2]; ++ posStateNext = (posStateNext + 1) & p->pbMask; ++ nextRepMatchPrice = curAndLenCharPrice + ++ GET_PRICE_1(p->isMatch[state2][posStateNext]) + ++ GET_PRICE_1(p->isRep[state2]); ++ ++ /* for (; lenTest2 >= 2; lenTest2--) */ ++ { ++ UInt32 offset = cur + lenTest + 1 + lenTest2; ++ UInt32 curAndLenPrice; ++ COptimal *opt; ++ while (lenEnd < offset) ++ p->opt[++lenEnd].price = kInfinityPrice; ++ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); ++ opt = &p->opt[offset]; ++ if (curAndLenPrice < opt->price) ++ { ++ opt->price = curAndLenPrice; ++ opt->posPrev = cur + lenTest + 1; ++ opt->backPrev = 0; ++ opt->prev1IsChar = True; ++ opt->prev2 = True; ++ opt->posPrev2 = cur; ++ opt->backPrev2 = curBack + LZMA_NUM_REPS; ++ } ++ } ++ } ++ offs += 2; ++ if (offs == numPairs) ++ break; ++ curBack = matches[offs + 1]; ++ if (curBack >= kNumFullDistances) ++ GetPosSlot2(curBack, posSlot); ++ } ++ } ++ } ++ } ++} ++ ++#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) ++ ++static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) ++{ ++ UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; ++ const Byte *data; ++ const UInt32 *matches; ++ ++ if (p->additionalOffset == 0) ++ mainLen = ReadMatchDistances(p, &numPairs); ++ else ++ { ++ mainLen = p->longestMatchLength; ++ numPairs = p->numPairs; ++ } ++ ++ numAvail = p->numAvail; ++ *backRes = (UInt32)-1; ++ if (numAvail < 2) ++ return 1; ++ if (numAvail > LZMA_MATCH_LEN_MAX) ++ numAvail = LZMA_MATCH_LEN_MAX; ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ ++ repLen = repIndex = 0; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 len; ++ const Byte *data2 = data - (p->reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ for (len = 2; len < numAvail && data[len] == data2[len]; len++); ++ if (len >= p->numFastBytes) ++ { ++ *backRes = i; ++ MovePos(p, len - 1); ++ return len; ++ } ++ if (len > repLen) ++ { ++ repIndex = i; ++ repLen = len; ++ } ++ } ++ ++ matches = p->matches; ++ if (mainLen >= p->numFastBytes) ++ { ++ *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 1); ++ return mainLen; ++ } ++ ++ mainDist = 0; /* for GCC */ ++ if (mainLen >= 2) ++ { ++ mainDist = matches[numPairs - 1]; ++ while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) ++ { ++ if (!ChangePair(matches[numPairs - 3], mainDist)) ++ break; ++ numPairs -= 2; ++ mainLen = matches[numPairs - 2]; ++ mainDist = matches[numPairs - 1]; ++ } ++ if (mainLen == 2 && mainDist >= 0x80) ++ mainLen = 1; ++ } ++ ++ if (repLen >= 2 && ( ++ (repLen + 1 >= mainLen) || ++ (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || ++ (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) ++ { ++ *backRes = repIndex; ++ MovePos(p, repLen - 1); ++ return repLen; ++ } ++ ++ if (mainLen < 2 || numAvail <= 2) ++ return 1; ++ ++ p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); ++ if (p->longestMatchLength >= 2) ++ { ++ UInt32 newDistance = matches[p->numPairs - 1]; ++ if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || ++ (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || ++ (p->longestMatchLength > mainLen + 1) || ++ (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) ++ return 1; ++ } ++ ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; ++ for (i = 0; i < LZMA_NUM_REPS; i++) ++ { ++ UInt32 len, limit; ++ const Byte *data2 = data - (p->reps[i] + 1); ++ if (data[0] != data2[0] || data[1] != data2[1]) ++ continue; ++ limit = mainLen - 1; ++ for (len = 2; len < limit && data[len] == data2[len]; len++); ++ if (len >= limit) ++ return 1; ++ } ++ *backRes = mainDist + LZMA_NUM_REPS; ++ MovePos(p, mainLen - 2); ++ return mainLen; ++} ++ ++static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) ++{ ++ UInt32 len; ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); ++ p->state = kMatchNextStates[p->state]; ++ len = LZMA_MATCH_LEN_MIN; ++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); ++ RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); ++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); ++} ++ ++static SRes CheckErrors(CLzmaEnc *p) ++{ ++ if (p->result != SZ_OK) ++ return p->result; ++ if (p->rc.res != SZ_OK) ++ p->result = SZ_ERROR_WRITE; ++ if (p->matchFinderBase.result != SZ_OK) ++ p->result = SZ_ERROR_READ; ++ if (p->result != SZ_OK) ++ p->finished = True; ++ return p->result; ++} ++ ++static SRes Flush(CLzmaEnc *p, UInt32 nowPos) ++{ ++ /* ReleaseMFStream(); */ ++ p->finished = True; ++ if (p->writeEndMark) ++ WriteEndMarker(p, nowPos & p->pbMask); ++ RangeEnc_FlushData(&p->rc); ++ RangeEnc_FlushStream(&p->rc); ++ return CheckErrors(p); ++} ++ ++static void FillAlignPrices(CLzmaEnc *p) ++{ ++ UInt32 i; ++ for (i = 0; i < kAlignTableSize; i++) ++ p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); ++ p->alignPriceCount = 0; ++} ++ ++static void FillDistancesPrices(CLzmaEnc *p) ++{ ++ UInt32 tempPrices[kNumFullDistances]; ++ UInt32 i, lenToPosState; ++ for (i = kStartPosModelIndex; i < kNumFullDistances; i++) ++ { ++ UInt32 posSlot = GetPosSlot1(i); ++ UInt32 footerBits = ((posSlot >> 1) - 1); ++ UInt32 base = ((2 | (posSlot & 1)) << footerBits); ++ tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); ++ } ++ ++ for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) ++ { ++ UInt32 posSlot; ++ const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; ++ UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; ++ for (posSlot = 0; posSlot < p->distTableSize; posSlot++) ++ posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); ++ for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) ++ posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); ++ ++ { ++ UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; ++ UInt32 i; ++ for (i = 0; i < kStartPosModelIndex; i++) ++ distancesPrices[i] = posSlotPrices[i]; ++ for (; i < kNumFullDistances; i++) ++ distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; ++ } ++ } ++ p->matchPriceCount = 0; ++} ++ ++void LzmaEnc_Construct(CLzmaEnc *p) ++{ ++ RangeEnc_Construct(&p->rc); ++ MatchFinder_Construct(&p->matchFinderBase); ++ #ifndef _7ZIP_ST ++ MatchFinderMt_Construct(&p->matchFinderMt); ++ p->matchFinderMt.MatchFinder = &p->matchFinderBase; ++ #endif ++ ++ { ++ CLzmaEncProps props; ++ LzmaEncProps_Init(&props); ++ LzmaEnc_SetProps(p, &props); ++ } ++ ++ #ifndef LZMA_LOG_BSR ++ LzmaEnc_FastPosInit(p->g_FastPos); ++ #endif ++ ++ LzmaEnc_InitPriceTables(p->ProbPrices); ++ p->litProbs = 0; ++ p->saveState.litProbs = 0; ++} ++ ++CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) ++{ ++ void *p; ++ p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); ++ if (p != 0) ++ LzmaEnc_Construct((CLzmaEnc *)p); ++ return p; ++} ++ ++void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) ++{ ++ alloc->Free(alloc, p->litProbs); ++ alloc->Free(alloc, p->saveState.litProbs); ++ p->litProbs = 0; ++ p->saveState.litProbs = 0; ++} ++ ++void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ #ifndef _7ZIP_ST ++ MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); ++ #endif ++ MatchFinder_Free(&p->matchFinderBase, allocBig); ++ LzmaEnc_FreeLits(p, alloc); ++ RangeEnc_Free(&p->rc, alloc); ++} ++ ++void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); ++ alloc->Free(alloc, p); ++} ++ ++static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) ++{ ++ UInt32 nowPos32, startPos32; ++ if (p->needInit) ++ { ++ p->matchFinder.Init(p->matchFinderObj); ++ p->needInit = 0; ++ } ++ ++ if (p->finished) ++ return p->result; ++ RINOK(CheckErrors(p)); ++ ++ nowPos32 = (UInt32)p->nowPos64; ++ startPos32 = nowPos32; ++ ++ if (p->nowPos64 == 0) ++ { ++ UInt32 numPairs; ++ Byte curByte; ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) ++ return Flush(p, nowPos32); ++ ReadMatchDistances(p, &numPairs); ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); ++ p->state = kLiteralNextStates[p->state]; ++ curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); ++ LitEnc_Encode(&p->rc, p->litProbs, curByte); ++ p->additionalOffset--; ++ nowPos32++; ++ } ++ ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) ++ for (;;) ++ { ++ UInt32 pos, len, posState; ++ ++ if (p->fastMode) ++ len = GetOptimumFast(p, &pos); ++ else ++ len = GetOptimum(p, nowPos32, &pos); ++ ++ #ifdef SHOW_STAT2 ++ printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); ++ #endif ++ ++ posState = nowPos32 & p->pbMask; ++ if (len == 1 && pos == (UInt32)-1) ++ { ++ Byte curByte; ++ CLzmaProb *probs; ++ const Byte *data; ++ ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); ++ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; ++ curByte = *data; ++ probs = LIT_PROBS(nowPos32, *(data - 1)); ++ if (IsCharState(p->state)) ++ LitEnc_Encode(&p->rc, probs, curByte); ++ else ++ LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); ++ p->state = kLiteralNextStates[p->state]; ++ } ++ else ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); ++ if (pos < LZMA_NUM_REPS) ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); ++ if (pos == 0) ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); ++ RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); ++ } ++ else ++ { ++ UInt32 distance = p->reps[pos]; ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); ++ if (pos == 1) ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); ++ else ++ { ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); ++ RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); ++ if (pos == 3) ++ p->reps[3] = p->reps[2]; ++ p->reps[2] = p->reps[1]; ++ } ++ p->reps[1] = p->reps[0]; ++ p->reps[0] = distance; ++ } ++ if (len == 1) ++ p->state = kShortRepNextStates[p->state]; ++ else ++ { ++ LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ p->state = kRepNextStates[p->state]; ++ } ++ } ++ else ++ { ++ UInt32 posSlot; ++ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); ++ p->state = kMatchNextStates[p->state]; ++ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); ++ pos -= LZMA_NUM_REPS; ++ GetPosSlot(pos, posSlot); ++ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); ++ ++ if (posSlot >= kStartPosModelIndex) ++ { ++ UInt32 footerBits = ((posSlot >> 1) - 1); ++ UInt32 base = ((2 | (posSlot & 1)) << footerBits); ++ UInt32 posReduced = pos - base; ++ ++ if (posSlot < kEndPosModelIndex) ++ RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); ++ else ++ { ++ RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); ++ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); ++ p->alignPriceCount++; ++ } ++ } ++ p->reps[3] = p->reps[2]; ++ p->reps[2] = p->reps[1]; ++ p->reps[1] = p->reps[0]; ++ p->reps[0] = pos; ++ p->matchPriceCount++; ++ } ++ } ++ p->additionalOffset -= len; ++ nowPos32 += len; ++ if (p->additionalOffset == 0) ++ { ++ UInt32 processed; ++ if (!p->fastMode) ++ { ++ if (p->matchPriceCount >= (1 << 7)) ++ FillDistancesPrices(p); ++ if (p->alignPriceCount >= kAlignTableSize) ++ FillAlignPrices(p); ++ } ++ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) ++ break; ++ processed = nowPos32 - startPos32; ++ if (useLimits) ++ { ++ if (processed + kNumOpts + 300 >= maxUnpackSize || ++ RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) ++ break; ++ } ++ else if (processed >= (1 << 15)) ++ { ++ p->nowPos64 += nowPos32 - startPos32; ++ return CheckErrors(p); ++ } ++ } ++ } ++ p->nowPos64 += nowPos32 - startPos32; ++ return Flush(p, nowPos32); ++} ++ ++#define kBigHashDicLimit ((UInt32)1 << 24) ++ ++static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ UInt32 beforeSize = kNumOpts; ++ Bool btMode; ++ if (!RangeEnc_Alloc(&p->rc, alloc)) ++ return SZ_ERROR_MEM; ++ btMode = (p->matchFinderBase.btMode != 0); ++ #ifndef _7ZIP_ST ++ p->mtMode = (p->multiThread && !p->fastMode && btMode); ++ #endif ++ ++ { ++ unsigned lclp = p->lc + p->lp; ++ if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) ++ { ++ LzmaEnc_FreeLits(p, alloc); ++ p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); ++ p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb)); ++ if (p->litProbs == 0 || p->saveState.litProbs == 0) ++ { ++ LzmaEnc_FreeLits(p, alloc); ++ return SZ_ERROR_MEM; ++ } ++ p->lclp = lclp; ++ } ++ } ++ ++ p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); ++ ++ if (beforeSize + p->dictSize < keepWindowSize) ++ beforeSize = keepWindowSize - p->dictSize; ++ ++ #ifndef _7ZIP_ST ++ if (p->mtMode) ++ { ++ RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); ++ p->matchFinderObj = &p->matchFinderMt; ++ MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); ++ } ++ else ++ #endif ++ { ++ if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)) ++ return SZ_ERROR_MEM; ++ p->matchFinderObj = &p->matchFinderBase; ++ MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); ++ } ++ return SZ_OK; ++} ++ ++void LzmaEnc_Init(CLzmaEnc *p) ++{ ++ UInt32 i; ++ p->state = 0; ++ for (i = 0 ; i < LZMA_NUM_REPS; i++) ++ p->reps[i] = 0; ++ ++ RangeEnc_Init(&p->rc); ++ ++ ++ for (i = 0; i < kNumStates; i++) ++ { ++ UInt32 j; ++ for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) ++ { ++ p->isMatch[i][j] = kProbInitValue; ++ p->isRep0Long[i][j] = kProbInitValue; ++ } ++ p->isRep[i] = kProbInitValue; ++ p->isRepG0[i] = kProbInitValue; ++ p->isRepG1[i] = kProbInitValue; ++ p->isRepG2[i] = kProbInitValue; ++ } ++ ++ { ++ UInt32 num = 0x300 << (p->lp + p->lc); ++ for (i = 0; i < num; i++) ++ p->litProbs[i] = kProbInitValue; ++ } ++ ++ { ++ for (i = 0; i < kNumLenToPosStates; i++) ++ { ++ CLzmaProb *probs = p->posSlotEncoder[i]; ++ UInt32 j; ++ for (j = 0; j < (1 << kNumPosSlotBits); j++) ++ probs[j] = kProbInitValue; ++ } ++ } ++ { ++ for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) ++ p->posEncoders[i] = kProbInitValue; ++ } ++ ++ LenEnc_Init(&p->lenEnc.p); ++ LenEnc_Init(&p->repLenEnc.p); ++ ++ for (i = 0; i < (1 << kNumAlignBits); i++) ++ p->posAlignEncoder[i] = kProbInitValue; ++ ++ p->optimumEndIndex = 0; ++ p->optimumCurrentIndex = 0; ++ p->additionalOffset = 0; ++ ++ p->pbMask = (1 << p->pb) - 1; ++ p->lpMask = (1 << p->lp) - 1; ++} ++ ++void LzmaEnc_InitPrices(CLzmaEnc *p) ++{ ++ if (!p->fastMode) ++ { ++ FillDistancesPrices(p); ++ FillAlignPrices(p); ++ } ++ ++ p->lenEnc.tableSize = ++ p->repLenEnc.tableSize = ++ p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; ++ LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); ++ LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); ++} ++ ++static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ UInt32 i; ++ for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) ++ if (p->dictSize <= ((UInt32)1 << i)) ++ break; ++ p->distTableSize = i * 2; ++ ++ p->finished = False; ++ p->result = SZ_OK; ++ RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig)); ++ LzmaEnc_Init(p); ++ LzmaEnc_InitPrices(p); ++ p->nowPos64 = 0; ++ return SZ_OK; ++} ++ ++static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ p->matchFinderBase.stream = inStream; ++ p->needInit = 1; ++ p->rc.outStream = outStream; ++ return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); ++} ++ ++SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ++ ISeqInStream *inStream, UInt32 keepWindowSize, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ p->matchFinderBase.stream = inStream; ++ p->needInit = 1; ++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); ++} ++ ++static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) ++{ ++ p->matchFinderBase.directInput = 1; ++ p->matchFinderBase.bufferBase = (Byte *)src; ++ p->matchFinderBase.directInputRem = srcLen; ++} ++ ++SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, ++ UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ LzmaEnc_SetInputBuf(p, src, srcLen); ++ p->needInit = 1; ++ ++ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); ++} ++ ++void LzmaEnc_Finish(CLzmaEncHandle pp) ++{ ++ #ifndef _7ZIP_ST ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ if (p->mtMode) ++ MatchFinderMt_ReleaseStream(&p->matchFinderMt); ++ #else ++ pp = pp; ++ #endif ++} ++ ++typedef struct ++{ ++ ISeqOutStream funcTable; ++ Byte *data; ++ SizeT rem; ++ Bool overflow; ++} CSeqOutStreamBuf; ++ ++static size_t MyWrite(void *pp, const void *data, size_t size) ++{ ++ CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; ++ if (p->rem < size) ++ { ++ size = p->rem; ++ p->overflow = True; ++ } ++ memcpy(p->data, data, size); ++ p->rem -= size; ++ p->data += size; ++ return size; ++} ++ ++ ++UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) ++{ ++ const CLzmaEnc *p = (CLzmaEnc *)pp; ++ return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); ++} ++ ++const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) ++{ ++ const CLzmaEnc *p = (CLzmaEnc *)pp; ++ return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; ++} ++ ++SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, ++ Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ UInt64 nowPos64; ++ SRes res; ++ CSeqOutStreamBuf outStream; ++ ++ outStream.funcTable.Write = MyWrite; ++ outStream.data = dest; ++ outStream.rem = *destLen; ++ outStream.overflow = False; ++ ++ p->writeEndMark = False; ++ p->finished = False; ++ p->result = SZ_OK; ++ ++ if (reInit) ++ LzmaEnc_Init(p); ++ LzmaEnc_InitPrices(p); ++ nowPos64 = p->nowPos64; ++ RangeEnc_Init(&p->rc); ++ p->rc.outStream = &outStream.funcTable; ++ ++ res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); ++ ++ *unpackSize = (UInt32)(p->nowPos64 - nowPos64); ++ *destLen -= outStream.rem; ++ if (outStream.overflow) ++ return SZ_ERROR_OUTPUT_EOF; ++ ++ return res; ++} ++ ++static SRes LzmaEnc_Encode2(CLzmaEnc *p, ICompressProgress *progress) ++{ ++ SRes res = SZ_OK; ++ ++ #ifndef _7ZIP_ST ++ Byte allocaDummy[0x300]; ++ int i = 0; ++ for (i = 0; i < 16; i++) ++ allocaDummy[i] = (Byte)i; ++ #endif ++ ++ for (;;) ++ { ++ res = LzmaEnc_CodeOneBlock(p, False, 0, 0); ++ if (res != SZ_OK || p->finished != 0) ++ break; ++ if (progress != 0) ++ { ++ res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); ++ if (res != SZ_OK) ++ { ++ res = SZ_ERROR_PROGRESS; ++ break; ++ } ++ } ++ } ++ LzmaEnc_Finish(p); ++ return res; ++} ++ ++SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ++ ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); ++ return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); ++} ++ ++SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ int i; ++ UInt32 dictSize = p->dictSize; ++ if (*size < LZMA_PROPS_SIZE) ++ return SZ_ERROR_PARAM; ++ *size = LZMA_PROPS_SIZE; ++ props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc); ++ ++ for (i = 11; i <= 30; i++) ++ { ++ if (dictSize <= ((UInt32)2 << i)) ++ { ++ dictSize = (2 << i); ++ break; ++ } ++ if (dictSize <= ((UInt32)3 << i)) ++ { ++ dictSize = (3 << i); ++ break; ++ } ++ } ++ ++ for (i = 0; i < 4; i++) ++ props[1 + i] = (Byte)(dictSize >> (8 * i)); ++ return SZ_OK; ++} ++ ++SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ SRes res; ++ CLzmaEnc *p = (CLzmaEnc *)pp; ++ ++ CSeqOutStreamBuf outStream; ++ ++ LzmaEnc_SetInputBuf(p, src, srcLen); ++ ++ outStream.funcTable.Write = MyWrite; ++ outStream.data = dest; ++ outStream.rem = *destLen; ++ outStream.overflow = False; ++ ++ p->writeEndMark = writeEndMark; ++ ++ p->rc.outStream = &outStream.funcTable; ++ res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); ++ if (res == SZ_OK) ++ res = LzmaEnc_Encode2(p, progress); ++ ++ *destLen -= outStream.rem; ++ if (outStream.overflow) ++ return SZ_ERROR_OUTPUT_EOF; ++ return res; ++} ++ ++SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, ++ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, ++ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) ++{ ++ CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); ++ SRes res; ++ if (p == 0) ++ return SZ_ERROR_MEM; ++ ++ res = LzmaEnc_SetProps(p, props); ++ if (res == SZ_OK) ++ { ++ res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); ++ if (res == SZ_OK) ++ res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, ++ writeEndMark, progress, alloc, allocBig); ++ } ++ ++ LzmaEnc_Destroy(p, alloc, allocBig); ++ return res; ++} +diff --git a/lib/lzma/Makefile b/lib/lzma/Makefile +new file mode 100644 +index 000000000000..02e799c99381 +--- /dev/null ++++ b/lib/lzma/Makefile +@@ -0,0 +1,7 @@ ++lzma_compress-objs := LzFind.o LzmaEnc.o ++lzma_decompress-objs := LzmaDec.o ++ ++obj-$(CONFIG_LZMA_COMPRESS) += lzma_compress.o ++obj-$(CONFIG_LZMA_DECOMPRESS) += lzma_decompress.o ++ ++EXTRA_CFLAGS += -Iinclude/linux -Iinclude/linux/lzma -include types.h +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0059-fs-jffs2-EOF-marker.patch b/nixos/modules/kernel-patches/0059-fs-jffs2-EOF-marker.patch new file mode 100644 index 0000000..f3c3fa4 --- /dev/null +++ b/nixos/modules/kernel-patches/0059-fs-jffs2-EOF-marker.patch @@ -0,0 +1,74 @@ +From c83ff8f115ec664a51c53201dea7573e2fb4ce17 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:22:04 +0200 +Subject: [PATCH 59/96] fs: jffs2: EOF marker + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + fs/jffs2/build.c | 10 ++++++++++ + fs/jffs2/scan.c | 21 +++++++++++++++++++-- + 2 files changed, 29 insertions(+), 2 deletions(-) + +diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c +index 837cd55fd4c5..e32b958eb990 100644 +--- a/fs/jffs2/build.c ++++ b/fs/jffs2/build.c +@@ -117,6 +117,16 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) + dbg_fsbuild("scanned flash completely\n"); + jffs2_dbg_dump_block_lists_nolock(c); + ++ if (c->flags & (1 << 7)) { ++ printk("%s(): unlocking the mtd device... ", __func__); ++ mtd_unlock(c->mtd, 0, c->mtd->size); ++ printk("done.\n"); ++ ++ printk("%s(): erasing all blocks after the end marker... ", __func__); ++ jffs2_erase_pending_blocks(c, -1); ++ printk("done.\n"); ++ } ++ + dbg_fsbuild("pass 1 starting\n"); + c->flags |= JFFS2_SB_FLAG_BUILDING; + /* Now scan the directory tree, increasing nlink according to every dirent found. */ +diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c +index 29671e33a171..893092159113 100644 +--- a/fs/jffs2/scan.c ++++ b/fs/jffs2/scan.c +@@ -148,8 +148,14 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) + /* reset summary info for next eraseblock scan */ + jffs2_sum_reset_collected(s); + +- ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), +- buf_size, s); ++ if (c->flags & (1 << 7)) { ++ if (mtd_block_isbad(c->mtd, jeb->offset)) ++ ret = BLK_STATE_BADBLOCK; ++ else ++ ret = BLK_STATE_ALLFF; ++ } else ++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), ++ buf_size, s); + + if (ret < 0) + goto out; +@@ -567,6 +573,17 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo + return err; + } + ++ if ((buf[0] == 0xde) && ++ (buf[1] == 0xad) && ++ (buf[2] == 0xc0) && ++ (buf[3] == 0xde)) { ++ /* end of filesystem. erase everything after this point */ ++ printk("%s(): End of filesystem marker found at 0x%x\n", __func__, jeb->offset); ++ c->flags |= (1 << 7); ++ ++ return BLK_STATE_ALLFF; ++ } ++ + /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ + ofs = 0; + max_ofs = EMPTY_SCAN_SIZE(c->sector_size); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0060-netfilter-add-support-for-flushing-conntrack-via-pro.patch b/nixos/modules/kernel-patches/0060-netfilter-add-support-for-flushing-conntrack-via-pro.patch new file mode 100644 index 0000000..76e1590 --- /dev/null +++ b/nixos/modules/kernel-patches/0060-netfilter-add-support-for-flushing-conntrack-via-pro.patch @@ -0,0 +1,95 @@ +From ec5167f51145842fe3aee19df68dc0fcebcd54e7 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:22:04 +0200 +Subject: [PATCH 60/96] netfilter: add support for flushing conntrack via /proc + +lede-commit 8193bbe59a74d34d6a26d4a8cb857b1952905314 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + net/netfilter/nf_conntrack_standalone.c | 56 ++++++++++++++++++++++++- + 1 file changed, 54 insertions(+), 2 deletions(-) + +diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c +index 05895878610c..8d3be4cc340e 100644 +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -9,6 +9,7 @@ + #include <linux/percpu.h> + #include <linux/netdevice.h> + #include <linux/security.h> ++#include <linux/inet.h> + #include <net/net_namespace.h> + #ifdef CONFIG_SYSCTL + #include <linux/sysctl.h> +@@ -465,6 +466,56 @@ static int ct_cpu_seq_show(struct seq_file *seq, void *v) + return 0; + } + ++struct kill_request { ++ u16 family; ++ union nf_inet_addr addr; ++}; ++ ++static int kill_matching(struct nf_conn *i, void *data) ++{ ++ struct kill_request *kr = data; ++ struct nf_conntrack_tuple *t1 = &i->tuplehash[IP_CT_DIR_ORIGINAL].tuple; ++ struct nf_conntrack_tuple *t2 = &i->tuplehash[IP_CT_DIR_REPLY].tuple; ++ ++ if (!kr->family) ++ return 1; ++ ++ if (t1->src.l3num != kr->family) ++ return 0; ++ ++ return (nf_inet_addr_cmp(&kr->addr, &t1->src.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t1->dst.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t2->src.u3) || ++ nf_inet_addr_cmp(&kr->addr, &t2->dst.u3)); ++} ++ ++static int ct_file_write(struct file *file, char *buf, size_t count) ++{ ++ struct seq_file *seq = file->private_data; ++ struct net *net = seq_file_net(seq); ++ struct kill_request kr = { }; ++ ++ if (count == 0) ++ return 0; ++ ++ if (count >= INET6_ADDRSTRLEN) ++ count = INET6_ADDRSTRLEN - 1; ++ ++ if (strnchr(buf, count, ':')) { ++ kr.family = AF_INET6; ++ if (!in6_pton(buf, count, (void *)&kr.addr, '\n', NULL)) ++ return -EINVAL; ++ } else if (strnchr(buf, count, '.')) { ++ kr.family = AF_INET; ++ if (!in4_pton(buf, count, (void *)&kr.addr, '\n', NULL)) ++ return -EINVAL; ++ } ++ ++ nf_ct_iterate_cleanup_net(net, kill_matching, &kr, 0, 0); ++ ++ return 0; ++} ++ + static const struct seq_operations ct_cpu_seq_ops = { + .start = ct_cpu_seq_start, + .next = ct_cpu_seq_next, +@@ -478,8 +529,9 @@ static int nf_conntrack_standalone_init_proc(struct net *net) + kuid_t root_uid; + kgid_t root_gid; + +- pde = proc_create_net("nf_conntrack", 0440, net->proc_net, &ct_seq_ops, +- sizeof(struct ct_iter_state)); ++ pde = proc_create_net_data_write("nf_conntrack", 0440, net->proc_net, ++ &ct_seq_ops, &ct_file_write, ++ sizeof(struct ct_iter_state), NULL); + if (!pde) + goto out_nf_conntrack; + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0061-kernel-add-a-new-version-of-my-netfilter-speedup-pat.patch b/nixos/modules/kernel-patches/0061-kernel-add-a-new-version-of-my-netfilter-speedup-pat.patch new file mode 100644 index 0000000..8a8c759 --- /dev/null +++ b/nixos/modules/kernel-patches/0061-kernel-add-a-new-version-of-my-netfilter-speedup-pat.patch @@ -0,0 +1,120 @@ +From fe64c15c96dfe5b5cbf99c89b5377b056790267c Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:22:04 +0200 +Subject: [PATCH 61/96] kernel: add a new version of my netfilter speedup + patches for linux 2.6.39 and 3.0 + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/uapi/linux/netfilter_ipv4/ip_tables.h | 1 + + net/ipv4/netfilter/ip_tables.c | 42 ++++++++++++++++++- + 2 files changed, 42 insertions(+), 1 deletion(-) + +diff --git a/include/uapi/linux/netfilter_ipv4/ip_tables.h b/include/uapi/linux/netfilter_ipv4/ip_tables.h +index 50c7fee625ae..c31564b13ba3 100644 +--- a/include/uapi/linux/netfilter_ipv4/ip_tables.h ++++ b/include/uapi/linux/netfilter_ipv4/ip_tables.h +@@ -89,6 +89,7 @@ struct ipt_ip { + #define IPT_F_FRAG 0x01 /* Set if rule is a fragment rule */ + #define IPT_F_GOTO 0x02 /* Set if jump is a goto */ + #define IPT_F_MASK 0x03 /* All possible flag bits mask. */ ++#define IPT_F_NO_DEF_MATCH 0x80 /* Internal: no default match rules present */ + + /* Values for "inv" field in struct ipt_ip. */ + #define IPT_INV_VIA_IN 0x01 /* Invert the sense of IN IFACE. */ +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 2ed7c58b471a..76c01661c4e4 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -50,6 +50,9 @@ ip_packet_match(const struct iphdr *ip, + { + unsigned long ret; + ++ if (ipinfo->flags & IPT_F_NO_DEF_MATCH) ++ return true; ++ + if (NF_INVF(ipinfo, IPT_INV_SRCIP, + (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) || + NF_INVF(ipinfo, IPT_INV_DSTIP, +@@ -80,6 +83,29 @@ ip_packet_match(const struct iphdr *ip, + return true; + } + ++static void ++ip_checkdefault(struct ipt_ip *ip) ++{ ++ static const char iface_mask[IFNAMSIZ] = {}; ++ ++ if (ip->invflags || ip->flags & IPT_F_FRAG) ++ return; ++ ++ if (memcmp(ip->iniface_mask, iface_mask, IFNAMSIZ) != 0) ++ return; ++ ++ if (memcmp(ip->outiface_mask, iface_mask, IFNAMSIZ) != 0) ++ return; ++ ++ if (ip->smsk.s_addr || ip->dmsk.s_addr) ++ return; ++ ++ if (ip->proto) ++ return; ++ ++ ip->flags |= IPT_F_NO_DEF_MATCH; ++} ++ + static bool + ip_checkentry(const struct ipt_ip *ip) + { +@@ -525,6 +551,8 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name, + struct xt_mtchk_param mtpar; + struct xt_entry_match *ematch; + ++ ip_checkdefault(&e->ip); ++ + if (!xt_percpu_counter_alloc(alloc_state, &e->counters)) + return -ENOMEM; + +@@ -819,6 +847,7 @@ copy_entries_to_user(unsigned int total_size, + const struct xt_table_info *private = table->private; + int ret = 0; + const void *loc_cpu_entry; ++ u8 flags; + + counters = alloc_counters(table); + if (IS_ERR(counters)) +@@ -846,6 +875,14 @@ copy_entries_to_user(unsigned int total_size, + goto free_counters; + } + ++ flags = e->ip.flags & IPT_F_MASK; ++ if (copy_to_user(userptr + off ++ + offsetof(struct ipt_entry, ip.flags), ++ &flags, sizeof(flags)) != 0) { ++ ret = -EFAULT; ++ goto free_counters; ++ } ++ + for (i = sizeof(struct ipt_entry); + i < e->target_offset; + i += m->u.match_size) { +@@ -1224,12 +1261,15 @@ compat_copy_entry_to_user(struct ipt_entry *e, void __user **dstptr, + compat_uint_t origsize; + const struct xt_entry_match *ematch; + int ret = 0; ++ u8 flags = e->ip.flags & IPT_F_MASK; + + origsize = *size; + ce = *dstptr; + if (copy_to_user(ce, e, sizeof(struct ipt_entry)) != 0 || + copy_to_user(&ce->counters, &counters[i], +- sizeof(counters[i])) != 0) ++ sizeof(counters[i])) != 0 || ++ copy_to_user(&ce->ip.flags, &flags, ++ sizeof(flags)) != 0) + return -EFAULT; + + *dstptr += sizeof(struct compat_ipt_entry); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0062-netfilter-reduce-match-memory-access.patch b/nixos/modules/kernel-patches/0062-netfilter-reduce-match-memory-access.patch new file mode 100644 index 0000000..543dd43 --- /dev/null +++ b/nixos/modules/kernel-patches/0062-netfilter-reduce-match-memory-access.patch @@ -0,0 +1,29 @@ +From 6fda2e62b3a2ab9acf5ac27b3d20745f3def32ae Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:22:05 +0200 +Subject: [PATCH 62/96] netfilter: reduce match memory access + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + net/ipv4/netfilter/ip_tables.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c +index 76c01661c4e4..e58342c5354a 100644 +--- a/net/ipv4/netfilter/ip_tables.c ++++ b/net/ipv4/netfilter/ip_tables.c +@@ -53,9 +53,9 @@ ip_packet_match(const struct iphdr *ip, + if (ipinfo->flags & IPT_F_NO_DEF_MATCH) + return true; + +- if (NF_INVF(ipinfo, IPT_INV_SRCIP, ++ if (NF_INVF(ipinfo, IPT_INV_SRCIP, ipinfo->smsk.s_addr && + (ip->saddr & ipinfo->smsk.s_addr) != ipinfo->src.s_addr) || +- NF_INVF(ipinfo, IPT_INV_DSTIP, ++ NF_INVF(ipinfo, IPT_INV_DSTIP, ipinfo->dmsk.s_addr && + (ip->daddr & ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr)) + return false; + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0063-netfilter-optional-tcp-window-check.patch b/nixos/modules/kernel-patches/0063-netfilter-optional-tcp-window-check.patch new file mode 100644 index 0000000..f0c2423 --- /dev/null +++ b/nixos/modules/kernel-patches/0063-netfilter-optional-tcp-window-check.patch @@ -0,0 +1,96 @@ +From 5433bb138a6a3499b05cb8722c8365cf56047aa1 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:22:06 +0200 +Subject: [PATCH 63/96] netfilter: optional tcp window check + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +Signed-off-by: Christian 'Ansuel' Marangi <ansuelsmth@gmail.com> +--- + include/net/netns/conntrack.h | 1 + + net/netfilter/nf_conntrack_proto_tcp.c | 8 +++++++- + net/netfilter/nf_conntrack_standalone.c | 10 ++++++++++ + 3 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h +index c396a3862e80..82598d767cc3 100644 +--- a/include/net/netns/conntrack.h ++++ b/include/net/netns/conntrack.h +@@ -26,6 +26,7 @@ struct nf_tcp_net { + unsigned int timeouts[TCP_CONNTRACK_TIMEOUT_MAX]; + u8 tcp_loose; + u8 tcp_be_liberal; ++ u8 tcp_no_window_check; + u8 tcp_max_retrans; + u8 tcp_ignore_invalid_rst; + #if IS_ENABLED(CONFIG_NF_FLOW_TABLE) +diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c +index a634c72b1ffc..8bbc8010170d 100644 +--- a/net/netfilter/nf_conntrack_proto_tcp.c ++++ b/net/netfilter/nf_conntrack_proto_tcp.c +@@ -490,6 +490,9 @@ static bool tcp_in_window(struct nf_conn *ct, + s32 receiver_offset; + bool res, in_recv_win; + ++ if (tn->tcp_no_window_check) ++ return true; ++ + /* + * Get the required data from the packet. + */ +@@ -1161,7 +1164,7 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct, + IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED && + timeouts[new_state] > timeouts[TCP_CONNTRACK_UNACK]) + timeout = timeouts[TCP_CONNTRACK_UNACK]; +- else if (ct->proto.tcp.last_win == 0 && ++ else if (!tn->tcp_no_window_check && ct->proto.tcp.last_win == 0 && + timeouts[new_state] > timeouts[TCP_CONNTRACK_RETRANS]) + timeout = timeouts[TCP_CONNTRACK_RETRANS]; + else +@@ -1477,6 +1480,9 @@ void nf_conntrack_tcp_init_net(struct net *net) + */ + tn->tcp_be_liberal = 0; + ++ /* Skip Windows Check */ ++ tn->tcp_no_window_check = 0; ++ + /* If it's non-zero, we turn off RST sequence number check */ + tn->tcp_ignore_invalid_rst = 0; + +diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c +index 8d3be4cc340e..73a1b6c1737e 100644 +--- a/net/netfilter/nf_conntrack_standalone.c ++++ b/net/netfilter/nf_conntrack_standalone.c +@@ -636,6 +636,7 @@ enum nf_ct_sysctl_index { + #endif + NF_SYSCTL_CT_PROTO_TCP_LOOSE, + NF_SYSCTL_CT_PROTO_TCP_LIBERAL, ++ NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK, + NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST, + NF_SYSCTL_CT_PROTO_TCP_MAX_RETRANS, + NF_SYSCTL_CT_PROTO_TIMEOUT_UDP, +@@ -852,6 +853,14 @@ static struct ctl_table nf_ct_sysctl_table[] = { + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, ++ [NF_SYSCTL_CT_PROTO_TCP_NO_WINDOW_CHECK] = { ++ .procname = "nf_conntrack_tcp_no_window_check", ++ .maxlen = sizeof(u8), ++ .mode = 0644, ++ .proc_handler = proc_dou8vec_minmax, ++ .extra1 = SYSCTL_ZERO, ++ .extra2 = SYSCTL_ONE, ++ }, + [NF_SYSCTL_CT_PROTO_TCP_IGNORE_INVALID_RST] = { + .procname = "nf_conntrack_tcp_ignore_invalid_rst", + .maxlen = sizeof(u8), +@@ -1068,6 +1077,7 @@ static void nf_conntrack_standalone_init_tcp_sysctl(struct net *net, + + XASSIGN(LOOSE, &tn->tcp_loose); + XASSIGN(LIBERAL, &tn->tcp_be_liberal); ++ XASSIGN(NO_WINDOW_CHECK, &tn->tcp_no_window_check); + XASSIGN(MAX_RETRANS, &tn->tcp_max_retrans); + XASSIGN(IGNORE_INVALID_RST, &tn->tcp_ignore_invalid_rst); + #undef XASSIGN +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0064-net_sched-codel-do-not-defer-queue-length-update.patch b/nixos/modules/kernel-patches/0064-net_sched-codel-do-not-defer-queue-length-update.patch new file mode 100644 index 0000000..8c57508 --- /dev/null +++ b/nixos/modules/kernel-patches/0064-net_sched-codel-do-not-defer-queue-length-update.patch @@ -0,0 +1,97 @@ +From 9eee7822b071e5a658a176300336feeedd2b6205 Mon Sep 17 00:00:00 2001 +From: Konstantin Khlebnikov <khlebnikov@yandex-team.ru> +Date: Mon, 21 Aug 2017 11:14:14 +0300 +Subject: [PATCH 64/96] net_sched/codel: do not defer queue length update + +When codel wants to drop last packet in ->dequeue() it cannot call +qdisc_tree_reduce_backlog() right away - it will notify parent qdisc +about zero qlen and HTB/HFSC will deactivate class. The same class will +be deactivated second time by caller of ->dequeue(). Currently codel and +fq_codel defer update. This triggers warning in HFSC when it's qlen != 0 +but there is no active classes. + +This patch update parent queue length immediately: just temporary increase +qlen around qdisc_tree_reduce_backlog() to prevent first class deactivation +if we have skb to return. + +This might open another problem in HFSC - now operation peek could fail and +deactivate parent class. + +Signed-off-by: Konstantin Khlebnikov <khlebnikov@yandex-team.ru> +Link: https://bugzilla.kernel.org/show_bug.cgi?id=109581 +--- + net/sched/sch_codel.c | 14 ++++++++++---- + net/sched/sch_fq_codel.c | 24 +++++++++++++++--------- + 2 files changed, 25 insertions(+), 13 deletions(-) + +diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c +index 30169b3adbbb..a2b81a5b2dfb 100644 +--- a/net/sched/sch_codel.c ++++ b/net/sched/sch_codel.c +@@ -95,11 +95,17 @@ static struct sk_buff *codel_qdisc_dequeue(struct Qdisc *sch) + &q->stats, qdisc_pkt_len, codel_get_enqueue_time, + drop_func, dequeue_func); + +- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0, +- * or HTB crashes. Defer it for next round. ++ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate ++ * parent class, dequeue in parent qdisc will do the same if we ++ * return skb. Temporary increment qlen if we have skb. + */ +- if (q->stats.drop_count && sch->q.qlen) { +- qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len); ++ if (q->stats.drop_count) { ++ if (skb) ++ sch->q.qlen++; ++ qdisc_tree_reduce_backlog(sch, q->stats.drop_count, ++ q->stats.drop_len); ++ if (skb) ++ sch->q.qlen--; + q->stats.drop_count = 0; + q->stats.drop_len = 0; + } +diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c +index 839e1235db05..fd2620da04db 100644 +--- a/net/sched/sch_fq_codel.c ++++ b/net/sched/sch_fq_codel.c +@@ -304,6 +304,21 @@ static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) + &flow->cvars, &q->cstats, qdisc_pkt_len, + codel_get_enqueue_time, drop_func, dequeue_func); + ++ /* If our qlen is 0 qdisc_tree_reduce_backlog() will deactivate ++ * parent class, dequeue in parent qdisc will do the same if we ++ * return skb. Temporary increment qlen if we have skb. ++ */ ++ if (q->cstats.drop_count) { ++ if (skb) ++ sch->q.qlen++; ++ qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, ++ q->cstats.drop_len); ++ if (skb) ++ sch->q.qlen--; ++ q->cstats.drop_count = 0; ++ q->cstats.drop_len = 0; ++ } ++ + if (!skb) { + /* force a pass through old_flows to prevent starvation */ + if ((head == &q->new_flows) && !list_empty(&q->old_flows)) +@@ -314,15 +329,6 @@ static struct sk_buff *fq_codel_dequeue(struct Qdisc *sch) + } + qdisc_bstats_update(sch, skb); + flow->deficit -= qdisc_pkt_len(skb); +- /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0, +- * or HTB crashes. Defer it for next round. +- */ +- if (q->cstats.drop_count && sch->q.qlen) { +- qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, +- q->cstats.drop_len); +- q->cstats.drop_count = 0; +- q->cstats.drop_len = 0; +- } + return skb; + } + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0065-net-add-an-optimization-for-dealing-with-raw-sockets.patch b/nixos/modules/kernel-patches/0065-net-add-an-optimization-for-dealing-with-raw-sockets.patch new file mode 100644 index 0000000..23cc5a4 --- /dev/null +++ b/nixos/modules/kernel-patches/0065-net-add-an-optimization-for-dealing-with-raw-sockets.patch @@ -0,0 +1,149 @@ +From 439a16fc3f6ea86b301b4885c1a27b4579a3e7d1 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:22:06 +0200 +Subject: [PATCH 65/96] net: add an optimization for dealing with raw sockets + +lede-commit: 4898039703d7315f0f3431c860123338ec3be0f6 +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/uapi/linux/if_packet.h | 3 +++ + net/packet/af_packet.c | 34 +++++++++++++++++++++++++++------- + net/packet/internal.h | 1 + + 3 files changed, 31 insertions(+), 7 deletions(-) + +diff --git a/include/uapi/linux/if_packet.h b/include/uapi/linux/if_packet.h +index c07caf7b40db..baa5581b1fa9 100644 +--- a/include/uapi/linux/if_packet.h ++++ b/include/uapi/linux/if_packet.h +@@ -33,6 +33,8 @@ struct sockaddr_ll { + #define PACKET_KERNEL 7 /* To kernel space */ + /* Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space */ + #define PACKET_FASTROUTE 6 /* Fastrouted frame */ ++#define PACKET_MASK_ANY 0xffffffff /* mask for packet type bits */ ++ + + /* Packet socket options */ + +@@ -59,6 +61,7 @@ struct sockaddr_ll { + #define PACKET_ROLLOVER_STATS 21 + #define PACKET_FANOUT_DATA 22 + #define PACKET_IGNORE_OUTGOING 23 ++#define PACKET_RECV_TYPE 24 + + #define PACKET_FANOUT_HASH 0 + #define PACKET_FANOUT_LB 1 +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 492bd35cccc0..e8746e713a25 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -1861,6 +1861,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, + { + struct sock *sk; + struct sockaddr_pkt *spkt; ++ struct packet_sock *po; + + /* + * When we registered the protocol we saved the socket in the data +@@ -1868,6 +1869,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, + */ + + sk = pt->af_packet_priv; ++ po = pkt_sk(sk); + + /* + * Yank back the headers [hope the device set this +@@ -1880,7 +1882,7 @@ static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev, + * so that this procedure is noop. + */ + +- if (skb->pkt_type == PACKET_LOOPBACK) ++ if (!(po->pkt_type & (1 << skb->pkt_type))) + goto out; + + if (!net_eq(dev_net(dev), sock_net(sk))) +@@ -2128,12 +2130,12 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, + unsigned int snaplen, res; + bool is_drop_n_account = false; + +- if (skb->pkt_type == PACKET_LOOPBACK) +- goto drop; +- + sk = pt->af_packet_priv; + po = pkt_sk(sk); + ++ if (!(po->pkt_type & (1 << skb->pkt_type))) ++ goto drop; ++ + if (!net_eq(dev_net(dev), sock_net(sk))) + goto drop; + +@@ -2260,12 +2262,12 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, + BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h2)) != 32); + BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h3)) != 48); + +- if (skb->pkt_type == PACKET_LOOPBACK) +- goto drop; +- + sk = pt->af_packet_priv; + po = pkt_sk(sk); + ++ if (!(po->pkt_type & (1 << skb->pkt_type))) ++ goto drop; ++ + if (!net_eq(dev_net(dev), sock_net(sk))) + goto drop; + +@@ -3372,6 +3374,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol, + mutex_init(&po->pg_vec_lock); + po->rollover = NULL; + po->prot_hook.func = packet_rcv; ++ po->pkt_type = PACKET_MASK_ANY & ~(1 << PACKET_LOOPBACK); + + if (sock->type == SOCK_PACKET) + po->prot_hook.func = packet_rcv_spkt; +@@ -4011,6 +4014,16 @@ packet_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, + po->xmit = val ? packet_direct_xmit : dev_queue_xmit; + return 0; + } ++ case PACKET_RECV_TYPE: ++ { ++ unsigned int val; ++ if (optlen != sizeof(val)) ++ return -EINVAL; ++ if (copy_from_sockptr(&val, optval, sizeof(val))) ++ return -EFAULT; ++ po->pkt_type = val & ~BIT(PACKET_LOOPBACK); ++ return 0; ++ } + default: + return -ENOPROTOOPT; + } +@@ -4067,6 +4080,13 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, + case PACKET_VNET_HDR: + val = po->has_vnet_hdr; + break; ++ case PACKET_RECV_TYPE: ++ if (len > sizeof(unsigned int)) ++ len = sizeof(unsigned int); ++ val = po->pkt_type; ++ ++ data = &val; ++ break; + case PACKET_VERSION: + val = po->tp_version; + break; +diff --git a/net/packet/internal.h b/net/packet/internal.h +index 48af35b1aed2..1361d2974453 100644 +--- a/net/packet/internal.h ++++ b/net/packet/internal.h +@@ -137,6 +137,7 @@ struct packet_sock { + int (*xmit)(struct sk_buff *skb); + struct packet_type prot_hook ____cacheline_aligned_in_smp; + atomic_t tp_drops ____cacheline_aligned_in_smp; ++ unsigned int pkt_type; + }; + + static inline struct packet_sock *pkt_sk(struct sock *sk) +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0066-kernel-add-a-few-patches-for-avoiding-unnecessary-sk.patch b/nixos/modules/kernel-patches/0066-kernel-add-a-few-patches-for-avoiding-unnecessary-sk.patch new file mode 100644 index 0000000..4bb9f5f --- /dev/null +++ b/nixos/modules/kernel-patches/0066-kernel-add-a-few-patches-for-avoiding-unnecessary-sk.patch @@ -0,0 +1,28 @@ +From 5a62944a6801f0da0a67b986002981442766614c Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:22:06 +0200 +Subject: [PATCH 66/96] kernel: add a few patches for avoiding unnecessary skb + reallocations - significantly improves ethernet<->wireless performance + +lede-commit: 6f89cffc9add6939d44a6b54cf9a5e77849aa7fd +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + include/linux/skbuff.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h +index 63d0a21b6316..ec3d3246c7a7 100644 +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -3123,7 +3123,7 @@ static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len) + * NET_IP_ALIGN(2) + ethernet_header(14) + IP_header(20/40) + ports(8) + */ + #ifndef NET_SKB_PAD +-#define NET_SKB_PAD max(32, L1_CACHE_BYTES) ++#define NET_SKB_PAD max(64, L1_CACHE_BYTES) + #endif + + int ___pskb_trim(struct sk_buff *skb, unsigned int len); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0067-Add-support-for-MAP-E-FMRs-mesh-mode.patch b/nixos/modules/kernel-patches/0067-Add-support-for-MAP-E-FMRs-mesh-mode.patch new file mode 100644 index 0000000..508a84c --- /dev/null +++ b/nixos/modules/kernel-patches/0067-Add-support-for-MAP-E-FMRs-mesh-mode.patch @@ -0,0 +1,522 @@ +From 2419f533e814e322a5b61ca4f2a63f84cc60a5a9 Mon Sep 17 00:00:00 2001 +From: Steven Barth <steven@midlink.org> +Date: Tue, 27 Sep 2022 16:22:06 +0200 +Subject: [PATCH 67/96] Add support for MAP-E FMRs (mesh mode) + +MAP-E FMRs (draft-ietf-softwire-map-10) are rules for IPv4-communication +between MAP CEs (mesh mode) without the need to forward such data to a +border relay. This is similar to how 6rd works but for IPv4 over IPv6. + +Signed-off-by: Steven Barth <cyrus@openwrt.org> +--- + include/net/ip6_tunnel.h | 13 ++ + include/uapi/linux/if_tunnel.h | 13 ++ + net/ipv6/ip6_tunnel.c | 281 ++++++++++++++++++++++++++++++++- + 3 files changed, 299 insertions(+), 8 deletions(-) + +diff --git a/include/net/ip6_tunnel.h b/include/net/ip6_tunnel.h +index 74b369bddf49..0d1df6f12c33 100644 +--- a/include/net/ip6_tunnel.h ++++ b/include/net/ip6_tunnel.h +@@ -18,6 +18,18 @@ + /* determine capability on a per-packet basis */ + #define IP6_TNL_F_CAP_PER_PACKET 0x40000 + ++/* IPv6 tunnel FMR */ ++struct __ip6_tnl_fmr { ++ struct __ip6_tnl_fmr *next; /* next fmr in list */ ++ struct in6_addr ip6_prefix; ++ struct in_addr ip4_prefix; ++ ++ __u8 ip6_prefix_len; ++ __u8 ip4_prefix_len; ++ __u8 ea_len; ++ __u8 offset; ++}; ++ + struct __ip6_tnl_parm { + char name[IFNAMSIZ]; /* name of tunnel device */ + int link; /* ifindex of underlying L2 interface */ +@@ -29,6 +41,7 @@ struct __ip6_tnl_parm { + __u32 flags; /* tunnel flags */ + struct in6_addr laddr; /* local tunnel end-point address */ + struct in6_addr raddr; /* remote tunnel end-point address */ ++ struct __ip6_tnl_fmr *fmrs; /* FMRs */ + + __be16 i_flags; + __be16 o_flags; +diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h +index 102119628ff5..420dec6ebb7e 100644 +--- a/include/uapi/linux/if_tunnel.h ++++ b/include/uapi/linux/if_tunnel.h +@@ -77,10 +77,23 @@ enum { + IFLA_IPTUN_ENCAP_DPORT, + IFLA_IPTUN_COLLECT_METADATA, + IFLA_IPTUN_FWMARK, ++ IFLA_IPTUN_FMRS, + __IFLA_IPTUN_MAX, + }; + #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) + ++enum { ++ IFLA_IPTUN_FMR_UNSPEC, ++ IFLA_IPTUN_FMR_IP6_PREFIX, ++ IFLA_IPTUN_FMR_IP4_PREFIX, ++ IFLA_IPTUN_FMR_IP6_PREFIX_LEN, ++ IFLA_IPTUN_FMR_IP4_PREFIX_LEN, ++ IFLA_IPTUN_FMR_EA_LEN, ++ IFLA_IPTUN_FMR_OFFSET, ++ __IFLA_IPTUN_FMR_MAX, ++}; ++#define IFLA_IPTUN_FMR_MAX (__IFLA_IPTUN_FMR_MAX - 1) ++ + enum tunnel_encap_types { + TUNNEL_ENCAP_NONE, + TUNNEL_ENCAP_FOU, +diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c +index 19325b7600bb..a53746fdafe5 100644 +--- a/net/ipv6/ip6_tunnel.c ++++ b/net/ipv6/ip6_tunnel.c +@@ -11,6 +11,9 @@ + * linux/net/ipv6/sit.c and linux/net/ipv4/ipip.c + * + * RFC 2473 ++ * ++ * Changes: ++ * Steven Barth <cyrus@openwrt.org>: MAP-E FMR support + */ + + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +@@ -67,9 +70,9 @@ static bool log_ecn_error = true; + module_param(log_ecn_error, bool, 0644); + MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); + +-static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2) ++static u32 HASH(const struct in6_addr *addr) + { +- u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2); ++ u32 hash = ipv6_addr_hash(addr); + + return hash_32(hash, IP6_TUNNEL_HASH_SIZE_SHIFT); + } +@@ -114,17 +117,33 @@ static struct ip6_tnl * + ip6_tnl_lookup(struct net *net, int link, + const struct in6_addr *remote, const struct in6_addr *local) + { +- unsigned int hash = HASH(remote, local); ++ unsigned int hash = HASH(local); + struct ip6_tnl *t, *cand = NULL; + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + struct in6_addr any; + + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(local, &t->parms.laddr) || +- !ipv6_addr_equal(remote, &t->parms.raddr) || + !(t->dev->flags & IFF_UP)) + continue; + ++ if (!ipv6_addr_equal(remote, &t->parms.raddr)) { ++ struct __ip6_tnl_fmr *fmr; ++ bool found = false; ++ ++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) { ++ if (!ipv6_prefix_equal(remote, &fmr->ip6_prefix, ++ fmr->ip6_prefix_len)) ++ continue; ++ ++ found = true; ++ break; ++ } ++ ++ if (!found) ++ continue; ++ } ++ + if (link == t->parms.link) + return t; + else +@@ -132,7 +151,7 @@ ip6_tnl_lookup(struct net *net, int link, + } + + memset(&any, 0, sizeof(any)); +- hash = HASH(&any, local); ++ hash = HASH(local); + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(local, &t->parms.laddr) || + !ipv6_addr_any(&t->parms.raddr) || +@@ -145,7 +164,7 @@ ip6_tnl_lookup(struct net *net, int link, + cand = t; + } + +- hash = HASH(remote, &any); ++ hash = HASH(&any); + for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (!ipv6_addr_equal(remote, &t->parms.raddr) || + !ipv6_addr_any(&t->parms.laddr) || +@@ -194,7 +213,7 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p) + + if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { + prio = 1; +- h = HASH(remote, local); ++ h = HASH(local); + } + return &ip6n->tnls[prio][h]; + } +@@ -376,6 +395,12 @@ ip6_tnl_dev_uninit(struct net_device *dev) + struct net *net = t->net; + struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); + ++ while (t->parms.fmrs) { ++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next; ++ kfree(t->parms.fmrs); ++ t->parms.fmrs = next; ++ } ++ + if (dev == ip6n->fb_tnl_dev) + RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL); + else +@@ -788,6 +813,107 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t, + } + EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl); + ++/** ++ * ip4ip6_fmr_calc - calculate target / source IPv6-address based on FMR ++ * @dest: destination IPv6 address buffer ++ * @skb: received socket buffer ++ * @fmr: MAP FMR ++ * @xmit: Calculate for xmit or rcv ++ **/ ++static void ip4ip6_fmr_calc(struct in6_addr *dest, ++ const struct iphdr *iph, const uint8_t *end, ++ const struct __ip6_tnl_fmr *fmr, bool xmit) ++{ ++ int psidlen = fmr->ea_len - (32 - fmr->ip4_prefix_len); ++ u8 *portp = NULL; ++ bool use_dest_addr; ++ const struct iphdr *dsth = iph; ++ ++ if ((u8*)dsth >= end) ++ return; ++ ++ /* find significant IP header */ ++ if (iph->protocol == IPPROTO_ICMP) { ++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4); ++ if (ih && ((u8*)&ih[1]) <= end && ( ++ ih->type == ICMP_DEST_UNREACH || ++ ih->type == ICMP_SOURCE_QUENCH || ++ ih->type == ICMP_TIME_EXCEEDED || ++ ih->type == ICMP_PARAMETERPROB || ++ ih->type == ICMP_REDIRECT)) ++ dsth = (const struct iphdr*)&ih[1]; ++ } ++ ++ /* in xmit-path use dest port by default and source port only if ++ this is an ICMP reply to something else; vice versa in rcv-path */ ++ use_dest_addr = (xmit && dsth == iph) || (!xmit && dsth != iph); ++ ++ /* get dst port */ ++ if (((u8*)&dsth[1]) <= end && ( ++ dsth->protocol == IPPROTO_UDP || ++ dsth->protocol == IPPROTO_TCP || ++ dsth->protocol == IPPROTO_SCTP || ++ dsth->protocol == IPPROTO_DCCP)) { ++ /* for UDP, TCP, SCTP and DCCP source and dest port ++ follow IPv4 header directly */ ++ portp = ((u8*)dsth) + dsth->ihl * 4; ++ ++ if (use_dest_addr) ++ portp += sizeof(u16); ++ } else if (iph->protocol == IPPROTO_ICMP) { ++ struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4); ++ ++ /* use icmp identifier as port */ ++ if (((u8*)&ih) <= end && ( ++ (use_dest_addr && ( ++ ih->type == ICMP_ECHOREPLY || ++ ih->type == ICMP_TIMESTAMPREPLY || ++ ih->type == ICMP_INFO_REPLY || ++ ih->type == ICMP_ADDRESSREPLY)) || ++ (!use_dest_addr && ( ++ ih->type == ICMP_ECHO || ++ ih->type == ICMP_TIMESTAMP || ++ ih->type == ICMP_INFO_REQUEST || ++ ih->type == ICMP_ADDRESS) ++ ))) ++ portp = (u8*)&ih->un.echo.id; ++ } ++ ++ if ((portp && &portp[2] <= end) || psidlen == 0) { ++ int frombyte = fmr->ip6_prefix_len / 8; ++ int fromrem = fmr->ip6_prefix_len % 8; ++ int bytes = sizeof(struct in6_addr) - frombyte; ++ const u32 *addr = (use_dest_addr) ? &iph->daddr : &iph->saddr; ++ u64 eabits = ((u64)ntohl(*addr)) << (32 + fmr->ip4_prefix_len); ++ u64 t = 0; ++ ++ /* extract PSID from port and add it to eabits */ ++ u16 psidbits = 0; ++ if (psidlen > 0) { ++ psidbits = ((u16)portp[0]) << 8 | ((u16)portp[1]); ++ psidbits >>= 16 - psidlen - fmr->offset; ++ psidbits = (u16)(psidbits << (16 - psidlen)); ++ eabits |= ((u64)psidbits) << (48 - (fmr->ea_len - psidlen)); ++ } ++ ++ /* rewrite destination address */ ++ *dest = fmr->ip6_prefix; ++ memcpy(&dest->s6_addr[10], addr, sizeof(*addr)); ++ dest->s6_addr16[7] = htons(psidbits >> (16 - psidlen)); ++ ++ if (bytes > sizeof(u64)) ++ bytes = sizeof(u64); ++ ++ /* insert eabits */ ++ memcpy(&t, &dest->s6_addr[frombyte], bytes); ++ t = be64_to_cpu(t) & ~(((((u64)1) << fmr->ea_len) - 1) ++ << (64 - fmr->ea_len - fromrem)); ++ t = cpu_to_be64(t | (eabits >> fromrem)); ++ memcpy(&dest->s6_addr[frombyte], &t, bytes); ++ } ++} ++ ++ + static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, + const struct tnl_ptk_info *tpi, + struct metadata_dst *tun_dst, +@@ -841,6 +967,27 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, + skb_reset_network_header(skb); + memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); + ++ if (tpi->proto == htons(ETH_P_IP) && tunnel->parms.fmrs && ++ !ipv6_addr_equal(&ipv6h->saddr, &tunnel->parms.raddr)) { ++ /* Packet didn't come from BR, so lookup FMR */ ++ struct __ip6_tnl_fmr *fmr; ++ struct in6_addr expected = tunnel->parms.raddr; ++ for (fmr = tunnel->parms.fmrs; fmr; fmr = fmr->next) ++ if (ipv6_prefix_equal(&ipv6h->saddr, ++ &fmr->ip6_prefix, fmr->ip6_prefix_len)) ++ break; ++ ++ /* Check that IPv6 matches IPv4 source to prevent spoofing */ ++ if (fmr) ++ ip4ip6_fmr_calc(&expected, ip_hdr(skb), ++ skb_tail_pointer(skb), fmr, false); ++ ++ if (!ipv6_addr_equal(&ipv6h->saddr, &expected)) { ++ rcu_read_unlock(); ++ goto drop; ++ } ++ } ++ + __skb_tunnel_rx(skb, tunnel->dev, tunnel->net); + + err = dscp_ecn_decapsulate(tunnel, ipv6h, skb); +@@ -992,6 +1139,7 @@ static void init_tel_txopt(struct ipv6_tel_txoption *opt, __u8 encap_limit) + opt->ops.opt_nflen = 8; + } + ++ + /** + * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own + * @t: the outgoing tunnel device +@@ -1280,6 +1428,7 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, + u8 protocol) + { + struct ip6_tnl *t = netdev_priv(dev); ++ struct __ip6_tnl_fmr *fmr; + struct ipv6hdr *ipv6h; + const struct iphdr *iph; + int encap_limit = -1; +@@ -1379,6 +1528,18 @@ ipxip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev, + fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL); + dsfield = INET_ECN_encapsulate(dsfield, orig_dsfield); + ++ /* try to find matching FMR */ ++ for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) { ++ unsigned mshift = 32 - fmr->ip4_prefix_len; ++ if (ntohl(fmr->ip4_prefix.s_addr) >> mshift == ++ ntohl(ip_hdr(skb)->daddr) >> mshift) ++ break; ++ } ++ ++ /* change dstaddr according to FMR */ ++ if (fmr) ++ ip4ip6_fmr_calc(&fl6.daddr, ip_hdr(skb), skb_tail_pointer(skb), fmr, true); ++ + if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) + return -1; + +@@ -1531,6 +1692,14 @@ ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p) + t->parms.link = p->link; + t->parms.proto = p->proto; + t->parms.fwmark = p->fwmark; ++ ++ while (t->parms.fmrs) { ++ struct __ip6_tnl_fmr *next = t->parms.fmrs->next; ++ kfree(t->parms.fmrs); ++ t->parms.fmrs = next; ++ } ++ t->parms.fmrs = p->fmrs; ++ + dst_cache_reset(&t->dst_cache); + ip6_tnl_link_config(t); + return 0; +@@ -1569,6 +1738,7 @@ ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u) + p->flowinfo = u->flowinfo; + p->link = u->link; + p->proto = u->proto; ++ p->fmrs = NULL; + memcpy(p->name, u->name, sizeof(u->name)); + } + +@@ -1955,6 +2125,15 @@ static int ip6_tnl_validate(struct nlattr *tb[], struct nlattr *data[], + return 0; + } + ++static const struct nla_policy ip6_tnl_fmr_policy[IFLA_IPTUN_FMR_MAX + 1] = { ++ [IFLA_IPTUN_FMR_IP6_PREFIX] = { .len = sizeof(struct in6_addr) }, ++ [IFLA_IPTUN_FMR_IP4_PREFIX] = { .len = sizeof(struct in_addr) }, ++ [IFLA_IPTUN_FMR_IP6_PREFIX_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_IP4_PREFIX_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_EA_LEN] = { .type = NLA_U8 }, ++ [IFLA_IPTUN_FMR_OFFSET] = { .type = NLA_U8 } ++}; ++ + static void ip6_tnl_netlink_parms(struct nlattr *data[], + struct __ip6_tnl_parm *parms) + { +@@ -1992,6 +2171,46 @@ static void ip6_tnl_netlink_parms(struct nlattr *data[], + + if (data[IFLA_IPTUN_FWMARK]) + parms->fwmark = nla_get_u32(data[IFLA_IPTUN_FWMARK]); ++ ++ if (data[IFLA_IPTUN_FMRS]) { ++ unsigned rem; ++ struct nlattr *fmr; ++ nla_for_each_nested(fmr, data[IFLA_IPTUN_FMRS], rem) { ++ struct nlattr *fmrd[IFLA_IPTUN_FMR_MAX + 1], *c; ++ struct __ip6_tnl_fmr *nfmr; ++ ++ nla_parse_nested(fmrd, IFLA_IPTUN_FMR_MAX, ++ fmr, ip6_tnl_fmr_policy, NULL); ++ ++ if (!(nfmr = kzalloc(sizeof(*nfmr), GFP_KERNEL))) ++ continue; ++ ++ nfmr->offset = 6; ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX])) ++ nla_memcpy(&nfmr->ip6_prefix, fmrd[IFLA_IPTUN_FMR_IP6_PREFIX], ++ sizeof(nfmr->ip6_prefix)); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX])) ++ nla_memcpy(&nfmr->ip4_prefix, fmrd[IFLA_IPTUN_FMR_IP4_PREFIX], ++ sizeof(nfmr->ip4_prefix)); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX_LEN])) ++ nfmr->ip6_prefix_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX_LEN])) ++ nfmr->ip4_prefix_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_EA_LEN])) ++ nfmr->ea_len = nla_get_u8(c); ++ ++ if ((c = fmrd[IFLA_IPTUN_FMR_OFFSET])) ++ nfmr->offset = nla_get_u8(c); ++ ++ nfmr->next = parms->fmrs; ++ parms->fmrs = nfmr; ++ } ++ } + } + + static bool ip6_tnl_netlink_encap_parms(struct nlattr *data[], +@@ -2107,6 +2326,12 @@ static void ip6_tnl_dellink(struct net_device *dev, struct list_head *head) + + static size_t ip6_tnl_get_size(const struct net_device *dev) + { ++ const struct ip6_tnl *t = netdev_priv(dev); ++ struct __ip6_tnl_fmr *c; ++ int fmrs = 0; ++ for (c = t->parms.fmrs; c; c = c->next) ++ ++fmrs; ++ + return + /* IFLA_IPTUN_LINK */ + nla_total_size(4) + +@@ -2136,6 +2361,24 @@ static size_t ip6_tnl_get_size(const struct net_device *dev) + nla_total_size(0) + + /* IFLA_IPTUN_FWMARK */ + nla_total_size(4) + ++ /* IFLA_IPTUN_FMRS */ ++ nla_total_size(0) + ++ ( ++ /* nest */ ++ nla_total_size(0) + ++ /* IFLA_IPTUN_FMR_IP6_PREFIX */ ++ nla_total_size(sizeof(struct in6_addr)) + ++ /* IFLA_IPTUN_FMR_IP4_PREFIX */ ++ nla_total_size(sizeof(struct in_addr)) + ++ /* IFLA_IPTUN_FMR_EA_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_IP6_PREFIX_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_IP4_PREFIX_LEN */ ++ nla_total_size(1) + ++ /* IFLA_IPTUN_FMR_OFFSET */ ++ nla_total_size(1) ++ ) * fmrs + + 0; + } + +@@ -2143,6 +2386,9 @@ static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev) + { + struct ip6_tnl *tunnel = netdev_priv(dev); + struct __ip6_tnl_parm *parm = &tunnel->parms; ++ struct __ip6_tnl_fmr *c; ++ int fmrcnt = 0; ++ struct nlattr *fmrs; + + if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || + nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) || +@@ -2152,9 +2398,27 @@ static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev) + nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) || + nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) || + nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto) || +- nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark)) ++ nla_put_u32(skb, IFLA_IPTUN_FWMARK, parm->fwmark) || ++ !(fmrs = nla_nest_start(skb, IFLA_IPTUN_FMRS))) + goto nla_put_failure; + ++ for (c = parm->fmrs; c; c = c->next) { ++ struct nlattr *fmr = nla_nest_start(skb, ++fmrcnt); ++ if (!fmr || ++ nla_put(skb, IFLA_IPTUN_FMR_IP6_PREFIX, ++ sizeof(c->ip6_prefix), &c->ip6_prefix) || ++ nla_put(skb, IFLA_IPTUN_FMR_IP4_PREFIX, ++ sizeof(c->ip4_prefix), &c->ip4_prefix) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, c->ip6_prefix_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, c->ip4_prefix_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_EA_LEN, c->ea_len) || ++ nla_put_u8(skb, IFLA_IPTUN_FMR_OFFSET, c->offset)) ++ goto nla_put_failure; ++ ++ nla_nest_end(skb, fmr); ++ } ++ nla_nest_end(skb, fmrs); ++ + if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, tunnel->encap.type) || + nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT, tunnel->encap.sport) || + nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) || +@@ -2194,6 +2458,7 @@ static const struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = { + [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 }, + [IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG }, + [IFLA_IPTUN_FWMARK] = { .type = NLA_U32 }, ++ [IFLA_IPTUN_FMRS] = { .type = NLA_NESTED }, + }; + + static struct rtnl_link_ops ip6_link_ops __read_mostly = { +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0068-ipv6-allow-rejecting-with-source-address-failed-poli.patch b/nixos/modules/kernel-patches/0068-ipv6-allow-rejecting-with-source-address-failed-poli.patch new file mode 100644 index 0000000..a05d094 --- /dev/null +++ b/nixos/modules/kernel-patches/0068-ipv6-allow-rejecting-with-source-address-failed-poli.patch @@ -0,0 +1,287 @@ +From 24dbd611c9221c07bd1e7f24b472171e2a34f68b Mon Sep 17 00:00:00 2001 +From: Jonas Gorski <jogo@openwrt.org> +Date: Tue, 27 Sep 2022 16:22:06 +0200 +Subject: [PATCH 68/96] ipv6: allow rejecting with "source address failed + policy" + +RFC6204 L-14 requires rejecting traffic from invalid addresses with +ICMPv6 Destination Unreachable, Code 5 (Source address failed ingress/ +egress policy) on the LAN side, so add an appropriate rule for that. + +Signed-off-by: Jonas Gorski <jogo@openwrt.org> +--- + include/net/netns/ipv6.h | 1 + + include/uapi/linux/fib_rules.h | 4 +++ + include/uapi/linux/rtnetlink.h | 1 + + net/ipv4/fib_semantics.c | 4 +++ + net/ipv4/fib_trie.c | 1 + + net/ipv4/ipmr.c | 1 + + net/ipv6/fib6_rules.c | 4 +++ + net/ipv6/ip6mr.c | 2 ++ + net/ipv6/route.c | 56 ++++++++++++++++++++++++++++++++-- + 9 files changed, 72 insertions(+), 2 deletions(-) + +diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h +index b4af4837d80b..094872c7df4a 100644 +--- a/include/net/netns/ipv6.h ++++ b/include/net/netns/ipv6.h +@@ -85,6 +85,7 @@ struct netns_ipv6 { + unsigned int fib6_routes_require_src; + #endif + struct rt6_info *ip6_prohibit_entry; ++ struct rt6_info *ip6_policy_failed_entry; + struct rt6_info *ip6_blk_hole_entry; + struct fib6_table *fib6_local_tbl; + struct fib_rules_ops *fib6_rules_ops; +diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h +index 232df14e1287..fdc18621ab75 100644 +--- a/include/uapi/linux/fib_rules.h ++++ b/include/uapi/linux/fib_rules.h +@@ -82,6 +82,10 @@ enum { + FR_ACT_BLACKHOLE, /* Drop without notification */ + FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */ + FR_ACT_PROHIBIT, /* Drop with EACCES */ ++ FR_ACT_RES9, ++ FR_ACT_RES10, ++ FR_ACT_RES11, ++ FR_ACT_POLICY_FAILED, /* Drop with EACCES */ + __FR_ACT_MAX, + }; + +diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h +index 83849a37db5b..b8e58371c0ef 100644 +--- a/include/uapi/linux/rtnetlink.h ++++ b/include/uapi/linux/rtnetlink.h +@@ -265,6 +265,7 @@ enum { + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ ++ RTN_POLICY_FAILED, /* Failed ingress/egress policy */ + __RTN_MAX + }; + +diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c +index db7b2503f068..dfeb22580309 100644 +--- a/net/ipv4/fib_semantics.c ++++ b/net/ipv4/fib_semantics.c +@@ -144,6 +144,10 @@ const struct fib_prop fib_props[RTN_MAX + 1] = { + .error = -EINVAL, + .scope = RT_SCOPE_NOWHERE, + }, ++ [RTN_POLICY_FAILED] = { ++ .error = -EACCES, ++ .scope = RT_SCOPE_UNIVERSE, ++ }, + }; + + static void rt_fibinfo_free(struct rtable __rcu **rtp) +diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c +index 452ff177e4da..833514126e1a 100644 +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -2776,6 +2776,7 @@ static const char *const rtn_type_names[__RTN_MAX] = { + [RTN_THROW] = "THROW", + [RTN_NAT] = "NAT", + [RTN_XRESOLVE] = "XRESOLVE", ++ [RTN_POLICY_FAILED] = "POLICY_FAILED", + }; + + static inline const char *rtn_type(char *buf, size_t len, unsigned int t) +diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c +index 13e6329784fb..31ab9eca976b 100644 +--- a/net/ipv4/ipmr.c ++++ b/net/ipv4/ipmr.c +@@ -175,6 +175,7 @@ static int ipmr_rule_action(struct fib_rule *rule, struct flowi *flp, + case FR_ACT_UNREACHABLE: + return -ENETUNREACH; + case FR_ACT_PROHIBIT: ++ case FR_ACT_POLICY_FAILED: + return -EACCES; + case FR_ACT_BLACKHOLE: + default: +diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c +index 7c2003833010..4679e25843d6 100644 +--- a/net/ipv6/fib6_rules.c ++++ b/net/ipv6/fib6_rules.c +@@ -221,6 +221,10 @@ static int __fib6_rule_action(struct fib_rule *rule, struct flowi *flp, + err = -EACCES; + rt = net->ipv6.ip6_prohibit_entry; + goto discard_pkt; ++ case FR_ACT_POLICY_FAILED: ++ err = -EACCES; ++ rt = net->ipv6.ip6_policy_failed_entry; ++ goto discard_pkt; + } + + tb_id = fib_rule_get_table(rule, arg); +diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c +index 4e74bc61a3db..b51a175e35b0 100644 +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -163,6 +163,8 @@ static int ip6mr_rule_action(struct fib_rule *rule, struct flowi *flp, + return -ENETUNREACH; + case FR_ACT_PROHIBIT: + return -EACCES; ++ case FR_ACT_POLICY_FAILED: ++ return -EACCES; + case FR_ACT_BLACKHOLE: + default: + return -EINVAL; +diff --git a/net/ipv6/route.c b/net/ipv6/route.c +index 916417944ec8..43bc02822169 100644 +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -97,6 +97,8 @@ static int ip6_pkt_discard(struct sk_buff *skb); + static int ip6_pkt_discard_out(struct net *net, struct sock *sk, struct sk_buff *skb); + static int ip6_pkt_prohibit(struct sk_buff *skb); + static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff *skb); ++static int ip6_pkt_policy_failed(struct sk_buff *skb); ++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb); + static void ip6_link_failure(struct sk_buff *skb); + static void ip6_rt_update_pmtu(struct dst_entry *dst, struct sock *sk, + struct sk_buff *skb, u32 mtu, +@@ -317,6 +319,18 @@ static const struct rt6_info ip6_prohibit_entry_template = { + .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), + }; + ++static const struct rt6_info ip6_policy_failed_entry_template = { ++ .dst = { ++ .__refcnt = ATOMIC_INIT(1), ++ .__use = 1, ++ .obsolete = DST_OBSOLETE_FORCE_CHK, ++ .error = -EACCES, ++ .input = ip6_pkt_policy_failed, ++ .output = ip6_pkt_policy_failed_out, ++ }, ++ .rt6i_flags = (RTF_REJECT | RTF_NONEXTHOP), ++}; ++ + static const struct rt6_info ip6_blk_hole_entry_template = { + .dst = { + .__refcnt = ATOMIC_INIT(1), +@@ -1036,6 +1050,7 @@ static const int fib6_prop[RTN_MAX + 1] = { + [RTN_BLACKHOLE] = -EINVAL, + [RTN_UNREACHABLE] = -EHOSTUNREACH, + [RTN_PROHIBIT] = -EACCES, ++ [RTN_POLICY_FAILED] = -EACCES, + [RTN_THROW] = -EAGAIN, + [RTN_NAT] = -EINVAL, + [RTN_XRESOLVE] = -EINVAL, +@@ -1071,6 +1086,10 @@ static void ip6_rt_init_dst_reject(struct rt6_info *rt, u8 fib6_type) + rt->dst.output = ip6_pkt_prohibit_out; + rt->dst.input = ip6_pkt_prohibit; + break; ++ case RTN_POLICY_FAILED: ++ rt->dst.output = ip6_pkt_policy_failed_out; ++ rt->dst.input = ip6_pkt_policy_failed; ++ break; + case RTN_THROW: + case RTN_UNREACHABLE: + default: +@@ -4539,6 +4558,17 @@ static int ip6_pkt_prohibit_out(struct net *net, struct sock *sk, struct sk_buff + return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES); + } + ++static int ip6_pkt_policy_failed(struct sk_buff *skb) ++{ ++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_INNOROUTES); ++} ++ ++static int ip6_pkt_policy_failed_out(struct net *net, struct sock *sk, struct sk_buff *skb) ++{ ++ skb->dev = skb_dst(skb)->dev; ++ return ip6_pkt_drop(skb, ICMPV6_POLICY_FAIL, IPSTATS_MIB_OUTNOROUTES); ++} ++ + /* + * Allocate a dst for local (unicast / anycast) address. + */ +@@ -5032,7 +5062,8 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, + if (rtm->rtm_type == RTN_UNREACHABLE || + rtm->rtm_type == RTN_BLACKHOLE || + rtm->rtm_type == RTN_PROHIBIT || +- rtm->rtm_type == RTN_THROW) ++ rtm->rtm_type == RTN_THROW || ++ rtm->rtm_type == RTN_POLICY_FAILED) + cfg->fc_flags |= RTF_REJECT; + + if (rtm->rtm_type == RTN_LOCAL) +@@ -6285,6 +6316,8 @@ static int ip6_route_dev_notify(struct notifier_block *this, + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + net->ipv6.ip6_prohibit_entry->dst.dev = dev; + net->ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(dev); ++ net->ipv6.ip6_policy_failed_entry->dst.dev = dev; ++ net->ipv6.ip6_policy_failed_entry->rt6i_idev = in6_dev_get(dev); + net->ipv6.ip6_blk_hole_entry->dst.dev = dev; + net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev); + #endif +@@ -6296,6 +6329,7 @@ static int ip6_route_dev_notify(struct notifier_block *this, + in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev); ++ in6_dev_put_clear(&net->ipv6.ip6_policy_failed_entry->rt6i_idev); + in6_dev_put_clear(&net->ipv6.ip6_blk_hole_entry->rt6i_idev); + #endif + } +@@ -6487,6 +6521,8 @@ static int __net_init ip6_route_net_init(struct net *net) + + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + net->ipv6.fib6_has_custom_rules = false; ++ ++ + net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, + sizeof(*net->ipv6.ip6_prohibit_entry), + GFP_KERNEL); +@@ -6497,11 +6533,21 @@ static int __net_init ip6_route_net_init(struct net *net) + ip6_template_metrics, true); + INIT_LIST_HEAD(&net->ipv6.ip6_prohibit_entry->rt6i_uncached); + ++ net->ipv6.ip6_policy_failed_entry = ++ kmemdup(&ip6_policy_failed_entry_template, ++ sizeof(*net->ipv6.ip6_policy_failed_entry), GFP_KERNEL); ++ if (!net->ipv6.ip6_policy_failed_entry) ++ goto out_ip6_prohibit_entry; ++ net->ipv6.ip6_policy_failed_entry->dst.ops = &net->ipv6.ip6_dst_ops; ++ dst_init_metrics(&net->ipv6.ip6_policy_failed_entry->dst, ++ ip6_template_metrics, true); ++ INIT_LIST_HEAD(&net->ipv6.ip6_policy_failed_entry->rt6i_uncached); ++ + net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template, + sizeof(*net->ipv6.ip6_blk_hole_entry), + GFP_KERNEL); + if (!net->ipv6.ip6_blk_hole_entry) +- goto out_ip6_prohibit_entry; ++ goto out_ip6_policy_failed_entry; + net->ipv6.ip6_blk_hole_entry->dst.ops = &net->ipv6.ip6_dst_ops; + dst_init_metrics(&net->ipv6.ip6_blk_hole_entry->dst, + ip6_template_metrics, true); +@@ -6528,6 +6574,8 @@ static int __net_init ip6_route_net_init(struct net *net) + return ret; + + #ifdef CONFIG_IPV6_MULTIPLE_TABLES ++out_ip6_policy_failed_entry: ++ kfree(net->ipv6.ip6_policy_failed_entry); + out_ip6_prohibit_entry: + kfree(net->ipv6.ip6_prohibit_entry); + out_ip6_null_entry: +@@ -6547,6 +6595,7 @@ static void __net_exit ip6_route_net_exit(struct net *net) + kfree(net->ipv6.ip6_null_entry); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES + kfree(net->ipv6.ip6_prohibit_entry); ++ kfree(net->ipv6.ip6_policy_failed_entry); + kfree(net->ipv6.ip6_blk_hole_entry); + #endif + dst_entries_destroy(&net->ipv6.ip6_dst_ops); +@@ -6624,6 +6673,9 @@ void __init ip6_route_init_special_entries(void) + init_net.ipv6.ip6_prohibit_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); + init_net.ipv6.ip6_blk_hole_entry->dst.dev = init_net.loopback_dev; + init_net.ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev); ++ init_net.ipv6.ip6_policy_failed_entry->dst.dev = init_net.loopback_dev; ++ init_net.ipv6.ip6_policy_failed_entry->rt6i_idev = ++ in6_dev_get(init_net.loopback_dev); + #endif + } + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0069-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch b/nixos/modules/kernel-patches/0069-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch new file mode 100644 index 0000000..7d24a5a --- /dev/null +++ b/nixos/modules/kernel-patches/0069-net-provide-defines-for-_POLICY_FAILED-until-all-cod.patch @@ -0,0 +1,62 @@ +From 60d85ef707d77be1a2a1233630c053807c54b596 Mon Sep 17 00:00:00 2001 +From: Jonas Gorski <jogo@openwrt.org> +Date: Tue, 27 Sep 2022 16:22:06 +0200 +Subject: [PATCH 69/96] net: provide defines for _POLICY_FAILED until all code + is updated + +Upstream introduced ICMPV6_POLICY_FAIL for code 5 of destination +unreachable, conflicting with our name. + +Add appropriate defines to allow our code to build with the new +name until we have updated our local patches for older kernels +and userspace packages. + +Signed-off-by: Jonas Gorski <jogo@openwrt.org> +--- + include/uapi/linux/fib_rules.h | 2 ++ + include/uapi/linux/icmpv6.h | 2 ++ + include/uapi/linux/rtnetlink.h | 2 ++ + 3 files changed, 6 insertions(+) + +diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h +index fdc18621ab75..9be1de81ab95 100644 +--- a/include/uapi/linux/fib_rules.h ++++ b/include/uapi/linux/fib_rules.h +@@ -89,6 +89,8 @@ enum { + __FR_ACT_MAX, + }; + ++#define FR_ACT_FAILED_POLICY FR_ACT_POLICY_FAILED ++ + #define FR_ACT_MAX (__FR_ACT_MAX - 1) + + #endif +diff --git a/include/uapi/linux/icmpv6.h b/include/uapi/linux/icmpv6.h +index ecaece3af38d..f21cb3786bc2 100644 +--- a/include/uapi/linux/icmpv6.h ++++ b/include/uapi/linux/icmpv6.h +@@ -126,6 +126,8 @@ struct icmp6hdr { + #define ICMPV6_POLICY_FAIL 5 + #define ICMPV6_REJECT_ROUTE 6 + ++#define ICMPV6_FAILED_POLICY ICMPV6_POLICY_FAIL ++ + /* + * Codes for Time Exceeded + */ +diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h +index b8e58371c0ef..2b82311f8035 100644 +--- a/include/uapi/linux/rtnetlink.h ++++ b/include/uapi/linux/rtnetlink.h +@@ -269,6 +269,8 @@ enum { + __RTN_MAX + }; + ++#define RTN_FAILED_POLICY RTN_POLICY_FAILED ++ + #define RTN_MAX (__RTN_MAX - 1) + + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0070-of_net-add-mac-address-increment-support.patch b/nixos/modules/kernel-patches/0070-of_net-add-mac-address-increment-support.patch new file mode 100644 index 0000000..d55e650 --- /dev/null +++ b/nixos/modules/kernel-patches/0070-of_net-add-mac-address-increment-support.patch @@ -0,0 +1,95 @@ +From 0cf7322f17785fc41d1e38a745a00ccd8fe7b8c0 Mon Sep 17 00:00:00 2001 +From: Ansuel Smith <ansuelsmth@gmail.com> +Date: Tue, 30 Mar 2021 18:21:14 +0200 +Subject: [PATCH 70/96] of_net: add mac-address-increment support + +Lots of embedded devices use the mac-address of other interface +extracted from nvmem cells and increments it by one or two. Add two +bindings to integrate this and directly use the right mac-address for +the interface. Some example are some routers that use the gmac +mac-address stored in the art partition and increments it by one for the +wifi. mac-address-increment-byte bindings is used to tell what byte of +the mac-address has to be increased (if not defined the last byte is +increased) and mac-address-increment tells how much the byte decided +early has to be increased. + +Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> +--- + net/core/of_net.c | 43 +++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 39 insertions(+), 4 deletions(-) + +diff --git a/net/core/of_net.c b/net/core/of_net.c +index f1a9bf7578e7..44c7cb0dbbe7 100644 +--- a/net/core/of_net.c ++++ b/net/core/of_net.c +@@ -119,28 +119,63 @@ static int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr) + * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists + * but is all zeros. + * ++ * DT can tell the system to increment the mac-address after is extracted by ++ * using: ++ * - mac-address-increment-byte to decide what byte to increase ++ * (if not defined is increased the last byte) ++ * - mac-address-increment to decide how much to increase. The value WILL ++ * overflow to other bytes if the increment is over 255 or the total ++ * increment will exceed 255 of the current byte. ++ * (example 00:01:02:03:04:ff + 1 == 00:01:02:03:05:00) ++ * (example 00:01:02:03:04:fe + 5 == 00:01:02:03:05:03) ++ * + * Return: 0 on success and errno in case of error. + */ + int of_get_mac_address(struct device_node *np, u8 *addr) + { ++ u32 inc_idx, mac_inc, mac_val; + int ret; + ++ /* Check first if the increment byte is present and valid. ++ * If not set assume to increment the last byte if found. ++ */ ++ if (of_property_read_u32(np, "mac-address-increment-byte", &inc_idx)) ++ inc_idx = 5; ++ if (inc_idx < 3 || inc_idx > 5) ++ return -EINVAL; ++ + if (!np) + return -ENODEV; + + ret = of_get_mac_addr(np, "mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "local-mac-address", addr); + if (!ret) +- return 0; ++ goto found; + + ret = of_get_mac_addr(np, "address", addr); + if (!ret) +- return 0; ++ goto found; + +- return of_get_mac_addr_nvmem(np, addr); ++ ret = of_get_mac_addr_nvmem(np, addr); ++ if (ret) ++ return ret; ++ ++found: ++ if (!of_property_read_u32(np, "mac-address-increment", &mac_inc)) { ++ /* Convert to a contiguous value */ ++ mac_val = (addr[3] << 16) + (addr[4] << 8) + addr[5]; ++ mac_val += mac_inc << 8 * (5-inc_idx); ++ ++ /* Apply the incremented value handling overflow case */ ++ addr[3] = (mac_val >> 16) & 0xff; ++ addr[4] = (mac_val >> 8) & 0xff; ++ addr[5] = (mac_val >> 0) & 0xff; ++ } ++ ++ return ret; + } + EXPORT_SYMBOL(of_get_mac_address); + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0071-of-of_net-write-back-netdev-MAC-address-to-device-tr.patch b/nixos/modules/kernel-patches/0071-of-of_net-write-back-netdev-MAC-address-to-device-tr.patch new file mode 100644 index 0000000..acbd2c9 --- /dev/null +++ b/nixos/modules/kernel-patches/0071-of-of_net-write-back-netdev-MAC-address-to-device-tr.patch @@ -0,0 +1,59 @@ +From 351deef2622af5e81dcd714743809ca7720ad0b7 Mon Sep 17 00:00:00 2001 +From: OpenWrt community <openwrt-devel@lists.openwrt.org> +Date: Wed, 13 Jul 2022 12:22:48 +0200 +Subject: [PATCH 71/96] of/of_net: write back netdev MAC-address to device-tree + +The label-mac logic relies on the mac-address property of a netdev +devices of-node. However, the mac address can also be stored as a +different property or read from e.g. an mtd device. + +Create this node when reading a mac-address from OF if it does not +already exist and copy the mac-address used for the device to this +property. This way, the MAC address can be accessed using procfs. +--- + net/core/of_net.c | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) + +diff --git a/net/core/of_net.c b/net/core/of_net.c +index 44c7cb0dbbe7..f1fa0aac725c 100644 +--- a/net/core/of_net.c ++++ b/net/core/of_net.c +@@ -95,6 +95,27 @@ static int of_get_mac_addr_nvmem(struct device_node *np, u8 *addr) + return 0; + } + ++static int of_add_mac_address(struct device_node *np, u8* addr) ++{ ++ struct property *prop; ++ ++ prop = kzalloc(sizeof(*prop), GFP_KERNEL); ++ if (!prop) ++ return -ENOMEM; ++ ++ prop->name = "mac-address"; ++ prop->length = ETH_ALEN; ++ prop->value = kmemdup(addr, ETH_ALEN, GFP_KERNEL); ++ if (!prop->value || of_update_property(np, prop)) ++ goto free; ++ ++ return 0; ++free: ++ kfree(prop->value); ++ kfree(prop); ++ return -ENOMEM; ++} ++ + /** + * of_get_mac_address() + * @np: Caller's Device Node +@@ -175,6 +196,7 @@ int of_get_mac_address(struct device_node *np, u8 *addr) + addr[5] = (mac_val >> 0) & 0xff; + } + ++ of_add_mac_address(np, addr); + return ret; + } + EXPORT_SYMBOL(of_get_mac_address); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0072-generic-add-detach-callback-to-struct-phy_driver.patch b/nixos/modules/kernel-patches/0072-generic-add-detach-callback-to-struct-phy_driver.patch new file mode 100644 index 0000000..55a87f6 --- /dev/null +++ b/nixos/modules/kernel-patches/0072-generic-add-detach-callback-to-struct-phy_driver.patch @@ -0,0 +1,47 @@ +From 2c9814f519bd0aea1ff9e9dbdee8f187e7d036bf Mon Sep 17 00:00:00 2001 +From: Gabor Juhos <juhosg@openwrt.org> +Date: Tue, 27 Sep 2022 16:22:09 +0200 +Subject: [PATCH 72/96] generic: add detach callback to struct phy_driver + +lede-commit: fe61fc2d7d0b3fb348b502f68f98243b3ddf5867 + +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +--- + drivers/net/phy/phy_device.c | 3 +++ + include/linux/phy.h | 6 ++++++ + 2 files changed, 9 insertions(+) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index f90a21781d8d..c8c6edb8e195 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -1744,6 +1744,9 @@ void phy_detach(struct phy_device *phydev) + struct module *ndev_owner = NULL; + struct mii_bus *bus; + ++ if (phydev->drv && phydev->drv->detach) ++ phydev->drv->detach(phydev); ++ + if (phydev->sysfs_links) { + if (dev) + sysfs_remove_link(&dev->dev.kobj, "phydev"); +diff --git a/include/linux/phy.h b/include/linux/phy.h +index b09f7d36cff2..77b853437e7c 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -827,6 +827,12 @@ struct phy_driver { + /** @handle_interrupt: Override default interrupt handling */ + irqreturn_t (*handle_interrupt)(struct phy_device *phydev); + ++ /* ++ * Called before an ethernet device is detached ++ * from the PHY. ++ */ ++ void (*detach)(struct phy_device *phydev); ++ + /** @remove: Clears up any memory if needed */ + void (*remove)(struct phy_device *phydev); + +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0073-net-dsa-tag_mtk-add-padding-for-tx-packets.patch b/nixos/modules/kernel-patches/0073-net-dsa-tag_mtk-add-padding-for-tx-packets.patch new file mode 100644 index 0000000..2870bf8 --- /dev/null +++ b/nixos/modules/kernel-patches/0073-net-dsa-tag_mtk-add-padding-for-tx-packets.patch @@ -0,0 +1,37 @@ +From 4347079c0a187cd344ff33497784e57bac30ff46 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Fri, 6 May 2022 21:38:42 +0200 +Subject: [PATCH 73/96] net: dsa: tag_mtk: add padding for tx packets + +Padding for transmitted packets needs to account for the special tag. +With not enough padding, garbage bytes are inserted by the switch at the +end of small packets. + +Fixes: 5cd8985a1909 ("net-next: dsa: add Mediatek tag RX/TX handler") +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + net/dsa/tag_mtk.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c +index 415d8ece242a..1d1f9dbd9e93 100644 +--- a/net/dsa/tag_mtk.c ++++ b/net/dsa/tag_mtk.c +@@ -25,6 +25,14 @@ static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb, + u8 xmit_tpid; + u8 *mtk_tag; + ++ /* The Ethernet switch we are interfaced with needs packets to be at ++ * least 64 bytes (including FCS) otherwise their padding might be ++ * corrupted. With tags enabled, we need to make sure that packets are ++ * at least 68 bytes (including FCS and tag). ++ */ ++ if (__skb_put_padto(skb, ETH_ZLEN + MTK_HDR_LEN, false)) ++ return NULL; ++ + /* Build the special tag after the MAC Source Address. If VLAN header + * is present, it's required that VLAN header and special tag is + * being combined. Only in this way we can allow the switch can parse +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0074-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-p.patch b/nixos/modules/kernel-patches/0074-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-p.patch new file mode 100644 index 0000000..e2393ea --- /dev/null +++ b/nixos/modules/kernel-patches/0074-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-p.patch @@ -0,0 +1,33 @@ +From 72f2e130775bcce8e5ddcb79d9991fcf464c0d6e Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:15 +0100 +Subject: [PATCH 74/96] net: dsa: mv88e6xxx: Request assisted learning on CPU + port + +While the hardware is capable of performing learning on the CPU port, +it requires alot of additions to the bridge's forwarding path in order +to handle multi-destination traffic correctly. + +Until that is in place, opt for the next best thing and let DSA sync +the relevant addresses down to the hardware FDB. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + drivers/net/dsa/mv88e6xxx/chip.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c +index 74f6db6bb804..241083ecc4e6 100644 +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -6915,6 +6915,7 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip) + ds->ops = &mv88e6xxx_switch_ops; + ds->ageing_time_min = chip->info->age_time_coeff; + ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; ++ ds->assisted_learning_on_cpu_port = true; + + /* Some chips support up to 32, but that requires enabling the + * 5-bit port mode, which we do not support. 640k^W16 ought to +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0075-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch b/nixos/modules/kernel-patches/0075-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch new file mode 100644 index 0000000..38b6925 --- /dev/null +++ b/nixos/modules/kernel-patches/0075-ARM-kirkwood-add-missing-linux-if_ether.h-for-ETH_AL.patch @@ -0,0 +1,49 @@ +From 07de3a98a89a8c5cfc78e26a45ac93972576efc2 Mon Sep 17 00:00:00 2001 +From: Daniel Golle <daniel@makrotopia.org> +Date: Thu, 5 Aug 2021 23:23:30 +0100 +Subject: [PATCH 75/96] ARM: kirkwood: add missing <linux/if_ether.h> for + ETH_ALEN + +After commit 83216e3988cd1 ("of: net: pass the dst buffer to +of_get_mac_address()") build fails for kirkwood as ETH_ALEN is not +defined. + +arch/arm/mach-mvebu/kirkwood.c: In function 'kirkwood_dt_eth_fixup': +arch/arm/mach-mvebu/kirkwood.c:87:13: error: 'ETH_ALEN' undeclared (first use in this function); did you mean 'ESTALE'? + u8 tmpmac[ETH_ALEN]; + ^~~~~~~~ + ESTALE +arch/arm/mach-mvebu/kirkwood.c:87:13: note: each undeclared identifier is reported only once for each function it appears in +arch/arm/mach-mvebu/kirkwood.c:87:6: warning: unused variable 'tmpmac' [-Wunused-variable] + u8 tmpmac[ETH_ALEN]; + ^~~~~~ +make[5]: *** [scripts/Makefile.build:262: arch/arm/mach-mvebu/kirkwood.o] Error 1 +make[5]: *** Waiting for unfinished jobs.... + +Add missing #include <linux/if_ether.h> to fix this. + +Cc: David S. Miller <davem@davemloft.net> +Cc: Andrew Lunn <andrew@lunn.ch> +Cc: Michael Walle <michael@walle.cc> +Reported-by: https://buildbot.openwrt.org/master/images/#/builders/56/builds/220/steps/44/logs/stdio +Fixes: 83216e3988cd1 ("of: net: pass the dst buffer to of_get_mac_address()") +Signed-off-by: Daniel Golle <daniel@makrotopia.org> +--- + arch/arm/mach-mvebu/kirkwood.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/mach-mvebu/kirkwood.c b/arch/arm/mach-mvebu/kirkwood.c +index 06b1706595f4..a493a896e6ee 100644 +--- a/arch/arm/mach-mvebu/kirkwood.c ++++ b/arch/arm/mach-mvebu/kirkwood.c +@@ -14,6 +14,7 @@ + #include <linux/kernel.h> + #include <linux/init.h> + #include <linux/mbus.h> ++#include <linux/if_ether.h> + #include <linux/of.h> + #include <linux/of_address.h> + #include <linux/of_net.h> +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0076-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch b/nixos/modules/kernel-patches/0076-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch new file mode 100644 index 0000000..b57de84 --- /dev/null +++ b/nixos/modules/kernel-patches/0076-bcma-get-SoC-device-struct-copy-its-DMA-params-to-th.patch @@ -0,0 +1,85 @@ +From 77ef3ddbb02d11c3aac1f4174959f7e3d898070b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> +Date: Tue, 27 Sep 2022 16:22:10 +0200 +Subject: [PATCH 76/96] bcma: get SoC device struct & copy its DMA params to + the subdevices +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +For bus devices to be fully usable it's required to set their DMA +parameters. + +For years it has been missing and remained unnoticed because of +mips_dma_alloc_coherent() silently handling the empty coherent_dma_mask. +Kernel 4.19 came with a lot of DMA changes and caused a regression on +the bcm47xx. Starting with the commit f8c55dc6e828 ("MIPS: use generic +dma noncoherent ops for simple noncoherent platforms") DMA coherent +allocations just fail. Example: +[ 1.114914] bgmac_bcma bcma0:2: Allocation of TX ring 0x200 failed +[ 1.121215] bgmac_bcma bcma0:2: Unable to alloc memory for DMA +[ 1.127626] bgmac_bcma: probe of bcma0:2 failed with error -12 +[ 1.133838] bgmac_bcma: Broadcom 47xx GBit MAC driver loaded + +This change fixes above regression in addition to the MIPS bcm47xx +commit 321c46b91550 ("MIPS: BCM47XX: Setup struct device for the SoC"). + +It also fixes another *old* GPIO regression caused by a parent pointing +to the NULL: +[ 0.157054] missing gpiochip .dev parent pointer +[ 0.157287] bcma: bus0: Error registering GPIO driver: -22 +introduced by the commit 74f4e0cc6108 ("bcma: switch GPIO portions to +use GPIOLIB_IRQCHIP"). + +Fixes: f8c55dc6e828 ("MIPS: use generic dma noncoherent ops for simple noncoherent platforms") +Fixes: 74f4e0cc6108 ("bcma: switch GPIO portions to use GPIOLIB_IRQCHIP") +Cc: linux-mips@linux-mips.org +Cc: Christoph Hellwig <hch@lst.de> +Cc: Linus Walleij <linus.walleij@linaro.org> +Signed-off-by: Rafał Miłecki <rafal@milecki.pl> +--- + drivers/bcma/host_soc.c | 2 ++ + drivers/bcma/main.c | 10 +++++++--- + 2 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/drivers/bcma/host_soc.c b/drivers/bcma/host_soc.c +index 90d5bdc12e03..fd2e8ff17c76 100644 +--- a/drivers/bcma/host_soc.c ++++ b/drivers/bcma/host_soc.c +@@ -191,6 +191,8 @@ int __init bcma_host_soc_init(struct bcma_soc *soc) + struct bcma_bus *bus = &soc->bus; + int err; + ++ bus->dev = soc->dev; ++ + /* Scan bus and initialize it */ + err = bcma_bus_early_register(bus); + if (err) +diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c +index 44392b624b20..71e8ce05079a 100644 +--- a/drivers/bcma/main.c ++++ b/drivers/bcma/main.c +@@ -236,13 +236,17 @@ EXPORT_SYMBOL(bcma_core_irq); + + void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core) + { +- device_initialize(&core->dev); ++ struct device *dev = &core->dev; ++ ++ device_initialize(dev); + core->dev.release = bcma_release_core_dev; + core->dev.bus = &bcma_bus_type; +- dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index); ++ dev_set_name(dev, "bcma%d:%d", bus->num, core->core_index); + core->dev.parent = bus->dev; +- if (bus->dev) ++ if (bus->dev) { + bcma_of_fill_device(bus->dev, core); ++ dma_coerce_mask_and_coherent(dev, bus->dev->coherent_dma_mask); ++ } + + switch (bus->hosttype) { + case BCMA_HOSTTYPE_PCI: +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0077-gpio-gpio-cascade-add-generic-GPIO-cascade.patch b/nixos/modules/kernel-patches/0077-gpio-gpio-cascade-add-generic-GPIO-cascade.patch new file mode 100644 index 0000000..98dfc24 --- /dev/null +++ b/nixos/modules/kernel-patches/0077-gpio-gpio-cascade-add-generic-GPIO-cascade.patch @@ -0,0 +1,189 @@ +From f5105edd6729159f332579da5416aa3a26c84ad6 Mon Sep 17 00:00:00 2001 +From: Mauri Sandberg <maukka@ext.kapsi.fi> +Date: Thu, 25 Mar 2021 11:48:05 +0200 +Subject: [PATCH 77/96] gpio: gpio-cascade: add generic GPIO cascade + +Adds support for building cascades of GPIO lines. That is, it allows +setups when there is one upstream line and multiple cascaded lines, out +of which one can be chosen at a time. The status of the upstream line +can be conveyed to the selected cascaded line or, vice versa, the status +of the cascaded line can be conveyed to the upstream line. + +A multiplexer is being used to select, which cascaded GPIO line is being +used at any given time. + +At the moment only input direction is supported. In future it should be +possible to add support for output direction, too. + +Signed-off-by: Mauri Sandberg <maukka@ext.kapsi.fi> +Reviewed-by: Linus Walleij <linus.walleij@linaro.org> +Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> +--- + drivers/gpio/Kconfig | 15 +++++ + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-cascade.c | 117 ++++++++++++++++++++++++++++++++++++ + 3 files changed, 133 insertions(+) + create mode 100644 drivers/gpio/gpio-cascade.c + +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index b01961999ced..b88980948950 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -1710,4 +1710,19 @@ config GPIO_SIM + + endmenu + ++comment "Other GPIO expanders" ++ ++config GPIO_CASCADE ++ tristate "General GPIO cascade" ++ select MULTIPLEXER ++ help ++ Say yes here to enable support for generic GPIO cascade. ++ ++ This allows building one-to-many cascades of GPIO lines using ++ different types of multiplexers readily available. At the ++ moment only input lines are supported. ++ ++ To build the driver as a module choose 'm' and the resulting module ++ will be called 'gpio-cascade'. ++ + endif +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index 14352f6dfe8e..561c060b0d9f 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -44,6 +44,7 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o + obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o + obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o + obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o ++obj-$(CONFIG_GPIO_CASCADE) += gpio-cascade.o + obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o + obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o + obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o +diff --git a/drivers/gpio/gpio-cascade.c b/drivers/gpio/gpio-cascade.c +new file mode 100644 +index 000000000000..5cbda882d79a +--- /dev/null ++++ b/drivers/gpio/gpio-cascade.c +@@ -0,0 +1,117 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * A generic GPIO cascade driver ++ * ++ * Copyright (C) 2021 Mauri Sandberg <maukka@ext.kapsi.fi> ++ * ++ * This allows building cascades of GPIO lines in a manner illustrated ++ * below: ++ * ++ * /|---- Cascaded GPIO line 0 ++ * Upstream | |---- Cascaded GPIO line 1 ++ * GPIO line ----+ | . ++ * | | . ++ * \|---- Cascaded GPIO line n ++ * ++ * A multiplexer is being used to select, which cascaded line is being ++ * addressed at any given time. ++ * ++ * At the moment only input mode is supported due to lack of means for ++ * testing output functionality. At least theoretically output should be ++ * possible with open drain constructions. ++ */ ++ ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/platform_device.h> ++#include <linux/mux/consumer.h> ++ ++#include <linux/gpio/consumer.h> ++#include <linux/gpio/driver.h> ++ ++struct gpio_cascade { ++ struct gpio_chip gpio_chip; ++ struct device *parent; ++ struct mux_control *mux_control; ++ struct gpio_desc *upstream_line; ++}; ++ ++static struct gpio_cascade *chip_to_cascade(struct gpio_chip *gc) ++{ ++ return container_of(gc, struct gpio_cascade, gpio_chip); ++} ++ ++static int gpio_cascade_get_direction(struct gpio_chip *gc, unsigned int offset) ++{ ++ return GPIO_LINE_DIRECTION_IN; ++} ++ ++static int gpio_cascade_get_value(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct gpio_cascade *cas = chip_to_cascade(gc); ++ int ret; ++ ++ ret = mux_control_select(cas->mux_control, offset); ++ if (ret) ++ return ret; ++ ++ ret = gpiod_get_value(cas->upstream_line); ++ mux_control_deselect(cas->mux_control); ++ return ret; ++} ++ ++static int gpio_cascade_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct gpio_cascade *cas; ++ struct mux_control *mc; ++ struct gpio_desc *upstream; ++ struct gpio_chip *gc; ++ ++ cas = devm_kzalloc(dev, sizeof(*cas), GFP_KERNEL); ++ if (!cas) ++ return -ENOMEM; ++ ++ mc = devm_mux_control_get(dev, NULL); ++ if (IS_ERR(mc)) ++ return dev_err_probe(dev, PTR_ERR(mc), "unable to get mux-control\n"); ++ ++ cas->mux_control = mc; ++ upstream = devm_gpiod_get(dev, "upstream", GPIOD_IN); ++ if (IS_ERR(upstream)) ++ return dev_err_probe(dev, PTR_ERR(upstream), "unable to claim upstream GPIO line\n"); ++ ++ cas->upstream_line = upstream; ++ cas->parent = dev; ++ ++ gc = &cas->gpio_chip; ++ gc->get = gpio_cascade_get_value; ++ gc->get_direction = gpio_cascade_get_direction; ++ gc->base = -1; ++ gc->ngpio = mux_control_states(mc); ++ gc->label = dev_name(cas->parent); ++ gc->parent = cas->parent; ++ gc->owner = THIS_MODULE; ++ ++ platform_set_drvdata(pdev, cas); ++ return devm_gpiochip_add_data(dev, &cas->gpio_chip, NULL); ++} ++ ++static const struct of_device_id gpio_cascade_id[] = { ++ { .compatible = "gpio-cascade" }, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, gpio_cascade_id); ++ ++static struct platform_driver gpio_cascade_driver = { ++ .driver = { ++ .name = "gpio-cascade", ++ .of_match_table = gpio_cascade_id, ++ }, ++ .probe = gpio_cascade_probe, ++}; ++module_platform_driver(gpio_cascade_driver); ++ ++MODULE_AUTHOR("Mauri Sandberg <maukka@ext.kapsi.fi>"); ++MODULE_DESCRIPTION("Generic GPIO cascade"); ++MODULE_LICENSE("GPL"); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0078-debloat-add-kernel-config-option-to-disabling-common.patch b/nixos/modules/kernel-patches/0078-debloat-add-kernel-config-option-to-disabling-common.patch new file mode 100644 index 0000000..02d8db0 --- /dev/null +++ b/nixos/modules/kernel-patches/0078-debloat-add-kernel-config-option-to-disabling-common.patch @@ -0,0 +1,72 @@ +From 3cc791c22083c2e426fd5a722069c2e6d81f7ee1 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos <juhosg@openwrt.org> +Date: Tue, 27 Sep 2022 16:22:10 +0200 +Subject: [PATCH 78/96] debloat: add kernel config option to disabling common + PCI quirks + +Signed-off-by: Gabor Juhos <juhosg@openwrt.org> +--- + drivers/pci/Kconfig | 7 +++++++ + drivers/pci/quirks.c | 7 +++++++ + 2 files changed, 14 insertions(+) + +diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig +index 133c73207782..70e2d7679199 100644 +--- a/drivers/pci/Kconfig ++++ b/drivers/pci/Kconfig +@@ -118,6 +118,13 @@ config XEN_PCIDEV_FRONTEND + The PCI device frontend driver allows the kernel to import arbitrary + PCI devices from a PCI backend to support PCI driver domains. + ++config PCI_DISABLE_COMMON_QUIRKS ++ bool "PCI disable common quirks" ++ depends on PCI ++ help ++ If you don't know what to do here, say N. ++ ++ + config PCI_ATS + bool + +diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c +index 2e68f50bc7ae..df09caf0dc8d 100644 +--- a/drivers/pci/quirks.c ++++ b/drivers/pci/quirks.c +@@ -207,6 +207,7 @@ static void quirk_mmio_always_on(struct pci_dev *dev) + DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS + /* + * The Mellanox Tavor device gives false positive parity errors. Disable + * parity error reporting. +@@ -3364,6 +3365,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f8, quirk_intel_mc_errata); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata); + ++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ + /* + * Ivytown NTB BAR sizes are misreported by the hardware due to an erratum. + * To work around this, query the size it should be configured to by the +@@ -3389,6 +3392,8 @@ static void quirk_intel_ntb(struct pci_dev *dev) + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e08, quirk_intel_ntb); + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0e0d, quirk_intel_ntb); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++ + /* + * Some BIOS implementations leave the Intel GPU interrupts enabled, even + * though no one is handling them (e.g., if the i915 driver is never +@@ -3427,6 +3432,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0106, disable_igfx_irq); + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq); + DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0152, disable_igfx_irq); + ++#endif /* !CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ + /* + * PCI devices which are on Intel chips can skip the 10ms delay + * before entering D3 mode. +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0079-debloat-disable-common-USB-quirks.patch b/nixos/modules/kernel-patches/0079-debloat-disable-common-USB-quirks.patch new file mode 100644 index 0000000..9daac66 --- /dev/null +++ b/nixos/modules/kernel-patches/0079-debloat-disable-common-USB-quirks.patch @@ -0,0 +1,126 @@ +From 87b1826cbf97eebbb5874c9b1963f4da7fa09d06 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau <nbd@nbd.name> +Date: Tue, 27 Sep 2022 16:22:11 +0200 +Subject: [PATCH 79/96] debloat: disable common USB quirks + +Signed-off-by: Felix Fietkau <nbd@nbd.name> +--- + drivers/usb/host/pci-quirks.c | 16 ++++++++++++++++ + drivers/usb/host/pci-quirks.h | 20 ++++++++++++++++++++ + include/linux/usb/hcd.h | 7 +++++++ + 3 files changed, 43 insertions(+) + +diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c +index ef08d68b9714..48c7475b3f71 100644 +--- a/drivers/usb/host/pci-quirks.c ++++ b/drivers/usb/host/pci-quirks.c +@@ -128,6 +128,8 @@ struct amd_chipset_type { + u8 rev; + }; + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++ + static struct amd_chipset_info { + struct pci_dev *nb_dev; + struct pci_dev *smbus_dev; +@@ -633,6 +635,10 @@ bool usb_amd_pt_check_port(struct device *device, int port) + } + EXPORT_SYMBOL_GPL(usb_amd_pt_check_port); + ++#endif /* CONFIG_PCI_DISABLE_COMMON_QUIRKS */ ++ ++#if IS_ENABLED(CONFIG_USB_UHCI_HCD) ++ + /* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. +@@ -712,8 +718,17 @@ int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base) + uhci_reset_hc(pdev, base); + return 1; + } ++#else ++int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base) ++{ ++ return 0; ++} ++ ++#endif + EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS ++ + static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask) + { + u16 cmd; +@@ -1285,3 +1300,4 @@ static void quirk_usb_early_handoff(struct pci_dev *pdev) + } + DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff); ++#endif +diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h +index e729de21fad7..1963d3ff63f1 100644 +--- a/drivers/usb/host/pci-quirks.h ++++ b/drivers/usb/host/pci-quirks.h +@@ -5,6 +5,9 @@ + #ifdef CONFIG_USB_PCI + void uhci_reset_hc(struct pci_dev *pdev, unsigned long base); + int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base); ++#endif /* CONFIG_USB_PCI */ ++ ++#if defined(CONFIG_USB_PCI) && !defined(CONFIG_PCI_DISABLE_COMMON_QUIRKS) + int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev); + bool usb_amd_hang_symptom_quirk(void); + bool usb_amd_prefetch_quirk(void); +@@ -19,6 +22,18 @@ void sb800_prefetch(struct device *dev, int on); + bool usb_amd_pt_check_port(struct device *device, int port); + #else + struct pci_dev; ++static inline int usb_amd_quirk_pll_check(void) ++{ ++ return 0; ++} ++static inline bool usb_amd_hang_symptom_quirk(void) ++{ ++ return false; ++} ++static inline bool usb_amd_prefetch_quirk(void) ++{ ++ return false; ++} + static inline void usb_amd_quirk_pll_disable(void) {} + static inline void usb_amd_quirk_pll_enable(void) {} + static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {} +@@ -29,6 +44,11 @@ static inline bool usb_amd_pt_check_port(struct device *device, int port) + { + return false; + } ++static inline void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) {} ++static inline bool usb_xhci_needs_pci_reset(struct pci_dev *pdev) ++{ ++ return false; ++} + #endif /* CONFIG_USB_PCI */ + + #endif /* __LINUX_USB_PCI_QUIRKS_H */ +diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h +index 98d1921f02b1..aef3bf66d939 100644 +--- a/include/linux/usb/hcd.h ++++ b/include/linux/usb/hcd.h +@@ -498,7 +498,14 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev, + extern void usb_hcd_pci_remove(struct pci_dev *dev); + extern void usb_hcd_pci_shutdown(struct pci_dev *dev); + ++#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS + extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev); ++#else ++static inline int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev) ++{ ++ return 0; ++} ++#endif + + #ifdef CONFIG_PM + extern const struct dev_pm_ops usb_hcd_pci_pm_ops; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0080-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch b/nixos/modules/kernel-patches/0080-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch new file mode 100644 index 0000000..4cab0e8 --- /dev/null +++ b/nixos/modules/kernel-patches/0080-w1-gpio-fix-problem-with-platfom-data-in-w1-gpio.patch @@ -0,0 +1,31 @@ +From a76012fae1d37495766ba8bde926aaa3e861d03a Mon Sep 17 00:00:00 2001 +From: Pawel Dembicki <paweldembicki@gmail.com> +Date: Sun, 18 Feb 2018 17:08:04 +0100 +Subject: [PATCH 80/96] w1: gpio: fix problem with platfom data in w1-gpio + +In devices, where fdt is used, is impossible to apply platform data +without proper fdt node. + +This patch allow to use platform data in devices with fdt. + +Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com> +--- + drivers/w1/masters/w1-gpio.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c +index d4632aace402..dc9eb99bf48d 100644 +--- a/drivers/w1/masters/w1-gpio.c ++++ b/drivers/w1/masters/w1-gpio.c +@@ -76,7 +76,7 @@ static int w1_gpio_probe(struct platform_device *pdev) + enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN; + int err; + +- if (of_have_populated_dt()) { ++ if (of_have_populated_dt() && !dev_get_platdata(&pdev->dev)) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0081-hwrng-bcm2835-set-quality-to-1000.patch b/nixos/modules/kernel-patches/0081-hwrng-bcm2835-set-quality-to-1000.patch new file mode 100644 index 0000000..243255c --- /dev/null +++ b/nixos/modules/kernel-patches/0081-hwrng-bcm2835-set-quality-to-1000.patch @@ -0,0 +1,31 @@ +From bd3b6ea1d7d04215943564bb5a9ea0e7baa6da9f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com> +Date: Sat, 20 Feb 2021 18:36:38 +0100 +Subject: [PATCH 81/96] hwrng: bcm2835: set quality to 1000 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This allows devices without a high precission timer to reduce boot from >100s +to <30s. + +Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com> +--- + drivers/char/hw_random/bcm2835-rng.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c +index e7dd457e9b22..9caf88e614d3 100644 +--- a/drivers/char/hw_random/bcm2835-rng.c ++++ b/drivers/char/hw_random/bcm2835-rng.c +@@ -170,6 +170,7 @@ static int bcm2835_rng_probe(struct platform_device *pdev) + priv->rng.init = bcm2835_rng_init; + priv->rng.read = bcm2835_rng_read; + priv->rng.cleanup = bcm2835_rng_cleanup; ++ priv->rng.quality = 1000; + + if (dev_of_node(dev)) { + rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0082-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch b/nixos/modules/kernel-patches/0082-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch new file mode 100644 index 0000000..0df6293 --- /dev/null +++ b/nixos/modules/kernel-patches/0082-PCI-aardvark-Make-main-irq_chip-structure-a-static-d.patch @@ -0,0 +1,107 @@ +From 28a50502f73ca557d714bfd37e9d01bd50177dd1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Mon, 10 Jan 2022 02:02:00 +0100 +Subject: [PATCH 82/96] PCI: aardvark: Make main irq_chip structure a static + driver structure +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Marc Zyngier says [1] that we should use struct irq_chip as a global +static struct in the driver. Even though the structure currently +contains a dynamic member (parent_device), Marc says [2] that he plans +to kill it and make the structure completely static. + +We have already converted others irq_chip structures in this driver in +this way, but we omitted this one because the .name member is +dynamically created from device's name, and the name is displayed in +sysfs, so changing it would break sysfs ABI. + +The rationale for changing the name (to "advk-INT") in spite of sysfs +ABI, and thus allowing to convert to a static structure, is that after +the other changes we made in this series, the IRQ chip is basically +something different: it no logner generates ERR and PME interrupts (they +are generated by emulated bridge's rp_irq_chip). + +[1] https://lore.kernel.org/linux-pci/877dbcvngf.wl-maz@kernel.org/ +[2] https://lore.kernel.org/linux-pci/874k6gvkhz.wl-maz@kernel.org/ + +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + drivers/pci/controller/pci-aardvark.c | 25 +++++++------------------ + 1 file changed, 7 insertions(+), 18 deletions(-) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 93e2e0234df6..a840788d9f1e 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -280,7 +280,6 @@ struct advk_pcie { + u8 wins_count; + struct irq_domain *rp_irq_domain; + struct irq_domain *irq_domain; +- struct irq_chip irq_chip; + raw_spinlock_t irq_lock; + struct irq_domain *msi_domain; + struct irq_domain *msi_inner_domain; +@@ -1496,14 +1495,19 @@ static void advk_pcie_irq_unmask(struct irq_data *d) + raw_spin_unlock_irqrestore(&pcie->irq_lock, flags); + } + ++static struct irq_chip advk_irq_chip = { ++ .name = "advk-INT", ++ .irq_mask = advk_pcie_irq_mask, ++ .irq_unmask = advk_pcie_irq_unmask, ++}; ++ + static int advk_pcie_irq_map(struct irq_domain *h, + unsigned int virq, irq_hw_number_t hwirq) + { + struct advk_pcie *pcie = h->host_data; + + irq_set_status_flags(virq, IRQ_LEVEL); +- irq_set_chip_and_handler(virq, &pcie->irq_chip, +- handle_level_irq); ++ irq_set_chip_and_handler(virq, &advk_irq_chip, handle_level_irq); + irq_set_chip_data(virq, pcie); + + return 0; +@@ -1562,7 +1566,6 @@ static int advk_pcie_init_irq_domain(struct advk_pcie *pcie) + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *pcie_intc_node; +- struct irq_chip *irq_chip; + int ret = 0; + + raw_spin_lock_init(&pcie->irq_lock); +@@ -1573,28 +1576,14 @@ static int advk_pcie_init_irq_domain(struct advk_pcie *pcie) + return -ENODEV; + } + +- irq_chip = &pcie->irq_chip; +- +- irq_chip->name = devm_kasprintf(dev, GFP_KERNEL, "%s-irq", +- dev_name(dev)); +- if (!irq_chip->name) { +- ret = -ENOMEM; +- goto out_put_node; +- } +- +- irq_chip->irq_mask = advk_pcie_irq_mask; +- irq_chip->irq_unmask = advk_pcie_irq_unmask; +- + pcie->irq_domain = + irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, + &advk_pcie_irq_domain_ops, pcie); + if (!pcie->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + ret = -ENOMEM; +- goto out_put_node; + } + +-out_put_node: + of_node_put(pcie_intc_node); + return ret; + } +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0083-init-add-CONFIG_MANGLE_BOOTARGS-and-disable-it-by-de.patch b/nixos/modules/kernel-patches/0083-init-add-CONFIG_MANGLE_BOOTARGS-and-disable-it-by-de.patch new file mode 100644 index 0000000..fe968e2 --- /dev/null +++ b/nixos/modules/kernel-patches/0083-init-add-CONFIG_MANGLE_BOOTARGS-and-disable-it-by-de.patch @@ -0,0 +1,81 @@ +From c5e478e7bb7bba5de4f9859d93a7422916aaea28 Mon Sep 17 00:00:00 2001 +From: Imre Kaloz <kaloz@openwrt.org> +Date: Tue, 27 Sep 2022 16:22:32 +0200 +Subject: [PATCH 83/96] init: add CONFIG_MANGLE_BOOTARGS and disable it by + default + +Enabling this option renames the bootloader supplied root= +and rootfstype= variables, which might have to be know but +would break the automatisms OpenWrt uses. + +Signed-off-by: Imre Kaloz <kaloz@openwrt.org> +--- + init/Kconfig | 9 +++++++++ + init/main.c | 24 ++++++++++++++++++++++++ + 2 files changed, 33 insertions(+) + +diff --git a/init/Kconfig b/init/Kconfig +index 4d5f1ee66139..04e232e38fe4 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1818,6 +1818,15 @@ config EMBEDDED + an embedded system so certain expert options are available + for configuration. + ++config MANGLE_BOOTARGS ++ bool "Rename offending bootargs" ++ depends on EXPERT ++ help ++ Sometimes the bootloader passed bogus root= and rootfstype= ++ parameters to the kernel, and while you want to ignore them, ++ you need to know the values f.e. to support dual firmware ++ layouts on the flash. ++ + config HAVE_PERF_EVENTS + bool + help +diff --git a/init/main.c b/init/main.c +index 1fe7942f5d4a..12b821626826 100644 +--- a/init/main.c ++++ b/init/main.c +@@ -607,6 +607,29 @@ static inline void setup_nr_cpu_ids(void) { } + static inline void smp_prepare_cpus(unsigned int maxcpus) { } + #endif + ++#ifdef CONFIG_MANGLE_BOOTARGS ++static void __init mangle_bootargs(char *command_line) ++{ ++ char *rootdev; ++ char *rootfs; ++ ++ rootdev = strstr(command_line, "root=/dev/mtdblock"); ++ ++ if (rootdev) ++ strncpy(rootdev, "mangled_rootblock=", 18); ++ ++ rootfs = strstr(command_line, "rootfstype"); ++ ++ if (rootfs) ++ strncpy(rootfs, "mangled_fs", 10); ++ ++} ++#else ++static void __init mangle_bootargs(char *command_line) ++{ ++} ++#endif ++ + /* + * We need to store the untouched command line for future reference. + * We also need to store the touched command line since the parameter +@@ -950,6 +973,7 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void) + pr_notice("%s", linux_banner); + early_security_init(); + setup_arch(&command_line); ++ mangle_bootargs(command_line); + setup_boot_config(); + setup_command_line(command_line); + setup_nr_cpu_ids(); +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0084-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-err.patch b/nixos/modules/kernel-patches/0084-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-err.patch new file mode 100644 index 0000000..29cccc6 --- /dev/null +++ b/nixos/modules/kernel-patches/0084-ARM-dts-armada-385.dtsi-Add-definitions-for-PCIe-err.patch @@ -0,0 +1,71 @@ +From 28d362fecd85d14c996f313af2fba5a6d81fd08d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Mon, 18 Apr 2022 00:40:05 +0200 +Subject: [PATCH 84/96] ARM: dts: armada-385.dtsi: Add definitions for PCIe + error interrupts +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PCIe error interrupt is reported by MPIC SoC Error IRQ controller. + +Signed-off-by: Pali Rohár <pali@kernel.org> +--- + arch/arm/boot/dts/armada-385.dtsi | 20 ++++++++++++-------- + 1 file changed, 12 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-385.dtsi b/arch/arm/boot/dts/armada-385.dtsi +index be8d607c59b2..d603de5aa574 100644 +--- a/arch/arm/boot/dts/armada-385.dtsi ++++ b/arch/arm/boot/dts/armada-385.dtsi +@@ -69,8 +69,9 @@ pcie1: pcie@1,0 { + reg = <0x0800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 4>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x1 0 1 0 + 0x81000000 0 0 0x81000000 0x1 0 1 0>; +@@ -97,8 +98,9 @@ pcie2: pcie@2,0 { + reg = <0x1000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 5>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x2 0 1 0 + 0x81000000 0 0 0x81000000 0x2 0 1 0>; +@@ -125,8 +127,9 @@ pcie3: pcie@3,0 { + reg = <0x1800 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 15>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x3 0 1 0 + 0x81000000 0 0 0x81000000 0x3 0 1 0>; +@@ -156,8 +159,9 @@ pcie4: pcie@4,0 { + reg = <0x2000 0 0 0 0>; + #address-cells = <3>; + #size-cells = <2>; +- interrupt-names = "intx"; +- interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>; ++ interrupt-names = "intx", "error"; ++ interrupts-extended = <&gic GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>, ++ <&soc_err 16>; + #interrupt-cells = <1>; + ranges = <0x82000000 0 0 0x82000000 0x4 0 1 0 + 0x81000000 0 0 0x81000000 0x4 0 1 0>; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0085-PCI-aardvark-Implement-workaround-for-PCIe-Completio.patch b/nixos/modules/kernel-patches/0085-PCI-aardvark-Implement-workaround-for-PCIe-Completio.patch new file mode 100644 index 0000000..e645f7d --- /dev/null +++ b/nixos/modules/kernel-patches/0085-PCI-aardvark-Implement-workaround-for-PCIe-Completio.patch @@ -0,0 +1,84 @@ +From 576b43cfbfc54db18bee2ced8c1df1536c2f002d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org> +Date: Wed, 31 Aug 2022 19:44:08 +0200 +Subject: [PATCH 85/96] PCI: aardvark: Implement workaround for PCIe Completion + Timeout +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Marvell Armada 3700 Functional Errata, Guidelines, and Restrictions +document describes in erratum 3.12 PCIe Completion Timeout (Ref #: 251), +that PCIe IP does not support a strong-ordered model for inbound posted vs. +outbound completion. + +As a workaround for this erratum, DIS_ORD_CHK flag in Debug Mux Control +register must be set. It disables the ordering check in the core between +Completions and Posted requests received from the link. + +Marvell also suggests to do full memory barrier at the beginning of +aardvark summary interrupt handler before calling interrupt handlers of +endpoint drivers in order to minimize the risk for the race condition +documented in the Erratum between the DMA done status reading and the +completion of writing to the host memory. + +More details about this issue and suggested workarounds are in discussion: +https://lore.kernel.org/linux-pci/BN9PR18MB425154FE5019DCAF2028A1D5DB8D9@BN9PR18MB4251.namprd18.prod.outlook.com/t/#u + +It was reported that enabling this workaround fixes instability issues and +"Unhandled fault" errors when using 60 GHz WiFi 802.11ad card with Qualcomm +QCA6335 chip under significant load which were caused by interrupt status +stuck in the outbound CMPLT queue traced back to this erratum. + +This workaround fixes also kernel panic triggered after some minutes of +usage 5 GHz WiFi 802.11ax card with Mediatek MT7915 chip: + + Internal error: synchronous external abort: 96000210 [#1] SMP + Kernel panic - not syncing: Fatal exception in interrupt + +Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> +Signed-off-by: Pali Rohár <pali@kernel.org> +Fixes: 8c39d710363c ("PCI: aardvark: Add Aardvark PCI host controller driver") +Cc: stable@vger.kernel.org +--- + drivers/pci/controller/pci-aardvark.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index a840788d9f1e..dfb11622c568 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -210,6 +210,8 @@ enum { + }; + + #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44) ++#define DEBUG_MUX_CTRL_REG (LMI_BASE_ADDR + 0x208) ++#define DIS_ORD_CHK BIT(30) + #define PME_MSG_GEN_CTRL (LMI_BASE_ADDR + 0x220) + #define SEND_SET_SLOT_POWER_LIMIT BIT(13) + #define SEND_PME_TURN_OFF BIT(14) +@@ -603,6 +605,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) + PCIE_CORE_CTRL2_TD_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + ++ /* Disable ordering checks, workaround for erratum 3.12 "PCIe completion timeout" */ ++ reg = advk_readl(pcie, DEBUG_MUX_CTRL_REG); ++ reg |= DIS_ORD_CHK; ++ advk_writel(pcie, reg, DEBUG_MUX_CTRL_REG); ++ + /* Set lane X1 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~LANE_CNT_MSK; +@@ -1783,6 +1790,9 @@ static irqreturn_t advk_pcie_irq_handler(int irq, void *arg) + struct advk_pcie *pcie = arg; + u32 status; + ++ /* Full memory barrier (ARM dsb sy), workaround for erratum 3.12 "PCIe completion timeout" */ ++ mb(); ++ + status = advk_readl(pcie, HOST_CTRL_INT_STATUS_REG); + if (!(status & PCIE_IRQ_CORE_INT)) + return IRQ_NONE; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0086-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch b/nixos/modules/kernel-patches/0086-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch new file mode 100644 index 0000000..08e81e0 --- /dev/null +++ b/nixos/modules/kernel-patches/0086-ARM-dts-turris-omnia-configure-LED-0-pin-function-to.patch @@ -0,0 +1,43 @@ +From 4e5edff1191eac21bb3a15eecef0fa1a2e5a0e33 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> +Date: Mon, 4 Jul 2022 13:36:21 +0200 +Subject: [PATCH 86/96] ARM: dts: turris-omnia: configure LED[0] pin function + to link/activity +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The marvell PHY driver changes the LED[0] pin function to "On - 1000 +Mbps Link, Off - Else". + +Turris Omnia expects that the function is "On - Link, Blink - Activity, +Off - No link". + +Use the `marvell,reg-init` DT property to change the function. + +In the future, once netdev trigger will support HW offloading, we will +be able to have this configured via the combination of PHY driver and +leds-turris-omnia driver. + +Signed-off-by: Marek Behún <kabel@kernel.org> +--- + arch/arm/boot/dts/armada-385-turris-omnia.dts | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts +index f4878df39753..1583c4b14ae8 100644 +--- a/arch/arm/boot/dts/armada-385-turris-omnia.dts ++++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts +@@ -397,7 +397,8 @@ &mdio { + phy1: ethernet-phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <1>; +- marvell,reg-init = <3 18 0 0x4985>; ++ marvell,reg-init = <3 18 0 0x4985>, ++ <3 16 0xfff0 0x0001>; + + /* irq is connected to &pcawan pin 7 */ + }; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0001-ARM-dts-turris-omnia-enable-LED-controller-node.patch b/nixos/modules/kernel-patches/0087-ARM-dts-turris-omnia-enable-LED-controller-node.patch index a3d421a..c90a022 100644 --- a/nixos/modules/kernel-patches/0001-ARM-dts-turris-omnia-enable-LED-controller-node.patch +++ b/nixos/modules/kernel-patches/0087-ARM-dts-turris-omnia-enable-LED-controller-node.patch @@ -1,7 +1,7 @@ -From d33a04c156e91d813fc4dae3ffc9f87715b13446 Mon Sep 17 00:00:00 2001 +From a18b8bf331feec7ad09b3d20c5de9fae7a9e210c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> Date: Mon, 4 Jul 2022 13:36:22 +0200 -Subject: [PATCH 01/10] ARM: dts: turris-omnia: enable LED controller node +Subject: [PATCH 87/96] ARM: dts: turris-omnia: enable LED controller node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -19,7 +19,7 @@ Signed-off-by: Marek Behún <kabel@kernel.org> 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/armada-385-turris-omnia.dts b/arch/arm/boot/dts/armada-385-turris-omnia.dts -index f4878df39753..bcced203c612 100644 +index 1583c4b14ae8..d1e0db6e5730 100644 --- a/arch/arm/boot/dts/armada-385-turris-omnia.dts +++ b/arch/arm/boot/dts/armada-385-turris-omnia.dts @@ -191,15 +191,13 @@ led-controller@2b { diff --git a/nixos/modules/kernel-patches/0002-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch b/nixos/modules/kernel-patches/0088-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch index 1bd6acc..e6e8685 100644 --- a/nixos/modules/kernel-patches/0002-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch +++ b/nixos/modules/kernel-patches/0088-leds-turris-omnia-support-HW-controlled-mode-via-pri.patch @@ -1,7 +1,7 @@ -From 2760691208ad6ce37503007c0f3596532fd813e3 Mon Sep 17 00:00:00 2001 +From d7234c39e0977c8b6a093f88997bd3ed954ae045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> Date: Mon, 4 Jul 2022 12:59:53 +0200 -Subject: [PATCH 02/10] leds: turris-omnia: support HW controlled mode via +Subject: [PATCH 88/96] leds: turris-omnia: support HW controlled mode via private trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/nixos/modules/kernel-patches/0003-leds-turris-omnia-initialize-multi-intensity-to-full.patch b/nixos/modules/kernel-patches/0089-leds-turris-omnia-initialize-multi-intensity-to-full.patch index 4f9cee4..8ca4501 100644 --- a/nixos/modules/kernel-patches/0003-leds-turris-omnia-initialize-multi-intensity-to-full.patch +++ b/nixos/modules/kernel-patches/0089-leds-turris-omnia-initialize-multi-intensity-to-full.patch @@ -1,7 +1,7 @@ -From 6ad5109b744e8f10bb14f948b75c38b639f97989 Mon Sep 17 00:00:00 2001 +From 647dfa4c8eb2b4186a46dbc8791b116e826bbcb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> Date: Mon, 4 Jul 2022 12:59:54 +0200 -Subject: [PATCH 03/10] leds: turris-omnia: initialize multi-intensity to full +Subject: [PATCH 89/96] leds: turris-omnia: initialize multi-intensity to full MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/nixos/modules/kernel-patches/0004-leds-turris-omnia-change-max-brightness-from-255-to-.patch b/nixos/modules/kernel-patches/0090-leds-turris-omnia-change-max-brightness-from-255-to-.patch index 9f63b79..bb50246 100644 --- a/nixos/modules/kernel-patches/0004-leds-turris-omnia-change-max-brightness-from-255-to-.patch +++ b/nixos/modules/kernel-patches/0090-leds-turris-omnia-change-max-brightness-from-255-to-.patch @@ -1,7 +1,7 @@ -From 74951d542c10fbb181454c31d28075170528a14f Mon Sep 17 00:00:00 2001 +From e9d9ea6ea5f698f18a9ec7037a9e4fa80dd5e429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> Date: Mon, 4 Jul 2022 12:59:55 +0200 -Subject: [PATCH 04/10] leds: turris-omnia: change max brightness from 255 to 1 +Subject: [PATCH 90/96] leds: turris-omnia: change max brightness from 255 to 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diff --git a/nixos/modules/kernel-patches/0005-generic-Mangle-bootloader-s-kernel-arguments.patch b/nixos/modules/kernel-patches/0091-generic-Mangle-bootloader-s-kernel-arguments.patch index 4eafb31..9fb798a 100644 --- a/nixos/modules/kernel-patches/0005-generic-Mangle-bootloader-s-kernel-arguments.patch +++ b/nixos/modules/kernel-patches/0091-generic-Mangle-bootloader-s-kernel-arguments.patch @@ -1,7 +1,7 @@ -From 7f478ddf690dfccd8aea406a9697e1448920bf0f Mon Sep 17 00:00:00 2001 +From e3c4d478259db4123ebb809d23371091354b906f Mon Sep 17 00:00:00 2001 From: Adrian Panella <ianchi74@outlook.com> Date: Thu, 9 Mar 2017 09:37:17 +0100 -Subject: [PATCH 05/10] generic: Mangle bootloader's kernel arguments +Subject: [PATCH 91/96] generic: Mangle bootloader's kernel arguments The command-line arguments provided by the boot loader will be appended to a new device tree property: bootloader-args. @@ -179,7 +179,7 @@ index 1feb6b0f7a1f..fb8cd4f07e60 100644 return fdt_pack(fdt); } diff --git a/init/main.c b/init/main.c -index 1fe7942f5d4a..fa08c129d3e1 100644 +index 12b821626826..45a73e8c7082 100644 --- a/init/main.c +++ b/init/main.c @@ -113,6 +113,10 @@ @@ -193,7 +193,7 @@ index 1fe7942f5d4a..fa08c129d3e1 100644 static int kernel_init(void *); extern void init_IRQ(void); -@@ -961,6 +965,18 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void) +@@ -985,6 +989,18 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void) page_alloc_init(); pr_notice("Kernel command line: %s\n", saved_command_line); diff --git a/nixos/modules/kernel-patches/0092-ARM-mvebu-385-ap-Add-partitions.patch b/nixos/modules/kernel-patches/0092-ARM-mvebu-385-ap-Add-partitions.patch new file mode 100644 index 0000000..c5cee5a --- /dev/null +++ b/nixos/modules/kernel-patches/0092-ARM-mvebu-385-ap-Add-partitions.patch @@ -0,0 +1,40 @@ +From b752a59c98eafa1cd78b06e10fa7ef17e6ace724 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard <maxime.ripard@free-electrons.com> +Date: Tue, 13 Jan 2015 11:14:09 +0100 +Subject: [PATCH 92/96] ARM: mvebu: 385-ap: Add partitions + +Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> +--- + arch/arm/boot/dts/armada-385-db-ap.dts | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-385-db-ap.dts b/arch/arm/boot/dts/armada-385-db-ap.dts +index 332f8fce77dc..c36ec6a50f78 100644 +--- a/arch/arm/boot/dts/armada-385-db-ap.dts ++++ b/arch/arm/boot/dts/armada-385-db-ap.dts +@@ -218,19 +218,19 @@ partitions { + #size-cells = <1>; + + partition@0 { +- label = "U-Boot"; ++ label = "u-boot"; + reg = <0x00000000 0x00800000>; + read-only; + }; + + partition@800000 { +- label = "uImage"; ++ label = "kernel"; + reg = <0x00800000 0x00400000>; + read-only; + }; + + partition@c00000 { +- label = "Root"; ++ label = "ubi"; + reg = <0x00c00000 0x3f400000>; + }; + }; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0093-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch b/nixos/modules/kernel-patches/0093-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch new file mode 100644 index 0000000..990485f --- /dev/null +++ b/nixos/modules/kernel-patches/0093-ARM-dts-armada388-clearfog-emmc-on-clearfog-base.patch @@ -0,0 +1,95 @@ +From 40958387c6993463d9322bcf50727b2221b21420 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@armlinux.org.uk> +Date: Tue, 29 Nov 2016 10:15:45 +0000 +Subject: [PATCH 93/96] ARM: dts: armada388-clearfog: emmc on clearfog base + +Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> +--- + .../arm/boot/dts/armada-388-clearfog-base.dts | 1 + + .../armada-38x-solidrun-microsom-emmc.dtsi | 62 +++++++++++++++++++ + 2 files changed, 63 insertions(+) + create mode 100644 arch/arm/boot/dts/armada-38x-solidrun-microsom-emmc.dtsi + +diff --git a/arch/arm/boot/dts/armada-388-clearfog-base.dts b/arch/arm/boot/dts/armada-388-clearfog-base.dts +index 53b4bd35522a..3c46daebb447 100644 +--- a/arch/arm/boot/dts/armada-388-clearfog-base.dts ++++ b/arch/arm/boot/dts/armada-388-clearfog-base.dts +@@ -7,6 +7,7 @@ + + /dts-v1/; + #include "armada-388-clearfog.dtsi" ++#include "armada-38x-solidrun-microsom-emmc.dtsi" + + / { + model = "SolidRun Clearfog Base A1"; +diff --git a/arch/arm/boot/dts/armada-38x-solidrun-microsom-emmc.dtsi b/arch/arm/boot/dts/armada-38x-solidrun-microsom-emmc.dtsi +new file mode 100644 +index 000000000000..6b623b6aa602 +--- /dev/null ++++ b/arch/arm/boot/dts/armada-38x-solidrun-microsom-emmc.dtsi +@@ -0,0 +1,62 @@ ++/* ++ * Device Tree file for SolidRun Armada 38x Microsom add-on for eMMC ++ * ++ * Copyright (C) 2015 Russell King ++ * ++ * This board is in development; the contents of this file work with ++ * the A1 rev 2.0 of the board, which does not represent final ++ * production board. Things will change, don't expect this file to ++ * remain compatible info the future. ++ * ++ * This file is dual-licensed: you can use it either under the terms ++ * of the GPL or the X11 license, at your option. Note that this dual ++ * licensing only applies to this file, and not this project as a ++ * whole. ++ * ++ * a) This file is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This file is distributed in the hope that it will be useful ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * Or, alternatively ++ * ++ * b) Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use ++ * copy, modify, merge, publish, distribute, sublicense, and/or ++ * sell copies of the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the following ++ * conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED , WITHOUT WARRANTY OF ANY KIND ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ */ ++/ { ++ soc { ++ internal-regs { ++ sdhci@d8000 { ++ bus-width = <4>; ++ no-1-8-v; ++ non-removable; ++ pinctrl-0 = <µsom_sdhci_pins>; ++ pinctrl-names = "default"; ++ status = "okay"; ++ wp-inverted; ++ }; ++ }; ++ }; ++}; +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0094-ARM-dts-armada-xp-linksys-mamba-Increase-kernel-part.patch b/nixos/modules/kernel-patches/0094-ARM-dts-armada-xp-linksys-mamba-Increase-kernel-part.patch new file mode 100644 index 0000000..cee07e7 --- /dev/null +++ b/nixos/modules/kernel-patches/0094-ARM-dts-armada-xp-linksys-mamba-Increase-kernel-part.patch @@ -0,0 +1,42 @@ +From df33ff93c6740523a4221f207b84f3b6d0075c3e Mon Sep 17 00:00:00 2001 +From: Tad <tad@spotco.us> +Date: Fri, 5 Feb 2021 22:32:11 -0500 +Subject: [PATCH 94/96] ARM: dts: armada-xp-linksys-mamba: Increase kernel + partition to 4MB + +Signed-off-by: Tad Davanzo <tad@spotco.us> +--- + arch/arm/boot/dts/armada-xp-linksys-mamba.dts | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +index 36932e3b781a..29f66dd62b85 100644 +--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts ++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +@@ -360,9 +360,9 @@ partition@a00000 { + reg = <0xa00000 0x2800000>; /* 40MB */ + }; + +- partition@d00000 { ++ partition@e00000 { + label = "rootfs1"; +- reg = <0xd00000 0x2500000>; /* 37MB */ ++ reg = <0xe00000 0x2400000>; /* 36MB */ + }; + + /* kernel2 overlaps with rootfs2 by design */ +@@ -371,9 +371,9 @@ partition@3200000 { + reg = <0x3200000 0x2800000>; /* 40MB */ + }; + +- partition@3500000 { ++ partition@3600000 { + label = "rootfs2"; +- reg = <0x3500000 0x2500000>; /* 37MB */ ++ reg = <0x3600000 0x2400000>; /* 36MB */ + }; + + /* +-- +2.37.2 + diff --git a/nixos/modules/kernel-patches/0009-phy-marvell-phy-mvebu-a3700-comphy-Change-2500base-x.patch b/nixos/modules/kernel-patches/0095-phy-marvell-phy-mvebu-a3700-comphy-Change-2500base-x.patch index bd094d1..918e5a1 100644 --- a/nixos/modules/kernel-patches/0009-phy-marvell-phy-mvebu-a3700-comphy-Change-2500base-x.patch +++ b/nixos/modules/kernel-patches/0095-phy-marvell-phy-mvebu-a3700-comphy-Change-2500base-x.patch @@ -1,7 +1,7 @@ -From dba4f89292a08c3b1ab1bcdb87a0e9b9eb3af0ad Mon Sep 17 00:00:00 2001 +From e5aa46ceca003444bd2e7a5f4fd43d5e61499515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org> Date: Wed, 17 Aug 2022 14:35:59 +0200 -Subject: [PATCH 09/10] phy: marvell: phy-mvebu-a3700-comphy: Change 2500base-x +Subject: [PATCH 95/96] phy: marvell: phy-mvebu-a3700-comphy: Change 2500base-x transmit amplitude MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/nixos/modules/kernel-patches/0006-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch b/nixos/modules/kernel-patches/0096-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch index 527a4f7..cd7cf3a 100644 --- a/nixos/modules/kernel-patches/0006-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch +++ b/nixos/modules/kernel-patches/0096-cpuidle-mvebu-indicate-failure-to-enter-deeper-sleep.patch @@ -1,7 +1,7 @@ -From 0ac42f2a36490ee410b7c88720652ded4ec6beff Mon Sep 17 00:00:00 2001 +From a8be83aa342b173df49ca938f49c9cd8f9433643 Mon Sep 17 00:00:00 2001 From: Russell King <rmk+kernel@arm.linux.org.uk> Date: Sat, 3 Oct 2015 09:13:05 +0100 -Subject: [PATCH 06/10] cpuidle: mvebu: indicate failure to enter deeper sleep +Subject: [PATCH 96/96] cpuidle: mvebu: indicate failure to enter deeper sleep states The cpuidle ->enter method expects the return value to be the sleep |