util/kconfig: Uprev to Linux 6.6's kconfig

Upstream reimplemented KCONFIG_STRICT, just calling it KCONFIG_WERROR.
Therefore, adapt our build system and documentation. Upstream is less
strict at this time, but there's a proposed patch that got imported.

TEST=`util/abuild/abuild -C` output (config.h and
config.build) remains the same. Also, the failure type fixed in
https://review.coreboot.org/c/coreboot/+/11272 can be detected,
which I tested by manually breaking our Kconfig in a similar way.

Change-Id: I322fb08a2f7308b93cff71a5dd4136f1a998773b
Signed-off-by: Patrick Georgi <patrick@coreboot.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/79259
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Martin L Roth <gaumless@gmail.com>
Reviewed-by: Felix Singer <service+coreboot-gerrit@felixsinger.de>
diff --git a/Documentation/getting_started/kconfig.md b/Documentation/getting_started/kconfig.md
index 5be4577..d40a4cc 100644
--- a/Documentation/getting_started/kconfig.md
+++ b/Documentation/getting_started/kconfig.md
@@ -69,9 +69,6 @@
 These variables were added to Kconfig specifically for coreboot and are not
 included in the Linux version.
 
-- KCONFIG_STRICT=value. Define to enable warnings as errors.   This is enabled
-  in coreboot, and should not be changed.
-
 - KCONFIG_NEGATIVES=value. Define to show negative values in the autoconf.h file
   (build/config.h). This is enabled in coreboot, and should not be changed.
 
@@ -102,6 +99,9 @@
 - KCONFIG_SPLITCONFIG=”directory name for individual SYMBOL.h files”.
   coreboot sets this to $(obj)/config.
 
+- KCONFIG_WERROR=value. Define to enable warnings as errors. This is enabled
+  in coreboot, and should not be changed.
+
 #### Used only for ‘make menuconfig’
 - MENUCONFIG_MODE=single_menu.  Set to "single_menu" to enable.  All other
   values disable the option.  This makes submenus appear below the menu option
@@ -1160,10 +1160,6 @@
 - coreboot has added the glob operator '*' for the 'source' keyword.
 - coreboot’s Kconfig always defines variables except for strings. In other
   Kconfig implementations, bools set to false/0/no are not defined.
-- coreboot’s version of Kconfig adds the KCONFIG_STRICT environment variable to
-  error out if there are any issues in the Kconfig files.  In the Linux kernel,
-  Kconfig will generate a warning, but will still output an updated .config or
-  config.h file.
 
 
 ## Kconfig Editor Highlighting
diff --git a/Makefile b/Makefile
index 29597c8..5ed6933 100644
--- a/Makefile
+++ b/Makefile
@@ -29,13 +29,15 @@
 KCONFIG_SPLITCONFIG := $(obj)/config/
 KCONFIG_TRISTATE := $(obj)/tristate.conf
 KCONFIG_NEGATIVES := 1
-KCONFIG_STRICT := 1
+KCONFIG_WERROR:= 1
+KCONFIG_WARN_UNKNOWN_SYMBOLS:= 1
 KCONFIG_PACKAGE := CB.Config
 KCONFIG_MAKEFILE_REAL ?= $(objk)/Makefile.real
 
 COREBOOT_EXPORTS += KCONFIG_CONFIG KCONFIG_AUTOHEADER KCONFIG_AUTOCONFIG
 COREBOOT_EXPORTS += KCONFIG_DEPENDENCIES KCONFIG_SPLITCONFIG KCONFIG_TRISTATE
-COREBOOT_EXPORTS += KCONFIG_NEGATIVES KCONFIG_STRICT
+COREBOOT_EXPORTS += KCONFIG_NEGATIVES KCONFIG_WERROR
+COREBOOT_EXPORTS += KCONFIG_WARN_UNKNOWN_SYMBOLS
 COREBOOT_EXPORTS += KCONFIG_AUTOADS KCONFIG_PACKAGE
 
 # Make does not offer a recursive wildcard function, so here's one:
diff --git a/util/kconfig/Makefile b/util/kconfig/Makefile
index af1c961..4eee155 100644
--- a/util/kconfig/Makefile
+++ b/util/kconfig/Makefile
@@ -93,11 +93,13 @@
 %_defconfig: $(obj)/conf
 	$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
 
-configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/configs/$@)
+configfiles = $(wildcard $(srctree)/kernel/configs/$(1) $(srctree)/arch/$(SRCARCH)/configs/$(1))
+all-config-fragments = $(call configfiles,*.config)
+config-fragments = $(call configfiles,$@)
 
 %.config: $(obj)/conf
-	$(if $(call configfiles),, $(error No configuration exists for this target on this architecture))
-	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(configfiles)
+	$(if $(config-fragments),, $(error $@ fragment does not exists on this architecture))
+	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(config-fragments)
 	$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
 
 PHONY += tinyconfig
@@ -115,6 +117,7 @@
 
 # Help text used by make help
 help:
+	@echo  'Configuration targets:'
 	@echo  '  config	  - Update current config utilising a line-oriented program'
 	@echo  '  nconfig         - Update current config utilising a ncurses menu based program'
 	@echo  '  menuconfig	  - Update current config utilising a menu based program'
@@ -141,6 +144,12 @@
 	@echo  '                    default value without prompting'
 	@echo  '  tinyconfig	  - Configure the tiniest possible kernel'
 	@echo  '  testconfig	  - Run Kconfig unit tests (requires python3 and pytest)'
+	@echo  ''
+	@echo  'Configuration topic targets:'
+	@$(foreach f, $(all-config-fragments), \
+		if help=$$(grep -m1 '^# Help: ' $(f)); then \
+			printf '  %-25s - %s\n' '$(notdir $(f))' "$${help#*: }"; \
+		fi;)
 
 # ===========================================================================
 # object files used by all kconfig flavours
diff --git a/util/kconfig/conf.c b/util/kconfig/conf.c
index fd33011..d91f5a2 100644
--- a/util/kconfig/conf.c
+++ b/util/kconfig/conf.c
@@ -16,8 +16,6 @@
 
 #include "lkc.h"
 
-int kconfig_warnings = 0;
-
 static void conf(struct menu *menu);
 static void check_conf(struct menu *menu);
 
@@ -722,7 +720,6 @@
 	const char *progname = av[0];
 	int opt;
 	const char *name, *defconfig_file = NULL /* gcc uninit */;
-	char *env;
 	int no_conf_write = 0;
 
 	tty_stdio = isatty(0) && isatty(1);
@@ -830,13 +827,6 @@
 		break;
 	}
 
