[PATCH v3 48/81] kconfig: Support writing separate SPL files

Simon Glass sjg at chromium.org
Mon Feb 6 20:05:16 CET 2023


At present kconfig writes out several files, including:

   auto.conf  - CONFIG settings used by make
   autoconf.h - header file used by C code

This works well but is a bit ugly in places, for example requiring the use
of a SPL_TPL_ macro in Makefiles to distinguish between options intended
for SPL and U-Boot proper.

Update the kconfig tool to also output separate files for each phase: e.g.
auto_spl.conf and autoconf_spl.h

These are similar to the existing files, but drop the SPL_ prefix so that
SPL_TPL_ is not needed. It also allows the CONFIG_IS_ENABLED() macro to be
simplified, in a later patch, eventually replacing it with IS_ENABLED().

When CONFIG_FOO is used within SPL, it means that FOO is enabled in that
SPL phase. For example if CONFIG_SPL_FOO is enabled in the Kconfig, that
means that CONFIG_FOO will be enabled in the SPL phase. So the SPL builds
can just use CONFIG_FOO to check it. There is no need to use
CONFIG_SPL_FOO or CONFIG_IS_ENABLED() anymore.

This of course means that if there is a need to access a PPL symbol from
an SPL build, there is no way to do it. To copy with that, we need a
CONFIG_PPL_FOO to be visibilty to all SPL builds.

So this change also adds new PPL_ output for U-Boot proper (Primary
Program Loader). So every CONFIG_FOO that is enabled in PPL also has a
CONFIG_PPL_FOO

This allows SPL to access the TEXT_BASE for U-Boot proper, for example, so
it knows where to load it. There are about 30 places where this is needed,
in addition to TEXT_BASE. The environment has the same problem, adding
another dozen or so caes in include/config_distro_bootcmd.h but it has
been decided to ignore that for now.

The feature is controlled by an environment variable, since it seems to be
bad form to add flags to the conf tool.

Rebuild the autoconf files if the split config is not present. This allows
building this commit as part of a chain, without generating build errors.

These changes may benefit from some reworking to send upstream, e.g. to
use a struct for the 'arg' parameter.

Signed-off-by: Simon Glass <sjg at chromium.org>
---

Changes in v3:
- Make sure that the config is rebuilt with this change

Changes in v2:
- Redo commit message
- Introduce CONFIG_PPL approach

 Makefile                    |   6 +
 scripts/kconfig/Makefile    |   5 +-
 scripts/kconfig/conf.c      |   6 +-
 scripts/kconfig/confdata.c  | 348 +++++++++++++++++++++++++++++++++++-
 scripts/kconfig/expr.h      |  27 ++-
 scripts/kconfig/lkc.h       |  10 +-
 scripts/kconfig/lkc_proto.h |  18 +-
 7 files changed, 406 insertions(+), 14 deletions(-)

