All of lore.kernel.org
 help / color / mirror / Atom feed
From: Chen Pei <cp0613@linux.alibaba.com>
To: opensbi@lists.infradead.org
Cc: anup@brainfault.org, atishp@rivosinc.com, wangxiang@iscas.ac.cn,
	ganboing@gmail.com, guoren@kernel.org
Subject: [PATCH v2] lib/utils/fdt: Reserve exactly enough FDT space for fdt_cpu_fixup
Date: Mon, 17 Aug 2026 16:43:03 +0800	[thread overview]
Message-ID: <20260817084303.1232-1-cp0613@linux.alibaba.com> (raw)

fdt_cpu_fixup() reserves a hardcoded 32 extra bytes, plus 16 more per
HART inside the "zicntr" loop. Those 32 bytes also have to cover every
"status" property rewritten to "disabled", so once more than a handful
of HARTs are disabled the fixups fail with FDT_ERR_NOSPACE (-3).

Bumping the reservation to a fixed worst case is not safe either:
fdt_open_into() cannot know how much space the caller's buffer really
has, it just trusts the requested size. As OpenSBI does not own the
memory following a device tree passed in by the previous booting stage,
over-declaring the blob risks corrupting whatever follows it.

Walk the HART nodes once without modifying them instead, and reserve
exactly what the fixups need. The read-only decision logic is factored
into fdt_cpu_fixup_needed() so the sizing and fixup passes cannot drift
apart, which also makes the per-HART fdt_open_into() redundant. Check
the return values of fdt_setprop_string() and fdt_appendprop_string()
as well, as running out of space used to fail silently.

Fixes: dd9439fbace2 ("lib: utils: Add a fdt_cpu_fixup() helper")
Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
---
Changes in v2:
- Scan the HART nodes and reserve only what the fixups need, instead of a
  fixed SBI_HARTMASK_MAX_BITS * 20 bytes: fdt_open_into() just trusts the
  requested size, so over-declaring a device tree owned by the previous
  booting stage may corrupt what follows it.
- Fix the per-HART estimate; a missing "status" counts as enabled, so the
  property may have to be created rather than overwritten.
- Add the read-only helper fdt_cpu_fixup_needed() so the sizing and fixup
  passes cannot drift apart.
- Check the fdt_setprop_string()/fdt_appendprop_string() return values.
- Re-resolve /cpus after fdt_open_into() and skip it when there is nothing
  to fix.
- Drop the dependency on SBI_HARTMASK_MAX_BITS.

 lib/utils/fdt/fdt_fixup.c | 133 +++++++++++++++++++++++++++-----------
 1 file changed, 95 insertions(+), 38 deletions(-)

diff --git a/lib/utils/fdt/fdt_fixup.c b/lib/utils/fdt/fdt_fixup.c
index b0ed20c8..23899c0e 100644
--- a/lib/utils/fdt/fdt_fixup.c
+++ b/lib/utils/fdt/fdt_fixup.c
@@ -106,14 +106,65 @@ int fdt_add_cpu_idle_states(void *fdt, const struct sbi_cpu_idle_state *state)
 	return 0;
 }
 
