Commit f9afce4f authored by Kees Cook's avatar Kees Cook
Browse files

kconfig: Add transitional symbol attribute for migration support



During kernel option migrations (e.g. CONFIG_CFI_CLANG to CONFIG_CFI),
existing .config files need to maintain backward compatibility while
preventing deprecated options from appearing in newly generated
configurations. This is challenging with existing Kconfig mechanisms
because:

1. Simply removing old options breaks existing .config files.
2. Manually listing an option as "deprecated" leaves it needlessly
   visible and still writes them to new .config files.
3. Using any method to remove visibility (.e.g no 'prompt', 'if n',
   etc) prevents the option from being processed at all.

Add a "transitional" attribute that creates symbols which are:
- Processed during configuration (can influence other symbols' defaults)
- Hidden from user menus (no prompts appear)
- Omitted from newly written .config files (gets migrated)
- Restricted to only having help sections (no defaults, selects, etc)
  making it truly just a "prior value pass-through" option.

The transitional syntax requires a type argument and prevents type
redefinition:

    config NEW_OPTION
        bool "New option"
        default OLD_OPTION

    config OLD_OPTION
        bool
        transitional
        help
          Transitional config for OLD_OPTION migration.

This allows seamless migration: olddefconfig processes existing
CONFIG_OLD_OPTION=y settings to enable CONFIG_NEW_OPTION=y, while
CONFIG_OLD_OPTION is omitted from newly generated .config files.

Added positive and negative testing via "testconfig" make target.

Co-developed-by: default avatarVegard Nossum <vegard.nossum@oracle.com>
Signed-off-by: default avatarVegard Nossum <vegard.nossum@oracle.com>
Reviewed-by: default avatarNathan Chancellor <nathan@kernel.org>
Tested-by: default avatarNathan Chancellor <nathan@kernel.org>
Link: https://lore.kernel.org/r/20250923213422.1105654-2-kees@kernel.org


Signed-off-by: default avatarKees Cook <kees@kernel.org>
parent 64f4ea20
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -232,6 +232,38 @@ applicable everywhere (see syntax).
  enables the third modular state for all config symbols.
  At most one symbol may have the "modules" option set.

- transitional attribute: "transitional"
  This declares the symbol as transitional, meaning it should be processed
  during configuration but omitted from newly written .config files.
  Transitional symbols are useful for backward compatibility during config
  option migrations - they allow olddefconfig to process existing .config
  files while ensuring the old option doesn't appear in new configurations.

  A transitional symbol:
  - Has no prompt (is not visible to users in menus)
  - Is processed normally during configuration (values are read and used)
  - Can be referenced in default expressions of other symbols
  - Is not written to new .config files
  - Cannot have any other properties (it is a pass-through option)

  Example migration from OLD_NAME to NEW_NAME::

    config NEW_NAME
	bool "New option name"
	default OLD_NAME
	help
	  This replaces the old CONFIG_OLD_NAME option.

    config OLD_NAME
	bool
	transitional
	help
	  Transitional config for OLD_NAME to NEW_NAME migration.

  With this setup, existing .config files with "CONFIG_OLD_NAME=y" will
  result in "CONFIG_NEW_NAME=y" being set, while CONFIG_OLD_NAME will be
  omitted from newly written .config files.

Menu dependencies
-----------------

+1 −0
Original line number Diff line number Diff line
@@ -145,6 +145,7 @@ struct symbol {
#define SYMBOL_CONST      0x0001  /* symbol is const */
#define SYMBOL_CHECK      0x0008  /* used during dependency checking */
#define SYMBOL_VALID      0x0080  /* set when symbol.curr is calculated */
#define SYMBOL_TRANS      0x0100  /* symbol is transitional only (not visible)*/
#define SYMBOL_WRITE      0x0200  /* write symbol to file (KCONFIG_CONFIG) */
#define SYMBOL_WRITTEN    0x0800  /* track info to avoid double-write to .config */
#define SYMBOL_CHECKED    0x2000  /* used during dependency checking */
+1 −0
Original line number Diff line number Diff line
@@ -126,6 +126,7 @@ n [A-Za-z0-9_-]
"select"		return T_SELECT;
"source"		return T_SOURCE;
"string"		return T_STRING;
"transitional"		return T_TRANSITIONAL;
"tristate"		return T_TRISTATE;
"visible"		return T_VISIBLE;
"||"			return T_OR;
+47 −0
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ struct menu *current_menu, *current_entry, *current_choice;
%token T_SELECT
%token T_SOURCE
%token T_STRING
%token T_TRANSITIONAL
%token T_TRISTATE
%token T_VISIBLE
%token T_EOL
@@ -205,6 +206,12 @@ config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL
	printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno);
};

config_option: T_TRANSITIONAL T_EOL
{
	current_entry->sym->flags |= SYMBOL_TRANS;
	printd(DEBUG_PARSE, "%s:%d:transitional\n", cur_filename, cur_lineno);
};

config_option: default expr if_expr T_EOL
{
	menu_add_expr(P_DEFAULT, $2, $3);
@@ -482,6 +489,43 @@ assign_val:

%%

/**
 * transitional_check_sanity - check transitional symbols have no other
 *			       properties
 *
 * @menu: menu of the potentially transitional symbol
 *
 * Return: -1 if an error is found, 0 otherwise.
 */
static int transitional_check_sanity(const struct menu *menu)
{
	struct property *prop;

	if (!menu->sym || !(menu->sym->flags & SYMBOL_TRANS))
		return 0;

	/* Check for depends and visible conditions. */
	if ((menu->dep && !expr_is_yes(menu->dep)) ||
	    (menu->visibility && !expr_is_yes(menu->visibility))) {
		fprintf(stderr, "%s:%d: error: %s",
			menu->filename, menu->lineno,
			"transitional symbols can only have help sections\n");
		return -1;
	}

	/* Check for any property other than "help". */
	for (prop = menu->sym->prop; prop; prop = prop->next) {
		if (prop->type != P_COMMENT) {
			fprintf(stderr, "%s:%d: error: %s",
				prop->filename, prop->lineno,
				"transitional symbols can only have help sections\n");
			return -1;
		}
	}

	return 0;
}

/**
 * choice_check_sanity - check sanity of a choice member
 *
@@ -558,6 +602,9 @@ void conf_parse(const char *name)
		if (menu->sym && sym_check_deps(menu->sym))
			yynerrs++;

		if (transitional_check_sanity(menu))
			yynerrs++;

		if (menu->sym && sym_is_choice(menu->sym)) {
			menu_for_each_sub_entry(child, menu)
				if (child->sym && choice_check_sanity(child))
+6 −1
Original line number Diff line number Diff line
@@ -214,6 +214,11 @@ static void sym_calc_visibility(struct symbol *sym)
	struct property *prop;
	tristate tri;

	if (sym->flags & SYMBOL_TRANS) {
		sym->visible = yes;
		return;
	}

	/* any prompt visible? */
	tri = no;
	for_all_prompts(sym, prop) {
@@ -526,7 +531,7 @@ void sym_calc_value(struct symbol *sym)
		}
	}

	if (sym_is_choice(sym))
	if (sym_is_choice(sym) || sym->flags & SYMBOL_TRANS)
		sym->flags &= ~SYMBOL_WRITE;
}

Loading