summaryrefslogtreecommitdiffstats
path: root/drivers/of/module.c
diff options
context:
space:
mode:
authorFuad Tabba <fuad.tabba@linux.dev>2026-09-22 19:14:30 +0100
committerWill Deacon <will@kernel.org>2026-09-23 12:34:30 +0000
commit2bc6b218717b9d08f466f88209251d54bc09b207 (patch)
treecb91c941eccb15f94cbec4e96aa3a9c8736b5d66 /drivers/of/module.c
downloadlinux-stable-2bc6b218717b9d08f466f88209251d54bc09b207.tar.gz
linux-stable-2bc6b218717b9d08f466f88209251d54bc09b207.zip
arm64/boot: Disable trapping of PMZR_EL0 writes to EL2grafted
__init_el2_fgt2() writes one mask to both HDFGRTR2_EL2 and HDFGWTR2_EL2. PMZR_EL0 is write-only, so its trap bit, nPMZR_EL0, exists only in HDFGWTR2_EL2 and is therefore never set: a PMZR_EL0 write from the host traps to EL2, where the nVHE hypervisor has no handler and BUG()s. The kernel never writes PMZR_EL0, but kernel.perf_user_access=1 has the PMU driver set PMUSERENR_EL0.UEN for a task with a user-read event, so a write from EL0 reaches the trap and takes the host down without a panic message. Accumulate the HDFGWTR2_EL2 bits separately, as __init_el2_fgt() already does for HDFGWTR_EL2, and set nPMZR_EL0 with the other FEAT_PMUv3p9 bits. Fixes: 858c7bfcb35e1 ("arm64/boot: Enable EL2 requirements for FEAT_PMUv3p9") Cc: stable@vger.kernel.org Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev> Reviewed-by: Anshuman Khandual <anshuman.khandual@arm.com> Reviewed-by: Oliver Upton <oupton@kernel.org> Signed-off-by: Will Deacon <will@kernel.org>
Diffstat (limited to 'drivers/of/module.c')
-rw-r--r--drivers/of/module.c81
1 files changed, 81 insertions, 0 deletions
diff --git a/drivers/of/module.c b/drivers/of/module.c
new file mode 100644
index 000000000..1e735fc13
--- /dev/null
+++ b/drivers/of/module.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Linux kernel module helpers.
+ */
+
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+ssize_t of_modalias(const struct device_node *np, char *str, ssize_t len)
+{
+ const char *compat;
+ char *c;
+ struct property *p;
+ ssize_t csize;
+ ssize_t tsize;
+
+ /*
+ * Prevent a kernel oops in vsnprintf() -- it only allows passing a
+ * NULL ptr when the length is also 0. Also filter out the negative
+ * lengths...
+ */
+ if ((len > 0 && !str) || len < 0)
+ return -EINVAL;
+
+ /* Name & Type */
+ /* %p eats all alphanum characters, so %c must be used here */
+ csize = snprintf(str, len, "of:N%pOFn%c%s", np, 'T',
+ of_node_get_device_type(np));
+ tsize = csize;
+ if (csize >= len)
+ csize = len > 0 ? len - 1 : 0;
+ len -= csize;
+ str += csize;
+
+ of_property_for_each_string(np, "compatible", p, compat) {
+ csize = snprintf(str, len, "C%s", compat);
+ tsize += csize;
+ if (csize >= len)
+ continue;
+ for (c = str; c; ) {
+ c = strchr(c, ' ');
+ if (c)
+ *c++ = '_';
+ }
+ len -= csize;
+ str += csize;
+ }
+
+ return tsize;
+}
+
+int of_request_module(const struct device_node *np)
+{
+ char *str;
+ ssize_t size;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ size = of_modalias(np, NULL, 0);
+ if (size < 0)
+ return size;
+
+ /* Reserve an additional byte for the trailing '\0' */
+ size++;
+
+ str = kmalloc(size, GFP_KERNEL);
+ if (!str)
+ return -ENOMEM;
+
+ of_modalias(np, str, size);
+ str[size - 1] = '\0';
+ ret = request_module(str);
+ kfree(str);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_request_module);