-	env = getenv("KCONFIG_STRICT");
-	if (env && *env && kconfig_warnings) {
-		fprintf(stderr, "\n*** ERROR: %d warnings encountered, and "
-			"warnings are errors.\n\n", kconfig_warnings);
-		exit(1);
-	}
-
 	if (sync_kconfig) {
 		name = getenv("KCONFIG_NOSILENTUPDATE");
 		if (name && *name) {
diff --git a/util/kconfig/confdata.c b/util/kconfig/confdata.c
index b5c6da3..a6ee708 100644
--- a/util/kconfig/confdata.c
+++ b/util/kconfig/confdata.c
@@ -370,7 +370,11 @@
 	char *p, *p2;
 	struct symbol *sym;
 	int i, def_flags;
+	const char *warn_unknown;
+	const char *werror;
 
+	warn_unknown = getenv("KCONFIG_WARN_UNKNOWN_SYMBOLS");
+	werror = getenv("KCONFIG_WERROR");
 	if (name) {
 		in = zconf_fopen(name);
 	} else {
@@ -458,9 +462,10 @@
 			if (def == S_DEF_USER) {
 				sym = sym_find(line + 2 + strlen(CONFIG_));
 				if (!sym) {
-					conf_message(
-						"ignoring nonexistent symbol %s",
-						line + 2 + strlen(CONFIG_));
+					if (warn_unknown)
+						conf_warning("unknown symbol: %s",
+							     line + 2 + strlen(CONFIG_));
+
 					conf_set_changed(true);
 					continue;
 				}
@@ -495,7 +500,7 @@
 
 			sym = sym_find(line + strlen(CONFIG_));
 			if (!sym) {
-				if (def == S_DEF_AUTO)
+				if (def == S_DEF_AUTO) {
 					/*
 					 * Reading from include/config/auto.conf
 					 * If CONFIG_FOO previously existed in
@@ -503,8 +508,13 @@
 					 * include/config/FOO must be touched.
 					 */
 					conf_touch_dep(line + strlen(CONFIG_));
-				else
+				} else {
+					if (warn_unknown)
+						conf_warning("unknown symbol: %s",
+							     line + strlen(CONFIG_));
+
 					conf_set_changed(true);
+				}
 				continue;
 			}
 
@@ -544,7 +554,10 @@
 	free(line);
 	fclose(in);
 
-	kconfig_warnings += conf_warnings;
+	if (conf_warnings && werror) {
+		fprintf(stderr, "\nERROR: %d warnings encountered, and warnings are errors.\n\n", conf_warnings);
+		exit(1);
+	}
 
 	return 0;
 }
diff --git a/util/kconfig/expr.h b/util/kconfig/expr.h
index 9c9caca..4a9a23b 100644
--- a/util/kconfig/expr.h
+++ b/util/kconfig/expr.h
@@ -275,7 +275,6 @@
 	struct list_head entries;
 	size_t offset;
 	struct menu *target;
-	int index;
 };
 
 extern struct file *file_list;
diff --git a/util/kconfig/lkc.h b/util/kconfig/lkc.h
index e0698d4..2bdee9c 100644
--- a/util/kconfig/lkc.h
+++ b/util/kconfig/lkc.h
@@ -40,9 +40,6 @@
 int zconf_lineno(void);
 const char *zconf_curname(void);
 
-/* conf.c */
-extern int kconfig_warnings;
-
 /* confdata.c */
 const char *conf_get_configname(void);
 void set_all_choice_values(struct symbol *csym);
@@ -105,6 +102,7 @@
 struct menu *menu_get_parent_menu(struct menu *menu);
 bool menu_has_help(struct menu *menu);
 const char *menu_get_help(struct menu *menu);
+int get_jump_key_char(void);
 struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head);
 void menu_get_ext_help(struct menu *menu, struct gstr *help);
 
diff --git a/util/kconfig/lxdialog/dialog.h b/util/kconfig/lxdialog/dialog.h
index 347daf2..a501abf 100644
--- a/util/kconfig/lxdialog/dialog.h
+++ b/util/kconfig/lxdialog/dialog.h
@@ -196,13 +196,9 @@
 int dialog_yesno(const char *title, const char *prompt, int height, int width);
 int dialog_msgbox(const char *title, const char *prompt, int height,
 		  int width, int pause);
-
-
-typedef void (*update_text_fn)(char *buf, size_t start, size_t end, void
-			       *_data);
-int dialog_textbox(const char *title, char *tbuf, int initial_height,
-		   int initial_width, int *keys, int *_vscroll, int *_hscroll,
-		   update_text_fn update_text, void *data);
+int dialog_textbox(const char *title, const char *tbuf, int initial_height,
+		   int initial_width, int *_vscroll, int *_hscroll,
+		   int (*extra_key_cb)(int, size_t, size_t, void *), void *data);
 int dialog_menu(const char *title, const char *prompt,
 		const void *selected, int *s_scroll);
 int dialog_checklist(const char *title, const char *prompt, int height,
diff --git a/util/kconfig/lxdialog/textbox.c b/util/kconfig/lxdialog/textbox.c
index bc4d4fb..058ed0e 100644
--- a/util/kconfig/lxdialog/textbox.c
+++ b/util/kconfig/lxdialog/textbox.c
@@ -10,8 +10,8 @@
 
 static int hscroll;
 static int begin_reached, end_reached, page_length;
-static char *buf;
-static char *page;
+static const char *buf, *page;
+static size_t start, end;
 
 /*
  * Go back 'n' lines in text. Called by dialog_textbox().
@@ -98,21 +98,10 @@
 /*
  * Print a new page of text.
  */
-static void print_page(WINDOW *win, int height, int width, update_text_fn
-		       update_text, void *data)
+static void print_page(WINDOW *win, int height, int width)
 {
 	int i, passed_end = 0;
 
-	if (update_text) {
-		char *end;
-
-		for (i = 0; i < height; i++)
-			get_line();
-		end = page;
-		back_lines(height);
-		update_text(buf, page - buf, end - buf, data);
-	}
-
 	page_length = 0;
 	for (i = 0; i < height; i++) {
 		print_line(win, i, width);
@@ -142,24 +131,26 @@
  * refresh window content
  */
 static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
-			     int cur_y, int cur_x, update_text_fn update_text,
-			     void *data)
+			     int cur_y, int cur_x)
 {
-	print_page(box, boxh, boxw, update_text, data);
+	start = page - buf;
+
+	print_page(box, boxh, boxw);
 	print_position(dialog);
 	wmove(dialog, cur_y, cur_x);	/* Restore cursor position */
 	wrefresh(dialog);
+
+	end = page - buf;
 }
 
 /*
  * Display text from a file in a dialog box.
  *
  * keys is a null-terminated array
- * update_text() may not add or remove any '\n' or '\0' in tbuf
  */
-int dialog_textbox(const char *title, char *tbuf, int initial_height,
-		   int initial_width, int *keys, int *_vscroll, int *_hscroll,
-		   update_text_fn update_text, void *data)
+int dialog_textbox(const char *title, const char *tbuf, int initial_height,
+		   int initial_width, int *_vscroll, int *_hscroll,
+		   int (*extra_key_cb)(int, size_t, size_t, void *), void *data)
 {
 	int i, x, y, cur_x, cur_y, key = 0;
 	int height, width, boxh, boxw;
@@ -239,8 +230,7 @@
 
 	/* Print first page of text */
 	attr_clear(box, boxh, boxw, dlg.dialog.atr);
-	refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text,
-			 data);
+	refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
 
 	while (!done) {
 		key = wgetch(dialog);
@@ -259,8 +249,7 @@
 				begin_reached = 1;
 				page = buf;
 				refresh_text_box(dialog, box, boxh, boxw,
-						 cur_y, cur_x, update_text,
-						 data);
+						 cur_y, cur_x);
 			}
 			break;
 		case 'G':	/* Last page */
@@ -270,8 +259,7 @@
 			/* point to last char in buf */
 			page = buf + strlen(buf);
 			back_lines(boxh);
-			refresh_text_box(dialog, box, boxh, boxw, cur_y,
-					 cur_x, update_text, data);
+			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
 			break;
 		case 'K':	/* Previous line */
 		case 'k':
@@ -280,8 +268,7 @@
 				break;
 
 			back_lines(page_length + 1);
-			refresh_text_box(dialog, box, boxh, boxw, cur_y,
-					 cur_x, update_text, data);
+			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
 			break;
 		case 'B':	/* Previous page */
 		case 'b':
@@ -290,8 +277,7 @@
 			if (begin_reached)
 				break;
 			back_lines(page_length + boxh);
-			refresh_text_box(dialog, box, boxh, boxw, cur_y,
-					 cur_x, update_text, data);
+			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
 			break;
 		case 'J':	/* Next line */
 		case 'j':
@@ -300,8 +286,7 @@
 				break;
 
 			back_lines(page_length - 1);
-			refresh_text_box(dialog, box, boxh, boxw, cur_y,
-					 cur_x, update_text, data);
+			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
 			break;
 		case KEY_NPAGE:	/* Next page */
 		case ' ':
@@ -310,8 +295,7 @@
 				break;
 
 			begin_reached = 0;
-			refresh_text_box(dialog, box, boxh, boxw, cur_y,
-					 cur_x, update_text, data);
+			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
 			break;
 		case '0':	/* Beginning of line */
 		case 'H':	/* Scroll left */
@@ -326,8 +310,7 @@
 				hscroll--;
 			/* Reprint current page to scroll horizontally */
 			back_lines(page_length);
-			refresh_text_box(dialog, box, boxh, boxw, cur_y,
-					 cur_x, update_text, data);
+			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
 			break;
 		case 'L':	/* Scroll right */
 		case 'l':
@@ -337,8 +320,7 @@
 			hscroll++;
 			/* Reprint current page to scroll horizontally */
 			back_lines(page_length);
-			refresh_text_box(dialog, box, boxh, boxw, cur_y,
-					 cur_x, update_text, data);
+			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
 			break;
 		case KEY_ESC:
 			if (on_key_esc(dialog) == KEY_ESC)
@@ -351,11 +333,9 @@
 			on_key_resize();
 			goto do_resize;
 		default:
-			for (i = 0; keys[i]; i++) {
-				if (key == keys[i]) {
-					done = true;
-					break;
-				}
+			if (extra_key_cb && extra_key_cb(key, start, end, data)) {
+				done = true;
+				break;
 			}
 		}
 	}