-void fdt_cpu_fixup(void *fdt)
+/**
+ * Report which fixups a HART DT node needs. Read-only, so the sizing pass
+ * and the fixup pass below stay in sync by construction.
+ */
+static void fdt_cpu_fixup_needed(const void *fdt, int cpu_offset,
+				 bool emulated_zicntr, bool *disable,
+				 bool *add_zicntr)
 {
-	struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
 	struct sbi_domain *dom = sbi_domain_thishart_ptr();
-	int err, cpu_offset, cpus_offset, len;
 	const char *mmu_type, *extensions;
 	u32 hartid, hartindex;
-	bool emulated_zicntr;
+	int len;
+
+	*disable = false;
+	*add_zicntr = false;
+
+	if (fdt_parse_hart_id(fdt, cpu_offset, &hartid))
+		return;
+
+	if (!fdt_node_is_enabled(fdt, cpu_offset))
+		return;
+
+	/*
+	 * Disable a HART DT node if one of the following is true:
+	 * 1. The HART is not assigned to the current domain
+	 * 2. MMU is not available for the HART
+	 */
+
+	hartindex = sbi_hartid_to_hartindex(hartid);
+	mmu_type = fdt_getprop(fdt, cpu_offset, "mmu-type", &len);
+	if (!sbi_domain_is_assigned_hart(dom, hartindex) || !mmu_type || !len)
+		*disable = true;
+
+	if (!emulated_zicntr)
+		return;
+
+	extensions = fdt_getprop(fdt, cpu_offset, "riscv,isa-extensions", &len);
+	/*
+	 * For legacy devicetrees, don't create riscv,isa-extensions
+	 * property if there hasn't been already one.
+	 */
+	if (extensions && !fdt_stringlist_contains(extensions, len, "zicntr"))
+		*add_zicntr = true;
+}
+
+void fdt_cpu_fixup(void *fdt)
+{
+	struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
+	bool emulated_zicntr, disable, add_zicntr;
+	int err, cpu_offset, cpus_offset;
+	int reserve = 0;
+	/*
+	 * A new "status" property costs a header, its tag-aligned value and its
+	 * name; appending "zicntr" only grows an existing tag-aligned value.
+	 */
+	const int status_size = sizeof(struct fdt_property) +
+				ROUNDUP(sizeof("disabled"), FDT_TAGSIZE) +
+				sizeof("status");
+	const int zicntr_size = ROUNDUP(sizeof("zicntr"), FDT_TAGSIZE);
 
 	/*
 	 * Claim Zicntr extension in riscv,isa-extensions if
@@ -124,52 +175,58 @@ void fdt_cpu_fixup(void *fdt)
 			  sbi_hart_has_csr(scratch, SBI_HART_CSR_CYCLE) &&
 			  sbi_hart_has_csr(scratch, SBI_HART_CSR_INSTRET);
 
-	err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 32);
-	if (err < 0)
-		return;
-
 	cpus_offset = fdt_path_offset(fdt, "/cpus");
 	if (cpus_offset < 0)
 		return;
 
+	/*
+	 * fdt_open_into() trusts the size it is given, so reserve only what is
+	 * really needed instead of a fixed worst case for every possible HART.
+	 */
 	fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
-		err = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
-		if (err)
-			continue;
+		fdt_cpu_fixup_needed(fdt, cpu_offset, emulated_zicntr,
+				     &disable, &add_zicntr);
+		if (disable)
+			reserve += status_size;
+		if (add_zicntr)
+			reserve += zicntr_size;
+	}
 
-		if (!fdt_node_is_enabled(fdt, cpu_offset))
-			continue;
+	if (!reserve)
+		return;
 
-		/*
-		 * Disable a HART DT node if one of the following is true:
-		 * 1. The HART is not assigned to the current domain
-		 * 2. MMU is not available for the HART
-		 */
+	err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + reserve);
+	if (err < 0)
+		return;
 
-		hartindex = sbi_hartid_to_hartindex(hartid);
-		mmu_type = fdt_getprop(fdt, cpu_offset, "mmu-type", &len);
-		if (!sbi_domain_is_assigned_hart(dom, hartindex) ||
-		    !mmu_type || !len)
-			fdt_setprop_string(fdt, cpu_offset, "status",
-					   "disabled");
+	/* fdt_open_into() may have reordered the blocks, so look up again. */
+	cpus_offset = fdt_path_offset(fdt, "/cpus");
+	if (cpus_offset < 0)
+		return;
 
-		if (!emulated_zicntr)
-			continue;
+	fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
+		fdt_cpu_fixup_needed(fdt, cpu_offset, emulated_zicntr,
+				     &disable, &add_zicntr);
 
-		extensions = fdt_getprop(fdt, cpu_offset,
-					 "riscv,isa-extensions", &len);
-		/*
-		 * For legacy devicetrees, don't create riscv,isa-extensions
-		 * property if there hasn't been already one.
-		 */
-		if (extensions &&
-		    !fdt_stringlist_contains(extensions, len, "zicntr")) {
-			err = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + 16);
+		if (disable) {
+			err = fdt_setprop_string(fdt, cpu_offset, "status",
+						 "disabled");
 			if (err)
-				continue;
+				sbi_printf("%s: failed to disable %s (%d)\n",
+					   __func__,
+					   fdt_get_name(fdt, cpu_offset, NULL),
+					   err);
+		}
 
-			fdt_appendprop_string(fdt, cpu_offset,
-					      "riscv,isa-extensions", "zicntr");
+		if (add_zicntr) {
+			err = fdt_appendprop_string(fdt, cpu_offset,
+						    "riscv,isa-extensions",
+						    "zicntr");
+			if (err)
+				sbi_printf("%s: failed to add zicntr to %s (%d)\n",
+					   __func__,
+					   fdt_get_name(fdt, cpu_offset, NULL),
+					   err);
 		}
 	}
 }
-- 
2.50.1


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

                 reply	other threads:[~2026-08-17  8:43 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260817084303.1232-1-cp0613@linux.alibaba.com \
    --to=cp0613@linux.alibaba.com \
    --cc=anup@brainfault.org \
    --cc=atishp@rivosinc.com \
    --cc=ganboing@gmail.com \
    --cc=guoren@kernel.org \
    --cc=opensbi@lists.infradead.org \
    --cc=wangxiang@iscas.ac.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.