diff --git a/Makefile b/Makefile
index 815ee0333d8..11fdc927531 100644
--- a/Makefile
+++ b/Makefile
@@ -597,6 +597,12 @@ ifeq ($(dot-config),1)
 # To avoid any implicit rule to kick in, define an empty command
 $(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;
 
+# Make sure this uses the split config
+has_split_config = $(shell grep CONFIG_PPL_LOCALVERSION include/config/auto.conf 2>/dev/null)
+ifeq ($(has_split_config),)
+include/config/auto.conf: FORCE
+endif
+
 # If .config is newer than include/config/auto.conf, someone tinkered
 # with it and forgot to run make oldconfig.
 # if auto.conf.cmd is missing then we are probably in a cleaned tree so
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 12e525ee31f..b539054645f 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -25,6 +25,8 @@ endif
 # We need this, in case the user has it in its environment
 unexport CONFIG_
 
+$(obj)/conf: $(srctree)/scripts/conf_nospl
+
 xconfig: $(obj)/qconf
 	$< $(silent) $(Kconfig)
 
@@ -71,8 +73,9 @@ simple-targets := oldconfig allnoconfig allyesconfig allmodconfig \
 	alldefconfig randconfig listnewconfig olddefconfig syncconfig
 PHONY += $(simple-targets)
 
+# Pass --u-boot to get SPL behaviour
 $(simple-targets): $(obj)/conf
-	$< $(silent) --$@ $(Kconfig)
+	KCONFIG_SPL=1 $< $(silent) --$@ $(Kconfig)
 
 PHONY += savedefconfig defconfig
 
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 376f796f674..c4463d252c1 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -490,9 +490,13 @@ int main(int ac, char **av)
 	const char *name, *defconfig_file = NULL /* gcc uninit */;
 	struct stat tmpstat;
 	int no_conf_write = 0;
+	bool support_spl;
 
 	tty_stdio = isatty(0) && isatty(1);
 
+	/* Special handling of SPL phases for U-Boot */
+	support_spl = getenv("KCONFIG_SPL");
+
 	while ((opt = getopt_long(ac, av, "s", long_opts, NULL)) != -1) {
 		if (opt == 's') {
 			conf_set_message_callback(NULL);
@@ -692,7 +696,7 @@ int main(int ac, char **av)
 			fprintf(stderr, "\n*** Error during writing of the configuration.\n\n");
 			exit(1);
 		}
-		if (conf_write_autoconf()) {
+		if (conf_write_autoconf(support_spl)) {
 			fprintf(stderr, "\n*** Error during update of the configuration.\n\n");
 			return 1;
 		}
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index 73bf43bcb95..c2f49bd87f3 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -16,6 +16,16 @@
 
 #include "lkc.h"
 
+/* Number of SPL prefixes we recognise */
+#define NUM_SPLS	 4
+
+/*
+ * SPL prefixes recognised. For example CONFIG_SPL_xxx is considered to be an
+ * SPL version of CONFIG_xxx
+ */
+static const char *spl_name[NUM_SPLS] = {"SPL", "TPL", "VPL", "TOOLS"};
+int num_spls;	/* 0 if not supporting SPL, else NUM_SPLS */
+
 /* return true if 'path' exists, false otherwise */
 static bool is_present(const char *path)
 {
@@ -531,6 +541,24 @@ static void print_makefile_sym(FILE *fp, const char *name,
 	fprintf(fp, "%s%s=%s\n", CONFIG_, name, value);
 }
 
+/**
+ * get_primary_name() - Prepends PPL to a symbol if it is an SPL symbol
+ *
+ * @sym: Symbol to process
+ * @str: Buffer space to use, of size SYMBOL_MAXLENGTH
+ * Returns: NULL if this is not an SPL symbol, otherwise returns the symbol
+ * name with PPL_ prepended
+ */
+static const char *get_primary_name(const struct symbol *sym, char *str)
+{
+	if (sym->flags & SYMBOL_SPL)
+		return NULL;
+
+	snprintf(str, SYMBOL_MAXLENGTH, "PPL_%s", sym->name);
+
+	return str;
+}
+
 /*
  * Kconfig configuration printer
  *
@@ -542,6 +570,15 @@ static void print_makefile_sym(FILE *fp, const char *name,
 static void
 kconfig_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
 {
+	char str[SYMBOL_MAXLENGTH];
+	const char *name;
+
+	if (num_spls) {
+		name = get_primary_name(sym, str);
+		if (name)
+			print_makefile_sym(fp, name, sym->type, value, arg);
+	}
+
 	print_makefile_sym(fp, sym->name, sym->type, value, arg != NULL);
 }
 
@@ -571,6 +608,89 @@ static struct conf_printer kconfig_printer_cb =
 	.print_comment = kconfig_print_comment,
 };
 
+/**
+ * get_spl_name() - Look up an SPL symbol
+ *
+ * This is used to get the name of a Kconfig option to write in an SPL context.
+ * If the symbol has an SPL symbol, this means it is used for U-Boot proper, so
+ * should not be written at all.
+ *
+ * Otherwise, this returns the name of the option. If the option is an SPL
+ * option, then the prefix (SPL_ or TPL_) is removed
+ *
+ * @sym: Symbol to look up
+ * @arg: Argument passed to the symbol function. This is void * but is actually
+ *	an int, indicating the SPL index / type (see spl_name[])
+ * @return name to write out for this symbol xxx:
+ *	NULL (don't write) if xxx has an associated SPL symbol
+ *	xxx if xxx is a non-SPL symbol
+ *	xxx if SPL_xxx is an SPL symbol
+ */
+static const char *get_spl_name(const struct symbol *sym, const void *arg)
+{
+	int spl = (long)arg;
+	const char *name = sym->name;
+
+	/*
+	 * Don't print it if this has an SPL symbol because processing of the
+	 * SPL symbol (e.g. SPL_FOO) will output CONFIG_FOO as well as
+	 * CONFIG_SPL_FOO
+	 */
+	if (sym->flags & SYMBOL_HAS_SPL)
+		return NULL;
+
+	/*
+	 * If it is SPL, only print it if the SPL_ prefix matches
+	 * Drop the prefix.
+	 */
+	if (sym->flags & SYMBOL_SPL) {
+		int len = strlen(spl_name[spl]);
+
+		if (!strncmp(name, spl_name[spl], len) && name[len] == '_')
+			name += len + 1;
+	}
+
+	return name;
+}
+
+/*
+ * auto.conf configuration printer for SPL
+ *
+ * This is used for creating auto.conf as well as SPL files like auto_spl.conf
+ *
+ * This printer is used when generating the resulting configuration after
+ * kconfig invocation and `defconfig' files. Unset symbol might be omitted by
+ * passing a non-NULL argument to the printer.
+ */
+static void spl_kconfig_print_symbol(FILE *fp, struct symbol *sym,
+				     const char *value, void *arg)
+{
+	char str[SYMBOL_MAXLENGTH];
+	const char *name;
+
+	name = get_primary_name(sym, str);
+	if (name)
+		print_makefile_sym(fp, name, sym->type, value, true);
+
+	name = get_spl_name(sym, arg);
+	if (!name)
+		return;
+
+	/*
+	 * If this is an SPL symbol, first print the symbol without the SPL
+	 * prefix
+	 */
+	if (name != sym->name)
+		print_makefile_sym(fp, name, sym->type, value, true);
+	if (!(sym->flags & SYMBOL_NO_SPL))
+		print_makefile_sym(fp, sym->name, sym->type, value, true);
+}
+
+static struct conf_printer spl_kconfig_printer_cb = {
+	.print_symbol = spl_kconfig_print_symbol,
+	.print_comment = kconfig_print_comment,
+};
+
 /* Print a symbol for a header file */
 static void print_header_sym(FILE *fp, const char *name, enum symbol_type type,
 			     const char *value)
@@ -620,6 +740,15 @@ static void print_header_sym(FILE *fp, const char *name, enum symbol_type type,
 static void
 header_print_symbol(FILE *fp, struct symbol *sym, const char *value, void *arg)
 {
+	char str[SYMBOL_MAXLENGTH];
+	const char *name;
+
+	if (num_spls) {
+		name = get_primary_name(sym, str);
+		if (name)
+			print_header_sym(fp, name, sym->type, value);
+	}
+
 	print_header_sym(fp, sym->name, sym->type, value);
 }
 
@@ -651,6 +780,37 @@ static struct conf_printer header_printer_cb =
 	.print_comment = header_print_comment,
 };
 
+/*
+ * SPL header printer
+ *
+ * This printer is used when generating SPL files such as
+ * `include/generated/autoconf_spl.h'
+ */
+static void spl_header_print_symbol(FILE *fp, struct symbol *sym,
+				    const char *value, void *arg)
+{
+	char str[SYMBOL_MAXLENGTH];
+	const char *name;
+
+	name = get_primary_name(sym, str);
+	if (name)
+		print_header_sym(fp, name, sym->type, value);
+
+	name = get_spl_name(sym, arg);
+	if (!name)
+		return;
+
+	if (name != sym->name)
+		print_header_sym(fp, name, sym->type, value);
+	if (!(sym->flags & SYMBOL_NO_SPL))
+		print_header_sym(fp, sym->name, sym->type, value);
+}
+
+static struct conf_printer spl_header_printer_cb = {
+	.print_symbol = spl_header_print_symbol,
+	.print_comment = header_print_comment,
+};
+
 /*
  * Tristate printer
  *
@@ -1022,15 +1182,24 @@ out:
 	return res;
 }
 
-int conf_write_autoconf(void)
+int conf_write_autoconf(bool support_spl)
 {
 	struct symbol *sym;
 	const char *name;
 	FILE *out, *tristate, *out_h;
-	int i;
+	FILE *out_spl[NUM_SPLS];
+	FILE *out_h_spl[NUM_SPLS];
+	int i, spl;
 
 	sym_clear_all_valid();
 
+	/* U-Boot: Mark symbols according to their SPL/non-SPL nature */
+	if (support_spl) {
+		num_spls = NUM_SPLS;
+		if (conf_mark_symbols())
+			return 1;
+	}
+
 	conf_write_dep("include/config/auto.conf.cmd");
 
 	if (conf_split_config())
@@ -1053,12 +1222,51 @@ int conf_write_autoconf(void)
 		return 1;
 	}
 
+	for (spl = 0; spl < num_spls; spl++) {
+		char fname[80];
+
+		snprintf(fname, sizeof(fname), ".tmpconfig_%s",
+			 spl_name[spl]);
+
+		out_spl[spl] = fopen(fname, "w");
+		if (!out_spl[spl]) {
+			while (spl--) {
+				fclose(out_spl[spl]);
+				fclose(out_h_spl[spl]);
+			}
+			fclose(out_h);
+			fclose(out);
+			fclose(tristate);
+			return 1;
+		}
+
+		snprintf(fname, sizeof(fname), ".tmpconfig_%s.h",
+			 spl_name[spl]);
+
+		out_h_spl[spl] = fopen(fname, "w");
+		if (!out_h_spl[spl]) {
+			fclose(out_spl[spl]);
+			while (spl--) {
+				fclose(out_spl[spl]);
+				fclose(out_h_spl[spl]);
+			}
+			fclose(out_h);
+			fclose(out);
+			fclose(tristate);
+			return 1;
+		}
+	}
+
 	conf_write_heading(out, &kconfig_printer_cb, NULL);
 
 	conf_write_heading(tristate, &tristate_printer_cb, NULL);
 
 	conf_write_heading(out_h, &header_printer_cb, NULL);
 
+	for (spl = 0; spl < num_spls; spl++)
+		conf_write_heading(out_h_spl[spl], &spl_header_printer_cb,
+				   (void *)(long)spl);
+
 	for_all_symbols(i, sym) {
 		sym_calc_value(sym);
 		if (!(sym->flags & SYMBOL_WRITE) || !sym->name)
@@ -1070,10 +1278,24 @@ int conf_write_autoconf(void)
 		conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1);
 
 		conf_write_symbol(out_h, sym, &header_printer_cb, NULL);
+
+		for (spl = 0; spl < num_spls; spl++) {
+			/* write make variables to auto_<spl>.conf */
+			conf_write_symbol(out_spl[spl], sym,
+					  &spl_kconfig_printer_cb,
+					  (void *)(long)spl);
+
+			/* write #defines to autoconf_<spl>.h */
+			conf_write_symbol(out_h_spl[spl], sym,
+					  &spl_header_printer_cb,
+					  (void *)(long)spl);
+		}
 	}
 	fclose(out);
 	fclose(tristate);
 	fclose(out_h);
+	for (spl = 0; spl < num_spls; spl++)
+		fclose(out_h_spl[spl]);
 
 	name = getenv("KCONFIG_AUTOHEADER");
 	if (!name)
@@ -1083,6 +1305,29 @@ int conf_write_autoconf(void)
 	if (rename(".tmpconfig.h", name))
 		return 1;
 
+	for (spl = 0; spl < num_spls; spl++) {
+		char tmpname[80], fname[80];
+		char *s;
+
+		snprintf(tmpname, sizeof(tmpname), ".tmpconfig_%s.h",
+			 spl_name[spl]);
+		snprintf(fname, sizeof(fname),
+			 "include/generated/autoconf_%s.h", spl_name[spl]);
+		for (s = fname; *s; s++)
+			*s = tolower(*s);
+		if (rename(tmpname, fname))
+			return 1;
+
+		snprintf(tmpname, sizeof(tmpname), ".tmpconfig_%s",
+			 spl_name[spl]);
+		snprintf(fname, sizeof(fname),
+			 "include/config/auto_%s.conf", spl_name[spl]);
+		for (s = fname; *s; s++)
+			*s = tolower(*s);
+		if (rename(tmpname, fname))
+			return 1;
+	}
+
 	name = getenv("KCONFIG_TRISTATE");
 	if (!name)
 		name = "include/config/tristate.conf";
@@ -1326,3 +1571,102 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode)
 
 	return has_changed;
 }