diff --git a/util/kconfig/mconf.c b/util/kconfig/mconf.c
index 07729fd..eccc87a 100644
--- a/util/kconfig/mconf.c
+++ b/util/kconfig/mconf.c
@@ -22,10 +22,6 @@
 #include "lkc.h"
 #include "lxdialog/dialog.h"
 
-#define JUMP_NB			9
-
-int kconfig_warnings = 0;
-
 static const char mconf_readme[] =
 "Overview\n"
 "--------\n"
@@ -290,6 +286,7 @@
 static int show_all_options;
 static int save_and_exit;
 static int silent;
+static int jump_key_char;
 
 static void conf(struct menu *menu, struct menu *active_menu);
 
@@ -350,19 +347,19 @@
 	set_dialog_subtitles(subtitles);
 }
 
-static int show_textbox_ext(const char *title, char *text, int r, int c, int
-			    *keys, int *vscroll, int *hscroll, update_text_fn
-			    update_text, void *data)
+static int show_textbox_ext(const char *title, const char *text, int r, int c,
+			    int *vscroll, int *hscroll,
+			    int (*extra_key_cb)(int, size_t, size_t, void *),
+			    void *data)
 {
 	dialog_clear();
-	return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
-			      update_text, data);
+	return dialog_textbox(title, text, r, c, vscroll, hscroll,
+			      extra_key_cb, data);
 }
 
 static void show_textbox(const char *title, const char *text, int r, int c)
 {
-	show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
-			 NULL, NULL);
+	show_textbox_ext(title, text, r, c, NULL, NULL, NULL, NULL);
 }
 
 static void show_helptext(const char *title, const char *text)
@@ -383,35 +380,54 @@
 
 struct search_data {
 	struct list_head *head;
-	struct menu **targets;
-	int *keys;
+	struct menu *target;
 };
 
