From: Greg Kroah-Hartman Date: Wed, 8 Apr 2026 12:50:37 +0000 (+0200) Subject: drop a mips patch X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b9738551070f93d86df18ef8a8b3db434d998b5f;p=thirdparty%2Fkernel%2Fstable-queue.git drop a mips patch --- diff --git a/queue-6.12/mips-mm-allocate-tlb_vpn-array-atomically.patch b/queue-6.12/mips-mm-allocate-tlb_vpn-array-atomically.patch index 675adb0181..fb2a44505d 100644 --- a/queue-6.12/mips-mm-allocate-tlb_vpn-array-atomically.patch +++ b/queue-6.12/mips-mm-allocate-tlb_vpn-array-atomically.patch @@ -53,7 +53,7 @@ Signed-off-by: Greg Kroah-Hartman --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c -@@ -751,7 +751,7 @@ static void __ref r4k_tlb_uniquify(void) +@@ -538,7 +538,7 @@ static void __ref r4k_tlb_uniquify(void) tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); tlb_vpns = (use_slab ? diff --git a/queue-6.12/mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch b/queue-6.12/mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch deleted file mode 100644 index 561c9125c1..0000000000 --- a/queue-6.12/mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch +++ /dev/null @@ -1,373 +0,0 @@ -From 540760b77b8fc49d39d1b2b76196e5ec57711a32 Mon Sep 17 00:00:00 2001 -From: "Maciej W. Rozycki" -Date: Fri, 27 Mar 2026 18:57:30 +0000 -Subject: MIPS: mm: Rewrite TLB uniquification for the hidden bit feature - -From: Maciej W. Rozycki - -commit 540760b77b8fc49d39d1b2b76196e5ec57711a32 upstream. - -Before the introduction of the EHINV feature, which lets software mark -TLB entries invalid, certain older implementations of the MIPS ISA were -equipped with an analogous bit, as a vendor extension, which however is -hidden from software and only ever set at reset, and then any software -write clears it, making the intended TLB entry valid. - -This feature makes it unsafe to read a TLB entry with TLBR, modify the -page mask, and write the entry back with TLBWI, because this operation -will implicitly clear the hidden bit and this may create a duplicate -entry, as with the presence of the hidden bit there is no guarantee all -the entries across the TLB are unique each. - -Usually the firmware has already uniquified TLB entries before handing -control over, in which case we only need to guarantee at bootstrap no -clash will happen with the VPN2 values chosen in local_flush_tlb_all(). - -However with systems such as Mikrotik RB532 we get handed the TLB as at -reset, with the hidden bit set across the entries and possibly duplicate -entries present. This then causes a machine check exception when page -sizes are reset in r4k_tlb_uniquify() and prevents the system from -booting. - -Rewrite the algorithm used in r4k_tlb_uniquify() then such as to avoid -the reuse of ASID/VPN values across the TLB. Get rid of global entries -first as they may be blocking the entire address space, e.g. 16 256MiB -pages will exhaust the whole address space of a 32-bit CPU and a single -big page can exhaust the 32-bit compatibility space on a 64-bit CPU. - -Details of the algorithm chosen are given across the code itself. - -Fixes: 9f048fa48740 ("MIPS: mm: Prevent a TLB shutdown on initial uniquification") -Signed-off-by: Maciej W. Rozycki -Cc: stable@vger.kernel.org # v6.18+ -Signed-off-by: Thomas Bogendoerfer -Signed-off-by: Greg Kroah-Hartman ---- - arch/mips/mm/tlb-r4k.c | 282 +++++++++++++++++++++++++++++++++++++++---------- - 1 file changed, 228 insertions(+), 54 deletions(-) - ---- a/arch/mips/mm/tlb-r4k.c -+++ b/arch/mips/mm/tlb-r4k.c -@@ -13,6 +13,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -24,6 +25,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -511,12 +513,229 @@ static int __init set_ntlb(char *str) - __setup("ntlb=", set_ntlb); - - --/* Comparison function for EntryHi VPN fields. */ --static int r4k_vpn_cmp(const void *a, const void *b) -+/* The start bit position of VPN2 and Mask in EntryHi/PageMask registers. */ -+#define VPN2_SHIFT 13 -+ -+/* Read full EntryHi even with CONFIG_32BIT. */ -+static inline unsigned long long read_c0_entryhi_native(void) -+{ -+ return cpu_has_64bits ? read_c0_entryhi_64() : read_c0_entryhi(); -+} -+ -+/* Write full EntryHi even with CONFIG_32BIT. */ -+static inline void write_c0_entryhi_native(unsigned long long v) -+{ -+ if (cpu_has_64bits) -+ write_c0_entryhi_64(v); -+ else -+ write_c0_entryhi(v); -+} -+ -+/* TLB entry state for uniquification. */ -+struct tlbent { -+ unsigned long long wired:1; -+ unsigned long long global:1; -+ unsigned long long asid:10; -+ unsigned long long vpn:51; -+ unsigned long long pagesz:5; -+ unsigned long long index:14; -+}; -+ -+/* -+ * Comparison function for TLB entry sorting. Place wired entries first, -+ * then global entries, then order by the increasing VPN/ASID and the -+ * decreasing page size. This lets us avoid clashes with wired entries -+ * easily and get entries for larger pages out of the way first. -+ * -+ * We could group bits so as to reduce the number of comparisons, but this -+ * is seldom executed and not performance-critical, so prefer legibility. -+ */ -+static int r4k_entry_cmp(const void *a, const void *b) - { -- long v = *(unsigned long *)a - *(unsigned long *)b; -- int s = sizeof(long) > sizeof(int) ? sizeof(long) * 8 - 1: 0; -- return s ? (v != 0) | v >> s : v; -+ struct tlbent ea = *(struct tlbent *)a, eb = *(struct tlbent *)b; -+ -+ if (ea.wired > eb.wired) -+ return -1; -+ else if (ea.wired < eb.wired) -+ return 1; -+ else if (ea.global > eb.global) -+ return -1; -+ else if (ea.global < eb.global) -+ return 1; -+ else if (ea.vpn < eb.vpn) -+ return -1; -+ else if (ea.vpn > eb.vpn) -+ return 1; -+ else if (ea.asid < eb.asid) -+ return -1; -+ else if (ea.asid > eb.asid) -+ return 1; -+ else if (ea.pagesz > eb.pagesz) -+ return -1; -+ else if (ea.pagesz < eb.pagesz) -+ return 1; -+ else -+ return 0; -+} -+ -+/* -+ * Fetch all the TLB entries. Mask individual VPN values retrieved with -+ * the corresponding page mask and ignoring any 1KiB extension as we'll -+ * be using 4KiB pages for uniquification. -+ */ -+static void __ref r4k_tlb_uniquify_read(struct tlbent *tlb_vpns, int tlbsize) -+{ -+ int start = num_wired_entries(); -+ unsigned long long vpn_mask; -+ bool global; -+ int i; -+ -+ vpn_mask = GENMASK(current_cpu_data.vmbits - 1, VPN2_SHIFT); -+ vpn_mask |= cpu_has_64bits ? 3ULL << 62 : 1 << 31; -+ -+ for (i = 0; i < tlbsize; i++) { -+ unsigned long long entryhi, vpn, mask, asid; -+ unsigned int pagesz; -+ -+ write_c0_index(i); -+ mtc0_tlbr_hazard(); -+ tlb_read(); -+ tlb_read_hazard(); -+ -+ global = !!(read_c0_entrylo0() & ENTRYLO_G); -+ entryhi = read_c0_entryhi_native(); -+ mask = read_c0_pagemask(); -+ -+ asid = entryhi & cpu_asid_mask(¤t_cpu_data); -+ vpn = (entryhi & vpn_mask & ~mask) >> VPN2_SHIFT; -+ pagesz = ilog2((mask >> VPN2_SHIFT) + 1); -+ -+ tlb_vpns[i].global = global; -+ tlb_vpns[i].asid = global ? 0 : asid; -+ tlb_vpns[i].vpn = vpn; -+ tlb_vpns[i].pagesz = pagesz; -+ tlb_vpns[i].wired = i < start; -+ tlb_vpns[i].index = i; -+ } -+} -+ -+/* -+ * Write unique values to all but the wired TLB entries each, using -+ * the 4KiB page size. This size might not be supported with R6, but -+ * EHINV is mandatory for R6, so we won't ever be called in that case. -+ * -+ * A sorted table is supplied with any wired entries at the beginning, -+ * followed by any global entries, and then finally regular entries. -+ * We start at the VPN and ASID values of zero and only assign user -+ * addresses, therefore guaranteeing no clash with addresses produced -+ * by UNIQUE_ENTRYHI. We avoid any VPN values used by wired or global -+ * entries, by increasing the VPN value beyond the span of such entry. -+ * -+ * When a VPN/ASID clash is found with a regular entry we increment the -+ * ASID instead until no VPN/ASID clash has been found or the ASID space -+ * has been exhausted, in which case we increase the VPN value beyond -+ * the span of the largest clashing entry. -+ * -+ * We do not need to be concerned about FTLB or MMID configurations as -+ * those are required to implement the EHINV feature. -+ */ -+static void __ref r4k_tlb_uniquify_write(struct tlbent *tlb_vpns, int tlbsize) -+{ -+ unsigned long long asid, vpn, vpn_size, pagesz; -+ int widx, gidx, idx, sidx, lidx, i; -+ -+ vpn_size = 1ULL << (current_cpu_data.vmbits - VPN2_SHIFT); -+ pagesz = ilog2((PM_4K >> VPN2_SHIFT) + 1); -+ -+ write_c0_pagemask(PM_4K); -+ write_c0_entrylo0(0); -+ write_c0_entrylo1(0); -+ -+ asid = 0; -+ vpn = 0; -+ widx = 0; -+ gidx = 0; -+ for (sidx = 0; sidx < tlbsize && tlb_vpns[sidx].wired; sidx++) -+ ; -+ for (lidx = sidx; lidx < tlbsize && tlb_vpns[lidx].global; lidx++) -+ ; -+ idx = gidx = sidx + 1; -+ for (i = sidx; i < tlbsize; i++) { -+ unsigned long long entryhi, vpn_pagesz = 0; -+ -+ while (1) { -+ if (WARN_ON(vpn >= vpn_size)) { -+ dump_tlb_all(); -+ /* Pray local_flush_tlb_all() will cope. */ -+ return; -+ } -+ -+ /* VPN must be below the next wired entry. */ -+ if (widx < sidx && vpn >= tlb_vpns[widx].vpn) { -+ vpn = max(vpn, -+ (tlb_vpns[widx].vpn + -+ (1ULL << tlb_vpns[widx].pagesz))); -+ asid = 0; -+ widx++; -+ continue; -+ } -+ /* VPN must be below the next global entry. */ -+ if (gidx < lidx && vpn >= tlb_vpns[gidx].vpn) { -+ vpn = max(vpn, -+ (tlb_vpns[gidx].vpn + -+ (1ULL << tlb_vpns[gidx].pagesz))); -+ asid = 0; -+ gidx++; -+ continue; -+ } -+ /* Try to find a free ASID so as to conserve VPNs. */ -+ if (idx < tlbsize && vpn == tlb_vpns[idx].vpn && -+ asid == tlb_vpns[idx].asid) { -+ unsigned long long idx_pagesz; -+ -+ idx_pagesz = tlb_vpns[idx].pagesz; -+ vpn_pagesz = max(vpn_pagesz, idx_pagesz); -+ do -+ idx++; -+ while (idx < tlbsize && -+ vpn == tlb_vpns[idx].vpn && -+ asid == tlb_vpns[idx].asid); -+ asid++; -+ if (asid > cpu_asid_mask(¤t_cpu_data)) { -+ vpn += vpn_pagesz; -+ asid = 0; -+ vpn_pagesz = 0; -+ } -+ continue; -+ } -+ /* VPN mustn't be above the next regular entry. */ -+ if (idx < tlbsize && vpn > tlb_vpns[idx].vpn) { -+ vpn = max(vpn, -+ (tlb_vpns[idx].vpn + -+ (1ULL << tlb_vpns[idx].pagesz))); -+ asid = 0; -+ idx++; -+ continue; -+ } -+ break; -+ } -+ -+ entryhi = (vpn << VPN2_SHIFT) | asid; -+ write_c0_entryhi_native(entryhi); -+ write_c0_index(tlb_vpns[i].index); -+ mtc0_tlbw_hazard(); -+ tlb_write_indexed(); -+ -+ tlb_vpns[i].asid = asid; -+ tlb_vpns[i].vpn = vpn; -+ tlb_vpns[i].pagesz = pagesz; -+ -+ asid++; -+ if (asid > cpu_asid_mask(¤t_cpu_data)) { -+ vpn += 1ULL << pagesz; -+ asid = 0; -+ } -+ } - } - - /* -@@ -527,14 +746,8 @@ static void __ref r4k_tlb_uniquify(void) - { - int tlbsize = current_cpu_data.tlbsize; - bool use_slab = slab_is_available(); -- int start = num_wired_entries(); - phys_addr_t tlb_vpn_size; -- unsigned long *tlb_vpns; -- unsigned long vpn_mask; -- int cnt, ent, idx, i; -- -- vpn_mask = GENMASK(cpu_vmbits - 1, 13); -- vpn_mask |= IS_ENABLED(CONFIG_64BIT) ? 3ULL << 62 : 1 << 31; -+ struct tlbent *tlb_vpns; - - tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); - tlb_vpns = (use_slab ? -@@ -545,52 +758,13 @@ static void __ref r4k_tlb_uniquify(void) - - htw_stop(); - -- for (i = start, cnt = 0; i < tlbsize; i++, cnt++) { -- unsigned long vpn; -+ r4k_tlb_uniquify_read(tlb_vpns, tlbsize); - -- write_c0_index(i); -- mtc0_tlbr_hazard(); -- tlb_read(); -- tlb_read_hazard(); -- vpn = read_c0_entryhi(); -- vpn &= vpn_mask & PAGE_MASK; -- tlb_vpns[cnt] = vpn; -- -- /* Prevent any large pages from overlapping regular ones. */ -- write_c0_pagemask(read_c0_pagemask() & PM_DEFAULT_MASK); -- mtc0_tlbw_hazard(); -- tlb_write_indexed(); -- tlbw_use_hazard(); -- } -+ sort(tlb_vpns, tlbsize, sizeof(*tlb_vpns), r4k_entry_cmp, NULL); - -- sort(tlb_vpns, cnt, sizeof(tlb_vpns[0]), r4k_vpn_cmp, NULL); -+ r4k_tlb_uniquify_write(tlb_vpns, tlbsize); - - write_c0_pagemask(PM_DEFAULT_MASK); -- write_c0_entrylo0(0); -- write_c0_entrylo1(0); -- -- idx = 0; -- ent = tlbsize; -- for (i = start; i < tlbsize; i++) -- while (1) { -- unsigned long entryhi, vpn; -- -- entryhi = UNIQUE_ENTRYHI(ent); -- vpn = entryhi & vpn_mask & PAGE_MASK; -- -- if (idx >= cnt || vpn < tlb_vpns[idx]) { -- write_c0_entryhi(entryhi); -- write_c0_index(i); -- mtc0_tlbw_hazard(); -- tlb_write_indexed(); -- ent++; -- break; -- } else if (vpn == tlb_vpns[idx]) { -- ent++; -- } else { -- idx++; -- } -- } - - tlbw_use_hazard(); - htw_start(); diff --git a/queue-6.12/series b/queue-6.12/series index a50945e557..7990d2c089 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -127,7 +127,6 @@ bluetooth-smp-derive-legacy-responder-stk-authentication-from-mitm-state.patch bluetooth-smp-force-responder-mitm-requirements-before-building-the-pairing-response.patch bluetooth-hci_sync-fix-stack-buffer-overflow-in-hci_le_big_create_sync.patch ksmbd-fix-oob-write-in-query_info-for-compound-requests.patch -mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch mips-sibyte-bring-back-cache-initialisation.patch mips-fix-the-gcc-version-check-for-__multi3-workaround.patch hwmon-occ-fix-division-by-zero-in-occ_show_power_1.patch diff --git a/queue-6.18/mips-mm-allocate-tlb_vpn-array-atomically.patch b/queue-6.18/mips-mm-allocate-tlb_vpn-array-atomically.patch index 675adb0181..fb2a44505d 100644 --- a/queue-6.18/mips-mm-allocate-tlb_vpn-array-atomically.patch +++ b/queue-6.18/mips-mm-allocate-tlb_vpn-array-atomically.patch @@ -53,7 +53,7 @@ Signed-off-by: Greg Kroah-Hartman --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c -@@ -751,7 +751,7 @@ static void __ref r4k_tlb_uniquify(void) +@@ -538,7 +538,7 @@ static void __ref r4k_tlb_uniquify(void) tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); tlb_vpns = (use_slab ? diff --git a/queue-6.18/mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch b/queue-6.18/mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch deleted file mode 100644 index 561c9125c1..0000000000 --- a/queue-6.18/mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch +++ /dev/null @@ -1,373 +0,0 @@ -From 540760b77b8fc49d39d1b2b76196e5ec57711a32 Mon Sep 17 00:00:00 2001 -From: "Maciej W. Rozycki" -Date: Fri, 27 Mar 2026 18:57:30 +0000 -Subject: MIPS: mm: Rewrite TLB uniquification for the hidden bit feature - -From: Maciej W. Rozycki - -commit 540760b77b8fc49d39d1b2b76196e5ec57711a32 upstream. - -Before the introduction of the EHINV feature, which lets software mark -TLB entries invalid, certain older implementations of the MIPS ISA were -equipped with an analogous bit, as a vendor extension, which however is -hidden from software and only ever set at reset, and then any software -write clears it, making the intended TLB entry valid. - -This feature makes it unsafe to read a TLB entry with TLBR, modify the -page mask, and write the entry back with TLBWI, because this operation -will implicitly clear the hidden bit and this may create a duplicate -entry, as with the presence of the hidden bit there is no guarantee all -the entries across the TLB are unique each. - -Usually the firmware has already uniquified TLB entries before handing -control over, in which case we only need to guarantee at bootstrap no -clash will happen with the VPN2 values chosen in local_flush_tlb_all(). - -However with systems such as Mikrotik RB532 we get handed the TLB as at -reset, with the hidden bit set across the entries and possibly duplicate -entries present. This then causes a machine check exception when page -sizes are reset in r4k_tlb_uniquify() and prevents the system from -booting. - -Rewrite the algorithm used in r4k_tlb_uniquify() then such as to avoid -the reuse of ASID/VPN values across the TLB. Get rid of global entries -first as they may be blocking the entire address space, e.g. 16 256MiB -pages will exhaust the whole address space of a 32-bit CPU and a single -big page can exhaust the 32-bit compatibility space on a 64-bit CPU. - -Details of the algorithm chosen are given across the code itself. - -Fixes: 9f048fa48740 ("MIPS: mm: Prevent a TLB shutdown on initial uniquification") -Signed-off-by: Maciej W. Rozycki -Cc: stable@vger.kernel.org # v6.18+ -Signed-off-by: Thomas Bogendoerfer -Signed-off-by: Greg Kroah-Hartman ---- - arch/mips/mm/tlb-r4k.c | 282 +++++++++++++++++++++++++++++++++++++++---------- - 1 file changed, 228 insertions(+), 54 deletions(-) - ---- a/arch/mips/mm/tlb-r4k.c -+++ b/arch/mips/mm/tlb-r4k.c -@@ -13,6 +13,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -24,6 +25,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -511,12 +513,229 @@ static int __init set_ntlb(char *str) - __setup("ntlb=", set_ntlb); - - --/* Comparison function for EntryHi VPN fields. */ --static int r4k_vpn_cmp(const void *a, const void *b) -+/* The start bit position of VPN2 and Mask in EntryHi/PageMask registers. */ -+#define VPN2_SHIFT 13 -+ -+/* Read full EntryHi even with CONFIG_32BIT. */ -+static inline unsigned long long read_c0_entryhi_native(void) -+{ -+ return cpu_has_64bits ? read_c0_entryhi_64() : read_c0_entryhi(); -+} -+ -+/* Write full EntryHi even with CONFIG_32BIT. */ -+static inline void write_c0_entryhi_native(unsigned long long v) -+{ -+ if (cpu_has_64bits) -+ write_c0_entryhi_64(v); -+ else -+ write_c0_entryhi(v); -+} -+ -+/* TLB entry state for uniquification. */ -+struct tlbent { -+ unsigned long long wired:1; -+ unsigned long long global:1; -+ unsigned long long asid:10; -+ unsigned long long vpn:51; -+ unsigned long long pagesz:5; -+ unsigned long long index:14; -+}; -+ -+/* -+ * Comparison function for TLB entry sorting. Place wired entries first, -+ * then global entries, then order by the increasing VPN/ASID and the -+ * decreasing page size. This lets us avoid clashes with wired entries -+ * easily and get entries for larger pages out of the way first. -+ * -+ * We could group bits so as to reduce the number of comparisons, but this -+ * is seldom executed and not performance-critical, so prefer legibility. -+ */ -+static int r4k_entry_cmp(const void *a, const void *b) - { -- long v = *(unsigned long *)a - *(unsigned long *)b; -- int s = sizeof(long) > sizeof(int) ? sizeof(long) * 8 - 1: 0; -- return s ? (v != 0) | v >> s : v; -+ struct tlbent ea = *(struct tlbent *)a, eb = *(struct tlbent *)b; -+ -+ if (ea.wired > eb.wired) -+ return -1; -+ else if (ea.wired < eb.wired) -+ return 1; -+ else if (ea.global > eb.global) -+ return -1; -+ else if (ea.global < eb.global) -+ return 1; -+ else if (ea.vpn < eb.vpn) -+ return -1; -+ else if (ea.vpn > eb.vpn) -+ return 1; -+ else if (ea.asid < eb.asid) -+ return -1; -+ else if (ea.asid > eb.asid) -+ return 1; -+ else if (ea.pagesz > eb.pagesz) -+ return -1; -+ else if (ea.pagesz < eb.pagesz) -+ return 1; -+ else -+ return 0; -+} -+ -+/* -+ * Fetch all the TLB entries. Mask individual VPN values retrieved with -+ * the corresponding page mask and ignoring any 1KiB extension as we'll -+ * be using 4KiB pages for uniquification. -+ */ -+static void __ref r4k_tlb_uniquify_read(struct tlbent *tlb_vpns, int tlbsize) -+{ -+ int start = num_wired_entries(); -+ unsigned long long vpn_mask; -+ bool global; -+ int i; -+ -+ vpn_mask = GENMASK(current_cpu_data.vmbits - 1, VPN2_SHIFT); -+ vpn_mask |= cpu_has_64bits ? 3ULL << 62 : 1 << 31; -+ -+ for (i = 0; i < tlbsize; i++) { -+ unsigned long long entryhi, vpn, mask, asid; -+ unsigned int pagesz; -+ -+ write_c0_index(i); -+ mtc0_tlbr_hazard(); -+ tlb_read(); -+ tlb_read_hazard(); -+ -+ global = !!(read_c0_entrylo0() & ENTRYLO_G); -+ entryhi = read_c0_entryhi_native(); -+ mask = read_c0_pagemask(); -+ -+ asid = entryhi & cpu_asid_mask(¤t_cpu_data); -+ vpn = (entryhi & vpn_mask & ~mask) >> VPN2_SHIFT; -+ pagesz = ilog2((mask >> VPN2_SHIFT) + 1); -+ -+ tlb_vpns[i].global = global; -+ tlb_vpns[i].asid = global ? 0 : asid; -+ tlb_vpns[i].vpn = vpn; -+ tlb_vpns[i].pagesz = pagesz; -+ tlb_vpns[i].wired = i < start; -+ tlb_vpns[i].index = i; -+ } -+} -+ -+/* -+ * Write unique values to all but the wired TLB entries each, using -+ * the 4KiB page size. This size might not be supported with R6, but -+ * EHINV is mandatory for R6, so we won't ever be called in that case. -+ * -+ * A sorted table is supplied with any wired entries at the beginning, -+ * followed by any global entries, and then finally regular entries. -+ * We start at the VPN and ASID values of zero and only assign user -+ * addresses, therefore guaranteeing no clash with addresses produced -+ * by UNIQUE_ENTRYHI. We avoid any VPN values used by wired or global -+ * entries, by increasing the VPN value beyond the span of such entry. -+ * -+ * When a VPN/ASID clash is found with a regular entry we increment the -+ * ASID instead until no VPN/ASID clash has been found or the ASID space -+ * has been exhausted, in which case we increase the VPN value beyond -+ * the span of the largest clashing entry. -+ * -+ * We do not need to be concerned about FTLB or MMID configurations as -+ * those are required to implement the EHINV feature. -+ */ -+static void __ref r4k_tlb_uniquify_write(struct tlbent *tlb_vpns, int tlbsize) -+{ -+ unsigned long long asid, vpn, vpn_size, pagesz; -+ int widx, gidx, idx, sidx, lidx, i; -+ -+ vpn_size = 1ULL << (current_cpu_data.vmbits - VPN2_SHIFT); -+ pagesz = ilog2((PM_4K >> VPN2_SHIFT) + 1); -+ -+ write_c0_pagemask(PM_4K); -+ write_c0_entrylo0(0); -+ write_c0_entrylo1(0); -+ -+ asid = 0; -+ vpn = 0; -+ widx = 0; -+ gidx = 0; -+ for (sidx = 0; sidx < tlbsize && tlb_vpns[sidx].wired; sidx++) -+ ; -+ for (lidx = sidx; lidx < tlbsize && tlb_vpns[lidx].global; lidx++) -+ ; -+ idx = gidx = sidx + 1; -+ for (i = sidx; i < tlbsize; i++) { -+ unsigned long long entryhi, vpn_pagesz = 0; -+ -+ while (1) { -+ if (WARN_ON(vpn >= vpn_size)) { -+ dump_tlb_all(); -+ /* Pray local_flush_tlb_all() will cope. */ -+ return; -+ } -+ -+ /* VPN must be below the next wired entry. */ -+ if (widx < sidx && vpn >= tlb_vpns[widx].vpn) { -+ vpn = max(vpn, -+ (tlb_vpns[widx].vpn + -+ (1ULL << tlb_vpns[widx].pagesz))); -+ asid = 0; -+ widx++; -+ continue; -+ } -+ /* VPN must be below the next global entry. */ -+ if (gidx < lidx && vpn >= tlb_vpns[gidx].vpn) { -+ vpn = max(vpn, -+ (tlb_vpns[gidx].vpn + -+ (1ULL << tlb_vpns[gidx].pagesz))); -+ asid = 0; -+ gidx++; -+ continue; -+ } -+ /* Try to find a free ASID so as to conserve VPNs. */ -+ if (idx < tlbsize && vpn == tlb_vpns[idx].vpn && -+ asid == tlb_vpns[idx].asid) { -+ unsigned long long idx_pagesz; -+ -+ idx_pagesz = tlb_vpns[idx].pagesz; -+ vpn_pagesz = max(vpn_pagesz, idx_pagesz); -+ do -+ idx++; -+ while (idx < tlbsize && -+ vpn == tlb_vpns[idx].vpn && -+ asid == tlb_vpns[idx].asid); -+ asid++; -+ if (asid > cpu_asid_mask(¤t_cpu_data)) { -+ vpn += vpn_pagesz; -+ asid = 0; -+ vpn_pagesz = 0; -+ } -+ continue; -+ } -+ /* VPN mustn't be above the next regular entry. */ -+ if (idx < tlbsize && vpn > tlb_vpns[idx].vpn) { -+ vpn = max(vpn, -+ (tlb_vpns[idx].vpn + -+ (1ULL << tlb_vpns[idx].pagesz))); -+ asid = 0; -+ idx++; -+ continue; -+ } -+ break; -+ } -+ -+ entryhi = (vpn << VPN2_SHIFT) | asid; -+ write_c0_entryhi_native(entryhi); -+ write_c0_index(tlb_vpns[i].index); -+ mtc0_tlbw_hazard(); -+ tlb_write_indexed(); -+ -+ tlb_vpns[i].asid = asid; -+ tlb_vpns[i].vpn = vpn; -+ tlb_vpns[i].pagesz = pagesz; -+ -+ asid++; -+ if (asid > cpu_asid_mask(¤t_cpu_data)) { -+ vpn += 1ULL << pagesz; -+ asid = 0; -+ } -+ } - } - - /* -@@ -527,14 +746,8 @@ static void __ref r4k_tlb_uniquify(void) - { - int tlbsize = current_cpu_data.tlbsize; - bool use_slab = slab_is_available(); -- int start = num_wired_entries(); - phys_addr_t tlb_vpn_size; -- unsigned long *tlb_vpns; -- unsigned long vpn_mask; -- int cnt, ent, idx, i; -- -- vpn_mask = GENMASK(cpu_vmbits - 1, 13); -- vpn_mask |= IS_ENABLED(CONFIG_64BIT) ? 3ULL << 62 : 1 << 31; -+ struct tlbent *tlb_vpns; - - tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); - tlb_vpns = (use_slab ? -@@ -545,52 +758,13 @@ static void __ref r4k_tlb_uniquify(void) - - htw_stop(); - -- for (i = start, cnt = 0; i < tlbsize; i++, cnt++) { -- unsigned long vpn; -+ r4k_tlb_uniquify_read(tlb_vpns, tlbsize); - -- write_c0_index(i); -- mtc0_tlbr_hazard(); -- tlb_read(); -- tlb_read_hazard(); -- vpn = read_c0_entryhi(); -- vpn &= vpn_mask & PAGE_MASK; -- tlb_vpns[cnt] = vpn; -- -- /* Prevent any large pages from overlapping regular ones. */ -- write_c0_pagemask(read_c0_pagemask() & PM_DEFAULT_MASK); -- mtc0_tlbw_hazard(); -- tlb_write_indexed(); -- tlbw_use_hazard(); -- } -+ sort(tlb_vpns, tlbsize, sizeof(*tlb_vpns), r4k_entry_cmp, NULL); - -- sort(tlb_vpns, cnt, sizeof(tlb_vpns[0]), r4k_vpn_cmp, NULL); -+ r4k_tlb_uniquify_write(tlb_vpns, tlbsize); - - write_c0_pagemask(PM_DEFAULT_MASK); -- write_c0_entrylo0(0); -- write_c0_entrylo1(0); -- -- idx = 0; -- ent = tlbsize; -- for (i = start; i < tlbsize; i++) -- while (1) { -- unsigned long entryhi, vpn; -- -- entryhi = UNIQUE_ENTRYHI(ent); -- vpn = entryhi & vpn_mask & PAGE_MASK; -- -- if (idx >= cnt || vpn < tlb_vpns[idx]) { -- write_c0_entryhi(entryhi); -- write_c0_index(i); -- mtc0_tlbw_hazard(); -- tlb_write_indexed(); -- ent++; -- break; -- } else if (vpn == tlb_vpns[idx]) { -- ent++; -- } else { -- idx++; -- } -- } - - tlbw_use_hazard(); - htw_start(); diff --git a/queue-6.18/series b/queue-6.18/series index 9511539652..dc4e9819ff 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -143,7 +143,6 @@ bluetooth-smp-derive-legacy-responder-stk-authentication-from-mitm-state.patch bluetooth-smp-force-responder-mitm-requirements-before-building-the-pairing-response.patch bluetooth-hci_sync-fix-stack-buffer-overflow-in-hci_le_big_create_sync.patch ksmbd-fix-oob-write-in-query_info-for-compound-requests.patch -mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch mips-sibyte-bring-back-cache-initialisation.patch mips-fix-the-gcc-version-check-for-__multi3-workaround.patch hwmon-occ-fix-division-by-zero-in-occ_show_power_1.patch diff --git a/queue-6.19/mips-mm-allocate-tlb_vpn-array-atomically.patch b/queue-6.19/mips-mm-allocate-tlb_vpn-array-atomically.patch index 675adb0181..fb2a44505d 100644 --- a/queue-6.19/mips-mm-allocate-tlb_vpn-array-atomically.patch +++ b/queue-6.19/mips-mm-allocate-tlb_vpn-array-atomically.patch @@ -53,7 +53,7 @@ Signed-off-by: Greg Kroah-Hartman --- a/arch/mips/mm/tlb-r4k.c +++ b/arch/mips/mm/tlb-r4k.c -@@ -751,7 +751,7 @@ static void __ref r4k_tlb_uniquify(void) +@@ -538,7 +538,7 @@ static void __ref r4k_tlb_uniquify(void) tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); tlb_vpns = (use_slab ? diff --git a/queue-6.19/mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch b/queue-6.19/mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch deleted file mode 100644 index 561c9125c1..0000000000 --- a/queue-6.19/mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch +++ /dev/null @@ -1,373 +0,0 @@ -From 540760b77b8fc49d39d1b2b76196e5ec57711a32 Mon Sep 17 00:00:00 2001 -From: "Maciej W. Rozycki" -Date: Fri, 27 Mar 2026 18:57:30 +0000 -Subject: MIPS: mm: Rewrite TLB uniquification for the hidden bit feature - -From: Maciej W. Rozycki - -commit 540760b77b8fc49d39d1b2b76196e5ec57711a32 upstream. - -Before the introduction of the EHINV feature, which lets software mark -TLB entries invalid, certain older implementations of the MIPS ISA were -equipped with an analogous bit, as a vendor extension, which however is -hidden from software and only ever set at reset, and then any software -write clears it, making the intended TLB entry valid. - -This feature makes it unsafe to read a TLB entry with TLBR, modify the -page mask, and write the entry back with TLBWI, because this operation -will implicitly clear the hidden bit and this may create a duplicate -entry, as with the presence of the hidden bit there is no guarantee all -the entries across the TLB are unique each. - -Usually the firmware has already uniquified TLB entries before handing -control over, in which case we only need to guarantee at bootstrap no -clash will happen with the VPN2 values chosen in local_flush_tlb_all(). - -However with systems such as Mikrotik RB532 we get handed the TLB as at -reset, with the hidden bit set across the entries and possibly duplicate -entries present. This then causes a machine check exception when page -sizes are reset in r4k_tlb_uniquify() and prevents the system from -booting. - -Rewrite the algorithm used in r4k_tlb_uniquify() then such as to avoid -the reuse of ASID/VPN values across the TLB. Get rid of global entries -first as they may be blocking the entire address space, e.g. 16 256MiB -pages will exhaust the whole address space of a 32-bit CPU and a single -big page can exhaust the 32-bit compatibility space on a 64-bit CPU. - -Details of the algorithm chosen are given across the code itself. - -Fixes: 9f048fa48740 ("MIPS: mm: Prevent a TLB shutdown on initial uniquification") -Signed-off-by: Maciej W. Rozycki -Cc: stable@vger.kernel.org # v6.18+ -Signed-off-by: Thomas Bogendoerfer -Signed-off-by: Greg Kroah-Hartman ---- - arch/mips/mm/tlb-r4k.c | 282 +++++++++++++++++++++++++++++++++++++++---------- - 1 file changed, 228 insertions(+), 54 deletions(-) - ---- a/arch/mips/mm/tlb-r4k.c -+++ b/arch/mips/mm/tlb-r4k.c -@@ -13,6 +13,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -24,6 +25,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -511,12 +513,229 @@ static int __init set_ntlb(char *str) - __setup("ntlb=", set_ntlb); - - --/* Comparison function for EntryHi VPN fields. */ --static int r4k_vpn_cmp(const void *a, const void *b) -+/* The start bit position of VPN2 and Mask in EntryHi/PageMask registers. */ -+#define VPN2_SHIFT 13 -+ -+/* Read full EntryHi even with CONFIG_32BIT. */ -+static inline unsigned long long read_c0_entryhi_native(void) -+{ -+ return cpu_has_64bits ? read_c0_entryhi_64() : read_c0_entryhi(); -+} -+ -+/* Write full EntryHi even with CONFIG_32BIT. */ -+static inline void write_c0_entryhi_native(unsigned long long v) -+{ -+ if (cpu_has_64bits) -+ write_c0_entryhi_64(v); -+ else -+ write_c0_entryhi(v); -+} -+ -+/* TLB entry state for uniquification. */ -+struct tlbent { -+ unsigned long long wired:1; -+ unsigned long long global:1; -+ unsigned long long asid:10; -+ unsigned long long vpn:51; -+ unsigned long long pagesz:5; -+ unsigned long long index:14; -+}; -+ -+/* -+ * Comparison function for TLB entry sorting. Place wired entries first, -+ * then global entries, then order by the increasing VPN/ASID and the -+ * decreasing page size. This lets us avoid clashes with wired entries -+ * easily and get entries for larger pages out of the way first. -+ * -+ * We could group bits so as to reduce the number of comparisons, but this -+ * is seldom executed and not performance-critical, so prefer legibility. -+ */ -+static int r4k_entry_cmp(const void *a, const void *b) - { -- long v = *(unsigned long *)a - *(unsigned long *)b; -- int s = sizeof(long) > sizeof(int) ? sizeof(long) * 8 - 1: 0; -- return s ? (v != 0) | v >> s : v; -+ struct tlbent ea = *(struct tlbent *)a, eb = *(struct tlbent *)b; -+ -+ if (ea.wired > eb.wired) -+ return -1; -+ else if (ea.wired < eb.wired) -+ return 1; -+ else if (ea.global > eb.global) -+ return -1; -+ else if (ea.global < eb.global) -+ return 1; -+ else if (ea.vpn < eb.vpn) -+ return -1; -+ else if (ea.vpn > eb.vpn) -+ return 1; -+ else if (ea.asid < eb.asid) -+ return -1; -+ else if (ea.asid > eb.asid) -+ return 1; -+ else if (ea.pagesz > eb.pagesz) -+ return -1; -+ else if (ea.pagesz < eb.pagesz) -+ return 1; -+ else -+ return 0; -+} -+ -+/* -+ * Fetch all the TLB entries. Mask individual VPN values retrieved with -+ * the corresponding page mask and ignoring any 1KiB extension as we'll -+ * be using 4KiB pages for uniquification. -+ */ -+static void __ref r4k_tlb_uniquify_read(struct tlbent *tlb_vpns, int tlbsize) -+{ -+ int start = num_wired_entries(); -+ unsigned long long vpn_mask; -+ bool global; -+ int i; -+ -+ vpn_mask = GENMASK(current_cpu_data.vmbits - 1, VPN2_SHIFT); -+ vpn_mask |= cpu_has_64bits ? 3ULL << 62 : 1 << 31; -+ -+ for (i = 0; i < tlbsize; i++) { -+ unsigned long long entryhi, vpn, mask, asid; -+ unsigned int pagesz; -+ -+ write_c0_index(i); -+ mtc0_tlbr_hazard(); -+ tlb_read(); -+ tlb_read_hazard(); -+ -+ global = !!(read_c0_entrylo0() & ENTRYLO_G); -+ entryhi = read_c0_entryhi_native(); -+ mask = read_c0_pagemask(); -+ -+ asid = entryhi & cpu_asid_mask(¤t_cpu_data); -+ vpn = (entryhi & vpn_mask & ~mask) >> VPN2_SHIFT; -+ pagesz = ilog2((mask >> VPN2_SHIFT) + 1); -+ -+ tlb_vpns[i].global = global; -+ tlb_vpns[i].asid = global ? 0 : asid; -+ tlb_vpns[i].vpn = vpn; -+ tlb_vpns[i].pagesz = pagesz; -+ tlb_vpns[i].wired = i < start; -+ tlb_vpns[i].index = i; -+ } -+} -+ -+/* -+ * Write unique values to all but the wired TLB entries each, using -+ * the 4KiB page size. This size might not be supported with R6, but -+ * EHINV is mandatory for R6, so we won't ever be called in that case. -+ * -+ * A sorted table is supplied with any wired entries at the beginning, -+ * followed by any global entries, and then finally regular entries. -+ * We start at the VPN and ASID values of zero and only assign user -+ * addresses, therefore guaranteeing no clash with addresses produced -+ * by UNIQUE_ENTRYHI. We avoid any VPN values used by wired or global -+ * entries, by increasing the VPN value beyond the span of such entry. -+ * -+ * When a VPN/ASID clash is found with a regular entry we increment the -+ * ASID instead until no VPN/ASID clash has been found or the ASID space -+ * has been exhausted, in which case we increase the VPN value beyond -+ * the span of the largest clashing entry. -+ * -+ * We do not need to be concerned about FTLB or MMID configurations as -+ * those are required to implement the EHINV feature. -+ */ -+static void __ref r4k_tlb_uniquify_write(struct tlbent *tlb_vpns, int tlbsize) -+{ -+ unsigned long long asid, vpn, vpn_size, pagesz; -+ int widx, gidx, idx, sidx, lidx, i; -+ -+ vpn_size = 1ULL << (current_cpu_data.vmbits - VPN2_SHIFT); -+ pagesz = ilog2((PM_4K >> VPN2_SHIFT) + 1); -+ -+ write_c0_pagemask(PM_4K); -+ write_c0_entrylo0(0); -+ write_c0_entrylo1(0); -+ -+ asid = 0; -+ vpn = 0; -+ widx = 0; -+ gidx = 0; -+ for (sidx = 0; sidx < tlbsize && tlb_vpns[sidx].wired; sidx++) -+ ; -+ for (lidx = sidx; lidx < tlbsize && tlb_vpns[lidx].global; lidx++) -+ ; -+ idx = gidx = sidx + 1; -+ for (i = sidx; i < tlbsize; i++) { -+ unsigned long long entryhi, vpn_pagesz = 0; -+ -+ while (1) { -+ if (WARN_ON(vpn >= vpn_size)) { -+ dump_tlb_all(); -+ /* Pray local_flush_tlb_all() will cope. */ -+ return; -+ } -+ -+ /* VPN must be below the next wired entry. */ -+ if (widx < sidx && vpn >= tlb_vpns[widx].vpn) { -+ vpn = max(vpn, -+ (tlb_vpns[widx].vpn + -+ (1ULL << tlb_vpns[widx].pagesz))); -+ asid = 0; -+ widx++; -+ continue; -+ } -+ /* VPN must be below the next global entry. */ -+ if (gidx < lidx && vpn >= tlb_vpns[gidx].vpn) { -+ vpn = max(vpn, -+ (tlb_vpns[gidx].vpn + -+ (1ULL << tlb_vpns[gidx].pagesz))); -+ asid = 0; -+ gidx++; -+ continue; -+ } -+ /* Try to find a free ASID so as to conserve VPNs. */ -+ if (idx < tlbsize && vpn == tlb_vpns[idx].vpn && -+ asid == tlb_vpns[idx].asid) { -+ unsigned long long idx_pagesz; -+ -+ idx_pagesz = tlb_vpns[idx].pagesz; -+ vpn_pagesz = max(vpn_pagesz, idx_pagesz); -+ do -+ idx++; -+ while (idx < tlbsize && -+ vpn == tlb_vpns[idx].vpn && -+ asid == tlb_vpns[idx].asid); -+ asid++; -+ if (asid > cpu_asid_mask(¤t_cpu_data)) { -+ vpn += vpn_pagesz; -+ asid = 0; -+ vpn_pagesz = 0; -+ } -+ continue; -+ } -+ /* VPN mustn't be above the next regular entry. */ -+ if (idx < tlbsize && vpn > tlb_vpns[idx].vpn) { -+ vpn = max(vpn, -+ (tlb_vpns[idx].vpn + -+ (1ULL << tlb_vpns[idx].pagesz))); -+ asid = 0; -+ idx++; -+ continue; -+ } -+ break; -+ } -+ -+ entryhi = (vpn << VPN2_SHIFT) | asid; -+ write_c0_entryhi_native(entryhi); -+ write_c0_index(tlb_vpns[i].index); -+ mtc0_tlbw_hazard(); -+ tlb_write_indexed(); -+ -+ tlb_vpns[i].asid = asid; -+ tlb_vpns[i].vpn = vpn; -+ tlb_vpns[i].pagesz = pagesz; -+ -+ asid++; -+ if (asid > cpu_asid_mask(¤t_cpu_data)) { -+ vpn += 1ULL << pagesz; -+ asid = 0; -+ } -+ } - } - - /* -@@ -527,14 +746,8 @@ static void __ref r4k_tlb_uniquify(void) - { - int tlbsize = current_cpu_data.tlbsize; - bool use_slab = slab_is_available(); -- int start = num_wired_entries(); - phys_addr_t tlb_vpn_size; -- unsigned long *tlb_vpns; -- unsigned long vpn_mask; -- int cnt, ent, idx, i; -- -- vpn_mask = GENMASK(cpu_vmbits - 1, 13); -- vpn_mask |= IS_ENABLED(CONFIG_64BIT) ? 3ULL << 62 : 1 << 31; -+ struct tlbent *tlb_vpns; - - tlb_vpn_size = tlbsize * sizeof(*tlb_vpns); - tlb_vpns = (use_slab ? -@@ -545,52 +758,13 @@ static void __ref r4k_tlb_uniquify(void) - - htw_stop(); - -- for (i = start, cnt = 0; i < tlbsize; i++, cnt++) { -- unsigned long vpn; -+ r4k_tlb_uniquify_read(tlb_vpns, tlbsize); - -- write_c0_index(i); -- mtc0_tlbr_hazard(); -- tlb_read(); -- tlb_read_hazard(); -- vpn = read_c0_entryhi(); -- vpn &= vpn_mask & PAGE_MASK; -- tlb_vpns[cnt] = vpn; -- -- /* Prevent any large pages from overlapping regular ones. */ -- write_c0_pagemask(read_c0_pagemask() & PM_DEFAULT_MASK); -- mtc0_tlbw_hazard(); -- tlb_write_indexed(); -- tlbw_use_hazard(); -- } -+ sort(tlb_vpns, tlbsize, sizeof(*tlb_vpns), r4k_entry_cmp, NULL); - -- sort(tlb_vpns, cnt, sizeof(tlb_vpns[0]), r4k_vpn_cmp, NULL); -+ r4k_tlb_uniquify_write(tlb_vpns, tlbsize); - - write_c0_pagemask(PM_DEFAULT_MASK); -- write_c0_entrylo0(0); -- write_c0_entrylo1(0); -- -- idx = 0; -- ent = tlbsize; -- for (i = start; i < tlbsize; i++) -- while (1) { -- unsigned long entryhi, vpn; -- -- entryhi = UNIQUE_ENTRYHI(ent); -- vpn = entryhi & vpn_mask & PAGE_MASK; -- -- if (idx >= cnt || vpn < tlb_vpns[idx]) { -- write_c0_entryhi(entryhi); -- write_c0_index(i); -- mtc0_tlbw_hazard(); -- tlb_write_indexed(); -- ent++; -- break; -- } else if (vpn == tlb_vpns[idx]) { -- ent++; -- } else { -- idx++; -- } -- } - - tlbw_use_hazard(); - htw_start(); diff --git a/queue-6.19/series b/queue-6.19/series index 63c169a684..a5239eba56 100644 --- a/queue-6.19/series +++ b/queue-6.19/series @@ -181,7 +181,6 @@ bluetooth-smp-force-responder-mitm-requirements-before-building-the-pairing-resp bluetooth-hci_sync-fix-stack-buffer-overflow-in-hci_le_big_create_sync.patch bluetooth-hci_event-move-wake-reason-storage-into-validated-event-handlers.patch ksmbd-fix-oob-write-in-query_info-for-compound-requests.patch -mips-mm-rewrite-tlb-uniquification-for-the-hidden-bit-feature.patch mips-sibyte-bring-back-cache-initialisation.patch mips-fix-the-gcc-version-check-for-__multi3-workaround.patch hwmon-occ-fix-division-by-zero-in-occ_show_power_1.patch