+
+static bool is_spl(const char *name, int *lenp)
+{
+	const char *uscore;
+	int len;
+	int i;
+
+	uscore = strchr(name, '_');
+	if (!uscore)
+		return false;
+
+	len = uscore - name;
+	for (i = 0; i < num_spls; i++) {
+		if (len == strlen(spl_name[i]) &&
+		    !strncmp(name, spl_name[i], len)) {
+			*lenp = len;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+void conf_mark_spl_symbols(void)
+{
+	struct symbol *sym;
+	int i;
+
+	for_all_symbols(i, sym) {
+		if (sym->name) {
+			int len;
+			bool spl = is_spl(sym->name, &len);
+
+			if (spl) {
+				struct symbol *non_spl;
+
+				sym->flags |= SYMBOL_SPL;
+				non_spl = sym_find(sym->name + len + 1);
+				if (non_spl)
+					non_spl->flags |= SYMBOL_HAS_SPL;
+			}
+		}
+	}
+}
+
+static int u_boot_read_conf_nospl(const char *fname)
+{
+	static char fullname[PATH_MAX + 1];
+	const char *env;
+	char buf[256];
+	FILE *f;
+	char *s;
+
+	env = getenv(SRCTREE);
+	if (!env) {
+		fprintf(stderr, "No %s environment variable\n", SRCTREE);
+		return 1;
+	}
+	snprintf(fullname, sizeof(fullname), "%s/scripts/%s", env, fname);
+	f = fopen(fullname, "r");
+	if (!f) {
+		fprintf(stderr, "Cannot open '%s'\n", fullname);
+		return 1;
+	}
+	while (s = fgets(buf, sizeof(buf), f), s) {
+		struct symbol *sym;
+		int len;
+
+		len = strlen(s);
+		if (len)
+			s[len - 1] = '\0';
+		if (*s == '#' || !*s)
+			continue;
+
+		sym = sym_find(s);
+		if (!sym) {
+			/*
+			 * perhaps we could drop these in config_nospl
+			 *
+			 * fprintf(stderr, "Unknown symbol from '%s': %s\n",
+			 *	fullname, s);
+			 */
+			continue;
+		}
+		sym->flags |= SYMBOL_NO_SPL;
+	}
+	fclose(f);
+
+	return 0;
+}
+
+int conf_mark_symbols(void)
+{
+	conf_mark_spl_symbols();
+	if (u_boot_read_conf_nospl("conf_nospl"))
+		return 1;
+
+	return 0;
+}
diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index 656c87fb4f3..934f4b10b25 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -158,14 +158,33 @@ struct symbol {
 /* Set symbol to y if allnoconfig; used for symbols that hide others */
 #define SYMBOL_ALLNOCONFIG_Y 0x200000
 
-/* U-Boot: Marks an SPL symbol */
+/*
+ * U-Boot: Marks an SPL symbol, i.e. one that starts with one of the strings in
+ * spl_name[]
+ */
 #define SYMBOL_SPL		0x400000
 
-/* U-Boot: Marks a non-SPL symbol that also has an SPL version */
+/*
+ * U-Boot: Marks a non-SPL symbol that also has an SPL version. This flag is
+ * added to symbols like FOO if there is also an SPL_FOO (or TPL_FOO, etc.)
+ */
 #define SYMBOL_HAS_SPL		0x800000
 
-/* U-Boot: Marks an-SPL symbol that does not have a non-SPL version */
-#define SYMBOL_SPL_ONLY		0x1000000
+/*
+ * U-Boot: Marks a symbol with no SPL version. These symbols cannot be computed
+ * by looking at the Kconfig, so there is a conf_nospl file which holds s list.
+ *
+ * Generally, options which have no SPL_ prefix (e.g. CONFIG_FOO) apply to all
+ * SPL build phases. This allows things like ARCH_ARM to propagate to all builds
+ * without the hassle of generating a separate SPL version fo each phase. But in
+ * some cases this is not wanted.
+ *
+ * This file lists options which don't have an SPL equivalent, but still should
+ * not be enabled in SPL builds. It is necessary since kconfig cannot tell (just
+ * by looking at the Kconfig description) whether it applies to Proper builds
+ * only, or to all builds.
+ */
+#define SYMBOL_NO_SPL		0x1000000
 
 #define SYMBOL_MAXLENGTH	256
 #define SYMBOL_HASHSIZE		9973
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index dec03cc927a..592610483f0 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -72,13 +72,13 @@ bool conf_set_all_new_symbols(enum conf_def_mode mode);
 void set_all_choice_values(struct symbol *csym);
 
 /**
- * conf_mark_spl_symbols() - Mark SPL symbols
+ * conf_mark_symbols() - Mark symbols with U-Boot flags
  *
- * Symbols which don't start with SPL_ (TPL_, etc.) but have an SPL version
- * should be marked with the SYMBOL_SPL flag, so we know to avoid writing them
- * in the SPL autoconf.h files.
+ * Symbols which don't have an SPL symbol are marked with SYMBOL_PROPER_ONLY and
+ * those with only SPL symbols are marked withSYMBOL_SPL_ONLY, so we know to
+ * avoid writing them to the wrong autoconf.h files.
  */
-void conf_mark_spl_symbols(void);
+int conf_mark_symbols(void);
 
 /* confdata.c and expr.c */
 static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
index cf4510a2bdc..4707a9155f7 100644
--- a/scripts/kconfig/lkc_proto.h
+++ b/scripts/kconfig/lkc_proto.h
@@ -7,7 +7,23 @@ int conf_read(const char *name);
 int conf_read_simple(const char *name, int);
 int conf_write_defconfig(const char *name);
 int conf_write(const char *name);
-int conf_write_autoconf(void);
+
+/**
+ * conf_write_autoconf() - Write out the autoconf files
+ *
+ * @support_spl: Support generation of files for additional secondary
+ *	program-loader builds, as used by U-Boot
+ *
+ * Writes out:
+ *    auto.conf which contains CONFIG definitions for inclusion by make
+ *    autoconf.h which contains CONFIG definitions for inclusion by C code
+ *
+ * if @support_spl then separate files are also created for SPL builds (spl,
+ * tpl, vpl, tools), with symbols like SPL_FOO being written to the SPL file
+ * as just FOO
+ */
+int conf_write_autoconf(bool support_spl);
+
 bool conf_get_changed(void);
 void conf_set_changed_callback(void (*fn)(void));
 void conf_set_message_callback(void (*fn)(const char *s));
-- 
2.39.1.519.gcb327c4b5f-goog



More information about the U-Boot mailing list