-static void update_text(char *buf, size_t start, size_t end, void *_data)
+static int next_jump_key(int key)
+{
+	if (key < '1' || key > '9')
+		return '1';
+
+	key++;
+
+	if (key > '9')
+		key = '1';
+
+	return key;
+}
+
+static int handle_search_keys(int key, size_t start, size_t end, void *_data)
 {
 	struct search_data *data = _data;
 	struct jump_key *pos;
-	int k = 0;
+	int index = 0;
+
+	if (key < '1' || key > '9')
+		return 0;
 
 	list_for_each_entry(pos, data->head, entries) {
-		if (pos->offset >= start && pos->offset < end) {
-			char header[4];
+		index = next_jump_key(index);
 
-			if (k < JUMP_NB) {
-				int key = '0' + (pos->index % JUMP_NB) + 1;
+		if (pos->offset < start)
+			continue;
 
-				sprintf(header, "(%c)", key);
-				data->keys[k] = key;
-				data->targets[k] = pos->target;
-				k++;
-			} else {
-				sprintf(header, "   ");
-			}
+		if (pos->offset >= end)
+			break;
 
-			memcpy(buf + pos->offset, header, sizeof(header) - 1);
+		if (key == index) {
+			data->target = pos->target;
+			return 1;
 		}
 	}
-	data->keys[k] = 0;
+
+	return 0;
+}
+
+int get_jump_key_char(void)
+{
+	jump_key_char = next_jump_key(jump_key_char);
+
+	return jump_key_char;
 }
 
 static void search_conf(void)
@@ -458,26 +474,23 @@
 	sym_arr = sym_re_search(dialog_input);
 	do {
 		LIST_HEAD(head);
-		struct menu *targets[JUMP_NB];
-		int keys[JUMP_NB + 1], i;
 		struct search_data data = {
 			.head = &head,
-			.targets = targets,
-			.keys = keys,
 		};
 		struct jump_key *pos, *tmp;
 
+		jump_key_char = 0;
 		res = get_relations_str(sym_arr, &head);
 		set_subtitle();
 		dres = show_textbox_ext("Search Results", str_get(&res), 0, 0,
-					keys, &vscroll, &hscroll, &update_text,
-					&data);
+					&vscroll, &hscroll,
+					handle_search_keys, &data);
 		again = false;
-		for (i = 0; i < JUMP_NB && keys[i]; i++)
-			if (dres == keys[i]) {
-				conf(targets[i]->parent, targets[i]);
-				again = true;
-			}
+		if (dres >= '1' && dres <= '9') {
+			assert(data.target != NULL);
+			conf(data.target->parent, data.target);
+			again = true;
+		}
 		str_free(&res);
 		list_for_each_entry_safe(pos, tmp, &head, entries)
 			free(pos);
@@ -945,7 +958,6 @@
 static int handle_exit(void)
 {
 	int res;
-	char *env;
 
 	save_and_exit = 1;
 	reset_subtitle();
@@ -960,13 +972,6 @@
 
 	end_dialog(saved_x, saved_y);
 
-	env = getenv("KCONFIG_STRICT");
-	if (env && *env && kconfig_warnings) {
-		fprintf(stderr, "\n*** ERROR: %d warnings encountered, and "
-			"warnings are errors.\n\n", kconfig_warnings);
-		res = 2;
-	}
-
 	switch (res) {
 	case 0:
 		if (conf_write(filename)) {
diff --git a/util/kconfig/menu.c b/util/kconfig/menu.c
index b90fff8..61c442d 100644
--- a/util/kconfig/menu.c
+++ b/util/kconfig/menu.c
@@ -701,6 +701,11 @@
 	}
 }
 
+int __attribute__((weak)) get_jump_key_char(void)
+{
+	return -1;
+}
+
 static void get_prompt_str(struct gstr *r, struct property *prop,
 			   struct list_head *head)
 {
@@ -730,24 +735,27 @@
 	}
 	if (head && location) {
 		jump = xmalloc(sizeof(struct jump_key));
-
 		jump->target = location;
-
-		if (list_empty(head))
-			jump->index = 0;
-		else
-			jump->index = list_entry(head->prev, struct jump_key,
-						 entries)->index + 1;
-
 		list_add_tail(&jump->entries, head);
 	}
 
 	str_printf(r, "  Location:\n");
-	for (j = 4; --i >= 0; j += 2) {
+	for (j = 0; --i >= 0; j++) {
+		int jk = -1;
+		int indent = 2 * j + 4;
+
 		menu = submenu[i];
-		if (jump && menu == location)
+		if (jump && menu == location) {
 			jump->offset = strlen(r->s);
-		str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
+			jk = get_jump_key_char();
+		}
+
+		if (jk >= 0) {
+			str_printf(r, "(%c)", jk);
+			indent -= 3;
+		}
+
+		str_printf(r, "%*c-> %s", indent, ' ', menu_get_prompt(menu));
 		if (menu->sym) {
 			str_printf(r, " (%s [=%s])", menu->sym->name ?
 				menu->sym->name : "<choice>",
diff --git a/util/kconfig/nconf.c b/util/kconfig/nconf.c
index d3afdbe..143a2c3 100644
--- a/util/kconfig/nconf.c
+++ b/util/kconfig/nconf.c
@@ -15,8 +15,6 @@
 #include "nconf.h"
 #include <ctype.h>
 
-int kconfig_warnings = 0;
-
 static const char nconf_global_help[] =
 "Help windows\n"
 "------------\n"
@@ -222,7 +220,7 @@
 "Location:\n"
 "  -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
 "    -> PCI support (PCI [ = y])\n"
-"      -> PCI access mode (<choice> [ = y])\n"
+"(1)   -> PCI access mode (<choice> [ = y])\n"
 "Selects: LIBCRC32\n"
 "Selected by: BAR\n"
 "-----------------------------------------------------------------\n"
@@ -233,9 +231,13 @@
 "o  The 'Depends on:' line lists symbols that need to be defined for\n"
 "   this symbol to be visible and selectable in the menu.\n"
 "o  The 'Location:' lines tell, where in the menu structure this symbol\n"
-"   is located.  A location followed by a [ = y] indicates that this is\n"
-"   a selectable menu item, and the current value is displayed inside\n"
-"   brackets.\n"
+"   is located.\n"
+"     A location followed by a [ = y] indicates that this is\n"
+"     a selectable menu item, and the current value is displayed inside\n"
+"     brackets.\n"
+"     Press the key in the (#) prefix to jump directly to that\n"
+"     location. You will be returned to the current search results\n"
+"     after exiting this new menu.\n"
 "o  The 'Selects:' line tells, what symbol will be automatically selected\n"
 "   if this symbol is selected (y or m).\n"
 "o  The 'Selected by' line tells what symbol has selected this symbol.\n"
@@ -277,7 +279,9 @@
 
 static char *dialog_input_result;
 static int dialog_input_result_len;
+static int jump_key_char;
 
+static void selected_conf(struct menu *menu, struct menu *active_menu);
 static void conf(struct menu *menu);
 static void conf_choice(struct menu *menu);
 static void conf_string(struct menu *menu);
@@ -647,8 +651,6 @@
 static int do_exit(void)
 {
 	int res;
-	char *env;
-
 	if (!conf_get_changed()) {
 		global_exit = 1;
 		return 0;
@@ -664,15 +666,6 @@
 		return -1;
 	}
 
-	env = getenv("KCONFIG_STRICT");
-	if (env && *env && kconfig_warnings) {
-		btn_dialog(main_window,
-			"\nWarnings encountered, and warnings are errors.\n\n",
-			1,
-			"<OK>");
-		res = 2;
-	}
-
 	/* if we got here, the user really wants to exit */
 	switch (res) {
 	case 0:
@@ -698,6 +691,57 @@
 	return 0;
 }
 
+struct search_data {
+	struct list_head *head;
+	struct menu *target;
+};
+
+static int next_jump_key(int key)
+{
+	if (key < '1' || key > '9')
+		return '1';
+
+	key++;
+
+	if (key > '9')
+		key = '1';
+
+	return key;
+}
+
+static int handle_search_keys(int key, size_t start, size_t end, void *_data)
+{
+	struct search_data *data = _data;
+	struct jump_key *pos;
+	int index = 0;
+
+	if (key < '1' || key > '9')
+		return 0;
+
+	list_for_each_entry(pos, data->head, entries) {
+		index = next_jump_key(index);
+
+		if (pos->offset < start)
+			continue;
+
+		if (pos->offset >= end)
+			break;
+
+		if (key == index) {
+			data->target = pos->target;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+int get_jump_key_char(void)
+{
+	jump_key_char = next_jump_key(jump_key_char);
+
+	return jump_key_char;
+}
 
 static void search_conf(void)
 {
@@ -705,7 +749,8 @@
 	struct gstr res;
 	struct gstr title;
 	char *dialog_input;
-	int dres;
+	int dres, vscroll = 0, hscroll = 0;
+	bool again;
 
 	title = str_new();
 	str_printf( &title, "Enter (sub)string or regexp to search for "
@@ -734,11 +779,28 @@
 		dialog_input += strlen(CONFIG_);
 
 	sym_arr = sym_re_search(dialog_input);
-	res = get_relations_str(sym_arr, NULL);
+
+	do {
+		LIST_HEAD(head);
+		struct search_data data = {
+			.head = &head,
+			.target = NULL,
+		};
+		jump_key_char = 0;
+		res = get_relations_str(sym_arr, &head);
+		dres = show_scroll_win_ext(main_window,
+				"Search Results", str_get(&res),
+				&vscroll, &hscroll,
+				handle_search_keys, &data);
+		again = false;
+		if (dres >= '1' && dres <= '9') {
+			assert(data.target != NULL);
+			selected_conf(data.target->parent, data.target);
+			again = true;
+		}
+		str_free(&res);
+	} while (again);
 	free(sym_arr);
-	show_scroll_win(main_window,
-			"Search Results", str_get(&res));
-	str_free(&res);
 	str_free(&title);
 }
 
@@ -1076,9 +1138,14 @@
 
 static void conf(struct menu *menu)
 {
+	selected_conf(menu, NULL);
+}
+
+static void selected_conf(struct menu *menu, struct menu *active_menu)
+{
 	struct menu *submenu = NULL;
 	struct symbol *sym;
-	int res;
+	int i, res;
 	int current_index = 0;
 	int last_top_row = 0;
 	struct match_state match_state = {
@@ -1094,6 +1161,19 @@
 		if (!child_count)
 			break;
 
+		if (active_menu != NULL) {
+			for (i = 0; i < items_num; i++) {
+				struct mitem *mcur;
+
+				mcur = (struct mitem *) item_userptr(curses_menu_items[i]);
+				if ((struct menu *) mcur->usrptr == active_menu) {
+					current_index = i;
+					break;
+				}
+			}
+			active_menu = NULL;
+		}
+
 		show_menu(menu_get_prompt(menu), menu_instructions,
 			  current_index, &last_top_row);
 		keypad((menu_win(curses_menu)), TRUE);
diff --git a/util/kconfig/nconf.gui.c b/util/kconfig/nconf.gui.c
index 9aedf40..25a7263 100644
--- a/util/kconfig/nconf.gui.c
+++ b/util/kconfig/nconf.gui.c
@@ -497,11 +497,18 @@
 	refresh();
 }
 
-/* layman's scrollable window... */
 void show_scroll_win(WINDOW *main_window,
 		const char *title,
 		const char *text)
 {
+	(void)show_scroll_win_ext(main_window, title, (char *)text, NULL, NULL, NULL, NULL);
+}
+
+/* layman's scrollable window... */
+int show_scroll_win_ext(WINDOW *main_window, const char *title, char *text,
+			int *vscroll, int *hscroll,
+			extra_key_cb_fn extra_key_cb, void *data)
+{
 	int res;
 	int total_lines = get_line_no(text);
 	int x, y, lines, columns;
@@ -514,6 +521,12 @@
 	WINDOW *win;
 	WINDOW *pad;
 	PANEL *panel;
+	bool done = false;
+
+	if (hscroll)
+		start_x = *hscroll;
+	if (vscroll)
+		start_y = *vscroll;
 
 	getmaxyx(stdscr, lines, columns);
 
@@ -549,8 +562,7 @@
 	panel = new_panel(win);
 
 	/* handle scrolling */
-	do {
-
+	while (!done) {
 		copywin(pad, win, start_y, start_x, 2, 2, text_lines,
 				text_cols, 0);
 		print_in_middle(win,
@@ -593,8 +605,18 @@
 		case 'l':
 			start_x++;
 			break;
+		default:
+			if (extra_key_cb) {
+				size_t start = (get_line(text, start_y) - text);
+				size_t end = (get_line(text, start_y + text_lines) - text);
+
+				if (extra_key_cb(res, start, end, data)) {
+					done = true;
+					break;
+				}
+			}
 		}
-		if (res == 10 || res == 27 || res == 'q' ||
+		if (res == 0 || res == 10 || res == 27 || res == 'q' ||
 			res == KEY_F(F_HELP) || res == KEY_F(F_BACK) ||
 			res == KEY_F(F_EXIT))
 			break;
@@ -606,9 +628,14 @@
 			start_x = 0;
 		if (start_x >= total_cols-text_cols)
 			start_x = total_cols-text_cols;
-	} while (res);
+	}
 
+	if (hscroll)
+		*hscroll = start_x;
+	if (vscroll)
+		*vscroll = start_y;
 	del_panel(panel);
 	delwin(win);
 	refresh_all_windows(main_window);
+	return res;
 }
diff --git a/util/kconfig/nconf.h b/util/kconfig/nconf.h
index 6f925bc..ab836d5 100644
--- a/util/kconfig/nconf.h
+++ b/util/kconfig/nconf.h
@@ -67,6 +67,8 @@
 
 void set_colors(void);
 
+typedef int (*extra_key_cb_fn)(int, size_t, size_t, void *);
+
 /* this changes the windows attributes !!! */
 void print_in_middle(WINDOW *win, int y, int width, const char *str, int attrs);
 int get_line_length(const char *line);
@@ -78,6 +80,9 @@
 		const char *title, const char *prompt,
 		const char *init, char **resultp, int *result_len);
 void refresh_all_windows(WINDOW *main_window);
+int show_scroll_win_ext(WINDOW *main_window, const char *title, char *text,
+			int *vscroll, int *hscroll,
+			extra_key_cb_fn extra_key_cb, void *data);
 void show_scroll_win(WINDOW *main_window,
 		const char *title,
 		const char *text);
diff --git a/util/kconfig/patches/0001-Kconfig-Add-KCONFIG_STRICT-mode.patch b/util/kconfig/patches/0001-Kconfig-Add-KCONFIG_STRICT-mode.patch
index 9e50eb3..4b1e5bb 100644
--- a/util/kconfig/patches/0001-Kconfig-Add-KCONFIG_STRICT-mode.patch
+++ b/util/kconfig/patches/0001-Kconfig-Add-KCONFIG_STRICT-mode.patch
@@ -1,43 +1,18 @@
-From c822f47921feb53b97f48f3aa8d1e843f5099c63 Mon Sep 17 00:00:00 2001
-From: Stefan Reinauer <stefan.reinauer@coreboot.org>
-Date: Fri, 17 Jul 2015 17:26:48 -0700
-Subject: [PATCH] Kconfig: Add KCONFIG_STRICT mode
-
-This is basically a -Werror mode for Kconfig. When exporting
-KCONFIG_STRICT in the Makefile, warnings in Kconfig will produce
-errors instead.
-
-This will make it easier to spot unclean Kconfig files, settings
-and dependencies.
-
-Signed-off-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
----
- util/kconfig/confdata.c | 8 ++++++++
- 1 file changed, 8 insertions(+)
+Make KCONFIG_WERROR more verbose.
 
 Index: kconfig/confdata.c
 ===================================================================
 --- kconfig.orig/confdata.c
 +++ kconfig/confdata.c
-@@ -437,6 +437,7 @@ load:
- 			if (def == S_DEF_USER) {
- 				sym = sym_find(line + 2 + strlen(CONFIG_));
- 				if (!sym) {
-+					conf_warning("trying to assign non-existent symbol %s", line + strlen(CONFIG_));
- 					conf_set_changed(true);
- 					continue;
- 				}
-@@ -519,6 +520,13 @@ load:
- 	}
+@@ -533,8 +533,10 @@ load:
  	free(line);
  	fclose(in);
-+
-+	name = getenv("KCONFIG_STRICT");
-+	if (name && *name && conf_warnings) {
+ 
+-	if (conf_warnings && werror)
++	if (conf_warnings && werror) {
 +		fprintf(stderr, "\nERROR: %d warnings encountered, and warnings are errors.\n\n", conf_warnings);
-+		return 1;
+ 		exit(1);
 +	}
-+
+ 
  	return 0;
  }
- 
diff --git a/util/kconfig/patches/0002-Kconfig-Change-symbol-override-from-warning-to-notic.patch b/util/kconfig/patches/0002-Kconfig-Change-symbol-override-from-warning-to-notic.patch
index c1ce249..3ad9f1e 100644
--- a/util/kconfig/patches/0002-Kconfig-Change-symbol-override-from-warning-to-notic.patch
+++ b/util/kconfig/patches/0002-Kconfig-Change-symbol-override-from-warning-to-notic.patch
@@ -40,7 +40,7 @@
  static void conf_default_message_callback(const char *s)
  {
  	printf("#\n# ");
-@@ -447,7 +457,7 @@ load:
+@@ -454,7 +464,7 @@ load:
  					sym->type = S_BOOLEAN;
  			}
  			if (sym->flags & def_flags) {
@@ -49,7 +49,7 @@
  			}
  			switch (sym->type) {
  			case S_BOOLEAN:
-@@ -486,7 +496,7 @@ load:
+@@ -498,7 +508,7 @@ load:
  			}
  
  			if (sym->flags & def_flags) {
@@ -58,7 +58,7 @@
  			}
  			if (conf_set_sym_val(sym, def, def_flags, p))
  				continue;
-@@ -511,7 +521,7 @@ load:
+@@ -523,7 +533,7 @@ load:
  				break;
  			case yes:
  				if (cs->def[def].tri != no)
diff --git a/util/kconfig/patches/0005-util-kconfig-Ignore-extra-symbols-in-configs-instead.patch b/util/kconfig/patches/0005-util-kconfig-Ignore-extra-symbols-in-configs-instead.patch
deleted file mode 100644
index c05e4a6..0000000
--- a/util/kconfig/patches/0005-util-kconfig-Ignore-extra-symbols-in-configs-instead.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From 2796443d5a2194400e56e6762e0f748ed0f0470c Mon Sep 17 00:00:00 2001
-From: Martin Roth <martinroth@google.com>
-Date: Wed, 10 Feb 2016 16:06:00 -0700
-Subject: [PATCH] util/kconfig: Ignore extra symbols in configs instead of
- failing
-
-When updating an old .config file that has a symbol that has been
-removed from the current Kconfig tree, kconfig will generate a warning
-and fail to save the updated file.  This is incredibly annoying, and
-not the goal when trying to eliminate Kconfig warnings.
-
-Instead of generating a warning, just print a message that it's being
-ignored.  This will remove the offending symbol, while allowing the
-updated config file to be saved.
-
-Split the change from 1 line to 3 lines to keep it at 80 characters.
-
-Signed-off-by: Martin Roth <martinroth@google.com>
----
- util/kconfig/confdata.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-Index: kconfig/confdata.c
-===================================================================
---- kconfig.orig/confdata.c
-+++ kconfig/confdata.c
-@@ -447,7 +447,9 @@ load:
- 			if (def == S_DEF_USER) {
- 				sym = sym_find(line + 2 + strlen(CONFIG_));
- 				if (!sym) {
--					conf_warning("trying to assign non-existent symbol %s", line + strlen(CONFIG_));
-+					conf_message(
-+						"ignoring nonexistent symbol %s",
-+						line + 2 + strlen(CONFIG_));
- 					conf_set_changed(true);
- 					continue;
- 				}
diff --git a/util/kconfig/patches/0007-kconfig-Allow-KCONFIG_STRICT-outside-of-confdata.c.patch b/util/kconfig/patches/0007-kconfig-Allow-KCONFIG_STRICT-outside-of-confdata.c.patch
index 8677be4..f98729e 100644
--- a/util/kconfig/patches/0007-kconfig-Allow-KCONFIG_STRICT-outside-of-confdata.c.patch
+++ b/util/kconfig/patches/0007-kconfig-Allow-KCONFIG_STRICT-outside-of-confdata.c.patch
@@ -1,189 +1,71 @@
-From af6c23be63d14860c8c1f0d9fcbc020f7c11d84d Mon Sep 17 00:00:00 2001
-From: Stefan Reinauer <reinauer@chromium.org>
-Date: Thu, 20 Aug 2015 11:19:34 -0700
-Subject: [PATCH] kconfig: Allow KCONFIG_STRICT outside of confdata.c
+From:   Sergey Senozhatsky <senozhatsky@chromium.org>
+To:     Masahiro Yamada <masahiroy@kernel.org>
+Cc:     Patrick Georgi <pgeorgi@google.com>, linux-kbuild@vger.kernel.org,
+        linux-kernel@vger.kernel.org,
+        Sergey Senozhatsky <senozhatsky@chromium.org>,
+        Stefan Reinauer <reinauer@google.com>
+Subject: [PATCH] kconfig: WERROR unmet symbol dependency
+Date:   Wed, 22 Nov 2023 12:47:45 +0900
+Message-ID: <20231122034753.1446513-1-senozhatsky@chromium.org>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
+List-ID: <linux-kernel.vger.kernel.org>
+X-Mailing-List: linux-kernel@vger.kernel.org
 
-To catch dependency errors in symbol.c (such as the ones
-fixed by I51b4ee326f082c6a656a813ee5772e9c34f5c343) we need
-to check for global kconfig warnings before saving config
-files.
+When KCONFIG_WERROR env variable is set treat unmet direct
+symbol dependency as a terminal condition (error).
 
-This patch will produce errors for wrong dependencies and
-add catching of errors to conf, nconf and mconf. Sorry,
-gconf users, you will have to wait.
-
-Signed-off-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
+Suggested-by: Stefan Reinauer <reinauer@google.com>
+Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
 ---
- util/kconfig/conf.c     | 10 ++++++++++
- util/kconfig/confdata.c |  6 +-----
- util/kconfig/lkc.h      |  3 +++
- util/kconfig/mconf.c    | 10 ++++++++++
- util/kconfig/nconf.c    | 13 +++++++++++++
- util/kconfig/qconf.cc   |  2 ++
- util/kconfig/symbol.c   |  1 +
- 7 files changed, 40 insertions(+), 5 deletions(-)
+ scripts/kconfig/symbol.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
 
-Index: kconfig/conf.c
-===================================================================
---- kconfig.orig/conf.c
-+++ kconfig/conf.c
-@@ -16,6 +16,8 @@
- 
- #include "lkc.h"
- 
-+int kconfig_warnings = 0;
-+
- static void conf(struct menu *menu);
- static void check_conf(struct menu *menu);
- 
-@@ -720,6 +722,7 @@ int main(int ac, char **av)
- 	const char *progname = av[0];
- 	int opt;
- 	const char *name, *defconfig_file = NULL /* gcc uninit */;
-+	char *env;
- 	int no_conf_write = 0;
- 
- 	tty_stdio = isatty(0) && isatty(1);
-@@ -827,6 +830,13 @@ int main(int ac, char **av)
- 		break;
- 	}
- 
-+	env = getenv("KCONFIG_STRICT");
-+	if (env && *env && kconfig_warnings) {
-+		fprintf(stderr, "\n*** ERROR: %d warnings encountered, and "
-+			"warnings are errors.\n\n", kconfig_warnings);
-+		exit(1);
-+	}
-+
- 	if (sync_kconfig) {
- 		name = getenv("KCONFIG_NOSILENTUPDATE");
- 		if (name && *name) {
-Index: kconfig/confdata.c
-===================================================================
---- kconfig.orig/confdata.c
-+++ kconfig/confdata.c
-@@ -537,11 +537,7 @@ load:
- 	free(line);
- 	fclose(in);
- 
--	name = getenv("KCONFIG_STRICT");
--	if (name && *name && conf_warnings) {
--		fprintf(stderr, "\nERROR: %d warnings encountered, and warnings are errors.\n\n", conf_warnings);
--		return 1;
--	}
-+	kconfig_warnings += conf_warnings;
- 
- 	return 0;
- }
-Index: kconfig/lkc.h
-===================================================================
---- kconfig.orig/lkc.h
-+++ kconfig/lkc.h
-@@ -39,6 +39,9 @@ void zconf_nextfile(const char *name);
- int zconf_lineno(void);
- const char *zconf_curname(void);
- 
-+/* conf.c */
-+extern int kconfig_warnings;
-+
- /* confdata.c */
- const char *conf_get_configname(void);
- void set_all_choice_values(struct symbol *csym);
-Index: kconfig/mconf.c
-===================================================================
---- kconfig.orig/mconf.c
-+++ kconfig/mconf.c
-@@ -24,6 +24,8 @@
- 
- #define JUMP_NB			9
- 
-+int kconfig_warnings = 0;
-+
- static const char mconf_readme[] =
- "Overview\n"
- "--------\n"
-@@ -943,6 +945,7 @@ static void conf_message_callback(const
- static int handle_exit(void)
- {
- 	int res;
-+	char *env;
- 
- 	save_and_exit = 1;
- 	reset_subtitle();
-@@ -957,6 +960,13 @@ static int handle_exit(void)
- 
- 	end_dialog(saved_x, saved_y);
- 
-+	env = getenv("KCONFIG_STRICT");
-+	if (env && *env && kconfig_warnings) {
-+		fprintf(stderr, "\n*** ERROR: %d warnings encountered, and "
-+			"warnings are errors.\n\n", kconfig_warnings);
-+		res = 2;
-+	}
-+
- 	switch (res) {
- 	case 0:
- 		if (conf_write(filename)) {
-Index: kconfig/nconf.c
-===================================================================
---- kconfig.orig/nconf.c
-+++ kconfig/nconf.c
-@@ -15,6 +15,8 @@
- #include "nconf.h"
- #include <ctype.h>
- 
-+int kconfig_warnings = 0;
-+
- static const char nconf_global_help[] =
- "Help windows\n"
- "------------\n"
-@@ -645,6 +647,8 @@ static void set_config_filename(const ch
- static int do_exit(void)
- {
- 	int res;
-+	char *env;
-+
- 	if (!conf_get_changed()) {
- 		global_exit = 1;
- 		return 0;
-@@ -660,6 +664,15 @@ static int do_exit(void)
- 		return -1;
- 	}
- 
-+	env = getenv("KCONFIG_STRICT");
-+	if (env && *env && kconfig_warnings) {
-+		btn_dialog(main_window,
-+			"\nWarnings encountered, and warnings are errors.\n\n",
-+			1,
-+			"<OK>");
-+		res = 2;
-+	}
-+
- 	/* if we got here, the user really wants to exit */
- 	switch (res) {
- 	case 0:
-Index: kconfig/qconf.cc
-===================================================================
---- kconfig.orig/qconf.cc
-+++ kconfig/qconf.cc
-@@ -26,6 +26,8 @@
- #include "images.h"
- 
- 
-+int kconfig_warnings = 0;
-+
- static QApplication *configApp;
- static ConfigSettings *configSettings;
- 
 Index: kconfig/symbol.c
 ===================================================================
 --- kconfig.orig/symbol.c
 +++ kconfig/symbol.c
-@@ -319,6 +319,7 @@ static void sym_warn_unmet_dep(struct sy
+@@ -37,6 +37,7 @@ static struct symbol symbol_empty = {
+
+ struct symbol *modules_sym;
+ static tristate modules_val;
++static int sym_warnings;
+
+ enum symbol_type sym_get_type(struct symbol *sym)
+ {
+@@ -319,12 +320,14 @@ static void sym_warn_unmet_dep(struct sy
  			       "  Selected by [m]:\n");
- 
+
  	fputs(str_get(&gs), stderr);
-+	kconfig_warnings++;
++	sym_warnings++;
  }
- 
+
  void sym_calc_value(struct symbol *sym)
+ {
+ 	struct symbol_value newval, oldval;
+ 	struct property *prop;
++	const char *werror;
+ 	struct expr *e;
+
+ 	if (!sym)
+@@ -340,8 +343,9 @@ void sym_calc_value(struct symbol *sym)
+ 		sym_calc_value(prop_get_symbol(prop));
+ 	}
+
++	werror = getenv("KCONFIG_WERROR");
++	sym_warnings = 0;
+ 	sym->flags |= SYMBOL_VALID;
+-
+ 	oldval = sym->curr;
+
+ 	switch (sym->type) {
+@@ -432,6 +436,9 @@ void sym_calc_value(struct symbol *sym)
+ 		;
+ 	}
+
++	if (sym_warnings && werror)
++		exit(1);
++
+ 	sym->curr = newval;
+ 	if (sym_is_choice(sym) && newval.tri == yes)
+ 		sym->curr.val = sym_calc_choice(sym);
diff --git a/util/kconfig/patches/0009-util-kconfig-Allow-emitting-false-booleans-into-kconfig-output.patch b/util/kconfig/patches/0009-util-kconfig-Allow-emitting-false-booleans-into-kconfig-output.patch
index bfb89f9..101bc69 100644
--- a/util/kconfig/patches/0009-util-kconfig-Allow-emitting-false-booleans-into-kconfig-output.patch
+++ b/util/kconfig/patches/0009-util-kconfig-Allow-emitting-false-booleans-into-kconfig-output.patch
@@ -14,7 +14,7 @@
 ===================================================================
 --- kconfig.orig/confdata.c
 +++ kconfig/confdata.c
-@@ -725,7 +725,12 @@ static void print_symbol_for_dotconfig(F
+@@ -738,7 +738,12 @@ static void print_symbol_for_dotconfig(F
  
  static void print_symbol_for_autoconf(FILE *fp, struct symbol *sym)
  {
@@ -28,7 +28,7 @@
  }
  
  void print_symbol_for_listconfig(struct symbol *sym)
-@@ -750,6 +755,10 @@ static void print_symbol_for_c(FILE *fp,
+@@ -763,6 +768,10 @@ static void print_symbol_for_c(FILE *fp,
  	case S_TRISTATE:
  		switch (*val) {
  		case 'n':
@@ -39,7 +39,7 @@
  			return;
  		case 'm':
  			sym_suffix = "_MODULE";
-@@ -761,6 +770,12 @@ static void print_symbol_for_c(FILE *fp,
+@@ -774,6 +783,12 @@ static void print_symbol_for_c(FILE *fp,
  	case S_HEX:
  		if (val[0] != '0' || (val[1] != 'x' && val[1] != 'X'))
  			val_prefix = "0x";
@@ -52,7 +52,7 @@
  		break;
  	case S_STRING:
  		escaped = escape_string_value(val);
-@@ -1177,8 +1192,9 @@ static int __conf_write_autoconf(const c
+@@ -1190,8 +1205,9 @@ static int __conf_write_autoconf(const c
  
  	conf_write_heading(file, comment_style);
  
diff --git a/util/kconfig/patches/0014-util-kconfig-Move-Kconfig-deps-back-into-build-confi.patch b/util/kconfig/patches/0014-util-kconfig-Move-Kconfig-deps-back-into-build-confi.patch
index 09079cd..54a79c3 100644
--- a/util/kconfig/patches/0014-util-kconfig-Move-Kconfig-deps-back-into-build-confi.patch
+++ b/util/kconfig/patches/0014-util-kconfig-Move-Kconfig-deps-back-into-build-confi.patch
@@ -35,7 +35,7 @@
  static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
  {
  	char *p2;
-@@ -1093,19 +1100,19 @@ static int conf_write_autoconf_cmd(const
+@@ -1106,19 +1113,19 @@ static int conf_write_autoconf_cmd(const
  
  static int conf_touch_deps(void)
  {
diff --git a/util/kconfig/patches/series b/util/kconfig/patches/series
index 655c493..48c72895 100644
--- a/util/kconfig/patches/series
+++ b/util/kconfig/patches/series
@@ -2,7 +2,6 @@
 0002-Kconfig-Change-symbol-override-from-warning-to-notic.patch
 0003-util-kconfig-conf.c-Fix-newline-in-error-printf.patch
 0004-src-util-Use-NULL-instead-of-0-for-pointer.patch
-0005-util-kconfig-Ignore-extra-symbols-in-configs-instead.patch
 0006-util-kconfig-Set-parameter-of-mkdir-to-only-one-for-.patch
 0007-kconfig-Allow-KCONFIG_STRICT-outside-of-confdata.c.patch
 0008-kconfig-Add-wildcard-support-for-source.patch
diff --git a/util/kconfig/preprocess.c b/util/kconfig/preprocess.c
index 748da578..d1f5bcf 100644
--- a/util/kconfig/preprocess.c
+++ b/util/kconfig/preprocess.c
@@ -396,6 +396,9 @@
 
 		p++;
 	}
+
+	if (new_argc >= FUNCTION_MAX_ARGS)
+		pperror("too many function arguments");
 	new_argv[new_argc++] = prev;
 
 	/*
diff --git a/util/kconfig/qconf-cfg.sh b/util/kconfig/qconf-cfg.sh
index 117f36e..0e113b0 100755
--- a/util/kconfig/qconf-cfg.sh
+++ b/util/kconfig/qconf-cfg.sh
@@ -5,7 +5,8 @@
 libs=$2
 bin=$3
 
-PKG="Qt5Core Qt5Gui Qt5Widgets"
+PKG5="Qt5Core Qt5Gui Qt5Widgets"
+PKG6="Qt6Core Qt6Gui Qt6Widgets"
 
 if [ -z "$(command -v ${HOSTPKG_CONFIG})" ]; then
 	echo >&2 "*"
@@ -14,16 +15,26 @@
 	exit 1
 fi
 
-if ${HOSTPKG_CONFIG} --exists $PKG; then
-	${HOSTPKG_CONFIG} --cflags ${PKG} > ${cflags}
-	${HOSTPKG_CONFIG} --libs ${PKG} > ${libs}
+if ${HOSTPKG_CONFIG} --exists $PKG6; then
+	${HOSTPKG_CONFIG} --cflags ${PKG6} > ${cflags}
+	# Qt6 requires C++17.
+	echo -std=c++17 >> ${cflags}
+	${HOSTPKG_CONFIG} --libs ${PKG6} > ${libs}
+	${HOSTPKG_CONFIG} --variable=libexecdir Qt6Core > ${bin}
+	exit 0
+fi
+
+if ${HOSTPKG_CONFIG} --exists $PKG5; then
+	${HOSTPKG_CONFIG} --cflags ${PKG5} > ${cflags}
+	${HOSTPKG_CONFIG} --libs ${PKG5} > ${libs}
 	${HOSTPKG_CONFIG} --variable=host_bins Qt5Core > ${bin}
 	exit 0
 fi
 
 echo >&2 "*"
-echo >&2 "* Could not find Qt5 via ${HOSTPKG_CONFIG}."
-echo >&2 "* Please install Qt5 and make sure it's in PKG_CONFIG_PATH"
-echo >&2 "* You need $PKG"
+echo >&2 "* Could not find Qt6 or Qt5 via ${HOSTPKG_CONFIG}."
+echo >&2 "* Please install Qt6 or Qt5 and make sure it's in PKG_CONFIG_PATH"
+echo >&2 "* You need $PKG6 for Qt6"
+echo >&2 "* You need $PKG5 for Qt5"
 echo >&2 "*"
 exit 1
diff --git a/util/kconfig/qconf.cc b/util/kconfig/qconf.cc
index e219df9..620a352 100644
--- a/util/kconfig/qconf.cc
+++ b/util/kconfig/qconf.cc
@@ -5,10 +5,10 @@
  */
 
 #include <QAction>
+#include <QActionGroup>
 #include <QApplication>
 #include <QCloseEvent>
 #include <QDebug>
-#include <QDesktopWidget>
 #include <QFileDialog>
 #include <QLabel>
 #include <QLayout>
@@ -16,6 +16,8 @@
 #include <QMenu>
 #include <QMenuBar>
 #include <QMessageBox>
+#include <QRegularExpression>
+#include <QScreen>
 #include <QToolBar>
 
 #include <stdlib.h>
@@ -26,8 +28,6 @@
 #include "images.h"
 
 
-int kconfig_warnings = 0;
-
 static QApplication *configApp;
 static ConfigSettings *configSettings;
 
@@ -1128,7 +1128,7 @@
 
 QString ConfigInfoView::print_filter(const QString &str)
 {
-	QRegExp re("[<>&\"\\n]");
+	QRegularExpression re("[<>&\"\\n]");
 	QString res = str;
 	for (int i = 0; (i = res.indexOf(re, i)) >= 0;) {
 		switch (res[i].toLatin1()) {
@@ -1324,15 +1324,15 @@
 	int width, height;
 	char title[256];
 
-	QDesktopWidget *d = configApp->desktop();
 	snprintf(title, sizeof(title), "%s%s",
 		rootmenu.prompt->text,
 		""
 		);
 	setWindowTitle(title);
 
-	width = configSettings->value("/window width", d->width() - 64).toInt();
-	height = configSettings->value("/window height", d->height() - 64).toInt();
+	QRect g = configApp->primaryScreen()->geometry();
+	width = configSettings->value("/window width", g.width() - 64).toInt();
+	height = configSettings->value("/window height", g.height() - 64).toInt();
 	resize(width, height);
 	x = configSettings->value("/window x");
 	y = configSettings->value("/window y");
@@ -1381,17 +1381,17 @@
 		this, &ConfigMainWindow::goBack);
 
 	QAction *quitAction = new QAction("&Quit", this);
-	quitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
+	quitAction->setShortcut(Qt::CTRL | Qt::Key_Q);
 	connect(quitAction, &QAction::triggered,
 		this, &ConfigMainWindow::close);
 
 	QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
-	loadAction->setShortcut(Qt::CTRL + Qt::Key_L);
+	loadAction->setShortcut(Qt::CTRL | Qt::Key_L);
 	connect(loadAction, &QAction::triggered,
 		this, &ConfigMainWindow::loadConfig);
 
 	saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
-	saveAction->setShortcut(Qt::CTRL + Qt::Key_S);
+	saveAction->setShortcut(Qt::CTRL | Qt::Key_S);
 	connect(saveAction, &QAction::triggered,
 		this, &ConfigMainWindow::saveConfig);
 
@@ -1405,7 +1405,7 @@
 	connect(saveAsAction, &QAction::triggered,
 		this, &ConfigMainWindow::saveConfigAs);
 	QAction *searchAction = new QAction("&Find", this);
-	searchAction->setShortcut(Qt::CTRL + Qt::Key_F);
+	searchAction->setShortcut(Qt::CTRL | Qt::Key_F);
 	connect(searchAction, &QAction::triggered,
 		this, &ConfigMainWindow::searchConfig);
 	singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
@@ -1752,11 +1752,21 @@
 		e->accept();
 		return;
 	}
-	QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
-			QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
-	mb.setButtonText(QMessageBox::Yes, "&Save Changes");
-	mb.setButtonText(QMessageBox::No, "&Discard Changes");
-	mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
+
+	QMessageBox mb(QMessageBox::Icon::Warning, "qconf",
+		       "Save configuration?");
+
+	QPushButton *yb = mb.addButton(QMessageBox::Yes);
+	QPushButton *db = mb.addButton(QMessageBox::No);
+	QPushButton *cb = mb.addButton(QMessageBox::Cancel);
+
+	yb->setText("&Save Changes");
+	db->setText("&Discard Changes");
+	cb->setText("Cancel Exit");
+
+	mb.setDefaultButton(yb);
+	mb.setEscapeButton(cb);
+
 	switch (mb.exec()) {
 	case QMessageBox::Yes:
 		if (saveConfig())
diff --git a/util/kconfig/symbol.c b/util/kconfig/symbol.c
index 6cbbac4..a50f131 100644
--- a/util/kconfig/symbol.c
+++ b/util/kconfig/symbol.c
@@ -37,6 +37,7 @@
 
 struct symbol *modules_sym;
 static tristate modules_val;
+static int sym_warnings;
 
 enum symbol_type sym_get_type(struct symbol *sym)
 {
@@ -319,13 +320,14 @@
 			       "  Selected by [m]:\n");
 
 	fputs(str_get(&gs), stderr);
-	kconfig_warnings++;
+	sym_warnings++;
 }
 
 void sym_calc_value(struct symbol *sym)
 {
 	struct symbol_value newval, oldval;
 	struct property *prop;
+	const char *werror;
 	struct expr *e;
 
 	if (!sym)
@@ -341,8 +343,9 @@
 		sym_calc_value(prop_get_symbol(prop));
 	}
 
+	werror = getenv("KCONFIG_WERROR");
+	sym_warnings = 0;
 	sym->flags |= SYMBOL_VALID;
-
 	oldval = sym->curr;
 
 	switch (sym->type) {
@@ -433,6 +436,9 @@
 		;
 	}
 
+	if (sym_warnings && werror)
+		exit(1);
+
 	sym->curr = newval;
 	if (sym_is_choice(sym) && newval.tri == yes)
 		sym->curr.val = sym_calc_choice(sym);